Flower Power for Android... an open source library for Parrot's Flower Power sensor platform. It is 100% pure Java. |
TutorialIn this tutorial you will learn how to use the provided features of Flower Power for Android. We will start with introducing the application and service lifecycle and how to connect to a Flower Power device. Before we are going to show how to program with the Flower Power for Android library, we want to make a short excursus to Android's application and service lifecycle. The main reason for this is the more or less complex connection handling with the Flower Power in order to consume as less energy as possible. Ideally, we want to connect to the Flower Power only, when we require data (e.g. display current state on the UI). But sometimes, we also want to keep a connection in order to continuously record data (e.g. to record, store and later on display time series over a certain time interval).In the following, we will see how to create a minimal Android application that establishes a connection to a Flower Power, requests some data, writes the data into a persistent storage and finally displays the data in a nice plot. PreparationsBut, we first need to make some preparations:
Foundations of Application and Service LifecycleNow that we have our initial project set up, we can have a look at an Activity's lifecycle. The main callback methods are shown below (you should already be familiar with Android programming as some basics are assumed to be known):public class FlowerPowerTutorialActivity extends Activity { /** Called once the Activity is initially created */ public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.flowerpower_main); Intent intent = getIntent(); String deviceName = intent.getStringExtra( FlowerPowerConstants.EXTRAS_DEVICE_NAME); String deviceAddress = intent.getStringExtra( FlowerPowerConstants.EXTRAS_DEVICE_ADDRESS); } /** Called once the Activity is visible after having been paused */ protected void onResume() { super.onResume(); } /** Called once the Activity is no longer visible (e.g. because another Activity is now in the foreground) */ protected void onPause() { super.onPause(); } /** Called once the Activity is finally being destroyed */ protected void onDestroy() { super.onDestroy(); } }Looking at the onCreate method we first set a specific layout (you probably want to set yours here) and then get the device's name and its address. In the Intent that triggered the creation of our Activity (the DeviceScanActivity of Flower Power for Android does this) two extra fields containing the name and address of the chosen Bluetooth device are provided. The address is the so called MAC address of the Bluetooth device and is a universally unique identifier. We need it to establish a connection. The connection is established by an Android service (FlowerPowerService), which runs in the background independently of the Activity. Hence you need to declare this service in your Android manifest: <service android:name="de.fp4a.service.FlowerPowerService" android:enabled="true" />The service offers several methods to interact with a Bluetooth device. But in order to transparently access the Flower Power device, you only need to interact with the FlowerPowerServiceManager. This class offers several convenience methods to manage the service's lifecycle. These methods should be called in the corresponding lifecycle callbacks of your activity: // in onCreate serviceManager = FlowerPowerServiceManager.getInstance(deviceAddress, this); serviceManager.bind(); // in onResume serviceManager.connect(); // in onPause serviceManager.pause(); // in onDestroy serviceManager.disconnect(); serviceManager.unbind();In the onCreate method an instance of the serviceManager is created (if it not already exists) and the device address is passed, because each physical Flower Power device is handled by its own service (if you have e.g. three Flower Powers you'll end up with three service instances). Afterwards, the manager is requested to create (if not already done so previously) and to bind the service. After the onCreate method, the onResume method is called. It is here, when we establish a connection to the device. In the onPause method which is called once the Activity is not visible anymore, we do not disconnect (though we may), but we only pause the receiving of state updates from the service (see below). It is the onDestroy method in which we actually disconnect and unbind the service, releasing all resources hold by the service. Reading DataSo far, we have seen how to handle the service lifecycle, but we have not yet seen how to actually retrieve some data. Understandably, we can only retrieve data if we successfully (!) connected to the device. But how do we know if we did ? All events that may happen in the service's lifecycle (connection, disconnection, failure, data available etc.) are dispatched to listeners. The IFlowerPowerServiceListener is an interface, which defines callback methods to be implemented by anyone interested in receiving such service status updates. And such a listener needs to be registered with the serviceManager:IFlowerPowerServiceListener serviceListener = new IFlowerPowerServiceListener() { public void deviceConnected() { } public void deviceDisconnected() { } public void deviceReady(IFlowerPowerDevice device) { } public void dataAvailable(FlowerPower fp) { } public void serviceConnected() { } public void serviceDisconnected() { } public void serviceFailed() { finish(); } }; serviceManager.addServiceListener(serviceListener);So, what you have to do is to implement the IFlowerPowerServiceListener interface (above it is implemented as an anonymous inner class) and to simply register the implementing class as a service listener with the serviceManager. Afterwards, the serviceManager will call your class once any events happen. The two most important methods are: deviceReady and dataAvailable. deviceReady is called once a connection to the Flower Power is established successfully and everything is set up in order to start requesting data. In order to do so, an IFlowerPowerDevice is provided as argument. Use this to read data from the Flower Power like this: device.readFriendlyName(); device.readBatteryLevel(); device.readTemperature(); device.readSunlight(); device.readSoilMoisture(); device.read ...As you can see, none of these methods has a return value. So, how to actually get the data ? Read requests are executed asynchronously, that means, the control flow returns immediately and once the requested data is available an event is triggered and all service listeners are informed (see above). This is, where the other method, dataAvailable, comes into play. Once new data is available, this method is called providing an instance of FlowerPower. That class represents the data model and holds all information that have been read so far. You can now access the results of your corresponding read requests: String friendlyName = fp.getMetadata().getFriendlyName(); int batteryLevel = fp.getBatteryLevel(); double temperature = fp.getTemperature(); ... NotificationsIf you want continuous updates, you do not have to call readXYZ() periodically. For this purpose, you can use notifications. If you subscribe for a notification, the corresponding sensor characteristic is updated automatically once in a second.device.notifyTemperature(true); device.notifySunlight(true); device.notifySoilMoisture(true); device.notifyBatteryLevel(true);You can enable notifications for dynamic characteristics, i.e. attributes that change over time. If you call notifyXYZ, you will receive subsequent updates via the dataAvailable method of your listener (see above). Do not forget to disable notifications once you do not use them anymore. Call notifyXYZ(false) to do so. PersistencyPersisting values allows to create time series over a certain time span. This is useful, e.g. to display the changes graphically or to export time series in order to postprocess these with an external programm (e.g. for statistics). In order to persist time series, you simply callserviceManager.enablePersistency(5000, 1000, FlowerPowerConstants.PERSISTENCY_STORAGE_LOCATION_INTERNAL, "flowerpower4android");This call requires four arguments:
Displaying PlotsWrite me ! |
|