Skip to content

IOIOLib Basics

ytai edited this page Feb 27, 2012 · 36 revisions

IOIOLib Basics

Overview

IOIOLib is an Android library, which enables your Android application to control the IOIO board. It exposes a set of Java interfaces, covering the various features of the board. When you build your application, IOIOLib gets packaged into your target .apk file, so your application is stand-alone and does not require any further installation on the Android device that runs it. The entire code is pure Java, depending solely on the standard Android 1.5 (or later) framework.

IOIOLib is all documented in standard Javadoc format, and this documentation is intended to be comprehensive and be used as reference while coding. In this User Guide, we try to cover usage of the library from a common-use-cases approach rather than be 100% formal.

The library is organized in several Java packages. The ioio.api package contains all the public API for controlling the IOIO. This is the package your application will be using. Within it, the ioio.api.exception package contains some exceptions thrown by the IOIO API. The ioio.impl package contains the implementation of these interfaces and is not intended to be used directly. The ioio.util package contains useful utilities that may make your life a little easier when writing IOIO applications, but do not provide core functionality. At the time of writing this, this package only contains a single class, serving as a base class for your IOIO-based application, which automatically handles proper connection / disconnection to the IOIO board in response for application events.

If you are not familiar with Android application development and/or have not yet setup your development environment, the IOIO Beginner's Guide page may provide useful information.

Obtaining and Using IOIOLib

The latest version IOIOLib can be downloaded from the Downloads page. It is a part of the "Android Software" zip. This zip also includes some sample applications.

We're only going to cover usage with Eclipse here, with the assumption that users not using Eclipse for Android application development probably know what they're doing :)

  • Extract IOIOLib to somewhere where you normally want to keep your Android projects.
  • Import it into Eclipse using File > Import... > General > Existing Projects into Workspace..., then choose the IOIOLib directory you just created and click Finish.
  • Reference it from your application project, according to these instructions
  • Make sure your application declares using the android.permission.INTERNET permission. This can set by opening the AndroidManifest.xml file found at your project's root, going to the Permissions tab > Add... > Uses Permission > Select android.permission.INTERNET under "Name".
  • Make sure you enabled USB debugging on your Android device, by going to Settings > Applications > Development > Enable USB Debugging.

The IOIO Interface

The IOIO interface is the heart of the IOIOLib. An instance of this interface represents a physical IOIO board, and through its methods you can control its various functions, such as reading or writing values to individual physical pins.

Creating a IOIO Instance and Establishing a Connection

Note: It is not very common to have to create the IOIO instance yourself. Refer to the utility classes described in the section below for a simpler, more common usage.

In order to create an instance of IOIO, you should use the IOIOFactory.create() method. Simply call:

IOIO ioio = IOIOFactory.create();

The call will return immediately, providing you with a valid instance. However, this instance is not yet usable, and any attempt to use it will throw an exception. A connection with the board must first be created, by calling:

ioio.waitForConnect();

This call will block until the board is powered, physically connected via USB and establishes communications with your application (if it doesn't, make sure you've got USB debugging turned on).

Once the IOIO is connected, you can start using it (well, that's what you bought it for, probably)! Its various functions are normally accessed via sub-instances obtained from the openXYZ() methods. These sub-instances remain associated with the IOIO instance which created them throughout their lifetime. They all extend the Closeable interface, which simply means your can call close() on them. Calling close() will invalidate the instance on which it was called, and will free and resources (e.g. pins) it used for future use. For documentation on specific functions, please refer to:

Disconnecting

In order to disconnect from the board (automatically resetting its state), or abort an on-going waitForConnect(), simply call:

ioio.disconnect();

You can do this, for example, when your application gets paused, or from a TimerTask, if you want to timeout on a connection attempt. The call is asynchronous. The disconnect process is normally very fast, but if you want to make sure all resources have been freed, you can use:

ioio.waitForDisconnect();

This call will block until the disconnect process completes. You can also use this call to implement a listener that does something upon disconnect.

From the moment a disconnection occurs, whether due to the client's explicit call to disconnect() or due to a physical disconnection, the instance, as well as any other instances obtained from it, will throw a ConnectionLostException upon every call. A IOIO instance is one-time use. This means that once a connection is lost, the instance is as good as dead. It cannot be recycled. Create a new instance if you want to re-establish connection, and preferably attempt to do some after calling ioio.waitForDisconnect on the old instance, to make sure you are waiting for all resources (such as the underlying network socket used for communication) to be freed.

Resets

There are two kinds of resets, soft and hard.

A soft reset means "return everything to its initial state". This includes closing all interfaces obtained from this IOIO instance, and in turn freeing all resources. All pins (save the stat LED) will become floating inputs. All modules will be powered off. These operations are done without dropping the connection with the IOIO, thus a soft reset is very fast. It is generally not a good practice to replace proper closing of sub-instances with a soft reset. However, under some circumstances, such as tests, this might make sense.

A soft reset is performed by calling:

ioio.softReset();

A hard reset is exactly like physically powering off the IOIO board and powering it back on. As a result, the connection with the IOIO will drop and the IOIO instance will become disconnected and unusable. The board will perform a full reboot, including going through the bootloader sequence, i.e. an attempt to update the board's firmware will be made. Hard reset is mostly useful for this very purpose - your application wants to initiate a firmware upgrade. In the future, applications (or IOIOLib) will be able to request an install of ad-hoc firmware on the board. A hard reset will then enable the board to pick up the new firmware and start executing it.

A hard reset is performed by calling:

ioio.hardReset();

Batching Operations

In some cases, your application uses a high rate of write operations to the IOIO. An example might be changing a bunch of digital outputs immediately one after the other to achieve a parallel-like interface. Doing these operations naively, will cause a message to be sent to the IOIO for every operation. Every such message incurs a certain latency and this latency adds up and may become significant for large amount of writes and latency-sensitive applications.

For that purpose, an optimization method is available in the IOIO interface, which enables grouping a number of such write operations into a single message that is sent to the IOIO, thus taking the latency hit only once per group rather than once per operation. This is strictly an advanced optimization and will not change functionality. It is recommended to use this technique only after everything works without it and improving latency is desired. Note that trying to group operations that block until response from the IOIO is obtained (such as SPI or TWI writeRead()) will hang your thread!

Grouping is achieved simply by surrounding your group of write operations with a

ioio.beginBatch()

and

ioio.endBatch()

calls. The former will cause the IOIO instance to delay all messages until the latter is called. Note that this is strictly a hint. The IOIO instance may decide to send a message nevertheless, if, for example, the buffered messages become too long. Also note that it is legal to nest such batch blocks. The messages to the IOIO will be delayed until the outermost block exits.

Take extra care about ending a batch cleanly in the presence of exceptions. Possibly, put it in a finally block immediately surrounding all the block contents, such as:

ioio.beginBatch();
try {
  pin1.write(false);
  pin2.write(true);
  ...
} finally {
  ioio.endBatch();
}

Using the Application-Level Utilities

Note: If you're looking for the old documentation of the now-deprecated AbstractIOIOActivity class, they are [here](Using-AbstractIOIOActivity-(deprecated)). It is recommended that you switch to IOIOActivity - the migration process is close to trivial.

IOIOActivity and IOIOService

Very often IOIO-based applications will have the following traits:

  • A single thread is dedicated to creating the IOIO instance, establishing connection and then controlling the IOIO. This is possibly done while communicating with other threads, such as the UI thread. In more advanced scenarios, one thread will be created per-possible communication channel, such as USB, Bluetooth, etc.
  • If the connection drops (as a result of a physical disconect), create a new IOIO instance and try to reconnect.
  • This thread will be created in onStart() and aborted in onStop() of an Activity, or similarly when a certain aspect of the application state is "started" and "stopped", in case of a non-Activity application.
  • Aborting the thread involves disconnecting from the IOIO or otherwise cancelling an on-going attempt to connect.
  • In this thread, there will be some operations done once, right after a connection is established. Typically, these will include creating sub-instances of the IOIO object, by calling some of the openXYZ() methods.
  • Then, there will be on-going tasks done repetitively.
  • And finally, we may want to do some operations when the connection is lost or when some problem is detected.

While this behavior surely doesn't cover every possible application, it does cover quite a few of them. So in order to save the boiler-plate code for achieving just that, a utility class has been created, called IOIOActivity, under the ioio.lib.util.android package. This is simply an abstract class, extending Activity which does exactly what's written above.

It leaves you, the application author, to:

  • Create your Activity class by extending IOIOActivity.
  • Create a (possibly inner-, possibly inline-) "Looper" class implementing the IOIOLooper interface. You can extend BaseIOIOLooper for some convenience of having the IOIO instance stored as a protected field.
  • In your Looper class, implement the setup() method, which gets called once the IOIO connection is established. It could use the ioio_ field as its IOIO instance.
  • In addition, implement the loop() method, which gets called repetitively as long as IOIO is connected. It could use the ioio_ field as its IOIO instance.
  • You may let any ConnectionLostException or InterruptedException get thrown from either of these methods - it will be caught and properly handled automatically, causing abortion of the IOIO connection and the thread running your Looper.
  • You may implement the disconnected() method for operations that should be done when the connection is lost. This method may no longer use the ioio_ instance, as it is already invalid. But it is still very useful, for example, for disabling some of the GUI widgets, or presenting a message to your user.
  • You may implement the incompatible() method if you want to somehow handle the case when a IOIO whose firmware version is incompatible with the version of IOIOLib your application was compiled against. Again, this is useful mainly for presenting an informative message to the user.
  • Finally, implement the createIOIOLooper() method in your main class, which simply returns a new instance of your Looper class.

The HelloIOIO example, provided along with IOIOLib, is a good example of using IOIOActivity. Here's a snippet of this example:

public class MainActivity extends IOIOActivity {
	...
	class Looper extends BaseIOIOLooper {
		private DigitalOutput led_;

		@Override
		protected void setup() throws ConnectionLostException {
			led_ = ioio_.openDigitalOutput(0, true);
		}

		public void loop() throws ConnectionLostException {
			led_.write(!button_.isChecked());
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
			}
		}
	}

	@Override
	protected IOIOLooper createIOIOLooper() {
		return new Looper();
	}
}

Similarly, there is a IOIOService base class, which simplifies authoring a service that uses IOIO. Check out the Javadocs of this class, or the HelloIOIOService example.

Advanced Use-Cases

In more advanced cases, extending either of the aforementioned classes is not an option. But you may still want to have the basic behavior of the framework mentioned above. This can be achieved using the IOIOAndroidApplicationHelper class, found in the ioio.lib.util.android package. This class is typically not extended, but rather used as a field of your class. The rules for using it are: call create() after construction. Call start() and stop() alternately whenever you want to start/stop the threads used for IOIO operations. Finally, call destroy(). FYI, internally, both IOIOActivity and IOIOService use this class.

If you want to use this behavior outside the scope of Android, ioio.lib.util.IOIOApplicationHelper is your friend, since it is platform independent. But take into account that some Android-specific functionality (such as open-accessory) would not work when using this class, so it is not recommended for direct usage for Android applications.

Multi-IOIO Applications

IOIOActivity and the other utilities support writing applications with multiple IOIO boards connected. Each can connect over one of the available connection channels (e.g. USB, Bluetooth). In such a case, IOIOActivity will call your createIOIOLooper() once for every possible connection channel. Your implementation can then create a looper for each IOIO, or possibly return null if the given channel is irrelevant. In order to distinguish between the different channels (e.g. in order to refuse creation for some connection types), implement createIOIOLooper(String, Object) instead. The arguments will provide information on the connection types.

For more information, please see the Javadocs of IOIOActivity.