Skip to content

Commit

Permalink
Add simplePio sample.
Browse files Browse the repository at this point in the history
A simple sample that covers the basic usage for the PIO APIs.

Change-Id: I56789ce84b7f167be832b6e4a7ff4205d833837c
  • Loading branch information
mangini committed Jun 24, 2016
0 parents commit 0dd5926
Show file tree
Hide file tree
Showing 17 changed files with 688 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
@@ -0,0 +1,6 @@
*.iml
.gradle
local.properties
.idea
.DS_Store
build
47 changes: 47 additions & 0 deletions README.md
@@ -0,0 +1,47 @@
Simple example of Brillo Peripheral I/O APIs
============================================

This Brillo app runs basic code that exercises the PIO APIs. There is a set of API samples that
can be "browsed" and executed by clicking on onboard buttons.

Pre-requisites
--------------

- Intel Edison
- Brillo 2.0


Build and install
=================

1. Plug your Intel Edison board to your computer via USB
2. Click on "Run" in Android Studio or use the command line: `./gradlew installDebug`


Run
===

Press the "RM" button on the Intel Edison to change from one task to another, and then press the
"PWR" button to execute the selected task. The selected task will be printed on
logcat (`adb logcat`).


License
-------

Copyright 2016 The Android Open Source Project, Inc.

Licensed to the Apache Software Foundation (ASF) under one or more contributor
license agreements. See the NOTICE file distributed with this work for
additional information regarding copyright ownership. The ASF licenses this
file to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
36 changes: 36 additions & 0 deletions app/build.gradle
@@ -0,0 +1,36 @@
apply plugin: 'com.android.application'

android {
compileSdkVersion 23
buildToolsVersion '24.0.0-rc4'

defaultConfig {
applicationId "com.google.samples.mysample"
minSdkVersion 21
targetSdkVersion 22
versionCode 1
versionName "1.0"
jackOptions {
enabled true
}
}
buildTypes {
debug {
multiDexEnabled true
}
release {
multiDexEnabled true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
}
Binary file added app/libs/headless.jar
Binary file not shown.
17 changes: 17 additions & 0 deletions app/proguard-rules.pro
@@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/google/home/stammt/Android/Sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
22 changes: 22 additions & 0 deletions app/src/main/AndroidManifest.xml
@@ -0,0 +1,22 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.samples.simplepio">

<application
android:allowBackup="true"
android:icon="@android:drawable/sym_def_app_icon"
android:label="@string/app_name">
<service
android:name="com.google.samples.simplepio.SimplePIOService"
android:exported="true"
android:permission="android.permission.BIND_HEADLESS_SERVICE"
>
<intent-filter>
<action android:name="android.service.headless.HomeService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>

</application>


</manifest>
@@ -0,0 +1,89 @@
package com.google.samples.simplepio;

import android.os.Handler;
import android.os.HandlerThread;
import android.service.headless.HomeService;
import android.util.Log;
import android.view.InputEvent;
import android.view.KeyEvent;

import com.google.samples.simplepio.task.GPIOTask;
import com.google.samples.simplepio.task.LEDTask;

/**
* Simple {@link HomeService} that executes {@link Runnable} in a separate thread.
*
* When certain input events (Intel Edison onboard PWR and RM buttons) are received,
* this class cycles through a list of tasks ({@link Runnable}) and schedules the selected task
* to execute in a separate thread.
*/
public class SimplePIOService extends HomeService {
public static final String TAG = "SimplePIOService";

private static final int BUTTON_EDISON_RM_KEYCODE = 148;
private static final int BUTTON_EDISON_PWR_KEYCODE = 116;

private HandlerThread mRunnerThread;
private Handler mRunnerThreadHandler;
private int mCurrentTask;
private Runnable[] mTasks;

@Override
public void onCreate() {
Log.d(TAG, "Headless service created");
mRunnerThread = new HandlerThread("runnerThread");
mRunnerThread.start();
mRunnerThreadHandler = new Handler(mRunnerThread.getLooper());

mCurrentTask = 0;
mTasks = new Runnable[] {
new GPIOTask(),
new LEDTask()
};

Log.i(TAG, mTasks.length + " tasks available. Current task is " +
getTaskName(mTasks[mCurrentTask]));
Log.i(TAG, "Press the RM onboard button to select another task.");
Log.i(TAG, "Press the PWR onboard button to execute the current task.");
}

private String getTaskName(Runnable r) {
return r.getClass().getSimpleName();
}

@Override
public void onInputEvent(InputEvent event) {
if (((KeyEvent) event).getAction() == KeyEvent.ACTION_DOWN) {
switch (((KeyEvent) event).getScanCode()) {
case BUTTON_EDISON_RM_KEYCODE:
mCurrentTask = (mCurrentTask + 1) % mTasks.length;
Log.d(TAG, "Button RM pressed. Changed current task to " + mCurrentTask +
" (" + getTaskName(mTasks[mCurrentTask]) + "). Now press " +
"PWR to execute it.");
break;
case BUTTON_EDISON_PWR_KEYCODE:
Log.d(TAG, "Button PWR pressed. Executing task " + mCurrentTask +
" (" + getTaskName(mTasks[mCurrentTask]) + ")");
executeCurrentTask();
break;
}
}
}

@Override
public void onDestroy() {
if (mRunnerThread != null) {
mRunnerThread.quit();
}
}

private void executeCurrentTask() {
Log.d(TAG, "Executing task " + mCurrentTask +
" (" + getTaskName(mTasks[mCurrentTask]) + ")");
Runnable r = mTasks[mCurrentTask];

// It is a good practice to execute I/O code in a separate thread
mRunnerThreadHandler.post(r);
}

}
102 changes: 102 additions & 0 deletions app/src/main/java/com/google/samples/simplepio/task/GPIOTask.java
@@ -0,0 +1,102 @@
package com.google.samples.simplepio.task;

import android.os.RemoteException;
import android.pio.Gpio;
import android.pio.PeripheralManagerService;
import android.system.ErrnoException;
import android.util.Log;

import com.google.samples.simplepio.SimplePIOService;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
* Simple GPIO port handling.
*/

public class GPIOTask implements Runnable {
private static final String TAG = SimplePIOService.TAG;

private PeripheralManagerService mService;

/**
* Preferred GPIO port to look for. For example, on Intel Edison Arduino-compatible boards,
* the port "IO13" has an onboard LED that turns on when the GPIO port is HIGH, and off
* otherwise, so it is easy to debug that GPIO port' state without any extra hardware.
*/
private static final String PREFERRED_GPIO_PORT = "IO13";

private static final int NUMBER_OF_BLINKS = 30;
private static final int INTERVAL_BETWEEN_BLINKS_MS = 50;

public GPIOTask() {
mService = new PeripheralManagerService();
}

@Override
public void run() {
Log.i(TAG, "Executing task GPIOTask");
try {
List<String> gpios = listGpios();
if (gpios == null || gpios.isEmpty()) {
return;
}

String portName;
// if the preferred port exists, use it, otherwise use the first port identified.
int preferred = gpios.indexOf(PREFERRED_GPIO_PORT);
if (preferred >= 0) {
portName = gpios.get(preferred);
} else {
portName = gpios.get(0);
}

// GPIO ports are single-user resources, so make sure you close them when you're done.
// The "try with resources" syntax below is a good way of guaranteeing it.
try (Gpio gpio = mService.openGpio(portName)) {
gpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW);
blink(gpio, NUMBER_OF_BLINKS, INTERVAL_BETWEEN_BLINKS_MS);
}
// Notice that the gpio resource will be closed anyway, either if the
// try(){} block finishes successfully or if an exception is thrown.
// If you do not use a try(resource){} block, make sure you close the gpio port
// in a finally clause, otherwise an ErrnoException will be thrown when
// openGpio is called again on this port.

} catch (RemoteException | ErrnoException e) {
Log.e(TAG, "Error on PeripheralIO API", e);
}
}

private List<String> listGpios() throws RemoteException {
// List available GPIOs:
List<String> gpios = mService.getGpioList();
if (gpios == null || gpios.isEmpty()) {
Log.i(TAG, "No GPIO port available on this device.");
} else {
Log.i(TAG, "List of available GPIO ports: " + gpios);
}
return gpios;
}

private void blink(Gpio gpio, int numberOfBlinks, int intervalBetweenBlinksMs)
throws RemoteException, ErrnoException {
// Alternate GPIO output in a loop. Connect an LED to the port to see it blinking.
for (int i=0; i < numberOfBlinks; i++) {

// Turn on when i is even, off otherwise
gpio.setValue( i % 2 == 0 );

// Sleep for a while
try {
TimeUnit.MILLISECONDS.sleep(intervalBetweenBlinksMs);
} catch (InterruptedException e) {
// Ignore sleep interruption.
}
}

// Turn off at the end
gpio.setValue(false);
}
}

0 comments on commit 0dd5926

Please sign in to comment.