Android Things可让您使用简单的代码制作出惊人的IoT设备。 在这篇文章中,我将向您展示如何将各个部分放在一起以构建更复杂的项目!
这将不是一个完整的自上而下的教程。 我将为您留出足够的空间来扩展和自定义设备和应用程序,以便您可以自己进行探索和学习。 我的目标是在使用此新开发平台的过程中获得乐趣,并向您展示Android Things不仅仅是闪烁的LED。
我们正在建设什么?
物联网项目的一半乐趣来自于“事物”。 对于本文,我将构建一个连接云的门铃,当有人靠近时,它将拍照,然后将该图像上传到Firebase并触发操作。 在开始之前,我们的项目将需要一些组件:
- Raspberry Pi 3B在SIM卡上具有Android Things
- 树莓派相机
- 运动探测器(组件:HCSR501)
此外,您可以自定义项目以适合自己的创作风格,并从中获得乐趣。 在我的项目中,我采取了自万圣节以来一直挂在门廊上的骨架装饰,并将其用作我的项目的外壳-钻出眼睛以固定相机和运动检测器。
我还添加了一个伺服电机来移动下颌,下颌用一块松紧带保持闭合状态,还添加了一个USB扬声器来支持文本到语音功能。
检测运动
我们将在此项目中使用两个主要组件:摄像机和运动检测器。 我们先看一下运动检测器。 这将需要一个新类来处理从GPIO
引脚读取数字信号的过程。 当检测到运动时,将触发一个回调,我们可以在MainActivity
上侦听。 有关GPIO的更多信息,请参阅关于Android Things外设的文章。
private GpioCallback mInterruptCallback = new GpioCallback() {
@Override
public boolean onGpioEdge(Gpio gpio) {
try {
if( gpio.getValue() != mLastState ) {
mLastState = gpio.getValue();
performMotionEvent(mLastState ? State.STATE_HIGH : State.STATE_LOW);
}
} catch( IOException e ) {
}
return true;
}
};
如果您一直关注Envato Tuts +上的Android Things系列,则可能想尝试自己编写完整的运动检测器类,因为它是一个简单的数字输入组件。 如果您想跳过,可以找到本教程的项目中编写的整个组件。
在您的Activity
您可以实例化HCSR501
组件,并将新的HCSR501.OnMotionDetectedEventListener
与其关联。
private void initMotionDetection() {
try {
mMotionSensor = new HCSR501(BoardDefaults.getMotionDetectorPin());
mMotionSensor.setOnMotionDetectedEventListener(this);
} catch (IOException e) {
}
}
@Override
public void onMotionDetectedEvent(HCSR501.State state) {
if (state == HCSR501.State.STATE_HIGH) {
performCustomActions();
}
}
运动探测器正常工作后,就该使用Raspberry Pi相机拍照了。
拍照
尽管您感兴趣的主要方法是takePicture()
,但您可以在此项目的示例中找到camera类的所有源代码。 此方法将获取图像并将其返回到应用程序中的回调。
public void takePicture() {
if (mCameraDevice == null) {
return;
}
try {
mCameraDevice.createCaptureSession(
Collections.singletonList(mImageReader.getSurface()),
mSessionCallback,
null);
} catch (CameraAccessException cae) {}
}
将此类添加到您的项目后,您需要将ImageReader.OnImageAvailableListener
接口添加到Activity
,从onCreate()
初始化相机,并侦听任何返回的结果。 当结果通过onImageAvailable()
返回时,您将需要将其转换为byte
数组以上传到Firebase。
private void initCamera() {
mCameraBackgroundThread = new HandlerThread("CameraInputThread");
mCameraBackgroundThread.start();
mCameraBackgroundHandler = new Handler(mCameraBackgroundThread.getLooper());
mCamera = DoorbellCamera.getInstance();
mCamera.initializeCamera(this, mCameraBackgroundHandler, this);
}
@Override
public void onImageAvailable(ImageReader imageReader) {
Image image = imageReader.acquireLatestImage();
ByteBuffer imageBuf = image.getPlanes()[0].getBuffer();
final byte[] imageBytes = new byte[imageBuf.remaining()];
imageBuf.get(imageBytes);
image.close();
onPictureTaken(imageBytes);
}
上传图片
现在您已经拥有了图像数据,是时候将其上传到Firebase了。 尽管我不会详细介绍如何为您的应用设置Firebase,但是您可以按照本教程进行操作以开始并运行 。 我们将使用Firebase Storage来存储图像,尽管一旦您的应用程序设置为使用Firebase,您就可以执行其他任务,例如将数据存储在Firebase数据库中,以便与伴随应用程序一起使用,当有人在您家门口时通知您。 让我们更新onPictureTaken()
方法以上传图像。
private void onPictureTaken(byte[] imageBytes) {
if (imageBytes != null) {
FirebaseStorage storage = FirebaseStorage.getInstance();
StorageReference storageReference = storage.getReferenceFromUrl(FIREBASE_URL).child(System.currentTimeMillis() + ".png");
UploadTask uploadTask = storageReference.putBytes(imageBytes);
uploadTask.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception exception) {
}
}).addOnSuccessListener(new OnSuccessListener() {
@Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
}
});
}
}
上载图像后,您应该可以在Firebase Storage中看到它们。
定制
既然您已具备构建门铃基本功能所需的功能,那么现在就该将其真正变成您的项目了。 早些时候,我提到我通过使用具有可移动下巴和文本转语音功能的骨骼进行了一些自定义。 可以通过从Google导入伺服器库并在MainActivity
包含以下代码来设置和运行伺服器来实现MainActivity
。
private final int MAX_MOUTH_MOVEMENT = 6;
int mouthCounter = MAX_MOUTH_MOVEMENT;
private Runnable mMoveServoRunnable = new Runnable() {
private static final long DELAY_MS = 1000L; // 5 seconds
private double mAngle = Float.NEGATIVE_INFINITY;
@Override
public void run() {
if (mServo == null || mouthCounter <= 0) {
return;
}
try {
if (mAngle <= mServo.getMinimumAngle()) {
mAngle = mServo.getMaximumAngle();
} else {
mAngle = mServo.getMinimumAngle();
}
mServo.setAngle(mAngle);
mouthCounter--;
mServoHandler.postDelayed(this, DELAY_MS);
} catch (IOException e) {
}
}
};
private void initServo() {
try {
mServo = new Servo(BoardDefaults.getServoPwmPin());
mServo.setAngleRange(0f, 180f);
mServo.setEnabled(true);
} catch (IOException e) {
Log.e("Camera App", e.getMessage());
return; // don't init handler. Stuff broke.
}
}
private void moveMouth() {
if (mServoHandler != null) {
mServoHandler.removeCallbacks(mMoveServoRunnable);
}
mouthCounter = MAX_MOUTH_MOVEMENT;
mServoHandler = new Handler();
mServoHandler.post(mMoveServoRunnable);
}
完成应用程序后,还需要取消引用伺服电动机。
if (mServoHandler != null) {
mServoHandler.removeCallbacks(mMoveServoRunnable);
}
if (mServo != null) {
try {
mServo.close();
} catch (IOException e) {
} finally {
mServo = null;
}
}
令人惊讶的是,文本到语音的转换更加简单。 您只需要初始化文本语音转换引擎,如下所示:
private void initTextToSpeech() {
textToSpeech = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
textToSpeech.setLanguage(Locale.UK);
textToSpeech.setOnUtteranceProgressListener(utteranceListener);
textToSpeech.setPitch(0.3f);
} else {
textToSpeech = null;
}
}
});
}
您可以播放设置以使声音适合您的应用程序。 在上面的示例中,我将声音设置为较低的音调,有点机械手的音调和英语口音。 当您准备好让设备说些什么时,可以在文本到语音引擎上调用speak()
。
textToSpeech.speak("Thanks for stopping by!", TextToSpeech.QUEUE_ADD, null, "skeletontts");
在Raspberry Pi上,如果使用的是伺服电机,则需要确保扬声器通过USB端口连接,因为在设备也创建PWM信号时无法使用模拟辅助连接。
我强烈建议您查看Google的示例驱动程序,以了解可以在项目中添加哪些其他硬件,并使您的项目更具创造力。 Android Things还支持可用于Android的大多数功能,包括对Google Play服务和TensorFlow机器学习的支持 。 为了获得一些启发,下面是完成的项目的视频:
结论
在本文中,我介绍了一些新工具,可用于构建更复杂的IoT应用程序。
这是我们关于Android Things的系列文章的结尾,所以我希望您已经了解了很多有关该平台的知识,并使用它来构建一些出色的项目。 创建应用程序是一回事,但是能够通过您的应用程序影响周围的世界更加令人兴奋。 发挥创造力,创造美好的事物,最重要的是,享受乐趣!
翻译自: https://code.tutsplus.com/tutorials/android-things-putting-together-a-full-project--cms-28268