Control Sphero using Temperature Sensor in Android
One of the most interesting topic around Android it is how we can connect our smart phones to other devices or smart devices to get information from them or to control them. In this post I want to introduce a new way to use our Android phone and explain how we can use it to control Sphero ball.
Introduction
In this Android project, I want to describe how we can integrate the temperature sensor inside the smart phone to control the Sphero ball color. In other words, I want to change the color ball according to the temperature measured by the smart phone even if the smart phones is in the stand by mode or the Activity is not in the foreground.
This is an interesting project because it can be used to describe some important concept:
and finally, but not less important, how to connect and use Sphero ball with its SDK. What we want to design an app like shown below:
Design the app
Now we know what we want to obtain, we can mix Android features and components to get it. We need a component that monitors the temperature sensor and another one that connect to the Sphero. As said before, we want to make this component working even if the app is not in the foreground or the smart phone is not active. So we need a Service, because this Android component can fulfill our requirements. We need, then,
- An Activity that is the app UI
- A Service that monitors the temperature sensor
- A Service that connects to the ball and control its color
Looking at the pic below, we can notice that the UI Activity starts two services and listens for events coming from these services, in more details the Activity set up an alarm that is used to start the Temperature Sensor Service so that we won’t drain the battery. The alarm can be configured to start every fixed amount of time. Every time the Temperature Sensor starts it measure the environment temperature using the smart phone sensor and broad cast the value. The UI Activity listens these events and shows the value to the UI, at the same time the Ball Connection Service listens for the same event and as soon as it gets the event, this service calculates the color components (R,G,B) and set the ball color.
Create Temperature Sensor Service: code
Now we have an overview about the main components in our app, we can start coding it. The first element we want to code is the Temperature Sensor service that reads the current temperature. As we know we need a service:
public class SensorService extends Service implements SensorEventListener { ... }
we must implement SensorEventListener to listen to the sensor events, then in onStartCommand we register this class as listener:
@Override public int onStartCommand(Intent intent, int flags, int startId) { sManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE); sensor = sManager.getDefaultSensor(Sensor.TYPE_AMBIENT_TEMPERATURE); sManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL); return Service.START_STICKY; }
finally when we get notified about the new temperature value we handle it:
@Override public void onSensorChanged(SensorEvent event) { // We get the temperature and notify it to the Activity float temp = event.values[0]; Intent i = new Intent(); i.setAction(TEMP_BALL_SENSOR); i.putExtra("value", temp); sendBroadcast(i); // stop listener if (sManager != null) sManager.unregisterListener(this); // stop service stopSelf(); }
At line 15,we stop the service because we don’t want to read all the times the values to not drain the battery.
Create Ball Connection Service: code
The other service we have to implement is to handle Sphero connection via bluetooth. You can refer to Sphero SDK to have more information. We want to handle the connection in the Android service:
public class BallConnectionService extends Service { .. }
now in the onStartCommand we start connecting to the Sphero and at the same time we start listening for incoming temperature event (line 8).
@Override public int onStartCommand(Intent intent, int flags, int startId) { if (mySphero == null) doConnection(); IntentFilter rec = new IntentFilter(); rec.addAction(SensorService.TEMP_BALL_SENSOR); registerReceiver(receiver, rec); return Service.START_STICKY; }
in doConnection we make the real connection:
private void doConnection() { sendStatus(CONNECTING); createNotification("Connecting..."); RobotProvider.getDefaultProvider().addConnectionListener(new ConnectionListener() { @Override public void onConnected(Robot robot) { Log.d("Temp", "Connected"); mySphero = (Sphero) robot; sendStatus(CONNECTED); createNotification("Connected"); } @Override public void onConnectionFailed(Robot robot) { Log.d("Temp", "Conection failed"); sendStatus(FAILED); } @Override public void onDisconnected(Robot robot) { Log.d("Temp", "Disconnected"); mySphero = null; createNotification("Disconnected!"); } }); RobotProvider.getDefaultProvider().addDiscoveryListener(new DiscoveryListener() { @Override public void onBluetoothDisabled() { Log.d("Temp", "BT Disabled"); } @Override public void discoveryComplete(List<Sphero> spheros) { Log.d("Temp", "Found ["+spheros.size()+"]"); } @Override public void onFound(List<Sphero> spheros) { // Do connection Log.d("Temp", "Found ball"); RobotProvider.getDefaultProvider().connect(spheros.get(0)); } }); boolean success = RobotProvider.getDefaultProvider().startDiscovery(this); }
The code seems complex but it is really simple if you look at it carefully. We start broadcasting the event that we are trying to connect to the Sphero (line 3), then, using Sphere API, we register a listener to know when the connection is established and broadcast a new event that the connection is active, at the end of this method we start discovering if new Sphero in around and ready to connect.
The last part of the service is used for listening to the temperature event and set the color ball:
private BroadcastReceiver receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { float val = intent.getFloatExtra("value", 0); Log.d("Temp", "Received value ["+val+"]"); if (mySphero != null) { // send color to sphero int red = (int) (255 * val / RANGE) * (val > 10 ? 1 : 0); int green = (int) ( (255 * (RANGE - Math.abs(val)) / RANGE) * (val < 10 ? 0.2 : 1) ); int blue = (int) (255 * (10 - val) / 10) * (val < 10 ? 1 : 0); mySphero.setColor(red, green, blue); } } ;
Create the Activity
The last step is creating the Activity that controls the UI and starts and stops the service. We provide two action bar buttons: one to start the services and another one to stop them. If we touch the start service, we start the AlarmManager to schedule when to run our service:
PendingIntent pi = createAlarm(); AlarmManager scheduler = (AlarmManager) getSystemService(Context.ALARM_SERVICE); scheduler.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 60 * 1000, pi); Intent i1 = new Intent(this, BallConnectionService.class); startService(i1);
In this simple code, we create a PendingIntent and get a reference to the AlarmManager, finally we schedule the alarm so that the service can be started after a fixed amount of time. (line 3). In createAlarm() method we setup the intent:
private PendingIntent createAlarm() { AlarmManager scheduler = (AlarmManager) getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(this,SensorService.class ); PendingIntent scheduledIntent = PendingIntent.getService(getApplicationContext(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); return scheduledIntent; }
Finally, we have to create two receivers that listen for event coming from temperature sensor and ball connection services:
private BroadcastReceiver sensorReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { float val = intent.getFloatExtra("value", 0); tempView.setText(String.format("%.1f",val)); } };
At line 5, we show the current temperature, while for the ball service we have:
private BroadcastReceiver ballReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { int status = intent.getIntExtra("status", -1000); Log.d("Temp", "Value Status ["+status+"]"); if (status == BallConnectionService.CONNECTING) { tempView.startAnimation(pulseAnim); Toast.makeText(MyActivity.this, "Connecting...", Toast.LENGTH_SHORT).show(); } else if (status == BallConnectionService.CONNECTED) { tempView.clearAnimation(); Intent i = new Intent(MyActivity.this, SensorService.class); startService(i); Toast.makeText(MyActivity.this, "Connected", Toast.LENGTH_LONG).show(); } else if (status == BallConnectionService.FAILED) { Toast.makeText(MyActivity.this, "Connection failed. Try again pressing start button", Toast.LENGTH_LONG).show(); } } };
- Source code available soon @ github.
Reference: | Control Sphero using Temperature Sensor in Android from our JCG partner Francesco Azzola at the Surviving w/ Android blog. |