Android – IOIO – RC Servo (or ESC)


This is a sequel to my first Hello World example with the IOIO / Android setup….

Android + IOIO controlling a RC servo

It simply adds a RC servo that is controlled by the same seekBar that controls the brightness of the LED.

Here’s a quick and pretty good explanation of how the standard RC protocol (just a specific case of PWM really !) works :
http://www.societyofrobots.com/robotforum/index.php?topic=4299.0
To sum it up, the RC signal is a 50Hz signal, so 20ms period, out of which it is HIGH between 1 and 2ms. So the duty cycle of the signal has to be between 5 and 10%, with the neutral being around 7.5%.

Here’s a video showing the theory working:

And here’s the Android code.

2 things to notice:

  1. _servo = ioio_.openPwmOutput(5, 50);Β  that starts a PWM signal on pin 5, with a 20ms period (corresponding to what the standard RC signal expects)
  2. _servo.setDutyCycle(0.05f + _varValue * 0.05f); which basically varies the duty cyle between 5 and 10% (depending on the position of the seekBar) so that the HIGH period of the signal will vary between 1 and 2 milliSeconds (again corresponding to that a RC servo/ESC or anything else, expects)

package org.trandi.helloioio;

import ioio.examples.hello.R;
import ioio.lib.api.PwmOutput;
import ioio.lib.api.exception.ConnectionLostException;
import ioio.lib.util.AbstractIOIOActivity;
import android.os.Bundle;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;

public class MainActivity extends AbstractIOIOActivity {
private SeekBar _seekBarVar;
private TextView _varField;
private volatile float _varValue;

/**
* Called when the activity is first created. Here we normally initialize our GUI.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
_seekBarVar = (SeekBar) findViewById(R.id.seekBarVar);
_varField = (TextView) findViewById(R.id.textViewVarVal);

_seekBarVar.setMax(100);

_seekBarVar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}

@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}

@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
_varValue = (float)progress / (float)_seekBarVar.getMax();
_varField.setText(String.valueOf(_varValue));
}
});
}

/**
* This is the thread on which all the IOIO activity happens. It will be run
* every time the application is resumed and aborted when it is paused. The
* method setup() will be called right after a connection with the IOIO has
* been established (which might happen several times!). Then, loop() will
* be called repetitively until the IOIO gets disconnected.
*/
class IOIOThread extends AbstractIOIOActivity.IOIOThread {
// The on-board LED
private PwmOutput _led;
// a attached servo
private PwmOutput _servo;

/**
* Called every time a connection with IOIO has been established.
* Typically used to open pins.
*
* @throws ConnectionLostException When IOIO connection is lost.
*
* @see ioio.lib.util.AbstractIOIOActivity.IOIOThread#setup()
*/
@Override
protected void setup() throws ConnectionLostException {
_led = ioio_.openPwmOutput(0, 300);
_servo = ioio_.openPwmOutput(5, 50); // 20ms periods
}

/**
* Called repetitively while the IOIO is connected.
*
* @throws ConnectionLostException When IOIO connection is lost.
*
* @see ioio.lib.util.AbstractIOIOActivity.IOIOThread#loop()
*/
@Override
protected void loop() throws ConnectionLostException {
_servo.setDutyCycle(0.05f + _varValue * 0.05f);
_led.setDutyCycle(1 - _varValue);
}
}

/**
* A method to create our IOIO thread.
*
* @see ioio.lib.util.AbstractIOIOActivity#createIOIOThread()
*/
@Override
protected AbstractIOIOActivity.IOIOThread createIOIOThread() {
return new IOIOThread();
}
}

24 Responses to Android – IOIO – RC Servo (or ESC)

  1. Goktug says:

    I do _varValue manually so _servo.setDutyCycle(0.05f + 1 * 0.05f); and i do it so _servo.setDutyCycle(0.05f + 0 * 0.05f); I mean big 0.5 and low 0.5 but it turn right always 😦

  2. Goktug says:

    How can we turn right or left? with a value? can u help pls? Thank you

    • trandi says:

      Hi,

      It’s ALL described in the post: look at the “_servo.setDutyCycle(0.05f + _varValue * 0.05f);” line in the code.
      The “_varValue” is the value you’re looking for, in my case it comes from the SeekBar (and it’s between 0 and 100), but it can come from anyhwere (and then you’ll have to normalise it accordingly).

      Hope this helps,
      Dan

      • Goktug says:

        Thank You But I do it and my servo is turn right 😦 why?
        package ioio.examples.hello;

        import ioio.lib.api.DigitalOutput;
        import ioio.lib.api.PwmOutput;
        import ioio.lib.api.exception.ConnectionLostException;
        import ioio.lib.util.BaseIOIOLooper;
        import ioio.lib.util.IOIOLooper;
        import ioio.lib.util.android.IOIOActivity;

        import android.os.Bundle;
        import android.view.View;
        import android.view.View.OnClickListener;
        import android.widget.Button;
        import android.widget.SeekBar;
        import android.widget.SeekBar.OnSeekBarChangeListener;
        import android.widget.TextView;
        import android.widget.Toast;
        import android.widget.ToggleButton;

        /**
        * This is the main activity of the HelloIOIO example application.
        *
        * It displays a toggle button on the screen, which enables control of the
        * on-board LED. This example shows a very simple usage of the IOIO, by using
        * the {@link IOIOActivity} class. For a more advanced use case, see the
        * HelloIOIOPower example.
        */
        public class MainActivity extends IOIOActivity {
        private ToggleButton button_;
        private Button z;
        private Button x;
        private int a;
        private boolean d ;

        private SeekBar seekBar_;
        private TextView _varField;
        private volatile float _varValue;

        /**
        * Called when the activity is first created. Here we normally initialize
        * our GUI.
        */
        @Override
        public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        button_ = (ToggleButton) findViewById(R.id.button);
        z = (Button)findViewById(R.id.button1);
        x = (Button)findViewById(R.id.button2);

        seekBar_ = (SeekBar)findViewById(R.id.seekBar1);
        seekBar_.setMax(100);

        z.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
        // TODO Auto-generated method stub
        d=true;
        a=50;
        Toast.makeText(getApplicationContext(),
        String.valueOf(a), Toast.LENGTH_SHORT).show();

        }
        });

        x.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
        // TODO Auto-generated method stub
        d=false;
        a=-50;
        Toast.makeText(getApplicationContext(),
        String.valueOf(a), Toast.LENGTH_SHORT).show();

        }
        });

        seekBar_.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
        @Override
        public void onStopTrackingTouch(SeekBar seekBar) {
        }

        @Override
        public void onStartTrackingTouch(SeekBar seekBar) {
        }

        @Override
        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        _varValue = (float)progress / (float)seekBar_.getMax();

        }
        });

        }

        /**
        * This is the thread on which all the IOIO activity happens. It will be run
        * every time the application is resumed and aborted when it is paused. The
        * method setup() will be called right after a connection with the IOIO has
        * been established (which might happen several times!). Then, loop() will
        * be called repetitively until the IOIO gets disconnected.
        */
        class Looper extends BaseIOIOLooper {
        /** The on-board LED. */
        private DigitalOutput led_;
        //servo attached
        private PwmOutput _servo;

        /**
        * Called every time a connection with IOIO has been established.
        * Typically used to open pins.
        *
        * @throws ConnectionLostException
        * When IOIO connection is lost.
        *
        * @see ioio.lib.util.AbstractIOIOActivity.IOIOThread#setup()
        */
        @Override
        protected void setup() throws ConnectionLostException {
        led_ = ioio_.openDigitalOutput(0, false);
        _servo = ioio_.openPwmOutput(5, 50); // 20ms periods

        }

        /**
        * Called repetitively while the IOIO is connected.
        *
        * @throws ConnectionLostException
        * When IOIO connection is lost.
        *
        * @see ioio.lib.util.AbstractIOIOActivity.IOIOThread#loop()
        */
        @Override
        public void loop() throws ConnectionLostException {

        led_.write(d);

        _servo.setDutyCycle(0.05f + _varValue * 0.05f);
        try {

        Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        }
        }

        /**
        * A method to create our IOIO thread.
        *
        * @see ioio.lib.util.AbstractIOIOActivity#createIOIOThread()
        */
        @Override
        protected IOIOLooper createIOIOLooper() {
        return new Looper();
        }

        }

      • trandi says:

        Actually, as you can see from this line _varValue = (float)progress / (float)seekBar_.getMax();

        _varValue is between 0 and 1 (not 100, my bad).

        So if _varValue is above 0.5 then it turns one direction, if it’s below 0.5 it turns the other. When _varValue == 0.5 then the _servo = ioio_.openPwmOutput(5, 50); // 20ms periods formula shows you that the PWM will do 1.5millis seconds pulses, which from the servos/esc specs is the middle.

        Dan

  3. lina says:

    can u send me the layout xml?

  4. Julio says:

    Hello! can you send me the all the code
    for to compile on eclipse please
    thanks

  5. Mike says:

    Hello trandi, thanks for the detailed demo! I tried running this on my Galaxy Nexus, and was having a slight problem with the code. I needed to add a “sleep(5);” to the loop method on line 84. Otherwise, the IOIOThread doesn’t yield control back to the rest of the app for controlling the sliders, for instance. That was enough to get the program working on my phone.

    Thanks again!
    – Mike

    • trandi says:

      Interesting… have you tried “Thread.yield()” instead ?
      It may be something to do with a different version of Android you’re using.
      In any case, your change makes sense, it’s not a good practice to let that loop use 100% of CPU, and a few millisecs latency are nothing for a human interface…

      Thanks for your feedback !
      Dan

  6. Tengo varios errores en el cΓ³digo, sera que tu me puedes ayudar por favor?

  7. Tengo varios errores en el cΓ³digo :S

  8. BjΓΆrn says:

    This video looks nice! What components did you buy to start with the android ioio and the servo?

    • trandi says:

      For this simple set up, I only had to buy the IOIO board itself, the servo (which I already had) and a battery.

      It’s often a good idea to run the servos / motors from a different VCC than the electronics boards, but in this case I couldn’t be bothered…

      Hope this helps,
      Dan

  9. trandi says:

    Yeah, actually the HelloWorld example.
    It’s quite easy to change / update…

    You can find the exact code I ended up using, towards the end of the post.

    Dan

  10. arun60 says:

    have u used the IOIOSimpleApp and made some modifications and used for this RC servo example application

  11. Krishna says:

    Hi Dan

    Can you let me know where did you get the black connector which is soldered from Vcc to Pin 6 of your board in UK.

  12. Hi!

    How did you make the power supply? Can I use this one http://www.sparkfun.com/products/9518?

    Regards,
    Pablo Cantero

  13. Pingback: Android IOIO Wii Nunchuck « Robotics / Electronics / Physical Computing

  14. Paul says:

    That’s pretty awesome. Great work man.
    Just today I started investigating how I can control servos (or an arduino) from my iPhone.

    • trandi says:

      Yeah… it was surprisingly simple and quick… though I suspect it will be less so on the iPhone…
      Keep us posted about your progress, I’d be curious to know more…

      dan

Leave a comment