Water drop controller

AllyCat

Senior Member
Hi,

Firstly I must declare that I have NOT read all of this thread nor even followed the links immediately above, or looked at your program code.

Aresby is absolutely correct that you should try to develop the keyboard code in isolation first, but one pin is better than 7 (particularly if one only has a 8M2 :D ).

You need to tell us (or at least me) what resistors are connected to the keyboard. For example I would connect (say), 0, 1k ,2k and 3k ohms to the "rows" and 0, 4k and 8k to the columns (then perhaps 10k up to the supply rail), but some of those are non-standard values so you might know or have better.

Repeatedly reading the A/D value (i.e. on the lower end of the 10k) should tell you the "present" status of the keyboard (this is not quite trivial as the A/D values may "wander" a little). Then your code needs to look for changes in the A/D value and each time it becomes some value other than probably 255 (no key pressed) for several consective readings then you can decode the keypress, add it's "weighted" value (i.e. 1000x, 100x, 10x or 1x) into a data word (which is initially loaded with 0) and display the (present) data word to the OLED.

I hope that's of some help; I will only be able to respond about once a day.

Cheers, Alan.
 

hippy

Technical Support
Staff member
Repeatedly reading the A/D value (i.e. on the lower end of the 10k) should tell you the "present" status of the keyboard (this is not quite trivial as the A/D values may "wander" a little).
That's usually true of most keypads which use conductive rubber pads to short the PCB traces together and will vary naturally and depend on how hard keys are pressed, and even metal switch contacts will vary slightly.

It's not a problem for matrix / scanning keypads as it's relatively easy to determine if there's an open or (somewhat) closed circuit regardless of the switch resistance itself but it's harder when trying to determine which key is pressed on a resistive keypad.

Choice of resistor values to give a wide spread of ADC readings per key with a good gap between each to allow discrimination of which is pressed is quite important. I'm sure there have been other posts suggesting suitable values for a 4 x 3 or 4 x 4 resistive keypad.
 

bfgstew

Senior Member
Hi Alan, the resistor matrix is fine and works well, I have a reasonable spread and get no 'glitches' when using serout to the OLED.

My main problem is changing the variables (w7 - w13) via the keypad, I just cannot get my head round it at present.

Stewart
 

premelec

Senior Member
You might consider some different input methods - and only one variable at a time - think of the various devices we have in our lives like setting clocks just holding a button down while one digit increments... If you can output to the OLED you just need to store the value in the Wxx variable and you can use the same variable redirected through a CASE instruction which is selected before executing the CASE. Anyhow lay it out in itty bitty steps and start with only one variable - then two etc... extend the thinking and structure to what you want to accomplish. If you can get it to the OLED why not just have a button that transfers that value to Wxx ... Something will work eventually as you've come this far... persist in the quest! [sorry I haven't kept up with your code - I've got my own troubles... :) ]
 

AllyCat

Senior Member
Hi Stewart,

Can you get the correct value into one w register first? A possible improvent on what I suggested yesterday might be to input (and store) the first digit, then each time before adding the second/subsequent digits multiply the storage register by 10. I'm assuming you will always enter 4 digits with leading zeros if necessary.

One you have values successfully into one register then the "final" destination should be easy. Either enter them all in sequence, or if only a few need to be updated each time, how about putting a fifth ("address") digit at the start (or end) of each group of 4? You also have the * and # keys if you want to anything more flexible/clever.

Cheers, Alan.
 

bfgstew

Senior Member
Hi Alan, no I can't get that bit to work as I don't know/understand how to do it! Very frustrating for me and for those who are trying to help..................:(
 

AllyCat

Senior Member
Hi Stewart,

You can't form the key entries into a data word? I'm very new to PICaxe basic myself so I'm not going to write code, just give a few hints.

The first factor to consider is that in addition to the 12 "key pressed" values there must be a "no key pressed" value, you need to check that it occurs between reading each digit.

Another important concept is that reading the keyboard produces a "logical" key value (related to the row and column numbers). This needs to be converted to the actual key value (i.e. what is printed on its face).

So, start with a word variable set to zero. Now, you're going to need for example a FOR loop with 4 passes (for the four digits to be read). When the keyboard scan (a loop inside the FOR) gives a digit value then multiply the word by 10 (this will do nothing on the first pass) and then add in the key value. Now wait until the key value goes back to "none pressed", print to the OLED (or sertxd/debug to test) if desired and then repeat the loop.

It's always difficult to understand exactly what "mental block" a person has (especially if one has been doing this sort of thing for 40+ years), but hopefully we'll get there in the end. :D

Cheers, Alan.
 

premelec

Senior Member
OK perhaps I've got more of an idea of the "problem". Say you want to enter 12345 into Wx - SO Wx = 1*10000 + 2 * 1000 + 3 * 100 + 4 * 10 + 5 . The problem might be knowing how many digits you are entering as the highest is first - you can accumulate the digits in 5 b variables and then process those into a word - clearing the 5 b variables before the next entry... does this give you some idea of what may be done?
 

bfgstew

Senior Member
Thanks Alan and Premelec.

I have decided to go from using the single wire keypad matrix using ADC, to the more general and easier to code 7 wire method, I think this will help my cause, plus your more detailed description has given me a helping hand in the right direction.

Watch this space.............:)

Stewart
 

bfgstew

Senior Member
Ok, I have re-wired the keypad to the more standard 7 wire system with the 3 inputs tied to 0v with 10k resistors and the other 4 as outputs. So far so good, coded with keyscan and get 1-9 no problem on OLED, I would like *, 0 and # instead of 10, 11 and 12 but that is a minor problem.
So transfered code into main programe and tweaked slightly because of labels etc and works fine, goes through the sequences as I want it to with no bother, so a result there.
Now to tackle this writing to the variables, I will see what I can do tonight as I am on nights, unfortunately I can't show any coding as works PC will not load it.

Thanks again to those who have been kind and patient enough to put up with my ignorance, but I am slowly learning, thanks to you all.

Stewart
 

bfgstew

Senior Member
Ok, I have added the keypad scan code and got that to work using debug and serout to my OLED.
Now added it to Nick12abs' code from earlier to write variables into w1, which is the pause time for the solenoid. I have tried and tried to get it to run properly but all I get is odd numbers, either 0, 3 or 867 I even got 3867 at one point, but nothing from using the keypad.
Any suggestions folks as I am running out of hair to pull out...........:p

Code:
symbol row = b1
symbol key = b2
symbol solenoid = B.7
symbol dropDelay1 = w1    ; use a word variable as we want values > 255
dropDelay1 = 0000     

init:
	pause 500           
	serout C.7,N2400,(254,1)	;initialize OLED
	pause 500
	serout C.7,N2400,(254,128,"Drops- ")	;1st screen display
	pause 30
	serout C.7,N2400,(254,136,"P1-     ")
	pause 30
	for b4 = 0 to 3
main:	
	key = 0
	for row = 0 to 3
		high row
		gosub keyscan
		low row
	next row
	goto main
keyscan:
	if pin0 = 1 then
		key = row*3+1
		do loop while pin0 = 1
	elseif pin1 = 1 then
		key = row *3+2
		do loop while pin1 = 1
	elseif pin2 = 1 then
		key = row*3+3
		do loop while pin2 = 1
	endif
	if b4 < 10 then
	bptr = 20 + b4
	@bptr = b4
	serout C.7,N2400,(254,139,#b4)
	endif
	next b4
w1 = b4 *10 + b5 * 10 + b6 * 10 + b7
	
mainA:

	key = 0
	for row = 0 to 3
		high row
		gosub keyscan1
		low row
	next row
	goto mainA
keyscan1:
	if pin0 = 1 then
		key = row*3+1
		do loop while pin0 = 1
	elseif pin1 = 1 then
		key = row *3+2
		do loop while pin1 = 1
	elseif pin2 = 1 then
		key = row*3+3
		do loop while pin2 = 1
	endif	                     
		if key = 12 then main1
		return
	gosub mainA
main1:                                 ;Gives 1 drop from solenoid
	high solenoid                 
	pause dropdelay1
	low solenoid
	gosub init
 
Last edited:

AllyCat

Senior Member
Hi Stewart,

I'm not sure of your intended program flow, but the areas that you may need to look at are:

main and mainA are both closed loops (end with a goto) only calling one keyscan subroutine each, so both cannot be continuously executed.

keyscan does not appear to have a return, but keyscan1 has a return immediately followed by (so unlikely to be exectued) gosub mainA (which isn't a subroutine anyway).

Similarly init is not (declared as) a subroutine so shouldn't be called in the last line.

You should really only have one "main" loop (near the top) which only calls subroutines (which may call further "nested" subroutines), but every subroutine should not contain too many lines and must have a clear return at the end.

Cheers, Alan.
 

hippy

Technical Support
Staff member
Any suggestions folks as I am running out of hair to pull out...........:p
Use modularisation; subroutines. Don't try to put the number creating code inside the keypad routine; keeping it all separate is far, far easier than a monolithic program.

Imagine you have a 'GetKeyPress' routine which only returns when a key is pressed and contains a value 0 to 9 in b0. You want to get a four digit value, 0000 to 9999 into w1, so ...

Gosub GetKeyPress : w1 = b0
Gosub GetKeyPress : w1 = w1 * 10 + b0
Gosub GetKeyPress : w1 = w1 * 10 + b0
Gosub GetKeyPress : w1 = w1 * 10 + b0

Simples :)

To develop actual code start simple, using what's called top-down design, code what you'd like and easy, even if you don't yet have the code which allows it to run as a program ...

Code:
Main:
  Do
    Gosub Get4DigitNumberIntoW1
    SerTxd( "Number entered = ", #w1, CR, LF )
  Loop
Then you can write and add the code to get a four digit number into w1 ...

Code:
Get4DigitNumberIntoW1:
  Gosub GetKeyPress : w1 = b0
  Gosub GetKeyPress : w1 = w1 * 10 + b0
  Gosub GetKeyPress : w1 = w1 * 10 + b0
  Gosub GetKeyPress : w1 = w1 * 10 + b0
  Return
Then you can write and add the GetKeyPress routine. This will call a routine which will return b0=$FF if no key pressed, b0=0..9 if one is ...

Code:
GetKeyPress:
  ' Wait until no key pressed
  Do
    Gosub GetTheKeyPressed
  Loop Until b0 = $FF
  ' Wait until key pressed
  Do
    Gosub GetTheKeyPressed
  Loop Until b0 <> $FF
  Return
Now all you have to do is write a 'GetTheKeyPressed' routine which will return b0=$FF if no key pressed, b0=0..9 if one is.
 
Last edited:

bfgstew

Senior Member
Immense thanks Alan and Hippy, will have a go tonight to get coding sorted. I feel I can see light at the end of the tunnel now (I hope!).
Thanks once again guys, much appreciated.

Stewart
 

AllyCat

Senior Member
Hi Stewart,

Just to add to Hippy's excellent advice. A subroutine is a very good place to put code that you've copied from somewhere else. Also, comments don't add to the size or execution time of your program and can even make editing easier because you can largely ignore the stuff between the comment and the return (once it is tested and working).

For example, IMHO it's perfectly acceptable (but preferable for you to understand how it does work) to start a subroutine with:

GetKeyPress:
; Code copied from xxxx, don't know quite how it works but
; it waits for a key to be pressed and released and returns the value in b0
; variables used are b0 and ......
Program Code
return

Note the "flow" conditions and all the variables which are used as this can help debugging (if any is needed).

In this case you can/should test all possible keypresses for the correct values and also what happens if you press a second key before releasing the first, or what happens if you hold down a key for a l-o-n-g time?

Cheers, Alan.
 
Last edited:

bfgstew

Senior Member
OK I have used this code for scanning the keypad and it gives me the correct digits to the OLED on each button press (still have to sort out the *, 0 and # as it comes up with 10, 11 and 12).
Now I know this sounds stupid but were do I put this in Hippy's coding that he has so kindly done for me?

Code:
symbol row = b1
symbol key = b0
init:
	pause 500
main:
	key = 0
		for row = 0 to 3
		high row
		gosub keyscan
		low row
		if key > 0 then gosub displayKey
		next row
	goto main
keyscan:
	if pin0 = 1 then
		key = row * 3 + 1
		do loop while pin0 = 1
		elseif pin1 = 1 then
		key = row * 3 + 2
		do loop while pin1 = 1
		elseif pin2 = 1 then
		key = row * 3 + 3
		do loop while pin2 = 1
		endif
	return
displayKey:
		serout 7, N2400, (254,1,#key)
		return
	end
 
Last edited:

AllyCat

Senior Member
Hi Stewart,

I still don't see any comments! :eek:

Change the name/label of your "main" to what the code actually does (probably GetKeyPress: or GetTheKeyPressed: ) and change the goto to a return.

Now introduce a "main:" like hippy's, but calling (i.e. gosub) the name of the latest subroutine that you've written and tested (i.e. as above). When that's working then "nest" this subroutine in code for the next level up (again as in hippy's example) and call that from main.

Keep going with your "main" never growing much larger than a few lines such as :

Code:
main:
           gosub initialise
      do
           gosub abc 
           gosub def
      loop
Cheers, Alan.
 

bfgstew

Senior Member
Well I have tried nesting the keypress routine but to no avail, it must be, just a bit long in the tooth to get it into my skull!!!
This is what I have so far, it works well but still need to get the keypad to write those variables w7 to w13!!!!

Code:
;#Picaxe 18M2 Water drop controller, 4 drops controlled via numeric 
;keypad and serial OLED screen interface.
;Pause times are altered using the keypad and range from 1ms to 9999ms
;4 drop selections can be made and these bring relevant screen to show and 
;alter pause times to suit
symbol key_pos = b0
symbol key_value = b1
symbol solenoid = B.7
symbol dropDelay1 = w7    ; use a word variable as we want values > 255
dropDelay1 = 0100          ; default delay value
symbol dropDelay2 = w8    ; use a word variable as we want values > 255
dropDelay2 = 1000          ; default delay value
symbol dropDelay3 = w9    ; use a word variable as we want values > 255
dropDelay3 = 0200          ; default delay value
symbol dropDelay4 = w10    ; use a word variable as we want values > 255
dropDelay4 = 1000          ; default delay value
symbol dropDelay5 = w11    ; use a word variable as we want values > 255
dropDelay5 = 0100          ; default delay value
symbol dropDelay6 = w12    ; use a word variable as we want values > 255
dropDelay6 = 1000          ; default delay value
symbol dropDelay7 = w13    ; use a word variable as we want values > 255
dropDelay7 = 0100          ; default delay value

init: 
	pause 500           
	serout C.7,N2400,(254,1)	;initialize OLED
	pause 500
	serout C.7,N2400,(254,128,"Drops- ")	;1st screen display
	pause 30	
	let dirsB = 255
	let key_pos = 0
main:
	
	let key_value = 0
	let pinsB = %00010001
	gosub key_test

	let key_value = 3
	let pinsB = %00010010
	gosub key_test

	let key_value = 6
	let pinsB = %00010100
	gosub key_test

	let key_value = 9
	let pinsB = %00011000
	gosub key_test

	goto main


' *** Score sub procedure. ***
' *** return straight away if no key pressed ***

key_test:
	if pinC.0 = 1 then add1
	if pinC.1 = 1 then add2
	if pinC.2 = 1 then add3
	return

' *** key value will already be 0, 3, 6, or 9 ***
' *** so add 1, 2 or 3 to this value *** 

add3:	let key_value = key_value + 1
add2:	let key_value = key_value + 1
add1:	let key_value = key_value + 1

		if key_value = 1 then:serout C.7,N2400,(254,134,"1");if 1 is keyed in, jump to P1
		pause 30						      
		goto P1
	endif
		if key_value = 2 then:serout C.7,N2400,(254,134,"2");if 2 is keyed in, jump to P2
		pause 30
		goto P2
	endif
		if key_value = 3 then:serout C.7,N2400,(254,134,"3");if 3 is keyed in, jump to P3
		pause 30
		 goto P3
	endif
		if key_value = 4 then:serout C.7,N2400,(254,134,"4");if 4 is keyed in, jump to P4
		pause 30
		goto P4
	endif
	return

P1: 	;P1 sends this message to the OLED screen showing pause value #7  
	serout C.7,N2400,(254,136,"P1-",254,139,#w7)
	pause 30
	goto mainA
	
P2:	;P2 sends this message to the OLED screen showing pause values #7 to #9
	serout C.7,N2400,(254,136,"P1-",254,139,#w7)
	serout C.7,N2400,(254,192,"P2-",254,195,#w8,254,200,"P3-",254,203,#w9)
	pause 30
	goto mainB
	
P3:	;P3 sends this message to the OLED screen showing pause values #7 to #w11										
	serout C.7,N2400,(254,136,"P1-",254,139,#w7)
	serout C.7,N2400,(254,192,"P2-",254,195,#w8,254,200,"P3-",254,203,#w9)
	serout C.7,N2400,(254,148,"P4-",254,151,#w10,254,156,"P5-",254,159,#w11)
	pause 30
	goto mainC
P4:	;P4 sends this message to the OLED screen showing pause values #7 to #w13			
	serout C.7,N2400,(254,136,"P1-",254,139,#w7)
	serout C.7,N2400,(254,192,"P2-",254,195,#w8,254,200,"P3-",254,203,#w9)
	serout C.7,N2400,(254,148,"P4-",254,151,#w10,254,156,"P5-",254,159,#w11)
	serout C.7,N2400,(254,212,"P6-",254,215,#w12,254,220,"P7-",254,223,#w13)
	pause 30
	goto mainD

mainA:
	
	let key_value = 0
	let pinsB = %00010001
	gosub key_testA

	let key_value = 3
	let pinsB = %00010010
	gosub key_testA

	let key_value = 6
	let pinsB = %00010100
	gosub key_testA

	let key_value = 9
	let pinsB = %00011000
	gosub key_testA

	goto mainA


' *** Score sub procedure. ***
' *** return straight away if no key pressed ***

key_testA:
	if pinC.0 = 1 then add11
	if pinC.1 = 1 then add21
	if pinC.2 = 1 then add31
	return

' *** key value will already be 0, 3, 6, or 9 ***
' *** so add 1, 2 or 3 to this value *** 

add31:	let key_value = key_value + 1
add21:	let key_value = key_value + 1
add11:	let key_value = key_value + 1
		if key_value = 12 then gosub main1 ; 12 is the # key on keypad and will start the run sequencw when pressed
	gosub mainA
main1:                                 ;Gives 1 drop from solenoid
	high solenoid                 
	pause dropdelay1
	low solenoid
	gosub init
mainB:
	
	let key_value = 0
	let pinsB = %00010001
	gosub key_testB

	let key_value = 3
	let pinsB = %00010010
	gosub key_testB

	let key_value = 6
	let pinsB = %00010100
	gosub key_testB

	let key_value = 9
	let pinsB = %00011000
	gosub key_testB

	goto mainB


' *** Score sub procedure. ***
' *** return straight away if no key pressed ***

key_testB:
	if pinC.0 = 1 then add12
	if pinC.1 = 1 then add22
	if pinC.2 = 1 then add32
	return

' *** key value will already be 0, 3, 6, or 9 ***
' *** so add 1, 2 or 3 to this value *** 

add32:	let key_value = key_value + 1
add22:	let key_value = key_value + 1
add12:	let key_value = key_value + 1          
	if key_value = 12 then gosub main2 ; 12 is the # key on keypad and will start the run sequencw when pressed
	gosub mainB
main2:                    ;Gives 2 drops from solenoid
	high solenoid
	pause dropdelay1
	low solenoid
	pause dropdelay2
	high solenoid
	pause dropdelay3
	low solenoid
	gosub init
mainC:
	
	let key_value = 0
	let pinsB = %00010001
	gosub key_testC

	let key_value = 3
	let pinsB = %00010010
	gosub key_testC

	let key_value = 6
	let pinsB = %00010100
	gosub key_testC

	let key_value = 9
	let pinsB = %00011000
	gosub key_testC

	goto mainC


' *** Score sub procedure. ***
' *** return straight away if no key pressed ***

key_testC:
	if pinC.0 = 1 then add13
	if pinC.1 = 1 then add23
	if pinC.2 = 1 then add33
	return

' *** key value will already be 0, 3, 6, or 9 ***
' *** so add 1, 2 or 3 to this value *** 

add33:	let key_value = key_value + 1
add23:	let key_value = key_value + 1
add13:	let key_value = key_value + 1
	if key_value = 12 then main3 ; 12 is the # key on keypad and will start the run sequencw when pressed
	gosub mainC
main3:                    ;Gives 3 drops from solenoid
	high solenoid
	pause dropdelay1
	low solenoid
	pause dropdelay2
	high solenoid
	pause dropdelay3
	low solenoid
	pause dropdelay4
	high solenoid
	pause dropdelay5
	low solenoid
	gosub init
mainD:
	
	let key_value = 0
	let pinsB = %00010001
	gosub key_testD

	let key_value = 3
	let pinsB = %00010010
	gosub key_testD

	let key_value = 6
	let pinsB = %00010100
	gosub key_testD

	let key_value = 9
	let pinsB = %00011000
	gosub key_testD

	goto mainD


' *** Score sub procedure. ***
' *** return straight away if no key pressed ***

key_testD:
	if pinC.0 = 1 then add14
	if pinC.1 = 1 then add24
	if pinC.2 = 1 then add34
	return

' *** key value will already be 0, 3, 6, or 9 ***
' *** so add 1, 2 or 3 to this value *** 

add34:	let key_value = key_value + 1
add24:	let key_value = key_value + 1
add14:	let key_value = key_value + 1
	if key_value = 12 then main4 ; 12 is the # key on keypad and will start the run sequencw when pressed
	gosub mainD	
main4:                    ;Gives 4 drops from solenoid
	high solenoid
	pause dropdelay1
	low solenoid
	pause dropdelay2
	high solenoid
	pause dropdelay3
	low solenoid
	pause dropdelay4
	high solenoid
	pause dropdelay5
	low solenoid
	pause dropdelay6
	high solenoid
	pause dropdelay7
	low solenoid
	gosub init
Sorry to those who have tried to help me, it's not your fault I am not getting anywhere, it's me............:confused:
 

AllyCat

Senior Member
Hi Stewart,

Whenever you find yourself cutting and pasting blocks of code and/or writing something very similar over and over again you should say to yourself "I can/must use a subroutine here". It not only helps you write compact code, it helps us read it. :)

I haven't read all through the code but what looks "suspicious" to me is that "key_test" is called very soon but has a comment before it which says:

' *** return straight away if no key pressed ***

but is that what you really want to happen, since main then moves onto the next digit?

However, the main issue seems to be that you have many gosubs and gotos and very few returns.

Whatever path is taken through the code, every gosub MUST be followed by an asoociated return. As far as I can see you have a path through labels:

add1,
P1,
mainA (which is not a main !)
gosub keytestA
etc.
gosub init (which isn't a subroutine, it "falls through" to main).

without ever encountering a return. :confused:

Cheers, Alan.
 
Last edited:

bfgstew

Senior Member
Hi Alan,
Sorry for the late reply!

I understand your concerns, but the code does work and works well, for me anyhow. Its just that I can't get my head around the coding and suggestions you have all so kindly given me to get the keypad to change these variables, that's the frustrating bit, not just for me but for you all.

I shall keep trying though, I will get it to work fully...........;)
 

hippy

Technical Support
Staff member
I shall keep trying though, I will get it to work fully...........;)
The problem seems to be that you are determined to go down the path of banging a square peg into a round hole. You very probably will get something which does what you want but you will be losing others along the way who can no longer follow what you are doing, are becoming too confused to understand what you have and unable to help further.

Sometimes it can help to keep knowledge gained, but throw everything you have out the window and start again, building on that knowledge. With software that's not only easy to do, it is relatively zero cost. Restarting using what you now know can often be quicker than banging away to try and make something work.

The best course I think is to take a step back. Imagine that someone was going to build you your water droplet controller and describe exactly how that will work, what the operation of that will be, what key presses do what. Effectively write a user manual which someone else could use if you were selling it as a product.

With that, others will be much better placed to help you achieve what you want.
 

bfgstew

Senior Member
Ok hippy.

The unit is a simple device, an OLED screen, a 12 digit keypad, an LED indicator lamp, an on/off switch, connector plug to download to chip and a power cable.
Switching on the unit brings up the opening screen shot to choose how many drops you wish to use, (DROPS - ?) simply key in 1, 2, 3 or 4. This will then, depending on the number chosen will take you to the next screen shot. Now if 1 drop has been chosen, only 1 pause time will be shown on screen (DROPS - 1 P1 - ????), using the keypad, key in the desired time value, hit the * key to accept the value and to run programme hit the # key. This will open the solenoid for desired time and then close, giving 1 drop of water from the solenoid, the screen will then return to the first screen and you repeat the process. If 2 drops has been chosen, this will take you to another screen shot and on this screen it will show 3 pause times (DROPS - 3 P1 - ???? P2 - ???? and P3 - ????), using the keypad again, key in the value you want in P1, hit the * key to accept and to move to P2, repeat process until all 3 values have been keyed in and then hit the # key, again, this will run the programme, P1 and P3 are the times the valve will be open and P2 is the time between the valve opening again. The screen will then go back to the start screen. The process is the same for 3 and 4 drops.
To trigger the camera a separate I/R LED gate will be triggered when the first drop passes so no other inputs are required to run the unit.
Hope this sheds a bit more light on how this needs to work?

Cheers

Stewart
 

AllyCat

Senior Member
Hi Stewart,

Well, that is definitely a step in the right direction , BUT....

but the code does work and works well,
I basically consider myself a hardware engineer, so shouldn't be "preaching" about software, but over the years I have been "concerned" about many designs, however the designer has assured me "But it works". Then some hours, days or weeks, etc. later it's "Why has it suddenly stopped working?". Now with (analogue) hardware design there are difficult and complex issues such as "tolerances", "parasitic components", etc., but an advantage of digital hardware and software is that it is relatively "predictable".

If you insist on writing "subroutines" that don't have a clear return path then it may work "for now", but will "mysteriously" (???) stop working. Try the following code for a minute in the simulator (I hope that you use the simulator, but rather doubt that you do):

Code:
main:
	gosub start
	sertxd ("I write good code")
	end
start:
	b0 = b0 + 2
	sertxd("My code works")
	pause 1000	
	sertxd(" .. for ",#b0," seconds",cr,lf)
	gosub start
	return
BTW there at at least two (intentional) errors in that code which may produce "unexpected" errors at some time or another.

Also, your present code does NOT "work" because it's not doing what you want it to do! Now if it was correctly structured, then adding the extra features would be just a matter "bells and whistles", but because it isn't, then I doubt if anyone (including you) is able (or can be bothered) to sort it out.

Cheers, Alan.
 
Last edited:

hippy

Technical Support
Staff member
Excellent start to a functional description there Stewart. The next step is to turn that into a flowchart or pseudo-code description which defines how that will be implemented in software, for example ...

Code:
Display "How many drops ?"
Gosub GetDigit
If digit = 1 Then OneDrop
If digit = 2 Then TwoDrops
If digit = 3 Then ThreeDrops
If digit = 4 Then FourDrops

ThreeDrops:

  Display "Enter P1"
  Gosub EnterNumber
  If hash pressed goto make some drops
  p1 = number

  Display "Enter P2"
  Gosub EnterNumber
  If hash pressed goto make some drops
  p2 = number

  Display "Enter P3"
  Gosub EnterNumber
  If hash pressed goto make some drops
  p3 = number
You can then mentally work through that, playing the role of a PICAXE, to see if it does what you want and tweak it until it does, tidying up loose ends.

Once you've got a good idea of how the code should work it's then only a matter of converting that into actual PICAXE code that does work.

Of course, the more refined your user interface needs to be, the more complicated things are, the more pseudo-code and real code you have to create. What can sound simple can often be quite complicated when it's broken down. The real challenge is in keeping that complexity manageable.
 

bfgstew

Senior Member
Point taken Alan..................:(

I do use the simulator though..........:p

Code:
main:

	sertxd ("I write good code")
	gosub start
	end
start:
	do
	b0 = b0 + 2
	sertxd("My code works")
	pause 1000	
	sertxd(" .. for ",#b0," seconds",cr,lf)
	loop
Thanks hippy, will re-write my code accordingly and see how that goes with my new found knowledge..........:D
 

Texy

Senior Member
Get rid of that 'gosub start' - it achieves nothing. Gosubs and subroutines in general need only be used where they need to be run multiple times, and often called from different places in the main code. When used efficiently they can be used to improve code readability, and in some cases, to reduce code length/program size.

What I would of done is get a single solenoid working as you want it before anything else. Then think about it controlling 4 or 5 solenoids.
My 2 cents ;-)

Texy
 

AllyCat

Senior Member
Hi Stewart,

Thanks for taking the time to try my example (and hopefully learning from it).

The point is that start: (still) ISN'T a subroutine, and never was! Previously it had a return that was never executed, now it doesn't have a return at all! So main: had to be "bodged" to make the code work.

The code was only intended to prove a point and I was intentionally non-commital about how many "errors" it contained. IMHO the main fault was NOT that the subroutine called itself (which sometimes is actually permissible, however not for novices) but that it wasn't a subroutine at all because it did not have a valid (i.e. executed) return.

My "fix" would have been to replace the (second) "gosub start" with something like if "if b0 < 50 then (goto) start", since I said you should run it for a minute. But IMHO other errors are that (2) it has no comments, (3) "start" is a rubbish name for a subroutine, and (4) b0 is not initialised to 0. As far as this last item is concerned, OK most versions of basic do initialise all constants to zero automatically (and in this case b0 was intended as an elapsed time counter) but normally any "local" variables MUST be initialised within a subroutine.

You may have noticed that I always refer to "calling" a subroutine and I see that PICaxe basic accepts CALL as an alternative to GOSUB. Personally I think that CALL is better because it emphasises that GOSUB and GOTO are fundamentally different. I'm not sure that this is a good example but you might "CALL" at a friend's house to deliver a parcel, but "GOTO" the house to watch TV, have a meal and maybe stay overnight.

Cheers, Alan.
 
Last edited:

bfgstew

Senior Member
Ok, just a quick update with my project.

I have got the code working, I can input the variables via the keypad and see it on the OLED screen and it does alter the pause times.
The only problem I have is the length of code, it has taken up so much space I can only get it to work for 2 drops instead of 4, ho hum. Now this is down to the keypad code I am sure as I cannot get it to work using subroutines and stuff so I have just re labelled it so it works individually for each number input, tedious and long winded I know but this is my question.
The keypad code I am using is it the best way forward or can something like the lookup table be better? Also I cannot for the life of me get 0 (zero) using the keypad, only 11, any ideas please.
Thanks

Stewart

Code:
symbol key_value = b1	'value of key pressed
symbol display=output4	'lcd display on pin 4

' scan each column in turn by setting only 1 column high
' then check each row for switch pressed by calling key_test

init:	pause 500	'for LCD to initialise
	serout display,N2400,("Ready")

scan:	let key_value = 1
	high col1
	gosub key_test
	low col1
	let key_value = 2
	high col2
	gosub key_test
	low col2
	let key_value = 3
	high col3
	gosub key_test
	low col3
	goto scan	'loop back

key_test:	' return straight away if no key pressed
	if row1 = 1 then add1
	if row2 = 1 then add2
	if row3 = 1 then add3
	if row4 = 1 then add4
	return

'key value will already be 1, 2, or 3 so add 0 or 3 to this value
add4:	let key_value = key_value + 3
add3:	let key_value = key_value + 3
add2:	let key_value = key_value + 3
add1:	&#8216;the following lines are only used for testing
	serout display,N2400,("Key is ",#b1,"  ")	'show number on LCD
	debug key_value	&#8216;show number on computer
 

premelec

Senior Member
If the "11" is unique then If keyval = 11 THEN Keyval = 0 ? I haven't discerned just how you are sensing row and column...
 

AllyCat

Senior Member
Hi Stewart,

You really MUST learn how to write subroutines. Start by (re-)reading the gosub and return sections in User manual 2.

My suggestion is to never jump (goto) any address (label) outside of the subroutine and (for now) don't make any subroutine longer than (say) 12 lines, even if you have to "artificially" call another subroutine to do more processing.

No, IMHO a lookup table is unlikely to help, the crucial issue is that you must get the overall program structure correct.

I can't really comment on your code as I still cannot see all the vital return paths from the subroutine(s).

How about? :
Code:
if key_value = 11 then : key_value = 0 endif
Cheers, Alan.
 

bfgstew

Senior Member
Hi guys,

Yes am in the throws of re writing code into more managable lumps but the point of the exercise was to just to see if I could get the writing of the variables to work, which I have succeeded. Which I can only thank you guys for helping me with. We are getting there, albeit slowly...................:D
 

hippy

Technical Support
Staff member
I cannot for the life of me get 0 (zero) using the keypad, only 11, any ideas please.
I'm surprised you can identify anything beyond three keys because it seems the code always sets key_value=3 before testing column 3. Your test code bales out and stops as soon as a key is pressed so you probably aren't seeing the subsequent overwrite of the values.

UNtested but this code hould scan a 3x4 keypad, return 0 to 11 in key_value for a key pressed and $FF (255) if none are pressed ...

Code:
GetKeyPressed:
  key_value = $FF
  key_scan  = 0
  High COL1 : Gosub ScanRow : Low COL1
  High COL2 : Gosub ScanRow : Low COL2
  High COL3 : Gosub ScanRow : Low COL3
  Return

ScanRow:
  If ROW1 = 1 Then : key_value = key_scan + 0 : End If
  If ROW2 = 1 Then : key_value = key_scan + 1 : End If
  If ROW3 = 1 Then : key_value = key_scan + 2 : End If
  If ROW4 = 1 Then : key_value = key_scan + 3 : End If
  key_scan = key_scan + 4
  Return
That will have to be tweaked to match your hardware so you get 0 to 11 matching the key legends on the keypad. The problem is that it's hard to know how it should be tweaked without the hardware. I'd suggest you code something like ...

Main:
Do
Gosub GetKeyPressed
SerTxd( "Pressed = ", #key_value, CR, LF )
Pause 250
Loop

And let us know what key_value is returned for each key pressed, working from top left of the keypad, across then down.
 

hippy

Technical Support
Staff member
@ Stewart : Don't forget to tell us what the key legends are against key_value ... some keypads have the top row as "123" others "789".
 

bfgstew

Senior Member
Hi Hippy,
It's the Rev-Ed keypad 1,2, and 3 on top row, *, 0 and # on bottom row.

Your code is giving me syntax error on ROW1? I have used symbol ROW1 = B.0
 

bfgstew

Senior Member
Well so far so good now Hippy, code working now and have got my 0 (zero). I had to swap rows and columns as columns are my inputs and tied down with 10K resistors, but done full simulation, then debug and now putting into my main code.
Many thanks
Stewart
 

hippy

Technical Support
Staff member
I had to swap rows and columns as columns are my inputs and tied down with 10K resistors, but done full simulation, then debug and now putting into my main code.
Considering my code was based on what you had posted ( sets columns then read rows ) that doesn't really make sense, or I'm not understanding what you actually have. That's why it's always best to post full circuit diagrams and the code being used, but as long as you have things working that's the important thing.
 

bfgstew

Senior Member
Sorry Hippy, my mistake, posted wrong code!
Anyway 99% success with the code, but I still can't get the 0 (zero) to work on the keypad, any ideas guys?
Code:
;#Picaxe 18M2 Water drop controller, 4 drops controlled via numeric 
;keypad and serial OLED screen interface.

symbol key_pos = b0
symbol key_value = b1
symbol solenoid = B.7
symbol COL1 = pin0
symbol COL2 = pin1
symbol COL3 = pin2
symbol ROW1 = B.0 
symbol ROW2 = B.1
symbol ROW3 = B.2
symbol ROW4 = B.3
symbol dropDelay1 = w7        
dropDelay1 = 0                
symbol dropDelay2 = w8        
dropDelay2 = 0                
symbol dropDelay3 = w9        
dropDelay3 = 0 		
symbol dropDelay4 = w10		
dropDelay4 = 0			
symbol dropDelay5 = w11		
dropDelay5 = 0			
symbol dropDelay6 = w12		
dropDelay6 = 0			
symbol dropDelay7 = w13 	
dropDelay7 = 0        	      


init: 
	pause 500           
		serout C.7,N2400,(254,1)	;initialize OLED
		pause 500
			serout C.7,N2400,(254,129,"CHOOSE THE NUMBER")	;1st screen display
			serout C.7,N2400,(254,195,"OF DROPS 1 - 4?")
			serout C.7,N2400,(254,157,"_")
		pause 500
	let dirsB = 255

drops:
	do
		gosub GetTheKeyPressed
			if key_value = 1 then goto DROPS1
			if key_value = 2 then goto DROPS2
			if key_value = 3 then goto DROPS3
			if key_value = 4 then goto DROPS4
		loop until key_value >0
	return
		
DROPS1:
	 serout C.7,N2400,(254,157,"1");if 1 is keyed in it brings P1 on screen and the pause value ready for altering
	 pause 1000
		serout C.7,N2400,(254,1)
		pause 30
		serout C.7,N2400,(254,134,"P1 -",254,139,#w7)
		pause 30
			do
				gosub Get4DigitNumberIntoW7
				loop until key_value > 0
			goto mainA
		return
	
DROPS2:
	      serout C.7,N2400,(254,157,"2");if 2 is keyed in it brings P1,P2 and P3 on screen and the pause values ready for altering
		pause 1000
		serout C.7,N2400,(254,1)
		pause 30
		serout C.7,N2400,(254,134,"P1 -",254,139,#w7)
		serout C.7,N2400,(254,192,"P2 -",254,197,#w8)
		serout C.7,N2400,(254,202,"P3 -",254,207,#w9)
		pause 30
		do
		gosub Get4DigitNumberIntoW7
		loop until key_value > 0
	      do
	      gosub Get4DigitNumberIntoW8
	      loop until key_value > 0
	      do
	      gosub Get4DigitNumberIntoW9
	      loop until key_value > 0
		goto mainB
		return
	
	
DROPS3:
		serout C.7,N2400,(254,157,"3");if 3 is keyed in it brings P1, P2, P3, P4 and P5 on screen and the pause values ready for altering
		pause 1000
		serout C.7,N2400,(254,1)
		pause 30
		serout C.7,N2400,(254,134,"P1 -",254,139,#w7)
		serout C.7,N2400,(254,192,"P2 -",254,197,#w8)
		serout C.7,N2400,(254,202,"P3 -",254,207,#w9)
		serout C.7,N2400,(254,148,"P4 -",254,153,#w10)
	      serout C.7,N2400,(254,158,"P5 -",254,163,#w11)
		pause 30
		do
		gosub Get4DigitNumberIntoW7
		loop until key_value > 0
	      do
	      gosub Get4DigitNumberIntoW8
	      loop until key_value > 0
	      do
	      gosub Get4DigitNumberIntoW9
	      loop until key_value > 0
		do
	      gosub Get4DigitNumberIntoW10
	      loop until key_value > 0
	      do
	      gosub Get4DigitNumberIntoW11
	      loop until key_value > 0
		goto mainC
		return
	
DROPS4:
		serout C.7,N2400,(254,157,"4");if 4 is keyed in it brings P1, P2, P3, P4, P5, P6 and P7 on screen and the pause values ready for altering
		pause 1000
		serout C.7,N2400,(254,1)
		pause 30
	      serout C.7,N2400,(254,134,"P1 -",254,139,#w7)
	      serout C.7,N2400,(254,192,"P2 -",254,197,#w8)
	      serout C.7,N2400,(254,202,"P3 -",254,207,#w9)
	      serout C.7,N2400,(254,148,"P4 -",254,153,#w10)
	      serout C.7,N2400,(254,158,"P5 -",254,163,#w11)
	      serout C.7,N2400,(254,212,"P6 -",254,217,#w12)
	      serout C.7,N2400,(254,222,"P7 -",254,227,#w13)
	      pause 30
	      do
		gosub Get4DigitNumberIntoW7
		loop until key_value > 0
	      do
	      gosub Get4DigitNumberIntoW8
	      loop until key_value > 0
	      do
	      gosub Get4DigitNumberIntoW9
	      loop until key_value > 0
		do
	      gosub Get4DigitNumberIntoW10
	      loop until key_value > 0
	      do
	      gosub Get4DigitNumberIntoW11
	      loop until key_value > 0
	      do
		gosub Get4DigitNumberIntoW12
		loop until key_value > 0
	      do
	      gosub Get4DigitNumberIntoW13
	      loop until key_value > 0
	      goto mainC
	      return
		
Get4DigitNumberIntoW7: ;Coding to change the pause time via keypad
				Gosub GetKeyPress : w7 = b1
				serout C.7,N2400,(254,139,#w7)
				pause 30
  				Gosub GetKeyPress : w7 = w7 * 10 + b1
  				serout C.7,N2400,(254,139,#w7)
  				pause 30
  				Gosub GetKeyPress : w7 = w7 * 10 + b1
  				serout C.7,N2400,(254,139,#w7)
  				pause 30
 				Gosub GetKeyPress : w7 = w7 * 10 + b1
 				serout C.7,N2400,(254,139,#w7)
 				pause 30
 			Return
 				 
Get4DigitNumberIntoW8:	;Coding to change the pause time via keypad
				Gosub GetKeyPress : w8 = b1
				serout C.7,N2400,(254,197,#w8)
				pause 30
  				Gosub GetKeyPress : w8 = w8 * 10 + b1
  				serout C.7,N2400,(254,197,#w8)
  				pause 30
  				Gosub GetKeyPress : w8 = w8 * 10 + b1
  				serout C.7,N2400,(254,197,#w8)
  				pause 30
 				Gosub GetKeyPress : w8 = w8 * 10 + b1
 				serout C.7,N2400,(254,197,#w8)
 				pause 30 
 			Return
 				 
Get4DigitNumberIntoW9:	;Coding to change the pause time via keypad
				Gosub GetKeyPress : w9 = b1
				serout C.7,N2400,(254,207,#w9)
				pause 30
  				Gosub GetKeyPress : w9 = w9 * 10 + b1
  				serout C.7,N2400,(254,207,#w9)
  				pause 30
  				Gosub GetKeyPress : w9 = w9 * 10 + b1
  				serout C.7,N2400,(254,207,#w9)
  				pause 30
 				Gosub GetKeyPress : w9 = w9 * 10 + b1
 				serout C.7,N2400,(254,207,#w9)
 				pause 30
 			Return
Get4DigitNumberIntoW10:	;Coding to change the pause time via keypad
				Gosub GetKeyPress : w10 = b1
				serout C.7,N2400,(254,153,#w10)
				pause 30
  				Gosub GetKeyPress : w10 = w10 * 10 + b1
  				serout C.7,N2400,(254,153,#w10)
  				pause 30
  				Gosub GetKeyPress : w10 = w10 * 10 + b1
  				serout C.7,N2400,(254,153,#w10)
  				pause 30
 				Gosub GetKeyPress : w10 = w10 * 10 + b1
 				serout C.7,N2400,(254,153,#w10)
 				pause 30
 			Return
Get4DigitNumberIntoW11:	;Coding to change the pause time via keypad
				Gosub GetKeyPress : w11 = b1
				serout C.7,N2400,(254,163,#w11)
				pause 30
  				Gosub GetKeyPress : w11 = w11 * 10 + b1
  				serout C.7,N2400,(254,163,#w11)
  				pause 30
  				Gosub GetKeyPress : w11 = w11 * 10 + b1
  				serout C.7,N2400,(254,163,#w11)
  				pause 30
 				Gosub GetKeyPress : w11 = w11 * 10 + b1
 				serout C.7,N2400,(254,163,#w11)
 				pause 30
 			Return
Get4DigitNumberIntoW12:	;Coding to change the pause time via keypad
				Gosub GetKeyPress : w12 = b1
				serout C.7,N2400,(254,217,#w12)
				pause 30
  				Gosub GetKeyPress : w12 = w12 * 10 + b1
  				serout C.7,N2400,(254,217,#w12)
  				pause 30
  				Gosub GetKeyPress : w12 = w12 * 10 + b1
  				serout C.7,N2400,(254,217,#w12)
  				pause 30
 				Gosub GetKeyPress : w12 = w12 * 10 + b1
 				serout C.7,N2400,(254,217,#w12)
 				pause 30
 			Return
Get4DigitNumberIntoW13:	;Coding to change the pause time via keypad
				Gosub GetKeyPress : w13 = b1
				serout C.7,N2400,(254,227,#w13)
				pause 30
  				Gosub GetKeyPress : w13 = w13 * 10 + b1
  				serout C.7,N2400,(254,227,#w13)
  				pause 30
  				Gosub GetKeyPress : w13 = w13 * 10 + b1
  				serout C.7,N2400,(254,227,#w13)
  				pause 30
 				Gosub GetKeyPress : w13 = w13 * 10 + b1
 				serout C.7,N2400,(254,227,#w13)
 				pause 30
 			Return
GetKeyPress:
	
  			' Wait until no key pressed
 	 Do
    		Gosub GetTheKeyPressed
  			Loop Until b1 = 0
 			 ' Wait until key pressed
 	 Do
   		 Gosub GetTheKeyPressed
  			Loop Until b1 <> 0								
 	 Return
		
GetTheKeyPressed:	;Keypad scan coding
		key_pos = 0
		key_value = 0
	 	High ROW1 : gosub ScanCol : low ROW1
	 	High ROW2 : gosub ScanCol : low ROW2
	 	High ROW3 : gosub ScanCol : low ROW3 
	 	High ROW4 : gosub ScanCol : low ROW4
	 	Return
	 	
	 	ScanCol:
	 		if COL1 = 1 then : key_value = key_pos + 1 : endif
	 		if COL2 = 1 then : key_value = key_pos + 2 : endif
	 		if COL3 = 1 then : key_value = key_pos + 3 : endif
	 		key_pos = key_pos + 3
	 		if key_value = 11 then let key_value = 0 endif	    
   		return
mainA:
	
	do
	  gosub GetTheKeyPressed
		loop until key_value = 12 ; 12 is the # key on keypad and will start the run sequencw when pressed
		goto main1 
	return
main1:                                 ;Gives 1 drop from solenoid
	high solenoid                 
	    pause dropdelay1
	    low solenoid
	goto init
mainB:
	
	do 
	  gosub GetTheKeyPressed          
	      loop until key_value = 12 ; 12 is the # key on keypad and will start the run sequencw when pressed
	      goto main2 
	return
main2:                    ;Gives 2 drops from solenoid
	high solenoid
 	    pause dropdelay1
	      low solenoid
	        pause dropdelay2
	      high solenoid
	    pause dropdelay3
	  low solenoid
	goto init

mainC:
	
	do 
	  gosub GetTheKeyPressed          
	      loop until key_value = 12 ; 12 is the # key on keypad and will start the run sequencw when pressed
	      goto main3 
	return
main3:                    ;Gives 3 drops from solenoid
	high solenoid
	    pause dropdelay1
	      low solenoid
	         pause dropdelay2
	            high solenoid
	               pause dropdelay3
	            low solenoid
	          pause dropdelay4
      	 high solenoid
     	    pause dropdelay5
	 low solenoid
	goto init
	
mainD:
	
	do 
	  gosub GetTheKeyPressed          
	      loop until key_value = 12 
	      goto main4 
	return
main4:                    ;Gives 4 drops from solenoid
	high solenoid
	    pause dropdelay1
	       low solenoid
 	          pause dropdelay2
	             high solenoid
	                pause dropdelay3
	                   low solenoid
	                     pause dropdelay4
	                   high solenoid
	                pause dropdelay5
	              low solenoid
	            pause dropdelay6
	          high solenoid
	        pause dropdelay7
	    low solenoid
	goto init
 

AllyCat

Senior Member
Hi Stewart,

Well that's looking a lot better, but I am rather worried that drops: and DROPSN: don't appear to be subroutines (they're not called by anything), but DO have returns at the end. :confused:

The problem you have with the 0 key is that 0 is used as a "no key pressed" marker (I believe hippy's code used FF). I think you can fix it by moving the "If b1 = 11 ..." to the subsequent subroutine.

You could also make the Get4Digitnumber.... section much more compact. Here's my attempt but I can't test it (and am assuming that b4 and b5 aren't used elsewhere).

Code:
.......
Get4DigitNumberIntoW11:	;Coding to change the pause time via keypad
			Let b4  = 163
			Gosub Get4Digits
			Let w11 = w13
 		Return

Get4DigitNumberIntoW12:	;Coding to change the pause time via keypad
			Let b4  = 217
			Gosub Get4Digits
			Let w12 = w13
 		Return
 		
Get4DigitNumberIntoW13:	;Coding to change the pause time via keypad
			Let b4 = 227			
Get4Digits:
		Let w13 = 0
		For b5 = 1 to 4				
  			Gosub GetKeyPress 
  			w13 = w13 * 10 + b1
  			serout C.7,N2400,(254,b4,#w13)
  			pause 30
  			next b5
 		Return
GetKeyPress:
	
  			' Wait until no key pressed
 	 Do
    		Gosub GetTheKeyPressed
  			Loop Until b1 = 0
 			 ' Wait until key pressed
 	 Do
   		 Gosub GetTheKeyPressed
  			Loop Until b1 <> 0
 		if key_value = 11 then : let key_value = 0 endif	
 	 Return

GetTheKeyPressed:	;Keypad scan coding
		key_pos = 0
		key_value = 0
	 	High ROW1 : gosub ScanCol : low ROW1
	 	High ROW2 : gosub ScanCol : low ROW2
	 	High ROW3 : gosub ScanCol : low ROW3 
	 	High ROW4 : gosub ScanCol : low ROW4
	 	Return
	 	
	 	ScanCol:
	 		if COL1 = 1 then : key_value = key_pos + 1 : endif
	 		if COL2 = 1 then : key_value = key_pos + 2 : endif
	 		if COL3 = 1 then : key_value = key_pos + 3 : endif
	 		key_pos = key_pos + 3	    
   		return
Cheers, Alan.
 
Top