Could do with some help with IF ... ELSEIF commands please

abenn

Senior Member
I wish to operate a pair of standard servos (operating some points on a model railway layout) in accordance with the inputs from two simple on/off toggle switches. There are three required combinations of the two servo positions -- one pulling while the other is pushing, one pushing while the other is pulling, and both pulling.

The two switches are connected to pins 3 and 4 of a PICAXE 08M2 via standard resistor dividers, and the two servos' signal leads are connected directly to pins c.1 and c.2. I've been using circuits based on the same principle to operate single servos with a single switch for some time now with no problems, but I can't get this particular program to work properly for two switches and two servos.

In this circuit the switch on pin4 is supposed to override the switch on pin3 when it's closed, and when it's open the switch on pin3 is supposed to operate the servos, but what was happening is the servos moved from pull/push to push/pull in response to pin4 (i.e. they're switching from "mainline" to "crossline"), but the switch on pin3 has no effect, and I'm getting myself in such a twist that right now when I power up the device one servo cycles back and forth continuously between the min_pulse and max_pulse limits while the other remains stationary, and neither switch has any effect!

Here's the code as it stands at the moment, if anyone is willing to help me out:

Code:
symbol position_A = b4	; servo A current position
symbol position_B = b5	; servo B current position
symbol min_pulse = b6
symbol max_pulse = b7
symbol servo_slow = b8
symbol set_route = b9	; flag to denote which route is set -- 1=main route 2=cross 3=slip
symbol whereis_A = b10	; flag to denote servo resting position -- 1=pushed 2=pulled
symbol whereis_B = b11	; flag to denote servo resting position -- 1=pushed 2=pulled

let position_A = 0
let position_B = 0
let whereis_A = 0
let whereis_B = 0
let min_pulse = 125	; change this value to alter left throw limit
let max_pulse = 175	; change this value to alter right throw limit
let servo_slow = 20	; increase this value to slow down the servos

				; Input switches are on pin3 and pin4
				; Servos are on c.1 and c.2

read b0,set_route

main:

	  if pin4 = 1 and set_route<>2 then 
	    goto crossline
	  elseif pin3 = 0 and pin4=0 and set_route<>1 then
	    goto mainline
	  elseif pin3 = 1 and pin4=0 and set_route<>3 then
	    goto slipline
	  endif
	  goto main
	
crossline:

	if whereis_A<>2 then
	  gosub pull_A
	endif
	if whereis_B<>1 then
	  gosub push_B
	endif
	let set_route=2
	write b0,set_route
	goto main

mainline:

	if whereis_A<>1 then
	  gosub push_A
	endif
	if whereis_B<>2 then
	  gosub pull_B
	endif
	let set_route=1
	write b0,set_route
	goto main

slipline:

	if whereis_A<>2 then
	  gosub pull_A
	endif
	if whereis_B<>2 then
	  gosub pull_B
	endif
	let set_route=3
	write b0,set_route
	goto main


pull_A:

	let position_A=min_pulse
	do 
	  servo c.1,position_A
	  inc position_A
	  pause servo_slow
	loop while b4<max_pulse
	let position_A=max_pulse
	let whereis_A=2
	pause 100
	servo c.1,OFF
	
	return
	
push_A:

	let position_A=max_pulse
	do
	  servo c.1,position_A
	  dec position_A
	  pause servo_slow
	loop while position_A>min_pulse
	let position_A=min_pulse
	let whereis_A=1
	pause 100
	servo c.1,OFF
	
	return

pull_B:

	let position_B=min_pulse
	do 
	  servo c.2,position_B
	  inc position_B
	  pause servo_slow
	loop while position_B<max_pulse
	let position_B=max_pulse
	let whereis_B=2
	pause 100
	servo c.2,OFF
	
	return
	
push_B:

	let position_B=max_pulse
	do
	  servo c.2,position_B
	  dec position_B
	  pause servo_slow
	loop while position_B>min_pulse
	let position_B=min_pulse
	let whereis_B=1
	pause 100
	servo c.2,OFF
	
	return
 

Technical

Technical Support
Staff member
Your read and writes don't look correct, by using a variable as the 'address' this address can then change depending on what the current state is. We think you probably want a static write/read address.
 

abenn

Senior Member
OK, so I haven't understood the concept of the read and write. I thought, from a post in this forum, and from the PICAXE instruction manual, that "write" was the way to put something into the EEPROM so that it could be then read. b0 is what they use as the address in the example in the manual, so that's why I used it.

So, how do I create, or access, a static read/write address please? Each time I set a route I want to save it (as "1", "2", or "3") into memory so that it can be read when I re-power the system after powering down and during operation, so that it knows which route is already set and doesn't unnecessarily cycle the servos when a switch is thrown.
 

westaust55

Moderator
Not sure that I am solving your problem but to help in reducing the number of IF...THEN tests in the main loop (to none required) I have massaged you code as follows.
This also takes into account the comment by Technical above.

Code:
symbol position_A = b4	; servo A current position
symbol position_B = b5	; servo B current position
symbol min_pulse = b6
symbol max_pulse = b7
symbol servo_slow = b8
symbol set_route = b9	; flag to denote which route is set -- 1=main route 2=cross 3=slip
symbol whereis_A = b10	; flag to denote servo resting position -- 1=pushed 2=pulled
symbol whereis_B = b11	; flag to denote servo resting position -- 1=pushed 2=pulled

SYMBOL Route_Select = b12

SYMBOL EEPROM_Route_Save = 1 ; use EEPROM location 1 to hold current selected route

Initialise:

	let position_A = 0
	let position_B = 0
	let whereis_A = 0
	let whereis_B = 0
	let min_pulse = 125	; change this value to alter left throw limit
	let max_pulse = 175	; change this value to alter right throw limit
	let servo_slow = 20	; increase this value to slow down the servos

				; Input switches for route selection are on pinC.3 and pinC.4
				; Servos are on C.1 and C.2
read b0,set_route

Main:

	Route_Select = pinsC AND %00011000 / 8 ; Fetch the state of the two switches - value range 0 to 3

	ON Route_Select GOSUB Mainline, Slipline,Crossline ; falls through to the GOTO Main if both C.3 and C.4 are high (=1)
	GOTO Main



Mainline:
	IF set_route = 1 THEN RETURN : ENDIF
	IF whereis_A <>1 THEN gosub push_A
	IF whereis_B <>2 THEN gosub pull_B
	set_route = 1
	WRITE EEPROM_Route_Save, set_route
	RETURN
	
	
Crossline:
	IF set_route = 2 THEN RETURN : ENDIF
	IF whereis_A <> 2 THEN gosub pull_A
	IF whereis_B <> 1 THEN gosub push_B
	set_route = 2
	WRITE EEPROM_Route_Save, set_route
	RETURN

Slipline:
	IF set_route = 3 THEN RETURN : ENDIF
	IF whereis_A<>2 then gosub pull_A
	IF whereis_B<>2 then gosub pull_B
	set_route = 3
	WRITE EEPROM_Route_Save, set_route
	RETURN


pull_A:

	position_A = min_pulse
	do 
	  servo c.1,position_A
	  inc position_A
	  pause servo_slow
	loop while b4 < max_pulse
	position_A = max_pulse
	whereis_A=2
	pause 100
	servo c.1,OFF
	RETURN
	
push_A:

	let position_A=max_pulse
	do
	  servo c.1,position_A
	  dec position_A
	  pause servo_slow
	loop while position_A>min_pulse
	let position_A=min_pulse
	let whereis_A = 1
	pause 100
	servo c.1,OFF
	RETURN

pull_B:

	let position_B=min_pulse
	do 
	  servo c.2,position_B
	  inc position_B
	  pause servo_slow
	loop while position_B<max_pulse
	position_B = max_pulse
	whereis_B = 2
	pause 100
	servo c.2,OFF
	RETURN
	
push_B:

	let position_B=max_pulse
	do
	  servo c.2,position_B
	  dec position_B
	  pause servo_slow
	loop while position_B>min_pulse
	position_B = min_pulse
	whereis_B = 1
	pause 100
	servo c.2,OFF
	RETURN

Some further comments:
1. EEPROM has a life of 1,000,000+ writes. However, even selecting a new route every 10 minutes for a 3 hour running session per day the EEPROM should not fail for around 50,000 days/sessions.

2. From a model railway (and prototype) perspective, to my knowledge the terms "push" and "pull" do not mean much for point control.
Terms such as "Straight", "Close" or "Normal" are often used for the straight route and terms "Turn", "Reverse" for the diverging/turning route including cross-overs
Using these will help others and maybe you also in future to quickly know which direction the point/slip is being set.
 

abenn

Senior Member
Thanks for your time and comments westaust55. I come from a railway background myself, but used the terms "push" and "pull" to help me visualise which way the servo arms needed to move, bearing in mind their positions relative to the points. As you've probably surmised, the particular situation I'm working on is a single slip crossing, where there are three possible routes to be set.

As ever, your coding has helped me a great deal. In my previous thread, a couple of weeks ago, I learned from a practical example how to assign meaningful names to variables using the "symbol" command. Today I've got to get my head around your "Route_select = pinsC AND %00011000 / 8" line, but I'm sure I'll get there in the end.

Unfortunately, I've loaded your amendments into my PICAXE, but it still doesn't respond to the switches correctly. But at least it's no longer cycling one of the servos back and forth!

I was aware, by the way, of the limited life of the EEPROM, though the figure of 100,000 seemed to be most often quoted. I'll never get to that limit in my lifetime of running my railway!
 

BESQUEUT

Senior Member
a couple of weeks ago, I learned from a practical example how to assign meaningful names to variables using the "symbol" command. ...
You can use it a little more...
To my mind this kind of comment is useless
Code:
symbol set_route = b9	; flag to denote which route is set -- 1=main route 2=cross 3=slip
symbol whereis_A = b10	; flag to denote servo resting position -- 1=pushed 2=pulled
symbol whereis_B = b11	; flag to denote servo resting position -- 1=pushed 2=pulled
I prefer to write :
Code:
symbol main_route=1
symbol cross=2
symbol slip=3
symbol pushed=1
symbol pulled=2
So I can write something like :
Code:
[color=Black]main:

        [/color][color=Blue]if [/color][color=Purple]pin4 [/color][color=DarkCyan]= [/color][color=Navy]1 [/color][color=DarkCyan]and [/color][color=Purple]set_route[/color][color=DarkCyan]<>[/color][color=Black]cross [/color][color=Blue]then 
          goto [/color][color=Black]crossline
        [/color][color=Blue]elseif [/color][color=Purple]pin3 [/color][color=DarkCyan]= [/color][color=Navy]0 [/color][color=DarkCyan]and [/color][color=Purple]pin4[/color][color=DarkCyan]=[/color][color=Navy]0 [/color][color=DarkCyan]and [/color][color=Purple]set_route[/color][color=DarkCyan]<>[/color][color=Black]main_route [/color][color=Blue]then
          goto [/color][color=Black]mainline
        [/color][color=Blue]elseif [/color][color=Purple]pin3 [/color][color=DarkCyan]= [/color][color=Navy]1 [/color][color=DarkCyan]and [/color][color=Purple]pin4[/color][color=DarkCyan]=[/color][color=Navy]0 [/color][color=DarkCyan]and [/color][color=Purple]set_route[/color][color=DarkCyan]<>[/color][color=Black]slip [/color][color=Blue]then
          goto [/color][color=Black]slipline
        [/color][color=Blue]endif
        goto [/color][color=Black]main
      
crossline:

      [/color][color=Blue]if [/color][color=Purple]whereis_A[/color][color=DarkCyan]<>[/color][color=Black]pulled [/color][color=Blue]then
        gosub [/color][color=Black]pull_A
      [/color][color=Blue]endif
      if [/color][color=Purple]whereis_B[/color][color=DarkCyan]<>[/color][color=Black]pushed [/color][color=Blue]then
        gosub [/color][color=Black]push_B
      [/color][color=Blue]endif
      let [/color][color=Purple]set_route[/color][color=DarkCyan]=[/color][color=Black]cross
      [/color][color=Blue]write [/color][color=Purple]b0[/color][color=Black],[/color][color=Purple]set_route
      [/color][color=Blue]goto [/color][color=Black]main[/color]
Note that code size is inchanged.
 

abenn

Senior Member
Thanks BESQUEUT, I'm always happy to learn from more-experienced programmers. From my earlier thread I'd understood that user-defined variable names always have to be "linked" to the standard b1, b2, etc. PICAXE variable names before they could be used. The syntax I used certainly made things easier for me to follow, but yours makes it even easier.

Westaus55 (or anyone else), I understand your ON Route_Select GOSUB Mainline, Slipline,Crossline code, but I still can't get my head around Route_Select = pinsC AND %00011000 / 8. I understand that the "1"s in the binary string are signifying that it's to read c.3 and c.4, and the "AND" is telling it to add the values it gets; so the sum could be 0 if both switches are open, 1 if either switch is open and the other closed, and 2 if both switches are closed. But the 1 result doesn't tell us which of the two switches is open, does it? And what is the "/8" at the end of the line for?

Edit: Having read my post again, I think I do understand! Am I right in thinking that the number returned if c.3 is closed will be 8, and c.4 will give 16, so that's what's being added together (to give 0, 8, 16, or 24 for the four possibilities) and then divided by 8 to give 0,1,2 or 3? Sorry I'm a bit slow!
 

westaust55

Moderator
Yes, you have worked it out correctly.
Having possible values 0,1,2 or 3 gives us the right sequence to use in the ON...GOSUB command which looks for a value starting with 0,1,2,3, ...
If the value is higher than the number of labels presented the program sequence falls through to the next command.


If the program is not working correctly if may be worthwhile breaking it down and first verifying that the input switches are doing what you want. That will ensure the switches are wired and working correctly.
To do that, instead of driving the servos, just use SERTXD to send the selected route number to the terminal screen in the Programming Editor via your AXE027 cable.
 

abenn

Senior Member
Thanks westaust55. I'm beginning to come to the conclusion that there's a problem with my PCB and/or toggle switches.

The annoying/embarrassing thing is I've got an older version of my program working satisfactorily at another single-slip crossing on my layout. It all went pear-shaped when I altered the code (without saving the original version) to use the eeprom to remember the servos' last position, so that they wouldn't cycle back and forth at full speed when I power up.

Just thinking aloud now, I wonder if my weekly PC backup still has the original working version -- I'll check that later today so that I can see if it's the hardware, or not.
 

BESQUEUT

Senior Member
This also takes into account the comment by Technical above.
Except for the line :
read b0,set_route
IMHO it should be :
read EEPROM_Route_Save , set_route
Unfortunately, I've loaded your amendments into my PICAXE, but it still doesn't respond to the switches correctly.
What happen for the 4 cases of switches settings ? and what it should be ?
Can you publish your code ?
 
Last edited:

abenn

Senior Member
... What happen for the 4 cases of switches settings ? and what it should be ?
Can you publish your code ?
Using westaus55's version of my code, with your correction to the "read" line, the two servos switch between "mainline" and "crossline" positions when switch c.4 is toggled.

When switch c.4 is closed, switch c.3 should have no effect, and doesn't. But when switch c.4 is open, c.3 should have control of the two servos, switching them between "mainline" and "slipline", but at the moment it has no effect -- the servos just stay at the last position switch c.4 set them. I've checked that switch c.3 is actually working by connecting it to another similar circuit, and I've visually inspected, and re-soldered, the PCB in case of a bad joint. Despite that, I still suspect there's a fault with the board somewhere, so I'm planning to make a new one this weekend.

Here's the current version of the code, for reference:

Code:
; Improvements by Westaus55 in Picaxeforum

symbol position_A = b4	; servo A current position
symbol position_B = b5	; servo B current position
symbol min_pulse = b6
symbol max_pulse = b7
symbol servo_slow = b8
symbol set_route = b9	; flag to denote which route is set -- 1=main route 2=cross 3=slip
symbol whereis_A = b10	; flag to denote servo resting position -- 1=pushed 2=pulled
symbol whereis_B = b11	; flag to denote servo resting position -- 1=pushed 2=pulled

SYMBOL Route_Select = b12

SYMBOL EEPROM_Route_Save = 1 ; use EEPROM location 1 to hold current selected route

Initialise:

	let position_A = 0
	let position_B = 0
	let whereis_A = 0
	let whereis_B = 0
	let min_pulse = 125	; change this value to alter left throw limit
	let max_pulse = 175	; change this value to alter right throw limit
	let servo_slow = 20	; increase this value to slow down the servos

				; Input switches for route selection are on pinC.3 and pinC.4
				; Servos are on C.1 and C.2
; read b0,set_route
read EEPROM_Route_Save , set_route

Main:

	Route_Select = pinsC AND %00011000 / 8 ; Fetch the state of the two switches - value range 0 to 3

	ON Route_Select GOSUB Mainline, Slipline,Crossline ; falls through to the GOTO Main if both C.3 and C.4 are high (=1)
	GOTO Main



Mainline:
	IF set_route = 1 THEN RETURN : ENDIF
	IF whereis_A <>1 THEN gosub push_A
	IF whereis_B <>2 THEN gosub pull_B
	set_route = 1
	WRITE EEPROM_Route_Save, set_route
	RETURN
	
	
Crossline:
	IF set_route = 2 THEN RETURN : ENDIF
	IF whereis_A <> 2 THEN gosub pull_A
	IF whereis_B <> 1 THEN gosub push_B
	set_route = 2
	WRITE EEPROM_Route_Save, set_route
	RETURN

Slipline:
	IF set_route = 3 THEN RETURN : ENDIF
	IF whereis_A<>2 then gosub pull_A
	IF whereis_B<>2 then gosub pull_B
	set_route = 3
	WRITE EEPROM_Route_Save, set_route
	RETURN


pull_A:

	position_A = min_pulse
	do 
	  servo c.1,position_A
	  inc position_A
	  pause servo_slow
	loop while b4 < max_pulse
	position_A = max_pulse
	whereis_A=2
	pause 100
	servo c.1,OFF
	RETURN
	
push_A:

	let position_A=max_pulse
	do
	  servo c.1,position_A
	  dec position_A
	  pause servo_slow
	loop while position_A>min_pulse
	let position_A=min_pulse
	let whereis_A = 1
	pause 100
	servo c.1,OFF
	RETURN

pull_B:

	let position_B=min_pulse
	do 
	  servo c.2,position_B
	  inc position_B
	  pause servo_slow
	loop while position_B<max_pulse
	position_B = max_pulse
	whereis_B = 2
	pause 100
	servo c.2,OFF
	RETURN
	
push_B:

	let position_B=max_pulse
	do
	  servo c.2,position_B
	  dec position_B
	  pause servo_slow
	loop while position_B>min_pulse
	position_B = min_pulse
	whereis_B = 1
	pause 100
	servo c.2,OFF
	RETURN
 

srnet

Senior Member
I would only build a new PCB if I knew for sure the old one had a fault.

If there is a fault in the PCB you ought to be able to find it with a continuity tester.
 

tmfkam

Senior Member
Could the logic be swapped temporarily? Something like swapping the order of Mainline and Slipline? Try to check that it is the PCB or switch input that is or isn't working before condemming the PCB.

Personally, on a populated PCB I prefer to use the Ohms range rather than continuity. I get sidetracked by low continuity readings that are due to semiconductor biasing as opposed to low resistance due to short circuits...
 

abenn

Senior Member
The more people read this thread and don't find a glaring error in the code, the more I'm sure the board must be faulty. It's as if switch c.3 is permanently open-circuit, even though I know the switch itself is working. I'll do another check with an ohm meter, but making a new board isn't going to take more than a couple of hours, if it comes to that.
 

BESQUEUT

Senior Member
The more people read this thread and don't find a glaring error in the code,.
hummm...
True table :
Code:
Route	0	1	2	3
C.3	0	1	0	1
C.4	0	0	1	1
	main	slip	cross	no action
When C.4 is high, action can only be cross or no action, IE : cross
so nothing happen...

You need to do something when route=3...
 

hippy

Technical Support
Staff member
The more people read this thread and don't find a glaring error in the code, the more I'm sure the board must be faulty.
Alternatively it could be that people haven't understood the code or the problem or don't have time to analyse the code and what the problem may be.

One thing you can do is write short test programs to report the switch inputs to check the switches are being read as expected, and likewise the servos are being handled as required. And you could do the same for other inputs, LED, LCD and buzzer outputs if you were using those.

If the inputs and outputs are all working when tested then it must be the logic of the code.

I suspect it is the logic. I think you are trying to be too clever in your switch and servo handling, trying to combine where you want to be and how the servos are set which is causing different paths through the code to be taken which is changing things unintentionally.

I think you need to split the code into two parts; using Eeprom and switches to determine where you want to be ( without regard to current servo positions ), and a following section of code which sets the servos to where they should be depending on how they are.

That way you can debug whether it's the telling the servos where to be code which has a problem or whether it's the servo handling part which has a problem.
 

abenn

Senior Member
Thanks for the further input. I think BESQUEUT may have pinpointed where my problem lies; there are in fact 4 different combinations of the c.3 and c.4 switches, but the program as it stands at the moment only covers three possibilities in the ON Route_Select GOSUB Mainline,Slipline,Crossline line.

As I mentioned in my opening post, c.4 = 1 should always result in the crossline route being set, irrespective of the status of c.3. So I believe the matrix posted by BESQUEUT should read:
Code:
Route           0       2       3       4
C.3             0       1       0       1
C.4             0       0       1       1
                 main  slip    cross  cross
But, even then, I think I see another flaw: The result of the Route_Select = pinsC AND %00011000 / 8 line gives us the following results:
Code:
C.3            0       1       0      1
C.4            0       0       1      1
pinsC result   0       8       16     24
divide by 8    0       1       2      3
What I understood from an earlier post is the ON Route-select ... line would ignore a zero, but would respond to 1,2, and 3. So can I make it Route_Select = pinsC AND %00011000 / 8 + 1 to give me 1,2,3, and 4, and then make the next line ON Route_Select GOSUB Mainline,Slipline,Crossline,Crossline to deal with the four cases?

hippy, as you can see I'm still learning. I'm going to amend my code first, as I've outlined; then I'll look at splitting it up if that doesn't work.
 

PieM

Senior Member
Your code is faulty.

C.3 = 0 and C4 = 0 --> route = 0
then if
C.3 = 0 and C.4 = 1 ---> route = 2 , not 3 !

What I understood from an earlier post is the ON Route-select ... line would ignore a zero,
No!
If offset is value 0, the program flow will gosub to address0, if offset is value 1 program flow will gosub to adddress1 etc.
 

BESQUEUT

Senior Member
Please look at the comments in Westaust code #4
Route_Select = pinsC AND %00011000 / 8 ; Fetch the state of the two switches - value range 0 to 3

ON Route_Select GOSUB Mainline, Slipline,Crossline ; falls through to the GOTO Main if both C.3 and C.4 are high (=1)

0=GOSUB Mainline
1=GOSUB Slipline
2=GOSUB Crossline
3=GOTO Main

Should be :
ON Route_Select GOSUB Mainline, Slipline,Crossline,Crossline
0=GOSUB Mainline
1=GOSUB Slipline
2=GOSUB Crossline
3=GOSUB Crossline
 
Last edited:

abenn

Senior Member
Cracked it!

I did read, and thought I understood Westaust55's code, but I've realised the problem is I want it to go to Crossline for value = 3, because a value of 3 is produced when both c.3 and c.4 are high. So changing the line to

ON Route_Select GOSUB Mainline, Slipline,Crossline,Crossline

seems to have got it working the way I want -- i.e. if c.4 is high it will always got to Crossline irrespective of the state of c.3.

Thanks for all your input.
 

abenn

Senior Member
Final update:

There is a fault in my PCB, for after my last post it stopped working again. I verified it by inputting a simple program to flash an LED 1,2,3, or 4 times according to the switch positions. and it behaved as if switch c.3 is permanently closed. Probably a solder bridge on the board, though I haven't taken it apart yet.

A new PCB, with ON Route_Select GOSUB Mainline, Slipline,Crossline,Crossline, is working correctly, consistently :)
 
Top