vrijdag 26 juni 2015

Controlling the Movement

In the previous post we:
  1. prepared a radio controlled boat to operate on an Arduino Uno board,
  2. set up the general software architecture that follows a modular, multi-tasking approach
  3. Made the boat change state between Manual and Autonomous by 'switching gears' on the RC control.
In this post, we will include the actual controls for the motor and rudder.

Note: the full software is available on GitHub

Controlling the Movement

As was mentioned earlier, the motor controller is a SparkFun Motor Driver Shield. The product page gives detailed schematics of the motor shield, and also provides example code. Based on this, the propulsion becomes a matter of copying and pasting, with a few modifications:

//Radio controls for Propulsion
#define MOTOR 3
#define MOTOR_RICHTING 12

static int fade = 0;

void Motor_Init(){
  pinMode( MOTOR, OUTPUT);  //Set control pins to be outputs
  pinMode( MOTOR_RICHTING, OUTPUT);
}

/**
 * Manual control motor controller
 */
static byte mdir = 0;
void Motor_ManualControl(){
  if( !State_IsManual() )
    return;
  byte dir = RCP_Direction();
  if( RCP_Forward() ){
      if( dir != mdir ){
        forw();         //Set Motors to go forward Note : No pwm is defined with the for function, so that fade in and out works
        fadein();       //fade in from 0-255
      }
      forward();      //continue full speed forward
  }else if ( RCP_Backward() ){
     if( dir != mdir ){
       back();         //Set motors to revers. Note : No pwm is defined with the back function, so that fade in and out works
       fadein();       //fade in from 0-255
     }
    backward();     //full speed backward
  }else if ( RCP_Stop() ){
    stopped();      // stop for 2 seconds
  }
  mdir = dir;
  //Serial.print( dir );
}

void forw() // no pwm defined
{
  digitalWrite(MOTOR_RICHTING, HIGH);  //Reverse motor direction, 1 high, 2 low
}

void back() // no pwm defined
{
  digitalWrite(MOTOR_RICHTING, LOW);  //Set motor direction, 1 low, 2 high
}

void forward() //full speed forward
{
  digitalWrite(MOTOR_RICHTING, HIGH);  //Reverse motor direction, 1 high, 2 low
  analogWrite(MOTOR, 255);    //set both motors to run at (100/255 = 39)% duty cycle
}

void backward() //full speed backward
{
  digitalWrite(MOTOR_RICHTING, LOW);  //Set motor direction, 1 low, 2 high
  analogWrite(MOTOR, 255);   //set both motors to run at 100% duty cycle (fast)
}

void stopped() //stop
{
  digitalWrite(MOTOR_RICHTING, LOW); //Set motor direction, 1 low, 2 high
  analogWrite(MOTOR, 0);    //set both motors to run at 100% duty cycle (fast)
}

void fadein()
{
  // fade in from min to max in increments of 5 points:
  for(int fadeValue = 0 ; fadeValue <= 255; fadeValue +=5)
  {
     // sets the value (range from 0 to 255):
    analogWrite(MOTOR, fadeValue);  
    // wait for 30 milliseconds to see the dimming effect   
    delay(30);                           
  }
}

void fadeout()
{
  // fade out from max to min in increments of 5 points:
  for(int fadeValue = 255 ; fadeValue >= 0; fadeValue -=5)
  {
    // sets the value (range from 0 to 255):
    analogWrite(MOTOR, fadeValue);
    // wait for 30 milliseconds to see the dimming effect   
    delay(30); 
}
}

void astop()  //stop motor A
{
  analogWrite(MOTOR, 0); //set both motors to run at 100% duty cycle (fast)
}


The majority of the functions are provided on the SparkFun website. The only new function is Motor_ManualControl(), which uses these functions to respond to the radio controls. Obviously, the Rudder control can be controlled in a similar fashion, when a motor is used. This control depends  on the mechanics of the boat that is used, as so the example code will not be included here.

In our particular case, we decided to change the boat a bit, so that the servo motor that is shipped with the Arduino Starter Kit could be used instead. The resulting code is as follows:
#include <Servo.h>

#define RUDDER 2 //input 2
#define MAX_ANGLE 17 //maximum turn of the angle

Servo rudder;

int angle;

void Rudder_Init(){
   //Activeer roer
  rudder.attach( RUDDER );
  angle = 0;
}

/**
 * Manual control motor controller
 */
void Rudder_ManualControl(){
  if( !State_IsManual() )
    return;
  byte dir = RCD_Steering();
  if( RCD_Left() ){
    Rudder_Right();
  }else if ( RCD_Right() ){
     Rudder_Left();
  }else if ( RCD_Straight() ){
    Rudder_Straight(); //reset
  }
  rudder.write( MAX_ANGLE + angle );
}

void Rudder_Right(){
  if( angle < MAX_ANGLE )
     angle++;
  rudder.write( MAX_ANGLE + angle );
}

void Rudder_Left(){
  if( angle > 0 )
     angle--;
  rudder.write( MAX_ANGLE + angle );
}

void Rudder_Straight(){
  rudder.write( MAX_ANGLE ); 
}


This code uses the default Servo.h library that is provided with the Arduino IDE. The code was based on Project Five, "Mood Cue", from the Arduino Projects Book that is shipped with the starter kit.  A definite advantage of the servo is that the rudder does not have to move between its maximum angle, but instead can move gradually by removing the 'Straight' option, to which the rudder defaults if no pulse is detected from the radio controller.

With this, only one more tab is needed to make the software compile without problems, and give the boat some autonomy. Add an 'Autonomy' tab and add code according to the following template:

  enum Steps{
   ST_REST = 0,
   ST_FORWARD = 5,
   ST_TURN = 10,
   ST_BACK = 15 
};

static int step = ST_REST;

void Autonomous_Init(){
  step = ST_REST;
}

void Autonomous_Update(){
  step++;
}

void Autonomous_Motion(){
  if( !State_IsAutonomous() )
    return;
  switch( step ){
    case ST_REST:
      break;
    case ST_FORWARD:
      fadein();
      forw();
      break;
    case ST_TURN:
      Rudder_Right();
      forw();
      break;
    case ST_BACK:
      forw();
      fadeout();
      break;
    default:
      break;
  }
}

This piece of codeis called when the 'switch gears' action has been carried out, and consists of a number of steps (in seconds), in which the boat will automatically carry out some of the motor and rudder tasks. In this case, the boat moves away, turns and comes back, but of course you are free to implement your own schedules. you could even record a movement with the radio control, for instance when you tip the left gear in autonomous mode, which is carried out the next time 'autonomy' is selected! Have fun with your ro-boat!

Switching States

In the previous post we prepared a radio controlled boat to operate on an Arduino Uno board, and set up the general software architecture. In this post, we will implement the states that the boat can be in:
  • manual: the boat is controlled by the radio controller
  • autonomous: the boat follows a pre-programmed route.
A nifty little trick with the radio controller will help us to switch between the two. If the speed control is pulled backward and forward within one second, the boat will switch between these states. It is a bit like switching gears in your car. One consequence of this approach is that an additional state called BLOCKED is required when the speed control is pulled backward. After a second, it will revert to normal operation.

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 called RC_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.

Software Setup

In the previous post we prepared a radio controlled boat to operate on an Arduino Uno board. Now we can start to program it. Usually the recipes for Arduino will include a lot of delay functions, which basically stops the programme for a certain amount of time, and thus leaves the boat uncontrollable. The solution presented here will use a simple form of multi-tasking, by means of the Timer) function.It also uses the 'Tabs' option of the Arduino IDE to enhance a modular approach, even though the code is predominantly written in ANSI-C.

Note: the full software is available on GitHub.

Software Setup

First we create a new Arduino project called rc_boat.  We open the Arduino IDE and select

File ?New, and call the file rc_boat (or something else if you prefer).

 The main function always contains a setup() function for initialisation, and a loop() for the actual control. We now replace the default (barebones) code with the following:

//Global variabeles
unsigned int centis = 0; //centi- seconds
byte seconden = 0; //seconds
byte minuten = 0; //minutes
int alarm = 0;

void setup() {

   //Set up Radio Control
   RCP_Init();
   RCD_Init();
   State_Init();
   Autonomous_Init();
  
  //Initialise LEDS
  TopLed_Init();
  LedControl_Init();
  LedControl_Disco( HIGH );
  TopLed_Out( HIGH ); 
  LedControl_OnBoard( HIGH ); 

  //Let the LEDs blink for a second
  delay(1000);
 
  TopLed_Out( LOW  );
  LedControl_Disco( LOW ); 
  LedControl_OnBoard( LOW ); 

  cli();//deactivate interrupts
  tmr0_init();
  sei();//Activate interrupts

//Initialise the motor and rudder control
  Motor_Init();
  Rudder_Init();
}

void loop() {
  State_Control();
  //LedControl_Disco( HIGH );
  delay(100);
}


The code is currently still quite abstract, but the general idea is to:
  • Initialise the Radio controller and other stuff
  • Initialise the LEDs and let them burn for a second
  • Set up the Timer for multi-tasking
  • Initialise the motor and rudder controllers
  • Run the programme (loop).
We can now begin to fill in the details. First add a Timer function. In order to do so, we add a 'new tab' to the programme, with the ?button on the right hand side of the IDE, and call it Timer0. The code is as follows

 //Constants
#define CMR_SECONDE 100 //Count to 10 to time every second
#define CMR_MINUUT 5901 //Count to 609 om time every minute

static byte secondCounter = 0;        //Seconds counter
static unsigned int minuteCounter = 0; //16-bits minute counter

//Initialise timer 0;
void tmr0_init(){
  TCCR0A = 0;   // TCCR0A register = 0
  TCCR0B = 0;   // TCCR0B as well
  TCNT0  = 0;   // Reset the counter
  OCR0A = 156;  // CMR = 156
  TCCR0A |= (1 << WGM01); //CTC mode
  TCCR0B |= (1 << CS02) | (1 << CS00);//Prescaler = 1024  
  TIMSK0 |= (1 << OCIE0A); // Activate interrupt Timer 0
}

/**
 * Respond to an interrupt of timer 0
 */
ISR(TIMER0_COMPA_vect){//timer0 interrupt
  RCP_Update();
  RCD_Update();
  State_Update();
  TopLed_Update();

  centis++;
  secondCounter++; //count up every interrupt
  minuteCounter++; // count up every interrupt
 
  //Monitor_Puls();
  if( secondCounter == CMR_SECONDE ){
    Autonomous_Update();
    secondCounter = 0;
    seconden = ( ++seconden ) % 60;
  }
  if( minuteCounter == CMR_MINUUT ){
    minuteCounter = 0;
    minuten = ( ++minuten ) % 60;
  }
}


The timer is programmed to fire every 10 msec, after which two counters -for seconds and minutes-  count up. Some of the control software is updated every 10 milliseconds, like RC, State or  TopLed.  But the code that controls the Autonomy is updated every second. The tmr_init code is called in the setup of the main programme. This should be enough to activate the timer.

From this point on, all we need to do is to activate the various controllers. For instance, the boat has two LEDs, one of which is mounted under the deck. This LED, called TopLED is activated on pin 5 of Arduino's port D:

#define LED_TOP 5 //Output PortD pin 5

/**
 * Initialise the Top LED
 */
void TopLed_Init(){
  pinMode( LED_TOP, OUTPUT);//Set the pin for output
}

/**
 * Update the top led through the timer interrupt
 */
static boolean led_on = false;
void TopLed_Update(){
  byte status = State_GetState();
  switch( status ){
    case AUTONOMOUS:
      if(( counter % AUTO_BLINK ) == 0 )
        led_on = !led_on;
     break;
    case BLOCKED:
      if(( counter % BLOCK_BLINK ) == 0)
        led_on = !led_on;
      break;
    default:
      led_on = true;
  }
  TopLed_Out( led_on );
}

byte TopLed_TOP(){
  return LED_TOP;
}

void TopLed_Out( boolean signal ){
   digitalWrite( LED_TOP, signal );
}


This code has three things that make a recurring pattern for all the controllers:
  • The functions all start with the name of the tab (TopLed_xxx)
  • The code contains an Init function that is called by the setup() of the main function
  • The code contains an Update function that is called by the timer. In this particular case, the timer is able to blink fast or slow, depending on the state of the boat.
Don't delve into the Update function too deeply, we will return to this in the next post. Likewise, the other LED, and the LED that is mounted on the Arduino UNO board (pin 13) is controlled by the following tab:
define LED_DISCO 6 //set up disco LED on pint 6
#define LED_ONBOARD 13 //On board LEd is pin 13

void LedControl_Init(){
  pinMode( LED_DISCO, OUTPUT );
  pinMode( LED_ONBOARD, OUTPUT);
}

byte LedControl_Disco(){
  return LED_DISCO;
}

byte LedControl_OnBoard(){
  return LED_ONBOARD;
}

void LedControl_Disco( boolean signaal ){
  digitalWrite( LED_DISCO, signaal );
}

void LedControl_OnBoard( boolean signaal ){
   digitalWrite( LED_ONBOARD, signaal );


In this particular case, the LED's do not use the timer, so a LedControl_Update() is not necessary.

In the next post, we will make the software a bit more complex, by allowing the boat to switch beteen manual and autonomous control.

Setting Up the Hardware


Making your RC Controlled Boat Programmable with Arduino

At the Rotterdam University of Applied Science we have started a small pilot project with secondary schools, concerning robotics and maritime technology. For this, we decided to see if we could make a cheap off-the-shelf RC boat programmable with an Arduino. What we wanted was to:
  1. Make the boat switch between manual control and autonomy
  2. Use the  stuff that was available on the boat, including the remote control
It only took four evenings before we had a programmable boat, that conformed to the above demands. Two evenings were spent on the hardware and two on the software, so that evened out quite nicely! We decided to share the experiment with the community, and maybe improve on the design as we move along.

The design and parts lists can be found here.

Setting Up the Hardware

We found a nice transparent boat, which allows students to see the construction from the outside. The boat was shipped with a 27 MHz Radio Controller (RC), which are often provided with toys; it can steer up and down, left and right in a binary fashion; you either stop or you go fast!
 

Figure 1: RC Boat with controller
 The propeller and the rudder are both controlled by a 9 Volt DC engine. Furthermore there were two lights (LEDs), which light up when the boat is powered up. In order to make this boat programmable, we bought:
  • An Arduino Uno microcontroller board
  • A Sparkfun motor controller (MC) for two motors (SparkFun ArduMoto DEV-09815 RoHS)
  • Five wires with connectors
  •  Four throughput connectors for the Arduino, to be assembled on the SparkFun MC. Note that you need two with six pins and two with eight pins.
  • Shrink sleeves
  • About 30 cm (a foot) of red, black, yellow and purple wire (0.25 mm2)
Furthermore we need a mini screwdriver set, a soldering iron and a paint burner (or a cigarette lighter) to shrink the sleeves.



Figure 2: Equipment and Materials
The boat and the Arduino can operate at 9 V DC, so no measures are needed to convert the power. The Sparkfun MC has some means to customise the electronic print, so we can use this for the soldering work. As a result, nothing needs to be done on the Arduino itself. The majority of the work is spent in extending the Sparkfun MC so that the motors, the LEDs and the 27 MHz RC can be connected to this. So in order to make this work we have to:
  1. Cut the wires between the receiver and the motors and LEDs
  2. Put the Arduino in between these.
The 27 MHz receiver is often a combination of a a receiver and an amplifier unit. The motors are connected between two class A transistor amplifiers, each of which can power the motor. This way you can switch between two directions.


Figure 3: Wiring the 27MC Unit to the Arduino
When you remove the motors, you end up with four wires that can be connected to the digital input port of the Arduino. In this example, we will be using ports D7-D10 for the 27MHz RC controller, and rewire the two LEDs to connect to D5 and D6. The Sparkfun MC uses D3and D11 for the control of the two motors, so these are reserved. With this, the LEDs become available for other things, and the RC controller can still be used.
As was mentioned earlier, the Sparkfun MC shield has ample space to make these connections. The figure below shows the result:


Figure 4: Wiring the 27MC Unit to the Arduino
The battery pack is connected to the 9V DC print of the SparkFun MC. The two motors are attached to the A and B outputs of the motor controllers, and the LEDs are soldered directly on the connector of Port D. Last, a six pin connector is added to connect the RC. The astute observer may have noticed the pull-down resistotrs (47kOhm) on the above photo. To my knowledge, these are not really needed.The blue and green wires connect the RC to correct entries of Port D, while the two remaining pins are connected to the 9V DC, so that this can power the RC unit.
in the next post, we will discuss the software.