Android – IOIO – RC Servo (or ESC)
July 3, 2011 24 Comments
This is a sequel to my first Hello World example with the IOIO / Android setup….
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:
- _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)
- _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(); } }
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 π¦
How can we turn right or left? with a value? can u help pls? Thank you
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
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();
}
}
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
can u send me the layout xml?
Hello! can you send me the all the code
for to compile on eclipse please
thanks
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
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
Tengo varios errores en el cΓ³digo, sera que tu me puedes ayudar por favor?
Tengo varios errores en el cΓ³digo :S
This video looks nice! What components did you buy to start with the android ioio and the servo?
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
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
have u used the IOIOSimpleApp and made some modifications and used for this RC servo example application
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.
Hi Krishna,
I’m not sure, I already had it…
It must have been from either http://uk.farnell.com/ or http://www.technobotsonline.com/
Why, is this something hard to find here in the UK ? Didn’t realise it… I would assume even Maplin has it…
dan
Couldn’t find it in Farnell or in RS, technobots seem to the one who stock. My local maplin is hopeless
Hi!
How did you make the power supply? Can I use this one http://www.sparkfun.com/products/9518?
Regards,
Pablo Cantero
Actually this http://www.sparkfun.com/products/91?
As you can see in the pictures I have simply soldered this standard RC JST connector (http://www.giantcod.co.uk/male-connector-12cm-long-silicon-wire-p-402549.html) DIRECTLY to some the VIN and GND pads.
http://www.sparkfun.com/products/9518 – definitely NOT, you have none of these connectors on the IOIO board.
http://www.sparkfun.com/products/91 – maybe, if you solder the relevant pair on the IOIO board
Again, the IOIO board does NOT come with any power connector soldered…
Hope this helps,
Dan
Pingback: Android IOIO Wii Nunchuck « Robotics / Electronics / Physical Computing
That’s pretty awesome. Great work man.
Just today I started investigating how I can control servos (or an arduino) from my iPhone.
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