- manual: the boat is controlled by the radio controller
- autonomous: the boat follows a pre-programmed route.
Note: the full software is available on GitHub.
Switching States
First we create a new tab that contains the software for the radio controller. Create a new tab calledRC_Propulsion
. If you have
implemented the code in the previous post, you will probably by now recognize the pattern that was used previously://Radio controls for Propulsion
#define IN_UP 8
#define IN_DOWN 7
#define MAX_PULSE 40
enum Direction{
STOP = 0,
FORWARD = 1,
BACKWARD = 2
};
static Direction direction = STOP;
static int directionTimer = 0;
void RCP_Init(){
pinMode( IN_UP, INPUT ); //Set input mode
pinMode( IN_DOWN, INPUT ); //Set input mode
}
/**
* Update the RC through the timer
*/
void RCP_Update(){
directionTimer++;
}
boolean RCP_Up(){
return digitalRead( IN_UP );
}
boolean RCP_Down(){
return digitalRead( IN_DOWN );
}
int RCP_Direction(){
if( RCP_Up()){
direction = FORWARD;
directionTimer = 0;
} else if( RCP_Down() ){
direction = BACKWARD;
directionTimer = 0;
}
else{
if( directionTimer > MAX_PULSE ){
direction = STOP;
directionTimer = 0;
}
}
return direction;
}
/**
* Returns true if the direction should be forward
*/
boolean RCP_Forward(){
return ( direction == FORWARD );
}
/**
* Returns true if the direction should be backward
*/
boolean RCP_Backward(){
return ( direction == BACKWARD );
}
/**
* Returns true if the direction should be forward
*/
boolean RCP_Stop(){
return ( direction == STOP );
}
Even though the code is more elaborate than the LED control, the general idea is still very much the same. The two inputs determined by the propulsion control are used to determine a direction for the boat. The corresponding variable is used by the rest of the code to steer the boat. It also returns to a
stop
state with a small delay, in order to
become less sensitive to losses in the communication. This means that when the communication is lost, the boat will stop The Left and Right controls are fairly similar and are controlled in the RC_Direction tab:
//Constants
#define IN_LEFT 9
#define IN_RIGHT 10
#define MAX_PULSE 40
enum Steering{
STRAIGHT = 0,
LEFT = 1,
RIGHT = 2,
};
static Steering steering = STRAIGHT;
static int steeringTimer = 0;
void RCD_Init(){
pinMode( IN_LEFT, INPUT );
pinMode( IN_RIGHT, INPUT );
}
/**
* Update the RC through the timer
*/
void RCD_Update(){
steeringTimer++;
}
boolean RCD_Left(){
return digitalRead( IN_LEFT );
}
boolean RCD_Right(){
return digitalRead( IN_RIGHT );
}
int RCD_Steering(){
if( RCD_Left()){
steering = LEFT;
steeringTimer = 0;
} else if( RCD_Right() ){
steering = RIGHT;
steeringTimer = 0;
}
else{
if( steeringTimer > MAX_PULSE ){
steering = STRAIGHT;
steeringTimer = 0;
}
}
return steering;
}
/**
* Returns true if the direction should be forward
*/
boolean RCD_LeftTurn(){
return ( steering == LEFT );
}
/**
* Returns true if the direction should be backward
*/
boolean RCD_RightTurn(){
return ( steering == RIGHT );
}
/**
* Returns true if the direction should be forward
*/
boolean RCD_Straight(){
return ( steering == STRAIGHT );
}
Last we need to define the state controller:
#define BLOCK_BLINK 5
#define AUTO_BLINK 10
#define BLOCK_TIME 80
enum State{
MANUAL,
BLOCKED,
AUTONOMOUS
};
static State state = MANUAL;
enum Control{
INIT,
START,
COMPLETED
};
static Control control;
static int counter;
static int downKeyTeller = 0;
void State_Init(){
state = MANUAL;
control = INIT;
counter = 0;
}
/**
* Update the RC through the timer interrupt
*/
static State prevState;
void State_Update(){
counter++;
downKeyTeller++;
switch( control ){
case INIT:
if( RCP_Down()){
downKeyTeller = 0;
prevState = state;
state = BLOCKED;
control = START;
}
break;
case START:
if( downKeyTeller > BLOCK_TIME ){
state = prevState;
downKeyTeller = 0;
control = COMPLETED;
break;
}
if( RCP_Up() ){
switch( prevState ){
case MANUAL:
state = AUTONOMOUS;
break;
case AUTONOMOUS:
state = MANUAL;
break;
}
control = COMPLETED;
}
break;
default:
if( !RCP_Down())
control = INIT;
break;
}
}
/**
* Control the states
*/
void State_Control(){
switch( state ){
case MANUAL:
Motor_ManualControl();
Rudder_ManualControl();
break;
case AUTONOMOUS:
Autonomous_Motion();
break;
default:
break;
}
}
byte State_GetState(){
return state;
}
boolean State_IsBlocked(){
return state == BLOCKED;
}
boolean State_IsManual(){
return state == MANUAL;
}
boolean State_IsAutonomous(){
return state == AUTONOMOUS;
}
This code bit of code is is a bit more elaborate than the previous, but still by large follows the same conventions as the previous. The
State_Update()
function is the most complex part, and determines the switching of state through the propulsion control. This part, like all Update()
functions is managed by the Timer. The
State_Control()
function is called by the main loop. When
in MANUAL
mode, the function directly controls the motor and
rudder functions, and switches to AUTONOMY
when the state
changes correspondingly.With these changes, we only need to add two tabs for the motor and the rudder in order for the code to compile, and to let our boat to be controlled by the Arduino. We will discuss this code in the next post.
Geen opmerkingen:
Een reactie posten