📜 ⬆️ ⬇️

Smart Lock on Android Things and Raspberry Pi3

In December 2016, Google announced the release of the first Developer Preview version of Android Things. Since then, the project has changed a lot. Only a preview version is still available, but with each step the platform has new features and the number of supported devices is growing.


Every day there are new examples of using IoT devices in the real world, and the platform itself is becoming more and more attractive. We at Live Typing also decided to dive into the most interesting world of the Internet of Things and tell about our experience. This article is for those who have heard about Android Things, but were afraid to try. And also about how we implemented our “smart lock” and use it in our own office.


img


Description of the idea


Problem # 1: Our company rents an office with an electronic pass system and glass doors. Often, employees forget their cards at home or simply go outside without them, and then knock or call colleagues to get back. The card must be applied to the magnetic lock and inside and outside the office. If inside we just tied a card on a string, then getting into the office without a key outside is a problem that we would like to solve.


Problem number 2: On weekends, various kinds of meetings are held in our office. The main part of those present are not our colleagues. Their number varies, but no matter how many of them we cannot give a key to a stranger, as well as keeping the door open all the time is not safe for our property. Therefore, it is now necessary to appoint a special "man-porter" or prop up the door than you have to.


We do not have the right to disable, remove or upgrade the gateway system, but restrictions are not applied to connecting something outside. We decided to equip the door with a servo drive, which will rotate the attached card to the reading sensor with successful face recognition. A face photo is provided by the camera. Thus we get a sort of smart door lock.


With such a lock, we have our own identification system with blackjack and ample opportunities. (For example, funny photos of employees, from which you can make stickers for internal humorous use). If we talk about the second problem, the lock allows you to register and pass through the photographs of the participants in the meeting. Awesome, is not it?


Further the idea was overgrown with accompanying nuances and questions. When to start and finish taking pictures? How often and how long to take photos? Should I turn off the system during off-hours and at night? How to visualize the system? But about all this further and separately. As a basis for the project, we took the example of Doorbell from the official page of Android Things. The original example is called the “bell”, however, we wanted the system user to unlock the door with minimal effort, and strangers did not get inside. Therefore, we considered it more correct to call it “smart lock”.


Thanks


At first we had nothing. Neither the Raspberry itself, nor the components, nor the experience of working with them - only theoretical knowledge obtained from articles and documentation. For the first time, it was possible to try to play around with Android Things on a CodeLab held in our IT capital of Siberia, Omsk, by the guys from the Mobilatorium . We quickly started a project where, instead of Google Cloud Vision, we implemented our implementation of FindFace on Tensor Flow . If you are interested in how the back-end works, then you can read the excellent article “We are looking for familiar faces” , where the author described in detail the principles and algorithms for working with face recognition. If not, you can use a bunch of Google Cloud Vision + Firebase Realtime Database , as is done in the above CodeLab.


When we returned to the office, it turned out that our employee Misha has all the necessary components and even the Raspberry Pi3 itself, which he recently acquired, wanting to indulge in something like that. Also there are components from the guys who conducted the [summer school] ( https://vk.com/mobilatorium?w=wall-130802553_81%2Fall ) with the study of Arduino. Many thanks to them for providing the pieces of iron.


Accessories


To implement a smart lock, we needed:



All components are easy to find and cheap to order on Chinese sites, and we specifically left their names in English for easy retrieval. It is worth mentioning that the kit will cost you about $ 100-125. The most expensive components are the camera and the Raspberry Pi3 itself.


Implementation


For better understanding, we will break the implementation description into separate steps. Connecting the scheme in parts, it is more convenient to restore the picture at any step. The code is small, and if you wrote at least one application for Android, then you will not have problems, in my opinion. For the development we will use the familiar Android Studio. You can even use your favorite libraries and frameworks such as Dagger, RxJava, Retrofit, OkHttp, Timber, etc.


Before starting work, you should familiarize yourself with a brief introduction to Android Things , as well as pins on the Raspberry Pi3 . And this color picture with pinout is a great visual guide and will come in handy for you more than once.


Raspberry Pi supports a different set of hardware interfaces. But we are mainly interested in GPIO (General-purpose input / output) and PWM (Pulse Width Modulation). They will be the main ways of interaction between the board and sensors in the implementation of our project.


Libraries for various peripheral devices have already been written to us, and many of them are available even immediately from the box. Therefore, when you begin to integrate a new sensor, first familiarize yourself with this and this repositories. There are a lot of drivers here. Most likely, you will find the right one. If not, Google provided a special concept of User Drivers , which extends the capabilities of the Android Framework Services . It redirects what is happening in the gland to the framework and allows you to process them with standard Android API tools and thus create your own driver. Briefly work with any driver can be divided into the following steps:



Step 1: Install Android Things


Install the latest Android Things image on the Raspberry Pi3. It also provides links to installation instructions for various operating systems.


You can make sure that everything is successfully installed by connecting any display to the Raspberry via an HDMI cable. If everything is okay, then you will see on the screen an Android Things download animation.


img


For more comfortable interaction with the device, the documentation suggests setting up a WiFi connection . After that, at the bottom of the screen under the Android Things screen, the IP address of the device will appear in your WiFi network.


img


If there is only one such device in your network, then you can not remember the address and do not check it every time you change, but use the reserved Raspberry host name and connect via the adb command.


$ adb connect Android.local 

Step 2: Create the application


Create a new application through Android Studio. You can visualize the work of your program with standard Android widgets by placing them on the screen, although this is not necessary. Check out the full instructions for creating the first Android Things app on the official website, and we will analyze only the main points.


Minimum requirements:



Add the Android Things support library to the app/build.gradle , which will give us access to the right API, which is not part of the standard Android SDK.


 dependencies { ... provided 'com.google.android.things:androidthings:0.4-devpreview' ... } 

Each application is associated with the default Android library, which contains basic packages for building applications (with standard classes such as Activity, Service, Intent, View, Button, Application, ContentProvider, and so on).
However, some packages are in their own libraries. If your application uses code from one of these packages, it should explicitly require that it be associated with this package. This is done through a separate <uses-library> element.


 <application ...> ... <uses-library android:name="com.google.android.things"/> ... </application> 

Android Things allows you to simultaneously install only one application, and the more we do not. Thanks to this restriction, it is possible to declare <intent-filter> for an Activity, like IOT_LAUCHER in an AndroidManifest application, which allows you to start this Activity by default immediately when the device starts. Also leave the standard <intent-filter> so that Android Studio can launch our application after the build and deployment.


 <activity ...> ... <!-- Launch activity as default from Android Studio --> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> <!-- Launch activity automatically on boot --> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.IOT_LAUNCHER"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> ... </activity> 

Step 3: Button


Let's start by connecting the clock button, when clicked, the camera will take one shot. This is a simple mechanism: the pusher is pressed - the circuit is closed. The button with four contacts consists of two pairs of connecting rails. When closing and opening between the plates of the button, micro-sparks appear, causing multiple switchings in an extremely short period of time. This phenomenon is called chatter. More about the button .


raspberry_step # 1


The button is connected through a prototype board using a 1 kΩ resistor. In order not to get confused in the resistors, pay attention to their color coding . We will not describe in detail the connection process. Just match the presented circuit with the Raspberry pinout given just above.


To integrate the buttons, we use a ready-made driver , which already takes into account the bounce effect. Add a dependency


 dependencies { ... compile 'com.google.android.things.contrib:driver-button:0.3' ... } 

Let's write a wrapper class for working with a button. Perhaps the implementation through the wrapper will seem a bit unnecessary, but in this way we will be able to encapsulate the work with the button driver code and create our own interaction interface.


ButtonWrapper.java
 import com.google.android.things.contrib.driver.button.Button; public class ButtonWrapper { private @Nullable Button mButton; private @Nullable OnButtonClickListener mOnButtonClickListener; public ButtonWrapper(final String gpioPin) { try { mButton = new Button(gpioPin, Button.LogicState.PRESSED_WHEN_HIGH); mButton.setOnButtonEventListener(new Button.OnButtonEventListener() { @Override public void onButtonEvent(Button button, boolean pressed) { if (pressed && mOnButtonClickListener != null) { mOnButtonClickListener.onClick(); } } }); } catch (IOException e) { e.printStackTrace(); } } public void setOnButtonClickListener(@Nullable final OnButtonClickListener listener) { mOnButtonClickListener = listener; } public void onDestroy() { if (mButton == null) { return; } try { mButton.close(); } catch (IOException e) { e.printStackTrace(); } finally { mButton = null; } } public interface OnButtonClickListener { public void onClick(); } } 

We will use this wrapper in our Activity. Simply transfer the name of the GPIO port ("BCM4") on the Raspberry Pi3, to which it is connected in the diagram, to the button object constructor


MainActivity.java
 public class MainActivity extends Activity { private static final String GPIO_PIN_BUTTON = "BCM4"; private ButtonWrapper mButtonWrapper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... mButtonWrapper = new ButtonWrapper(GPIO_PIN_BUTTON); mButtonWrapper.setOnButtonClickListener(new ButtonWrapper.OnButtonClickListener() { @Override public void onClick() { Timber.d("BUTTON WAS CLICKED"); startTakingImage(); } }); ... } @Override protected void onDestroy() { super.onDestroy(); ... mButtonWrapper.onDestroy(); ... } private void startTakingImage() { // TODO take photo ... } } 

Step 4: Camera


We used the NoIR Camera V2 . For about $ 45 you will receive the characteristics sufficient for our project:



The camera connects to the control board with a loop through the CSI video input (Camera Serial Interface). This method reduces the load on the CPU in comparison with the connection of similar cameras via USB.


Add to the manifest permission to use the camera and the requirements that the device must have it. Adding permissions is necessary, but consent for all Permissions, including Dangerous Permissions, is obtained automatically when you install the application (there is a known problem that when you add a new perm after reinstalling the application, you need to completely reboot the device).


  <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> 

To further connect the camera, you need to write pieces of code large by the standards of this article. Please read them yourself at this link on the official website. The code sets the camera settings. We did not use all its power and chose a resolution of 480x320.


Step 5: LED


An LED is a type of diode that glows when current flows through it. His own resistance after saturation is very small. When you connect, you will need a resistor that will limit the current passing through the LED, otherwise the latter will simply burn out. More about LEDs


We will use three LEDs of different colors:



You can use one tricolor LED instead of three monochrome. We enjoyed what was at hand. Using 1 kOhm resistors, we alternately connect our LEDs through a prototype board in accordance with the diagram.


raspberry_step # 2


When implementing a wrapper for working with LED, we will use PeripheralManagerService , a service that gives access to the GPIO interface. Open the connection and configure it to transmit the signal. Unfortunately, if you look at the implementation of the abstract com.google.android.things.pio.Gpio class, you can see that calling almost every method can generate a java.io.IOException . For simplicity, hide all try-catch expressions in our wrapper.


LedWrapper.java
 public class LedWrapper { private @Nullable Gpio mGpio; public LedWrapper(String gpioPin) { try { PeripheralManagerService service = new PeripheralManagerService(); mGpio = service.openGpio(gpioPin); mGpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW); } catch (IOException e) { e.printStackTrace(); } } public void turnOn() { if (mGpio == null) { return; } try { mGpio.setValue(true); } catch (IOException e) { e.printStackTrace(); } } public void turnOff() { if (mGpio == null) { return; } try { mGpio.setValue(false); } catch (IOException e) { e.printStackTrace(); } } public void onDestroy() { try { mGpio.close(); } catch (IOException e) { e.printStackTrace(); } finally { mGpio = null; } } } 

We implement it into our Activity for each LED separately.


MainActivity.java
 public class MainActivity extends Activity { private final static String GPIO_PIN_LED_GREEN = “BCM21”; private LedWrapper mLedWrapper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... mLedWrapper = new LedWrapper(GPIO_PIN_LED_GREEN); mLedWrapper.turnOff(); ... } private void turnOn() { mLedWrapper.turnOn(); } @Override protected void onDestroy() { super.onDestroy(); ... mLedWrapper.onDestroy(); ... } } 

Step 6: Motion Sensor


Each time, approaching the door and pressing the button, as if it were a doorbell, is boring. We wanted to save the guest of our office from unnecessary actions or even completely open the door while the person is just coming to the door. Therefore, we decided to use the motion sensor as the main trigger to start the work of the entire system. Just in case, leave the button with duplicate functionality as is. More about the motion sensor .


We connect the motion sensor via BCM6 pin according to the scheme below.


img_motion


MotionWrapper.java
 public class MotionWrapper { private @Nullable Gpio mGpio; private @Nullable MotionEventListener mMotionEventListener; public MotionWrapper(String gpioPin) { try { mGpio = new PeripheralManagerService().openGpio(gpioPin); } catch (IOException e) { e.printStackTrace(); } } public void setMotionEventListener(@Nullable final MotionEventListener listener) { mMotionEventListener = listener; } public void startup() { try { mGpio.setDirection(Gpio.DIRECTION_IN); mGpio.setActiveType(Gpio.ACTIVE_HIGH); mGpio.setEdgeTriggerType(Gpio.EDGE_RISING); mGpio.registerGpioCallback(mCallback); } catch (IOException e) { e.printStackTrace(); } } public void shutdown() { if (mGpio == null) { return; } try { mGpio.unregisterGpioCallback(mCallback); mGpio.close(); } catch (IOException e) { e.printStackTrace(); } } public void onDestroy() { try { mGpio.close(); } catch (IOException e) { e.printStackTrace(); } finally { mGpio = null; } } private final GpioCallback mCallback = new GpioCallback() { @Override public boolean onGpioEdge(Gpio gpio) { if (mMotionEventListener != null) { mMotionEventListener.onMovement(); } return true; } }; public interface MotionEventListener { void onMovement(); } } 

MainActivity.java

public class MainActivity extends Activity {


 private static final String GPIO_PIN_MOTION_SENSOR = "BCM6"; private MotionWrapper mMotionWrapper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... mMotionWrapper = new MotionWrapper(GPIO_PIN_MOTION_SENSOR); mMotionWrapper.setMotionEventListener(new MotionWrapper.MotionEventListener() { @Override public void onMovement() { startTakingPhotos(); } }); mMotionWrapper.startup(); ... 

}


 @Override protected void onDestroy() { super.onDestroy(); ... mMotionWrapper.shutdown(); mMotionWrapper.onDestroy(); ... } private void startTakingPhotos() { ... } 

}


Step 7: Servo Drive


The servo performs the basic mechanical work in our device. It is he who turns the output shaft 180 degrees brings the card to the reader. More about servos .


We again use the already existing driver , over which we will write our wrapper. Add dependency to app/build.gradle .


 dependencies { ... compile 'com.google.android.things.contrib:driver-pwmservo:0.2' ... } 

We connect the drive through the PWM1 pulse width modulation interface in accordance with the diagram below. The use of the PWM interface is due to the fact that, unlike in the previous cases, a specific value is required to be transmitted via a control signal, and not just a binary pulse. The control signal is pulses of constant frequency and variable width. The servo drive uses a pulse-width incoming PWM signal, converting it into a specific angle of rotation of the output shaft.


img_servo


ServoWrapper.java
 public class ServoWrapper { private static final float ANGLE_CLOSE = 0f; private static final float ANGLE_OPEN = 180f; private Servo mServo; private Handler mHandler = new Handler(); public ServoWrapper(final String gpioPin) { try { mServo = new Servo(gpioPin); mServo.setAngleRange(ANGLE_CLOSE, ANGLE_OPEN); mServo.setEnabled(true); } catch (IOException e) { e.printStackTrace(); } } public void open(final long delayMillis) { try { mServo.setAngle(ANGLE_OPEN); } catch (IOException e) { e.printStackTrace(); } mHandler.removeCallbacks(mMoveServoRunnable); if (delayMillis > 0) { mHandler.postDelayed(mMoveServoRunnable, delayMillis); } } public void close() { if (mServo == null) { return; } try { mServo.setAngle(ANGLE_CLOSE); } catch (IOException e) { e.printStackTrace(); } } public void onDestroy() { mHandler.removeCallbacks(mMoveServoRunnable); mMoveServoRunnable = null; if (mServo != null) { try { mServo.close(); } catch (IOException e) { e.printStackTrace(); } finally { mServo = null; } } } private Runnable mMoveServoRunnable = new Runnable() { @Override public void run() { mHandler.removeCallbacks(this); close(); } }; } 

MainActivity.java

public class MainActivity extends Activity {


 private static final String GPIO_PIN_SERVO = "PWM1"; private ServoWrapper mServoWrapper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... mServoWrapper = new ServoWrapper(GPIO_PIN_SERVO); ... 

}


 private void openDoor() { ... mServoWrapper.open(DELAY_SERVO_MS); ... } @Override protected void onDestroy() { super.onDestroy(); ... mServoWrapper.onDestroy(); ... } 

}


Step 8: Photo Resistor


At night, the work of the castle is meaningless, because the photographs obtained make it more difficult to recognize faces. This means that the system can be temporarily disabled. To do this, as a light sensor we use a photoresistor - Light Dependent Resistors (LDR). More about the photoresistor .


The button driver described earlier is suitable for working with a photoresistor. This is logical, because the essence and mechanics of work really coincide. The library must already be connected to app/build.gradle .


 dependencies { ... compile 'com.google.android.things.contrib:driver-button:0.3' ... } 

The connection scheme to the breadboard board is similar to the button connection scheme. The only difference is the use of a 10 kΩ resistor. Use the BCM25 port.


doorbell_full_scheme_version


Despite all the similarity, we will write a separate wrapper for it.


BrightrWrapper.java
 public class BrightrWrapper { private @Nullable Button mLightDetector; private @Nullable OnLightStateChangeListener mOnLightStateChangeListener; public BrightrWrapper(final String gpioPin) { try { mLightDetector = new Button(gpioPin, Button.LogicState.PRESSED_WHEN_HIGH); mLightDetector.setOnButtonEventListener(new Button.OnButtonEventListener() { @Override public void onButtonEvent(Button button, boolean isLighted) { if (mOnLightStateChangeListener != null) { mOnLightStateChangeListener.onLightStateChange(isLighted); } } }); } catch (IOException e) { e.printStackTrace(); } } public void setOnLightStateListener(@Nullable final OnLightStateChangeListener listener) { mOnLightStateChangeListener = listener; } public void onDestroy() { if (mLightDetector == null) { return; } try { mLightDetector.close(); } catch (IOException e) { e.printStackTrace(); } } public interface OnLightStateChangeListener { public void onLightStateChange(boolean isLighted); } } 

MainActivity.java
 public class MainActivity extends Activity { private static final String GPIO_PIN_LIGHT_DETECTOR = "BCM25"; private BrightrWrapper mBrightrWrapper; private boolean mIsTakePhotoAllowed = true; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... mBrightrWrapper = new BrightrWrapper(GPIO_PIN_LIGHT_DETECTOR); mBrightrWrapper.setOnLightStateListener(new BrightrWrapper.OnLightStateChangeListener() { @Override public void onLightStateChange(final boolean isLighted) { mIsTakePhotoAllowed = isLighted; handleLightState(); } }); ... } private void handleLightState() { if (mIsTakePhotoAllowed) { ... } else { ... } } @Override protected void onDestroy() { super.onDestroy(); ... mBrightrWrapper.onDestroy(); ... } } 

Implementation demonstration


After reviewing all the episodes of the TV show “Crazy Pens,” we skillfully packed our “monster” into a box from under the old phone. You could see the result at the beginning of the article.


Video demonstration



Problems


Poor quality sensors and sensors. Work or do not work, when it is necessary and when not necessary. Servo crackles in standby mode.


If you are familiar with the principle of the motion sensor, you could understand that it does not work through glass. So I had to take him outside.


Having placed our design from the inside on a triple glazed window, all the photos turned out to be strongly exposed. Quickly realizing that the problem lies in the reflection of light from the white surface of the box and its multiple refraction through the glass unit, we simply stuck a black sheet of paper around the camera so that it absorbs some of the light.


No protection against printed photos.


Results


Sample code on Github .


Interesting, exciting, fashionable, youth. It was an interesting project, and there are still ideas for its development. For example, inside the door also opens on the card. You can solve this problem - open the door to everyone who comes to it from the inside. Please suggest in the comments your options for finalizing our smart lock. If we like them, we will definitely implement them.


What options of monetization of development under IoT do you know? Share your experiences in the comments.


Not as an advertisement I want to share a great article where the author came up with and implemented a self-made news reader in Braille BrailleBox for those who can hardly see on Android Things. Looks and implemented as cool as it sounds. Excellent, inspiring project.


useful links



')

Source: https://habr.com/ru/post/331888/


All Articles