Android Things 教程:创建一个云连接的智能门卫

Android Things 允许您使用简单的代码制作令人惊叹的物联网设备。 在这篇文章中,我会告诉你如何把这些东西拼凑在一起,来构建一个更复杂的项目!

这不会是一个完整的从上到下的教程。我将给你留出很多空间,来扩展和定制你的设备和应用程序,这样你就可以自己进一步探索和学习。 我的目标是在使用这个新的开发平台的同时获得乐趣,并告诉你,Android Things 不仅仅是闪烁的 LED。

我们将要构建什么?

物联网项目的一半乐趣就是“物”。对于这篇文章,我将建立一个云连接的门铃,当有人接近时会拍照,将图像上传到 Firebase,并触发一个动作。在我们开始之前,我们的项目将需要一些组件:

  • 安装有 Android Things 的 Raspberry Pi 3B
  • Raspberry Pi 相机
  • 运动检测器(组件:HCSR501)

此外,您可以自定义您的项目,以适应自己的创意风格,并有一些乐趣。对于我的项目,我从万圣节以后一直工作在自己的门廊上的骨架装饰,用它作为我的项目的外壳 - 用眼睛钻出来拿着相机和移动探测器。

骨架

我还增加了一个伺服电机来移动用弹性片封闭的下颚,以及一个 USB 扬声器,以支持文本到语音的功能。

门卫电路连接图

你可以通过建立你的电路来开始这个项目。一定要注意你的移动探测器使用的是什么引脚,以及你如何连接任何附加的外围设备。例如,相机模块连接到 Raspberry Pi 的相机插槽。通过一些定制,每个人的最终产品会有所不同,您可以在本文的评论部分分享您自己完成的物联网项目。有关连接电路的信息,请参阅我的关于创建第一个项目的教程

检测运动

这个项目有两个主要的组成部分:相机和运动检测器。我们先看看运动检测器。它将需要一个新的类,来处理从我们的 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();
    }
}

一旦你的动作探测器工作,就可以开始使用树莓派相机拍照。

拍照

快速学习新工具或平台的最好方法之一是,通过创建者提供的示例代码。在这种情况下,我们将使用 Google 创建的类来使用 Camera2 API 拍摄照片。 如果您想了解更多有关 Camera2 API 的信息,可以在 Envato Tuts + 上查看我们完整的视频课程

你可以在这个项目的示例中,找到摄像机类的所有源代码,但是你会感兴趣的主要方法是 takePicture()。 该方法将获取图像并将其返回给应用程序中的回调。

public void takePicture() {
    if (mCameraDevice == null) {
        return;
    }

    try {
        mCameraDevice.createCaptureSession(
                Collections.singletonList(mImageReader.getSurface()),
                mSessionCallback,
                null);
    } catch (CameraAccessException cae) {}
}

一旦这个类被添加到你的项目中,你将需要添加 ImageReader.OnImageAvailableListener 接口到你的 Activity,从 onCreate()初始化摄像机,并监听返回的结果。当您的结果在 onImageAvailable() 中返回时,您需要将它们转换为字节数组,以便上传到 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<uploadtask.tasksnapshot>() {
            @Override
            public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
            }
        });

    }
}

上传图片后,您应该可以在 Firebase Storage 中看到它们。

Firebase Storage 示例

自定义

现在,您已经拥有了为门铃建立基本功能所需的东西,现在是时候真正使您的项目成为现实了。之前我提到过,我通过使用具有移动下巴和文本到语音功能的骨架进行了一些自定义。 可以通过从 Google 导入伺服库,并在 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 &lt;= 0) {
            return;
        }

        try {
            if (mAngle &lt;= 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 机器学习的支持。有一点启发,下面是一个已完成项目的视频:

Android Things: Creating a Cloud-Connected Doorman

结论

在本文中,我介绍了一些可用于构建更复杂的 IoT 应用程序的新工具。

这是我们关于 Android Things 的系列文章的结尾,所以我希望您已经从这个平台上学到了很多东西,并用它来构建一些令人惊叹的项目。创建应用程序是一回事,但能够通过应用程序来影响周围的世界更加令人兴奋。创造力,创造美好的事物,高于一切,玩得开心!

请记住,Envato Tuts+ 充满了 Android 开发的信息,您可以在这里找到很多灵感来为您的下一个应用程序或物联网项目。

英语原文连接:https://code.tutsplus.com/tutorials/android-things-putting-together-a-full-project--cms-28268

尚未评分
您的评分将帮助我们做出更好的玩法

观光\评论区

Copyright © 2017 玩点什么. All Rights Reserved.