AndroidThings传感器添加
Android 传感器框架支持多种传感器类型来感知周边环境数据。使用Androidthings提供的传感器驱动接口,可以通过Peripheral I/O接口来添加新的传感器设备。对于Android系统中已经集成的传感器的数据通过SensorManager的接口即可获取,另外我们的Androidtings应用也可以通过创建新的传感器驱动的方式来为系统添加一个新类型的传感器,比如温感器,血糖检测器等传感器。
创建一个新的传感器驱动并将其注册进系统后,系统将会轮询监听传感器来周期性的获取传感器数据, 为了响应系统对传感器数据的轮训请求, 我们需要实现UserSensorDriver 类并且重写read()方法,在read方法中返回传感器数据.下面我们来详细描述添加一个新的传感器的过程。
首先要添加系统权限
<uses-permission android:name="com.google.android.things.permission.MANAGE_SENSOR_DRIVERS" />
然后要实现一个传感器驱动,用来读取传感器数据,每次调用read方法都要返回一个新的包含当前传感器数据的UserSensorReading对象。代码示例如下
UserSensorDriver mDriver = new UserSensorDriver() {
// Sensor data values
float x, y, z;
@Override
public UserSensorReading read() {
try {
// ...读取传感器硬件数据的步骤此处略过...
// 将读取到的传感器数据返回
return new UserSensorReading(new float[]{x, y, z});
} (catch Exception e) {
throw new IOException("Unable to read sensor");
}
}
};
创建完传感器驱动后,开始为系统添加传感器,传感器分为系统已支持的类型和系统还未支持的类型,两者的添加步骤有区别。
如果需要添加一个系统支持类型的传感器设备:
- 使用 UserSensor.Builder 描述传感器类型,传感器类型为Android 目前已经支持的 Sensor types.
- 设置 sensor 名称以及驱动供应商名称
- 设置传感器数据范围,分辨率,更新频率,电量需求(如果需要).这些参数将帮助framework在接到SensorManager的数据请求时来选择最佳的传感器。
- 使用setDriver()方法attach UserSensorDriver
如果要添加一个系统尚未支持的传感器类型:
- 使用setCustomType() 方法,设置sensor type 为大于等于 TYPE_DEVICE_PRIVATE_BASE的类型值
- 设置传感器类型名称,传感器名称在系统范围内需要是唯一的
- 设置传感器数据报告模式
代码示例如下:
// 添加已支持类型的传感器
UserSensor accelerometer = UserSensor.builder()
.setName("GroveAccelerometer")
.setVendor("Seeed")
.setType(Sensor.TYPE_ACCELEROMETER)
.setDriver(mDriver)
.build();
// 添加未支持类型的传感器
UserSensor custom = UserSensor.builder()
.setName("MySensor")
.setVendor("MyCompany")
.setCustomType(Sensor.TYPE_DEVICE_PRIVATE_BASE,
"com.example.mysensor",
Sensor.REPORTING_MODE_CONTINUOUS)
.setDriver(mDriver)
.build();
创建完传感器后就要将其注册到系统,使用UserDriverManager来 注册传感器,将新传感器连接到系统,应用就可以可以通过Android传感器系统服务来获取传感器的数据。实例代码如下
public class SensorDriverService extends Service {
UserSensor mAccelerometer;
@Override
public void onCreate() {
super.onCreate();
...
UserDriverManager manager = UserDriverManager.getManager();
// 创建一个新的传感器
mAccelerometer = ...;
// 将传感器注册到系统
manager.registerSensor(mAccelerometer);
}
}
至此,如何添加一个新的传感器的步骤我们就描述完了。下面我们介绍如何为Androidthings应用添加相机功能,这也是官方提供的一个示例。
Androidthings相机功能添加
首先添加系统权限:
<uses-permission android:name="android.permission.CAMERA" />
然后将相机连接到开发板,将相机模块连接到开发板上的CS1-2相机接口,如下图所示(图片来自于谷歌开发者网站)
完成上面的准备工作后,我们正式开始进行图片获取相关的流程,获取图片的第一步是需要找到相机并且建立和设备之间的连接:
- 使用CameraManager 系统服务的getCameraIdList()找到所有可用的相机设备列表。
- 创建一个ImageReader 实例用来处理相机原始数据并且生成JPEG编码格式的图像数据。
- 建立和相机设备的连接,相机被成功打开后将会调用CameraDevice.StateCallback的onOpened() 回调方法
示例代码如下:
public class CameraSample {
// 图像参数 (device-specific)
private static final int IMAGE_WIDTH = ...;
private static final int IMAGE_HEIGHT = ...;
private static final int MAX_IMAGES = ...;
// 用于处理图像结果
private ImageReader mImageReader;
// 激活相机设备连接
private CameraDevice mCameraDevice;
// 激活图像捕捉
private CameraCaptureSession mCaptureSession;
// Initialize a new camera device connection
public void initializeCamera(Context context,
Handler backgroundHandler,
ImageReader.OnImageAvailableListener imageAvailableListener) {
// 获取可用的相机设备列表
CameraManager manager = (CameraManager) context.getSystemService(CAMERA_SERVICE);
String[] camIds = {};
try {
camIds = manager.getCameraIdList();
} catch (CameraAccessException e) {
Log.d(TAG, "Cam access exception getting IDs", e);
}
if (camIds.length < 1) {
Log.d(TAG, "No cameras found");
return;
}
String id = camIds[0];
// 初始化图片处理器 imagereader
mImageReader = ImageReader.newInstance(IMAGE_WIDTH, IMAGE_HEIGHT,
ImageFormat.JPEG, MAX_IMAGES);
mImageReader.setOnImageAvailableListener(imageAvailableListener, backgroundHandler);
// 打开相机
try {
manager.openCamera(id, mStateCallback, backgroundHandler);
} catch (CameraAccessException cae) {
Log.d(TAG, "Camera access exception", cae);
}
}
// 相机相关回调
private final CameraDevice.StateCallback mStateCallback =
new CameraDevice.StateCallback() {
@Override
public void onOpened(CameraDevice cameraDevice) {
mCameraDevice = cameraDevice;
}
...
};
// 关闭相机资源
public void shutDown() {
if (mCameraDevice != null) {
mCameraDevice.close();
}
}
}
上面详细描述了相机初始化,建立设备连接相关的工作,接下来描述一下如何调用相机来捕获图片
和相机建立连接之后, 需要创建一个相机拍照会话,具体步骤如下:
- 使用createCaptureSession() 方法创建一个新的CameraCaptureSession 实例
- 传入和ImageReader建立连接的surface
- 创建一个 CameraCaptureSession.StateCallback回调来监控相机会话配置以及激活的状态 。示例代码如下
public class CameraSample {
...
public void takePicture() {
// 为了捕获静态图像,首先创建一个图像捕捉会话
try {
mCameraDevice.createCaptureSession(
Collections.singletonList(mImageReader.getSurface()),
mSessionCallback,
null);
} catch (CameraAccessException cae) {
Log.d(TAG, "access exception while preparing pic", cae);
}
}
// 通过回调来监控会话状态
private CameraCaptureSession.StateCallback mSessionCallback =
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(CameraCaptureSession cameraCaptureSession) {
// 会话已经准备好,则可以开始拍照.
mCaptureSession = cameraCaptureSession;
triggerImageCapture();
}
@Override
public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
Log.w(TAG, "Failed to configure camera");
}
};
}
接下来进入到图片捕捉阶段 :
- 初始化一个新的 CaptureRequest ,如果静态图片使用TEMPLATE_STILL_CAPTURE 参数.
- 设置其他相机拍照参数, 比如 auto-focus ,auto-exposure等
- 调用CameraCaptureSession的capture方法来启动拍照请求,拍照完成后,关闭capture session。通过CameraCaptureSession.CaptureCallback回调来处理拍照结果。具体示例代码如下:
public class CameraSample {
// Active camera device connection
private CameraDevice mCameraDevice;
// Active camera capture session
private CameraCaptureSession mCaptureSession;
...
private void triggerImageCapture() {
try {
final CaptureRequest.Builder captureBuilder =
mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(mImageReader.getSurface());
captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
mCaptureSession.capture(captureBuilder.build(), mCaptureCallback, null);
} catch (CameraAccessException cae) {
Log.d(TAG, "camera capture exception");
}
}
// Callback handling capture progress events
private final CameraCaptureSession.CaptureCallback mCaptureCallback =
new CameraCaptureSession.CaptureCallback() {
...
@Override
public void onCaptureCompleted(CameraCaptureSession session,
CaptureRequest request,
TotalCaptureResult result) {
if (session != null) {
session.close();
mCaptureSession = null;
Log.d(TAG, "CaptureSession closed");
}
}
};
}
最后是对获取到的原始图像数据的处理:
- 通过ImageReader 的onImageAvailable 方法获取最新的图像数据 .
- 通过getBuffer()方法来获取JPEG编码格式的数据。代码实例如下:
// Callback to receive captured camera image data
private ImageReader.OnImageAvailableListener mOnImageAvailableListener =
new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
// Get the raw image bytes
Image image = reader.acquireLatestImage();
ByteBuffer imageBuf = image.getPlanes()[0].getBuffer();
final byte[] imageBytes = new byte[imageBuf.remaining()];
imageBuf.get(imageBytes);
image.close();
onPictureTaken(imageBytes);
}
};
private void onPictureTaken(final byte[] imageBytes) {
if (imageBytes != null) {
// ...process the captured image...
}
}
这样就完成了通过相机设备来获取静态图像数据的整个流程。
参考资料:https://developer.android.google.cn/things/sdk/drivers/sensors.html#describing_the_sensor