This section will give you a step-by-step guide to installing the Mynewt Sensors app to your Android device from this repository.
- Download Android Studio.
- Clone this repository:
git clone https://github.com/runtimeco/android_sensor.git
- Open Android Studio and select "Open an existing Android Studio project" from the welcome window. Navigate to where you cloned the project and select open on the
android_sensors
folder. - Run the project using the green play button icon.
This high-level overview will briefly explain OIC and how to use the Android Iotivity library, followed by an overview of the Mynewt Sensor app, which includes a number of useful classes for jump-starting your Android OIC application.
The OIC framework is designed to provide transport agnostic communication and interoperability between IoT devices. The Open Connectivity Foundation (OCF) has developed an open source OIC implementation called Iotivity which we will leverage in this tutorial to communicate with Mynewt devices exposing sensor data via the sensor framework.
First, we will need some basic, high level knowledge about how OIC exposes information. Physical entities in the real world such as lights, appliances, or in this case sensors, are represented as resources. These resources can be communicated with using RESTful style interactions (i.e. GET, PUT, POST, DELETE). More specifically, due to the constrained nature of devices running Mynewt, we will be using The Constrained Application Protocol (CoAP) as the RESTful style web transfer protocol implementation.
From a high level, a resource is hosted by a device and is identified by its host address, URI, resource types, and interfaces. Additionally, a resource contains a map of values specific to that resource. Resources are found using the discovery process which is described below. Since we will be dealing primarily with sensors in this tutorial, here is an example of a light sensor resource exposed by the Mynewt sensor framework over Bluetooth Low Energy (BLE):
Host Address: coap+tcp://AB:CD:EF:12:34:56
URI: /tsl2561_0/lt
Resource Types: [x.mynewt.snsr.lt]
Interfaces: [oic.if.r, oic.if.baseline]
Values: {lux=134.0, ts_secs=258, ts_usecs=505505, ts_cputime=258758537, ir=7.0, full=19.0}
Resource discovery is the process in which a client retrieves the interactive resources from the server. More specifically, resources are discovered by sending a GET command to the host's /oic/res
resource. Discovery can also be queried to discover only resources which have certain resource types or interfaces.
First, it is imperative to note that the Iotivity library which is used in the OIC Sensor App is actually a modified fork of the main Iotivity project's 1.1.1 release which can be found in the runtimeco iotivity repository (1.1.1_android_darwin branch). From Iotivity 1.2 and on, Iotivity adds additional headers and packet fragmentation over BLE which interferes with communication with Mynewt devices. Furthermore, the iOS BLE transport implementation (which is not available in the stock release) was initially built on top of Iotivity 1.1.1 and has yet to be ported to later releases.
The Iotivity library for Android provides a Java wrapper for the base C++ libraries, which, in turn, calls the base C SDK.
Because this tutorial is specifically for Android devices we won’t be covering the interactions between the Java SDK and the C++ and C SDKs, but if you are interested in learning what's going on underneath, check out the iotivity source.
In order to communicate with a Mynewt device using the Iotivity library in your OIC client, you must first create a configuration of the Iotivity platform using the PlatformConfig
class, then configure the platform using OcPlatform.Configure(platformConfig)
. Here is the javadoc comment describing PlatformConfig
constructor:
/**
* @param context app context
* @param serviceType indicate IN_PROC or OUT_OF_PROC
* @param modeType indicate whether we want to do server, client or both
* @param ipAddress ip address of server
* if you specify 0.0.0.0 : it listens on any interface
* @param port port of server
* if you specify 0 : next available random port is used
* if you specify 5683 : client discovery can work even if
* they don't specify port
* @param qualityOfService quality of service
*/
And an example of usage from the Sensor app:
//Configure the Iotivity Platform
PlatformConfig platformConfig = new PlatformConfig(mContext, ServiceType.IN_PROC,
ModeType.CLIENT, "0.0.0.0", 0,
QualityOfService.LOW);
OcPlatform.Configure(platformConfig);
Your platform is now configured!
After you have configured your platform, the next step is to discover resources. Resources can be discovered over multiple transports including IP, BLE, BT EDR, and NFC; however, in this tutorial we will only be going over IP and BLE. Resource discovery is arguably the most difficult part of using the Iotivity library, especially over BLE. All resource discovery on Android uses the method OcPlatform.findResoucrce(...)
. Here is the javadoc comment for OcPlatform.findResoucrce(...)
:
/**
* API for Service and Resource Discovery.
* <p>
* Note: This API is for client side only.
* </p>
*
* @param host Host IP Address of a service to direct resource discovery query.
* If empty, performs multicast resource discovery query
* @param resourceUri name of the resource. If null or empty, performs search for all
* resource names
* @param connectivityTypeSet Set of types of connectivity. Example: IP
* @param onResourceFoundListener Handles events, success states and failure states.
* @param qualityOfService the quality of communication
* @throws OcException if failure
*/
The easiest way to discover resources is using IP. IP is fast and can easily be multicasted to many devices, or unicast to just one. To do multicast discovery over IP, just add IP to your connectivityTypeSet
and don't specify a host:
OcPlatform.findResource("", OcPlatform.WELL_KNOWN_QUERY,
EnumSet.of(OcConnectivityType.CT_ADAPTER_IP),
this, QualityOfService.LOW);
The discovered resources will be returned via the given OnResourceFoundListener
's onResourceFound(OcResource)
callback.
Note: the OcPlatform.WELL_KNOWN_QUERY
constant is equal to "/oic/res"
, the resource query URI defined in the OIC specification.
Discovery over BLE is the same as IP, however BLE is not able to multicast easily like IP. Rather, in order to do a muticast BLE discovery, we have to do an initial scan using the native Android BLE scanner in order to get all available OIC enabled hosts. Next, we must do unicast discovery for each host device discovered in the initial BLE scan one-by-one. In the Mynewt Sensors app, there is an AsyncTask which handles this process (described below). However, unicast discovery of a BLE device is pretty much the same as using IP:
OcPlatform.findResource("AB:CD:EF:12:34:56", OcPlatform.WELL_KNOWN_QUERY,
EnumSet.of(OcConnectivityType.CT_ADAPTER_GATT_BTLE),
this, QualityOfService.LOW);
Getting values from a resource is simple and works similarly to resource discovery. Once resources have been discovered, we can use the returned OcResource to get the resource's values. OcResource
's get(...)
method takes as input a map of values to query (empty if you want to get all values) and an OnGetListener
as a callback:
resource.get(new HashMap<String, String>(), this);
OnGetListener
's callback onGetCompleted
will return the OcRepresentation
, which holds the map of values for the resource.
Observing a resource is nearly the same as get, except that observe will periodically return values without having to make successive calls to get. The rate at which observe notifies the client is decided by the server. observe(...)
takes an additional argument, ObserveType
, which specifies whether to notify all the registered observers.
resource.observe(ObserveType.OBSERVE, new HashMap<String, String>(), this);
When building client applications, it is important to make sure to cancel the observation. Multiple observe requests that are not canceled can slow down the server. To cancel an observation, call resource.cancelObserve()
.
Setting values in a resource is also easy to do using Iotivity. First, create a new OcRepresentation object and put the keys and values you wish to modify. Next, call put using the representation, a query parameters map (usually empty), and a put callback:
OcRepresentation rep = new OcRepresentation();
rep.setValue("value", true);
mResource.put(rep, new HashMap<String, String>(), this);
The Mynewt Sensor application is a sample for developers looking to use OIC to communicate with Mynewt devices which use the sensor framework to expose sensor data. The source code is available on github as a part of runtimeco.
To discover OIC enabled devices, hit the discovery button floating in the bottom right of the Main screen. This will discover devices using the options given to the DiscoveryTask. By default, we will do a general discovery of all OIC enabled devices over BLE and IP transport unless modified in the source. Any resource containing a valid Mynewt sensor framework resource type will be added to the Sensors list while any other device will be added to the Smart Devices list
To Observe a sensor, click the sensor in the list. This will bring up the Sensor activity which will immediately start observing the sensor. You can stop/start observing the sensor by hitting the start/stop observe button in the top right of the actionbar. When not observing the sensor, you may want to turn on an off certain sensor values from the chart using the switches on the each sensor value in the list.
If you're making your own OIC application and want to get started quickly there are some useful files that you may want to use in your own application.
The DisocoveryTask
class is an AsyncTask which can be initialized to do discovery over BLE, IP, or both. DiscoveryTask
also optionally takes in a ProgressDialog and a whitelist of BLE host addresses. Because the discovery process may take some time (up to 30 seconds in the worst case), the input ProgressDialog's message will be given discovery status updates to show the user progress. If you choose to use the whitelist, DiscoveryTask
will forgo the BLE scan and instead do direct discovery of all the host address's in the whitelist.
Here are all the available constructors to DiscoveryTask:
/**
* General multicast resource discovery constructor for DiscoveryTask. This DiscoveryTask will
* discover all OIC enabled devices over BLE and IP transport
*
* @param context context
* @param listener the OnDiscoveryListener for discovery callbacks
*/
public DiscoveryTask(Context context, OnDiscoveryListener listener) {
// ...
}
/**
* Multicast resoruce discovery constructor for DiscoveryTask. This DiscoveryTask will publish
* progress updates to the given ProgressDialog's message TextView.
*
* @param context context
* @param listener the OnDiscoveryListener for discovery callbacks
* @param discoverBle whether or not to discover devices over BLE transport
* @param discoverIp whether or not to discover devices over IP transport
* @param progressdialog the progress dialog to publish progress to
*/
public DiscoveryTask(Context context, OnDiscoveryListener listener, boolean discoverBle,
boolean discoverIp, ProgressDialog progressdialog) {
// ...
}
/**
* Unicast BLE, Multicast IP resource discovery constructor for DiscoveryTask. This
* DiscoveryTask will multicast discovery IP devices and only discover BLE devices for hosts
* in the whitelist.
*
* @param context context
* @param listener the OnDiscoveryListener for discovery callbacks
* @param discoverBle whether or not to discover devices over BLE transport
* @param discoverIp whether or not to discover devices over IP transport
* @param bleWhitelist the list of BLE host addresses to do resource discovery on
*/
public DiscoveryTask(Context context, OnDiscoveryListener listener, boolean discoverBle,
boolean discoverIp, List<String> bleWhitelist) {
// ...
}
/**
* Unicast BLE, Multicast IP resource discovery constructor for DiscoveryTask. This
* DiscoveryTask will publish progress updates to the given ProgressDialog's message TextView.
* This DiscoveryTask will multicast discovery IP devices and only discover BLE devices for
* hosts in the whitelist.
*
* @param context context
* @param listener the OnDiscoveryListener for discovery callbacks
* @param discoverBle whether or not to discover devices over BLE transport
* @param discoverIp whether or not to discover devices over IP transport
* @param progressdialog the progress dialog to publish progress to
* @param bleWhitelist the list of BLE host addresses to do resource discovery on
*/
public DiscoveryTask(Context context, OnDiscoveryListener listener, boolean discoverBle,
boolean discoverIp, ProgressDialog progressdialog, List<String> bleWhitelist) {
// ...
}
After you construct your desired DiscoveryTask, just execute!
new DiscoveryTask(this, this, true, true, mDiscoveryDialog).execute();
Observe task is like DiscoveryTask but simpler. It is primarily used to handle the progress dialog when calling observe and waiting for a response. A similar AsyncTask could be used to do GET and PUT commands with similar UI handling.
Constructor:
/**
* Constructor for ObserveTask.
*
* @param context context
* @param resource resource to observe
* @param listener OnObserveListener for callback
*/
public ObserveTask(Context context, OcResource resource, OcResource.OnObserveListener listener) {
// ...
}
Usage:
new ObserveTask(this, resource, this).execute();
The MynewtSensor
class is a wrapper for an OcRepresentation of a valid Mynewt sensor exposed using the Mynewt sensor framework. Mynewt Sensor
is used primarily for assisting SensorActivity
in charting sensor values. The class also contains a few useful utility functions for determining whether an OcResource is a Mynewt sensor. Finally, this class contains the constants for all Mynewt sensor resource types and human readable names.
Since this tutorial only covers the Android client side, you may want to look at some tutorials for setting up your sensors using the Mynewt sensor framework.