Legacy Forum: Preserving Nearly 20 Years of Community History - A Time Capsule of Discussions, Memories, and Shared Experiences.

programing questions

Hitec robotics including ROBONOVA humanoid, HSR-8498HB servos, MR C-3024 Controllers and RoboBasic
15 postsPage 1 of 1
15 postsPage 1 of 1

programing questions

Post by aguiloco » Thu Oct 04, 2007 8:49 pm

Post by aguiloco
Thu Oct 04, 2007 8:49 pm

Hi! I have several problems to program the RN for more than simple movement scenes. I want to use subrutines to create some kind of cotinuous moves, for example:

-pressing key 1 the left arm up´s and stay for comands, with the arm standing up

-pressing key 2 call a routine to move the right arm and come back to the key 1 state

-pressing key 3 go to main and get down the left arm

Is really hard to me program something like this, if someone could put an example code and explain the different comand would be great, this will help me to understand this kind of comands.

PD:this program will be used to manage a new kind of RN´s accessory than I´m developing... coming soon
Hi! I have several problems to program the RN for more than simple movement scenes. I want to use subrutines to create some kind of cotinuous moves, for example:

-pressing key 1 the left arm up´s and stay for comands, with the arm standing up

-pressing key 2 call a routine to move the right arm and come back to the key 1 state

-pressing key 3 go to main and get down the left arm

Is really hard to me program something like this, if someone could put an example code and explain the different comand would be great, this will help me to understand this kind of comands.

PD:this program will be used to manage a new kind of RN´s accessory than I´m developing... coming soon
aguiloco
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 47
Joined: Fri Dec 01, 2006 1:00 am

Post by aguiloco » Sun Oct 07, 2007 6:38 pm

Post by aguiloco
Sun Oct 07, 2007 6:38 pm

I made this code, it works and I can make a movement and return to the previous state, but surely isnt a good code, I cant understand the REMOCON comands well...

GOTO AUTO
FILL 255,10000

DIM RR AS BYTE
DIM A AS BYTE

CONST ID = 0 ' 1:0, 2:32, 3:64, 4:96,

'== Action command check (50 - 82)
IF RR > 50 AND RR <83> 50 THEN RETURN
RR = 0
GOTO MAIN

main_exit2:
IF RR > 50 THEN RETURN
RR = 0
GOTO pistola_quieta

'===============================================
k1:
GOSUB cojer_arma
GOSUB pistola_quieta
GOTO main_exit
k2:
GOSUB giro_izquierda

k3:
GOSUB giro_derecha

k4:
GOSUB standard_pose
GOTO MAIN

k5:
GOSUB giro_izquierda2


k6:
GOSUB giro_derecha2

k7:
GOSUB recarga_arma


standard_pose:

MOVE G6A,100, 76, 145, 93, 100, 100
MOVE G6D,100, 76, 145, 93, 100, 100
MOVE G6B,100, 30, 80, 100, 100, 100
MOVE G6C,100, 30, 80, 100, 100, 100
WAIT

RETURN

'===============================================

cojer_arma:

SPEED 8
MOVE G6B,146, 16, 44, 25, 104, 100
MOVE G6B,146, 16, 10, 25, 104, 100
MOVE G6B,146, 16, 44, 25, 104, 100
MOVE G6B,146, 16, 10, 25, 104, 100
MOVE G6B,146, 16, 44, 25, 104, 100
WAIT

DELAY 1000

MOVE G6B,100, 30, 80, 100, 100, 100
MOVE G6C,190, 30, 80, 186, 100, 100
MOVE G6A,100, 76, 145, 85, 100, 100
MOVE G6D,100, 76, 145, 85, 100, 100
DELAY 3000
MOVE G6C,190, 30, 80, 40, 100, 100
DELAY 3000

SPEED 3
GOSUB pistola_quieta


'===============================================

pistola_quieta:

MOVE G6C,190, 30, 80, 40, 100, 100

IF RR = 0 THEN GOTO MAIN2

ON RR GOTO pistola_quieta,K1,K2,K3,K4,K5,K6,K7
GOTO main_exit2

'===============================================

MAIN2:
A = REMOCON(1)
A = A - ID
ON A GOTO pistola_quieta,K1,K2,K3,K4,K5,K6,K7
GOTO pistola_quieta

'===============================================

giro_izquierda:
MOVE G6C,190, 10, 80, 40, 100, 100
WAIT
A = REMOCON (1)
A = A - ID
ON A GOTO giro_izquierda,K1,K2,K3,K4,K5,K6,K7
GOTO giro_izquierda

'===============================================

giro_derecha:
MOVE G6C,190, 60, 80, 40, 100, 100
A = REMOCON (1)
A = A - ID
ON A GOTO giro_derecha,K1,K2,K3,K4,K5,K6,K7
GOTO giro_derecha

'===============================================

giro_izquierda2:
MOVE G6C,190, 10, 55, 40, 100, 100
DELAY 2000
GOSUB giro_izquierda

'===============================================

giro_derecha2:
MOVE G6C,190, 80, 80, 40, 100, 100
DELAY 2000
GOSUB giro_derecha

'================================================

recarga_arma:

SPEED 3
MOVE G6B,174, 44, 10, 106, 100, 100
MOVE G6C,165, 19, 43, 47, 100, 100
WAIT
MOVE G6B,178, 30, 10, 106, 100, 100
MOVE G6C,165, 18, 40, 47, 100, 100
WAIT

MOVE G6B,178, 18, 10, 106, 100, 100
MOVE G6C,165, 14, 39, 47, 100, 100
WAIT

MOVE G6B,178, 10, 10, 106, 100, 100
MOVE G6C,160, 10, 43, 47, 100, 100
WAIT

MOVE G6B,178, 18, 10, 106, 100, 100
MOVE G6C,165, 14, 41, 47, 100, 100

MOVE G6B,178, 30, 10, 106, 100, 100
MOVE G6C,165, 18, 41, 47, 100, 100

MOVE G6B,174, 44, 10, 106, 100, 100
MOVE G6C,165, 19, 43, 47, 100, 100
WAIT

MOVE G6B,100, 30, 80, 100, 100, 100

GOSUB pistola_quieta
I made this code, it works and I can make a movement and return to the previous state, but surely isnt a good code, I cant understand the REMOCON comands well...

GOTO AUTO
FILL 255,10000

DIM RR AS BYTE
DIM A AS BYTE

CONST ID = 0 ' 1:0, 2:32, 3:64, 4:96,

'== Action command check (50 - 82)
IF RR > 50 AND RR <83> 50 THEN RETURN
RR = 0
GOTO MAIN

main_exit2:
IF RR > 50 THEN RETURN
RR = 0
GOTO pistola_quieta

'===============================================
k1:
GOSUB cojer_arma
GOSUB pistola_quieta
GOTO main_exit
k2:
GOSUB giro_izquierda

k3:
GOSUB giro_derecha

k4:
GOSUB standard_pose
GOTO MAIN

k5:
GOSUB giro_izquierda2


k6:
GOSUB giro_derecha2

k7:
GOSUB recarga_arma


standard_pose:

MOVE G6A,100, 76, 145, 93, 100, 100
MOVE G6D,100, 76, 145, 93, 100, 100
MOVE G6B,100, 30, 80, 100, 100, 100
MOVE G6C,100, 30, 80, 100, 100, 100
WAIT

RETURN

'===============================================

cojer_arma:

SPEED 8
MOVE G6B,146, 16, 44, 25, 104, 100
MOVE G6B,146, 16, 10, 25, 104, 100
MOVE G6B,146, 16, 44, 25, 104, 100
MOVE G6B,146, 16, 10, 25, 104, 100
MOVE G6B,146, 16, 44, 25, 104, 100
WAIT

DELAY 1000

MOVE G6B,100, 30, 80, 100, 100, 100
MOVE G6C,190, 30, 80, 186, 100, 100
MOVE G6A,100, 76, 145, 85, 100, 100
MOVE G6D,100, 76, 145, 85, 100, 100
DELAY 3000
MOVE G6C,190, 30, 80, 40, 100, 100
DELAY 3000

SPEED 3
GOSUB pistola_quieta


'===============================================

pistola_quieta:

MOVE G6C,190, 30, 80, 40, 100, 100

IF RR = 0 THEN GOTO MAIN2

ON RR GOTO pistola_quieta,K1,K2,K3,K4,K5,K6,K7
GOTO main_exit2

'===============================================

MAIN2:
A = REMOCON(1)
A = A - ID
ON A GOTO pistola_quieta,K1,K2,K3,K4,K5,K6,K7
GOTO pistola_quieta

'===============================================

giro_izquierda:
MOVE G6C,190, 10, 80, 40, 100, 100
WAIT
A = REMOCON (1)
A = A - ID
ON A GOTO giro_izquierda,K1,K2,K3,K4,K5,K6,K7
GOTO giro_izquierda

'===============================================

giro_derecha:
MOVE G6C,190, 60, 80, 40, 100, 100
A = REMOCON (1)
A = A - ID
ON A GOTO giro_derecha,K1,K2,K3,K4,K5,K6,K7
GOTO giro_derecha

'===============================================

giro_izquierda2:
MOVE G6C,190, 10, 55, 40, 100, 100
DELAY 2000
GOSUB giro_izquierda

'===============================================

giro_derecha2:
MOVE G6C,190, 80, 80, 40, 100, 100
DELAY 2000
GOSUB giro_derecha

'================================================

recarga_arma:

SPEED 3
MOVE G6B,174, 44, 10, 106, 100, 100
MOVE G6C,165, 19, 43, 47, 100, 100
WAIT
MOVE G6B,178, 30, 10, 106, 100, 100
MOVE G6C,165, 18, 40, 47, 100, 100
WAIT

MOVE G6B,178, 18, 10, 106, 100, 100
MOVE G6C,165, 14, 39, 47, 100, 100
WAIT

MOVE G6B,178, 10, 10, 106, 100, 100
MOVE G6C,160, 10, 43, 47, 100, 100
WAIT

MOVE G6B,178, 18, 10, 106, 100, 100
MOVE G6C,165, 14, 41, 47, 100, 100

MOVE G6B,178, 30, 10, 106, 100, 100
MOVE G6C,165, 18, 41, 47, 100, 100

MOVE G6B,174, 44, 10, 106, 100, 100
MOVE G6C,165, 19, 43, 47, 100, 100
WAIT

MOVE G6B,100, 30, 80, 100, 100, 100

GOSUB pistola_quieta
aguiloco
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 47
Joined: Fri Dec 01, 2006 1:00 am

Post by aguiloco » Tue Oct 09, 2007 9:13 pm

Post by aguiloco
Tue Oct 09, 2007 9:13 pm

I find the way to make the movements than I want, removing the "GOTO standart_pose" the RN stand at the last position, really simple...

I have more questions, is posible to increase the servo angle using only one button? for example G6B 100, 100, 100, press k1 and go to G6B 101, 100, 100, one more k1 and go to G6B 102, 100, 100. In summary, to apply a variable to the servo movements
I find the way to make the movements than I want, removing the "GOTO standart_pose" the RN stand at the last position, really simple...

I have more questions, is posible to increase the servo angle using only one button? for example G6B 100, 100, 100, press k1 and go to G6B 101, 100, 100, one more k1 and go to G6B 102, 100, 100. In summary, to apply a variable to the servo movements
aguiloco
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 47
Joined: Fri Dec 01, 2006 1:00 am

Post by Gort » Wed Oct 10, 2007 1:34 pm

Post by Gort
Wed Oct 10, 2007 1:34 pm

That should work. I think that it would be something like this.

DIM Move_variable AS BYTE




k1:
Move_variable = Move_variable +1

G6B Move_variable,100, 100, 100

Keep up the posts. You are giving me cool programming ideas to think about.
That should work. I think that it would be something like this.

DIM Move_variable AS BYTE




k1:
Move_variable = Move_variable +1

G6B Move_variable,100, 100, 100

Keep up the posts. You are giving me cool programming ideas to think about.
Gort
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 555
Joined: Wed May 31, 2006 1:00 am
Location: KC, MO, USA

Post by i-Bot » Wed Oct 10, 2007 2:34 pm

Post by i-Bot
Wed Oct 10, 2007 2:34 pm

What is the G6B line ?

The move command will only take numbers or constants.
What is the G6B line ?

The move command will only take numbers or constants.
i-Bot
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 1142
Joined: Wed May 17, 2006 1:00 am

Post by Gort » Wed Oct 10, 2007 5:40 pm

Post by Gort
Wed Oct 10, 2007 5:40 pm

I have tried to find that restriction for the move command in the Robobasic manual but have not found it? If that will not work, what about the Bit operators.
*****A <<1>> 1*******

It sounds like a good thing to test.
I have tried to find that restriction for the move command in the Robobasic manual but have not found it? If that will not work, what about the Bit operators.
*****A <<1>> 1*******

It sounds like a good thing to test.
Gort
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 555
Joined: Wed May 31, 2006 1:00 am
Location: KC, MO, USA

Post by aguiloco » Wed Oct 10, 2007 7:53 pm

Post by aguiloco
Wed Oct 10, 2007 7:53 pm

I was working on this and I think isn´t possible... Only constant or numbers can work with "MOVE" comands, and cant change the value of a constant...

Any idea?
I was working on this and I think isn´t possible... Only constant or numbers can work with "MOVE" comands, and cant change the value of a constant...

Any idea?
aguiloco
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 47
Joined: Fri Dec 01, 2006 1:00 am

Post by NovaOne » Wed Oct 10, 2007 8:23 pm

Post by NovaOne
Wed Oct 10, 2007 8:23 pm

i-Bot is a master MR-C3024 hacker, if anyone can add this facility its Richard

It would be another excellent i-Bot RN-1 Flash upgrade 8)

(Sorry Richard, I'm sure you have better projects to work on.)

Chris
i-Bot is a master MR-C3024 hacker, if anyone can add this facility its Richard

It would be another excellent i-Bot RN-1 Flash upgrade 8)

(Sorry Richard, I'm sure you have better projects to work on.)

Chris
NovaOne
Savvy Roboteer
Savvy Roboteer
Posts: 405
Joined: Thu Jul 05, 2007 7:30 am

Post by Gort » Wed Oct 10, 2007 8:24 pm

Post by Gort
Wed Oct 10, 2007 8:24 pm

I can only think of two other things to try.

1. MOVE G6B CONSTANT_variable + 1, 100, 100, 100, 100

2.

DIM button_push as integer

Button_push = 0

K1:

If button_push = 0

MOVE G6B 100,100,100 ……………………………
button_push = button_push +1

ElseIf button_push = 1

MOVE G6B 101,100,100 ………………………………
button_push = button_push +1

ElseIf button_push = 2

MOVE G6B 102,100,100…………………………………..
button_push = button_push +1

ElseIf button_push = 3

MOVE G6B 103,100,100…………………………………..
button_push = button_push +1

ELSE
MOVE G6B 100,100,100…………………………………..

END-IF.

I just wish Robobasic was not so limited or we could program in Java or C.
I can only think of two other things to try.

1. MOVE G6B CONSTANT_variable + 1, 100, 100, 100, 100

2.

DIM button_push as integer

Button_push = 0

K1:

If button_push = 0

MOVE G6B 100,100,100 ……………………………
button_push = button_push +1

ElseIf button_push = 1

MOVE G6B 101,100,100 ………………………………
button_push = button_push +1

ElseIf button_push = 2

MOVE G6B 102,100,100…………………………………..
button_push = button_push +1

ElseIf button_push = 3

MOVE G6B 103,100,100…………………………………..
button_push = button_push +1

ELSE
MOVE G6B 100,100,100…………………………………..

END-IF.

I just wish Robobasic was not so limited or we could program in Java or C.
Gort
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 555
Joined: Wed May 31, 2006 1:00 am
Location: KC, MO, USA

Post by aguiloco » Wed Oct 10, 2007 9:09 pm

Post by aguiloco
Wed Oct 10, 2007 9:09 pm

I find another way to do it, using the SERVO comand we van apply a variable to a servo angle, but I need to know whats the code to read the REMOCOM signals from one specific key and store it at a variable, and how to wait for the next key press. I did that code, but doesn´t work...

DIM B AS BYTE
DIM C AS BYTE

C=0
C=REMOCON(1) "when I use this, it reads the signall from any remocon´s key?"
C= C+1
B=10
B=B+C
SERVO 13,B

I think using this method is posible to move one servo using a variable controlled by a key, but I dont know how to compile it...
I find another way to do it, using the SERVO comand we van apply a variable to a servo angle, but I need to know whats the code to read the REMOCOM signals from one specific key and store it at a variable, and how to wait for the next key press. I did that code, but doesn´t work...

DIM B AS BYTE
DIM C AS BYTE

C=0
C=REMOCON(1) "when I use this, it reads the signall from any remocon´s key?"
C= C+1
B=10
B=B+C
SERVO 13,B

I think using this method is posible to move one servo using a variable controlled by a key, but I dont know how to compile it...
aguiloco
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 47
Joined: Fri Dec 01, 2006 1:00 am

Post by i-Bot » Wed Oct 10, 2007 10:10 pm

Post by i-Bot
Wed Oct 10, 2007 10:10 pm

It's a long time since I used the REMCON, but if I recall it returns 0 if no key was pressed, else the key value. You should ignore if zero is returned. else take the value and add an offset and scale to the servo range. Do the servo command. Then goto do it again

You are correct that the servo command takes variables, though you lose the point to point capability.

There are a few tricks to use variables in move commands using pokes and rompokes, but they limit the code flexibility and add complexity.

As Gort says, I usually program the RoboNova in C for greater flexibility in the move commands, though still use RoboBasic for prototyping the moves. My C move function takes variables OK, but otherwise is the same as the RoboBasic move including PTP and Gyro

extern uint8_t move(uint8_t, uint8_t, uint8_t *); // as RoboBasic MOVE()

The Robobasic limitation to a constant is in the C3024 firmware as well as compiler, so not an easy fix to add variables. I am thinking I might be able to patch the C3024 code to directly inject variables in place of Gyro readings. I was planning this is to take the joystick values from the PS2 controller, but it could be any variable
It's a long time since I used the REMCON, but if I recall it returns 0 if no key was pressed, else the key value. You should ignore if zero is returned. else take the value and add an offset and scale to the servo range. Do the servo command. Then goto do it again

You are correct that the servo command takes variables, though you lose the point to point capability.

There are a few tricks to use variables in move commands using pokes and rompokes, but they limit the code flexibility and add complexity.

As Gort says, I usually program the RoboNova in C for greater flexibility in the move commands, though still use RoboBasic for prototyping the moves. My C move function takes variables OK, but otherwise is the same as the RoboBasic move including PTP and Gyro

extern uint8_t move(uint8_t, uint8_t, uint8_t *); // as RoboBasic MOVE()

The Robobasic limitation to a constant is in the C3024 firmware as well as compiler, so not an easy fix to add variables. I am thinking I might be able to patch the C3024 code to directly inject variables in place of Gyro readings. I was planning this is to take the joystick values from the PS2 controller, but it could be any variable
i-Bot
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 1142
Joined: Wed May 17, 2006 1:00 am

Post by DirtyRoboto » Thu Oct 11, 2007 11:50 am

Post by DirtyRoboto
Thu Oct 11, 2007 11:50 am

Make the standard pose using variables with the values set before main. After a move such as raising arm, copy the value of the current pos into the variables and then jump to standard pose where your new position will be written into the standard pose variables.
So you will have to include a servo read at the end of every move before jumping back to main.

I have about 4 active standard poses and two modular poses that will grab values from the last move done to modify the pose characteristics.

(standard_pose must be made using SERVO command, all moves are ok using MOVE command)
Make the standard pose using variables with the values set before main. After a move such as raising arm, copy the value of the current pos into the variables and then jump to standard pose where your new position will be written into the standard pose variables.
So you will have to include a servo read at the end of every move before jumping back to main.

I have about 4 active standard poses and two modular poses that will grab values from the last move done to modify the pose characteristics.

(standard_pose must be made using SERVO command, all moves are ok using MOVE command)
In servo's we trust!
DirtyRoboto
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 412
Joined: Tue Sep 19, 2006 1:00 am
Location: London

Post by aguiloco » Thu Oct 11, 2007 9:36 pm

Post by aguiloco
Thu Oct 11, 2007 9:36 pm

I did that code:

k4: GOSUB mueve_mas
k5: GOSUB mueve_menos
mueve_mas:
C=REMOCON(1)
IF C=0 THEN B=B+0
IF C=1 THEN B=B+1
SERVO 13,B
RETURN

And this works! I can increase 1º to one specific servo using only one key, but now the problem is to decrease 1º, with the comand "C=REMOCON(1)" I take a 1 if I press the key K4 and store at C, doing the servo move, but how I can decrease 1º? if I make another subroutine like this:

mueve_menos:
C=REMOCON(1)
IF C=0 THEN B=B+0
IF C=1 THEN B=B-1
SERVO 13,B
RETURN

Robobasic can´t diference between k4 and k5 and increase or decrease the angle aleatory. I need to specify to robobasic the key who affect the variable, because I cant do it using only 0 or 1 at the remocon signal...
I did that code:

k4: GOSUB mueve_mas
k5: GOSUB mueve_menos
mueve_mas:
C=REMOCON(1)
IF C=0 THEN B=B+0
IF C=1 THEN B=B+1
SERVO 13,B
RETURN

And this works! I can increase 1º to one specific servo using only one key, but now the problem is to decrease 1º, with the comand "C=REMOCON(1)" I take a 1 if I press the key K4 and store at C, doing the servo move, but how I can decrease 1º? if I make another subroutine like this:

mueve_menos:
C=REMOCON(1)
IF C=0 THEN B=B+0
IF C=1 THEN B=B-1
SERVO 13,B
RETURN

Robobasic can´t diference between k4 and k5 and increase or decrease the angle aleatory. I need to specify to robobasic the key who affect the variable, because I cant do it using only 0 or 1 at the remocon signal...
aguiloco
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 47
Joined: Fri Dec 01, 2006 1:00 am

Post by Gort » Fri Oct 12, 2007 8:00 pm

Post by Gort
Fri Oct 12, 2007 8:00 pm

I think this would work better?

k4: GOSUB mueve_mas

k5: GOSUB mueve_menos


mueve_mas:

B = B + 1
SERVO 13,B
RETURN


mueve_menos:

B = B - 1
SERVO 13,B
RETURN
I think this would work better?

k4: GOSUB mueve_mas

k5: GOSUB mueve_menos


mueve_mas:

B = B + 1
SERVO 13,B
RETURN


mueve_menos:

B = B - 1
SERVO 13,B
RETURN
Gort
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 555
Joined: Wed May 31, 2006 1:00 am
Location: KC, MO, USA

Post by aguiloco » Sun Oct 14, 2007 8:46 pm

Post by aguiloco
Sun Oct 14, 2007 8:46 pm

Yes, this is better than mine, really I didn´t need to read the remocon signal if I can use subroutienes to increase or decrease the variable. I had to refine a little the code to make it work fine, thanks Gort!

k4:
GOSUB mueve_mas
GOTO main_exit

k5:
GOSUB mueve_menos
GOTO main_exit


mueve_mas:
SPEED 2
B=B+1
IF B>120 THEN B=B-1
SERVO 13,B
RETURN


mueve_menos:
SPEED 2
B=B-1
IF B<10 THEN B=B+1
SERVO 13,B
RETURN
Yes, this is better than mine, really I didn´t need to read the remocon signal if I can use subroutienes to increase or decrease the variable. I had to refine a little the code to make it work fine, thanks Gort!

k4:
GOSUB mueve_mas
GOTO main_exit

k5:
GOSUB mueve_menos
GOTO main_exit


mueve_mas:
SPEED 2
B=B+1
IF B>120 THEN B=B-1
SERVO 13,B
RETURN


mueve_menos:
SPEED 2
B=B-1
IF B<10 THEN B=B+1
SERVO 13,B
RETURN
aguiloco
Savvy Roboteer
Savvy Roboteer
User avatar
Posts: 47
Joined: Fri Dec 01, 2006 1:00 am


15 postsPage 1 of 1
15 postsPage 1 of 1