IntroductionC1 Board 14052015

In this tutorial we will walk you through the steps necessary to develop  Android apps for the OpenOTT board.

First we describe how to set up your development PC and build and run a simple Android app. These steps are not specific to the OpenOTT board, so we will refer to official Android documentation.

Next we run the same app on the OpenOTT board and finally we build a small example to exercise the RapberryPi compatible connector.

To have a simple, common setup, we assume you have the OpenOTT breadboard starter kit. (Similar to this: https://www.adafruit.com/products/1754 and this: http://www.amazon.co.uk/Electronic-Starter-Raspberry-Model-included/dp/B0082LD2B8 )

All but the final step can be run without the breadboard starter kit. Should you have different electronics connected to the RaspberryPi connector, the procedures are very similar, and the changes required should be minimal.

Requirements and Setup

  • OpenOTT board
  • 12v power supply for OpenOTT board
  • PC or Mac
  • Ethernet cable.
  • HDMI TV or monitor for OpenOTT
  • Mouse and/or keyboard for the OpenOTT

The following outlines what your development setup should be (power cables and upstream ethernet omitted):

OpenOTT Development Introduction

PC Software Installation

Development of Android apps require at least two installs on your PC

First install the Java JDK, as mentioned here:
http://www.oracle.com/technetwork/java/javase/downloads/index.html

Then install Android Studio, which includes the Android SDK:
https://developer.android.com/sdk/index.html

First App

To ensure that your Android development environment is properly set up, we recommend that you first try running an example app consisting of pure Android Java code.

Follow for instance this tutorial:
http://developer.android.com/training/basics/firstapp/creating-project.html

Run app in simulator

This will ensure that the HelloWorld app is working correctly. See e.g.
http://developer.android.com/training/basics/firstapp/running-app.html#Emulator

Run app on OpenOTT

This assumes that your PC and OpenOTT is connected as described above. Now debugging between your PC and the OpenOTT needs to be enabled as follows:

  1. On the OpenOTT, open the Settings app and click Ethernet.
  2. Note down the OpenOTT’s IP address.
  3. Go to the bottom menu in the settings app, called MediaBox. Repeatedly click the “Build number” until it says “You are now a developer”
  4. In a terminal on your development PC, type “adb connect <openOTT IP>”, e.g. “adb connect 192.168.1.137”. Note: On Windows, the adb command is not installed in the system’s path, so you need to manually add it to your path or cd to it. It should be located in C:\Users\<your username>\AppData\Local\Android\sdk\platform-tools
  5. On the OpenOTT’s display, there should be a popup, saying “Allow USB debugging” – click “Always allow from this computer” and press OK.

Now proceed running the HelloWorld app from before on the OpenOTT as described here:

http://developer.android.com/training/basics/firstapp/running-app.html#RealDevice

Electronics setup and test

For the following tutorial, we will interface with electronics connected to the I/O connector. For this first, simple example, we will use an LED as output and control it from the UI of our program. The I/O connector is electrically compatible with the Raspberry Pi 2 concector, which has the following pin assignments:

RPi2Connector

To set this up on a breadboard, do as follows:

  1. Connect pin 3 of the IO connector to the longest pin (+) of the LED.
  2. Connect to the shortest pin (-) of the LED to a 220 Ohm resistor. This resistor will protect both the LED and the GPIO pin from excessive current.
  3. Connect the resistor to any ground pin – e.g pin 6

Now start the IO app, on the OpenOTT:

screen

 

The IO app is a test app that can be used to get and set the values of the pins on the OpenOTT’s RaspberryPi connector. It can not, however, perform logic between the inputs and outputs.

To ensure that electronics is wired correctly,, configure pin 3 as output, and change its value back and forth between low and high – watch the LED turn on and off.

App that Accesses OpenOTT Hardware

Getting our feet slightly more wet, we will now turn the LED on and off from our own program. The IO app we used before is a fine example of how to use all the features of the IO connector, but its complex UI may make it a bit hard to understand, so we have prepared a minimalistic example, that has a very simple UI. Fetch the project from here (if you are not confident with using git as version control, just download and unpack the zip file)

https://github.com/OpenOTT/IOExamples

From Android Studio, chose the File/Open menu, navigate to where you unpacked the zipfile, and select the FirstExample project. Refer to you previous experience and compile and run the project on the OpenOTT board. This should present you with the following app UI:

FirstExample

You should now be able to turn the LED on/off using this UI, rather than the IO app.

Code walkthrough

We assume that the previous Android tutorials have made you somewhat familiar with the general structure of an Android studio project, so feel free to investigate the example project.

To understand how your code that control physical hardware on the OpenOTT board, consider the following diagram:

OpenOTT Development Introduction jpg

 

In Android terms, a service is a software component, which runs in the background, offering functionality to other apps without having a UI of its own. Futarque’s Mediarite service is exactly that – and one of the functionalities it offers is an IOService, which is used to access the IO pins of the Raspberry Pi compatible connector.

The way an Android app communicates with a service is through an IPC mechanism called Binder, which uses an interface language called AIDL. Fortunately you do not need to know about this, as we have created a library called libioservice, which maps regular Java methods to AIDL. But we do need to do two things: Instruct your Android project that it should use libioservice and create a connection to the Mediarite service.

Include libioservice

Using the libioservice is a two step process – first we instruct Android Studio to look in the Futarque Maven repository at BinTray. This is done with the following lines in the project’s build.gradle file:

maven {
url ‘https://dl.bintray.com/futarque/maven/’
}

Then we tell Android Studio that we want to use the library “libioservice” with the following line in the app module’s build.gradle file. This also dictates the required version of the library (0.110) and the format (.aar, which is the Android equivalent of a .jar file)

compile ‘com.futarque.openott:libioservice:0.110@aar’

Create Connection to Mediarite service

With this in place we are now ready to write some code to use the IO service. In the FirstExample project, all code is done in the HwExampleActivity.java file. As you should know by now, the onCreate method is the starting point for activities. First a listener is installed for the UI’s switch button – more on that later. The bottom part of the onCreate method says something like:

Intent implicitIntent = new Intent("com.futarque.mediarite.IOService");
List resolveInfo = getPackageManager().queryIntentServices(implicitIntent, 0);

if (resolveInfo == null || resolveInfo.size() != 1) {
    mStatusText.setText("Status: No unique com.futarque.mediarite.IOService service found");
    return;
}

// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);

// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);

// Set the component to be explicit
explicitIntent.setComponent(component);

startService(explicitIntent);
mStatusText.setText("Status: Attempting to connect to Mediarite");
bindService(explicitIntent, mServiceConnection, 0);

Long story short, this tries to create a connection to the service named com.futarque.mediarite.IOService. Specifically, the final bindService() call starts an asynchronous operation whose result is sent as a callback to the provided mServiceConnection class, which is defined above as:

private ServiceConnection mServiceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mIMediarite = IMediaRite.Stub.asInterface(service);
        try {
            mStatusText.setText("Status: Attempting access IOService");
            mIOController = mIMediarite.getIOController();
            mIOController.getGpioProvider();
            IGpioProvider provider = mIOController.getGpioProvider();
            mPin = provider.openPinNumber(3);
            PioConfig cfg = new PioConfig();
            cfg.mIsOutput=true;
            mPin.setPioConfig(cfg);
            mPin.setState(PinState.LOW);
            mStatusText.setText("Status: GPIO 3 initialized as output");
            mSwitch.setEnabled(true);
        } catch(RemoteException e) {
            mStatusText.setText("Status: Error creating IOController");
        }
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
        mStatusText.setText("Status: Could not connect to IOService");
        mIMediarite = null;
    }
};

If the connection is successful, the onServiceConnected() method is called, and this is where the interesting stuff starts to happen – we get the IOController, get a IGpioProvider from it, configure pin 3 as output and set the pin to low. Finally we set the UI’s switch button to enabled – it was previously disabled (in the UI’s xml file) and thus could not be clicked until the IO sevice connection is made and the GPIO pin was properly configured. When the switch button is now clicked, the following code is now called:

mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
    @Override
    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
         try {
             if (isChecked) {
                 mPin.setState(PinState.HIGH);
             } else {
                 mPin.setState(PinState.LOW);
             }
         } catch (RemoteException e) {
                 mStatusText.setText("Status: Error setting GPIO pin 3");
         }
    }
});

This very simply sets the GPIO pin’s state to the same state as the switch button.

The use of the IO library should be pretty straight forward – there is autobuilt help available from within Android studio and we are working on Javadoc documentation.

The part about the service connection may not be obvious, unless you are familiar with Android services, but there are plenty of documentation on this, for instance the official one:

http://developer.android.com/guide/components/services.html

For the moment, you can use the FirstExample as boilerplate code for new projects, but in the future we may wrap the service connection into a helper class in the libioservice.

Using the Tuner

The DVB tuner is accessed via a REST API. The REST API consists of the needed methods for tuning, monitoring and streaming the transport stream from the tuner. The REST API is exposed over a local Unix socket on the Android system and over a tcp port. If you implement a local client, we suggest that you use the local socket for your implementation. Using the IOController java API you can get the name of local socket. External clients will need to use the http port. Discovery of the server url can be done via UPnp (SSDP) with the type “urn:futarque-com:device:FutarqueNetworkTuner:1”.

For testing purposes, we have implemented a CLI client into the IOClient App, that can be found on https://github.com/OpenOTT/IOClient. It is supplied in source code and can be used as a reference for your implementation. The underlying communication is done via JSON over the socket, so if your usage is JavaScript, read the Java source code for reference. The CLI commands of the IOClient app is specified in the following:

connectInfo[c]                  - Returns the info on where to connect
connect[cx]                     - Connects to the LocalSocket
disconnect[dx]                  - Disconnects from the LocalSocket
help[h] <OPTION>                - Displays This message
    OPTION [caps|tune|tune2|status]  - Extended help for the command

**** Tuning and Monitoring Methods ****
numTuners[n]                    - Returns number of tuners detected
caps[cp] <tunerIdx> <OPTION>    - Returns TunerCapabilities - <tunerIdx>: 0 is the first tuner
    OPTION []         - no supplied option returns the combined tuner Caps SupportedFrontEndTypes is a bitfield with the supported FrontEndTypes
    OPTION [#]        - # is the enum value of the FrontEndType to query
                                  FrontEndType : DVB_S=0, DVB_S2=1, DVB_T=2, DVB_T2=3, DVB_C=4, ATSC=5, ANALOG=6, DVB_ASI=7, ISDB_T=8, DVB_C2=9, UNKNOWN=10
tune[t] <OPTION>                - Returns streamID
    OPTION [l]  - Lists build in test urls
    OPTION [#]  - Tunes to url item # in the build in List
    OPTION [url]  - Tunes to url string
status[s] <streamID>            - Returns tuner status Enum FrontEndStatus
                                  FutarqueFrontEndStatus : UNKNOWN=0, SCANNING=1, LOCKED=2, UNLOCKED=3, TIMEOUT=4, NOT_FOUND=5, STANDBY=6, TUNER_WAS_NICKED=7, ANTENNA_ERROR=8
info[i] <streamID>              - Returns tuner info from the Tuner

**** Pid Subscription List methods ****
getpids[gp]                     - Displays the Pids currently in the list
addpid[ap] <pid>                - Add a pid to the list - NOTE. The list is applied by calling setpids with an empty pid list
rempid[rp] <pid>                - Remove a pid from the list - NOTE. The list is applied by calling setpids with an empty pid list
setpids[sp] <streamID> <OPTIONS
    OPTIONS []            - no supplied option will apply the current pid list to the specified streamID
    OPTIONS [<pid> ...]   - the supplied pid list will replace the existing pid list and is applied to the specified streamID

**** Dump Ts to file methods ****
dump[d] <streamID> <filename>   - Dumps the transport stream from the specified streamID to the file. Filename is full path to writable file
dumpStop[ds]                    - Stop file dumping
dumpInfo[di]                    - Print Dump progress



Tune Url Documentation
==============================
  ex 1. tune://dvb-c?frequency=34600000&bandwidth=8000
  ex 2. tune://dvb-t?frequency=53800000&bandwidth=8000

Url Format:
<mediaurl> ::= <protocol>\
<protocol> ::= \
<protocol specific params> ::= <tune protocol specific params>
<tune protocol specific params> ::= <path>\
<path> ::= \
<tuner params> ::= <key>\
<key> ::= <path specific keys>
<path specific keys> ::= <dvb-s required-keys> <dvb-s required-keys> | <dvb-s2 keys> | <dvb-c keys> | <dvb-t keys>
<dvb-s keys> ::= <dvb-s required-keys> <dvb-s optional-keys>
<dvb-s2 keys> ::= <dvb-s required-keys> <dvb-s optional-keys>
<dvb-s required-keys> ::= \
<dvb-s required-keys> ::= \
<dvb-c keys> ::= <dvb-c required-keys> <dvb-c optional-keys>
<dvb-c required-keys> ::= \
<dvb-c optional-keys> ::= \
<dvb-t keys> ::= <dvb-t required-keys> <dvb-t optional-keys>
<dvb-t required-keys> ::= \
<dvb-t optional-keys> ::= \
<value> ::= integer | string


Tune protocol parameters
============================
frequency  : input frequency, integer value in decahertz (eg. 1132500000,53800000)
symbolrate : symbol rate, integer value (eg. 24500,6900)
polarity   : polarity, char value (eg. 'V' or 'H')
bandwidth  : Bandwidth, integer value in kHz
modulation : Modulation, integer value
(QPSK = 1, 8PSK = 2, QAM = 3, 4QAM = 4, 16QAM = 5, 32QAM = 6, 64QAM = 7, 128QAM = 8, 256QAM = 9, BPSK = 10)
lnbtype    : LNB Type, integer value
(1=universal, 2=5150C, 3=9750KU, 4=10000KU, 5=10050KU, 6=10600KU, 7=10750KU, 8=11300KU)
Default is universal if value is not set.
lnblolow   : LNB low frequency, integer value in decahertz, default is 975000000 if not set.
lnblohigh  : LNB high frequency, integer value in decahertz, default is 1060000000 if not set.
lnbtone    : Handling of 22Khz tone
(-1 = automatic, 0 = disabled, 1 = enabled)
default is automatic if not set.
modulation : Modulation, integer value
(QPSK = 1, 8PSK = 2, QAM = 3, 4QAM = 4, 16QAM = 5, 32QAM = 6, 64QAM = 7, 128QAM = 8, 256QAM = 9, BPSK = 10)
diseqc1    : DiseqC1 device, integer value
diseqc2    : DiseqC2 device, integer value
voltage    : Sets the specific LNB voltage (overrides polarity), integer value (eg. 14)
iqmode     : Input spectrum handling, char value
('N' = normal, 'I' = inverted) default when not set is automatic handling

Usage Example

Below is a usage example of which command to use to tuner to a DVB frequency, monitor its status, select some PIDs and save the resulting transport stream.

TunerScreengrab

We hope this tutorial has been interesting to you, and sufficient to get you started with OpenOTT hardware development programming.

 

  • Introduction

    MStar semiconductor, working with the Danish design house Futarque has launched a development system called “Rho board” which enables designers to easily incorporate Android into Smart home devices.
  • Recent Posts

  • The solution

    The solution is available as a ready to use small motherboard that can be incorporated into many devices. The devices includes a 40 pin GPIO connector that can interface to the hardware in the home device.