Sphero球。

介绍

Sphero球的颜色。 换句话说,即使智能手机处于待机模式或“活动”不在前台,我也要根据智能手机测得的温度来更改色球。

这是一个有趣的项目,因为它可以用来描述一些重要的概念:

  • Android服务
  • Android广播接收器
  • 感测器
  • 警报管理器

最后,但同样重要的是,如何通过其SDK连接和使用Sphero ball 。 我们想要设计一个如下所示的应用程序:

android 获取温度传感器数据 温度传感器手机app_java

设计应用

现在我们知道了要获取的内容,可以将Android功能和组件混合使用。 我们需要一个监视温度传感器的组件和另一个连接到Sphero的组件。 如前所述,即使应用程序不在前台或智能手机未激活,我们也希望使此组件正常工作。 所以我们需要一个服务,因为这个Android组件可以满足我们的要求。 那么,我们需要

  • 作为应用程序用户界面的活动
  • 监视温度传感器的服务
  • 连接到球并控制其颜色的服务

android 获取温度传感器数据 温度传感器手机app_java_02

查看下面的图片,我们可以注意到UI Activity启动了两个服务并侦听来自这些服务的事件,更详细地讲,该Activity设置了一个警报,用于启动温度传感器服务,这样我们就不会耗尽电池。 可以将警报配置为每隔固定的时间启动一次。 每次温度传感器启动时,它都会使用智能手机传感器测量环境温度,并广泛地应用该值。 UI Activity侦听这些事件并向UI显示值,与此同时Ball Connection Service侦听同一事件,并在获取事件后立即计算颜色分量(R,G,B)和设置球的颜色。

创建温度传感器服务:代码

现在,我们对应用程序中的主要组件有了一个概述,我们可以开始对其进行编码。 我们要编写的第一个元素是读取当前温度的温度传感器服务。 我们知道我们需要一项服务:

public class SensorService  extends Service implements SensorEventListener {
...
}

我们必须实现SensorEventListener来侦听传感器事件,然后在onStartCommand中将此类注册为侦听器:

@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;
}

最后,当我们收到有关新温度值的通知时,我们将其处理:

@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();
}

在第15行,我们停止服务,因为我们不想一直读取值以免耗尽电池。

创建Ball Connection服务:代码

我们必须实现的另一项服务是通过蓝牙处理Sphero连接。 您可以参考Sphero SDK以获得更多信息。 我们要处理Android服务中的连接:

public class BallConnectionService extends Service {
..
}

现在,在onStartCommand中,我们开始连接到Sphero,同时我们开始侦听传入的温度事件(第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;
}

在doConnection中,我们进行真正的连接:

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);
  }

代码看起来很复杂,但是如果仔细看,它确实很简单。 我们开始广播尝试连接到Sphero的事件(第3行),然后使用Sphere API注册一个侦听器,以了解何时建立连接,并最后广播一个新的事件,表明该连接处于活动状态通过这种方法,我们开始发现周围是否有新的Sphero并准备连接。

服务的最后一部分用于监听温度事件并设置色球:

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);
        }
      }
 ;

创建活动

最后一步是创建用于控制UI并启动和停止服务的Activity。 我们提供了两个操作栏按钮:一个用于启动服务,另一个用于停止服务。 如果触摸启动服务,我们将启动AlarmManager来安排何时运行我们的服务:

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);

在这个简单的代码中,我们创建一个PendingIntent并获取对AlarmManager的引用,最后我们调度警报,以便可以在固定的时间后启动该服务。 (第3行)。 在createAlarm()方法中,我们设置意图:

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;
}

最后,我们必须创建两个接收器来侦听来自温度传感器和球形连接服务的事件:

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));
    }
 };

在第5行,我们显示当前温度,而对于球类服务,我们有:

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();
        }

      }
    };
  • 源代码即将在github上提供。