Android ThingsずRaspberry Pi3のSmart Lock

2016幎12月、GoogleはAndroid Thingsの最初の開発者プレビュヌバヌゞョンのリリヌスを発衚したした 。 それ以来、プロゞェクトは倧きく倉わりたした。 プレビュヌ版のみが匕き続き利甚可胜ですが、すべおのステップでプラットフォヌムに新機胜が远加され、サポヌトされるデバむスの数が増えおいたす。







珟実の䞖界でIoTデバむスを䜿甚する新しい䟋が毎日あり、プラットフォヌム自䜓がより魅力的になっおいたす。 Live Typingの私たちは、モノのむンタヌネットの興味深い䞖界に飛び蟌んで、私たちの経隓に぀いお話すこずも決めたした。 この蚘事は、Android Thingsに぀いお聞いたこずがありたすが、詊しおみるのが怖い人向けです。 たた、「スマヌトロック」をどのように実装し、自瀟のオフィスで䜿甚するかに぀いおも説明したす。







img







アむデアの説明



問題1圓瀟は、電子バッゞシステムずガラスドアを備えたオフィスを借りおいたす。 倚くの堎合、埓業員は自宅でカヌドを忘れるか、単にカヌドなしで倖に出お、同僚をノックするか電話しお戻っおきたす。 カヌドは、オフィスの内倖で磁気ロックに適甚する必芁がありたす。 内偎でカヌドをロヌプに瞛っただけなら、倖偎からキヌなしでオフィスに入るこずは解決したい問題です。







問題番号2週末には、さたざたな皮類の䌚議がオフィスで開催されたす。 出垭者の倧郚分は同僚ではありたせん。 その数はさたざたですが、どれだけ倚くおも、垞にドアを開けたたたにするように、郚倖者にキヌを枡すこずはできたせん。これは私たちの財産にずっお安党ではありたせん。 そのため、特別な「個人甚ドアマン」を任呜するか、必芁以䞊にドアをサポヌトする必芁がありたす。







パススルヌシステムを切断、削陀、たたはアップグレヌドする暩利はありたせんが、倖郚からの接続には制限は適甚されたせん。 ドアにサヌボドラむブを装備し、顔認識が成功した堎合、接続されたカヌドを読み取りセンサヌに回転させるこずにしたした。 顔写真はカメラによっお提䟛されたす。 したがっお、䞀皮のスマヌトドアロックを取埗したす。







このようなロックを䜿甚するず、ブラックゞャックず幅広い可胜性を持぀独自の識別システムを䜿甚できたす。 たずえば、埓業員の面癜い写真から、瀟内で遊び心のある䜿甚のためにステッカヌを䜜成できたす。 2番目の問題に぀いお話す堎合、城では䌚議の参加者の写真を登録しおスキップできたす。 すごいですね







さらに、アむデアは付随するニュアンスず質問に囲たれおいたした。 写真の撮圱を開始および終了するタむミングは 写真を撮る頻床ず長時間 勀務時間倖や暗闇の䞭でシステムを切断する䟡倀はありたすか システムを芖芚化する方法は しかし、これに぀いおはさらに個別に。 プロゞェクトの基瀎ずしお、公匏のAndroid ThingsペヌゞからDoorbellの䟋を取り䞊げたした。 元の䟋は「ベル」ず呌ばれたすが、システムナヌザヌに最小限の劎力でドアのロックを解陀しおもらい、芋知らぬ人は䞭に入りたせんでした。 したがっお、「スマヌトロック」ず呌ぶ方が正しいず考えたした。







謝蟞



最初は䜕もありたせんでした。 Raspberry自䜓も、アクセサリも、それらの経隓もありたせん。蚘事ずドキュメントから埗られた理論的知識のみです。 Android Thingsで初めおプレむしたのは、 Mobilatoriumのメンバヌがオムスク垂のシベリアのIT銖郜で開催したCodeLabでした 。 Google Cloud Visionの代わりに、 Tensor Flowに FindFace実装を実装するプロゞェクトをすぐに開始したした。 バック゚ンドがどのように機胜するかに興味がある堎合は、優れた蚘事「Looking for Familiar Faces」を読むこずができたす。著者は、顔認識を䜿甚するための原理ずアルゎリズムに぀いお詳しく説明しおいたす。 そうでない堎合は、䞊蚘のCodeLabで行ったように、 Google Cloud Vision + Firebase Realtime Databaseバンドルを䜿甚できたす。







私たちがオフィスに戻ったずき、埓業員のミシャは必芁なすべおのコンポヌネントず、最近賌入したRaspberry Pi3さえも持っおいお、そのようなものに倢䞭になっおいるこずがわかりたした。 Arduinoの研究で[倏孊期] https://vk.com/mobilatorium?w=wall-130802553_81%2Fall を費やした男たちのコンポヌネントもありたした。 提䟛されたハヌドりェアに感謝したす。







付属品



スマヌトロックを実装するには、次のものが必芁でした。









すべおのコンポヌネントは、䞭囜のサむトで簡単に芋぀けるこずができ、安䟡に泚文できたす。たた、怜玢しやすいように、特に名前は英語のたたにしたした。 キットの費甚が玄100〜125ドルになるこずは蚀及する䟡倀がありたす。 最も高䟡なコンポヌネントは、カメラずRaspberry Pi3自䜓です。







実装



理解を深めるために、実装の説明を個別のステップに分けたす。 スキヌムを郚分的に組み合わせるず、任意のステップで画像を埩元する方が䟿利です。 コヌドは十分ではありたせん。Android甚のアプリケヌションを少なくずも1぀曞いおいれば、問題はないず思いたす。 開発には䜿い慣れたAndroid Studioを䜿甚したす。 Dagger、RxJava、Retrofit、OkHttp、Timberなどのお気に入りのラむブラリずフレヌムワヌクを䜿甚するこずもできたす。







始める前に、 Android Thingsの簡単な玹介ずRaspberry Pi3のピンに慣れおおく必芁がありたす。 たた、このピン配列のカラヌ写真は優れた芖芚的ガむドであり、䜕床も圹立ちたす。







Raspberry Piは、異なるハヌドりェアむンタヌフェむスのセットをサポヌトしおいたす。 ただし、䞻にGPIO 汎甚入出力およびPWM パルス幅倉調に関心がありたす。 これらは、プロゞェクトの実装䞭にボヌドずセンサヌの間の盞互䜜甚の䞻な方法になりたす。







さたざたな呚蟺機噚のラむブラリがすでに䜜成されおおり、それらの倚くはすぐに䜿甚できたす。 したがっお、新しいセンサヌの統合を開始するずきは、たずこのリポゞトリずこのリポゞトリに慣れおください。 倚くのドラむバヌがここでコンパむルされおいたす。 おそらく、あなたは正しいものを芋぀けるでしょう。 そうでない堎合、GoogleはAndroid Framework Servicesの機胜を拡匵する特別なナヌザヌドラむバヌの抂念を提䟛しおいたす 。 ハヌドりェアで発生しおいるこずをフレヌムワヌクにリダむレクトし、暙準のAndroid APIツヌルで凊理しお、独自のドラむバヌを䜜成できるようにしたす。 簡単に蚀えば、任意のドラむバヌでの䜜業は、次の手順に分けるこずができたす。









ステップ1Android Thingsをむンストヌルする



Raspberry Pi3に最新のAndroid Thingsむメヌゞをむンストヌルしたす。 さたざたなオペレヌティングシステムのむメヌゞをむンストヌルするための手順ぞのリンクもありたす。







HDMIケヌブルを介しおディスプレむをRaspberryに接続するず、すべおが正垞にむンストヌルされたこずを確認できたす。 すべおが正垞であれば、画面にAndroid Thingsの起動アニメヌションが衚瀺されたす。







img







WiFi. Android Things IP- WiFi .







img







, , Raspberry adb .







$ adb connect Android.local
      
      





ステップ2アプリケヌションを䜜成したす。



Androidのメヌカヌを通じお新しいアプリケヌションを䜜成したす。 これは必須ではありたせんが、あなたは、自分の画面を掲茉、プログラムの暙準的なAndroidのりィゞェットの動䜜を可芖化するこずができたす。 チェックアりト初のAndroid物事のアプリケヌションを䜜成するための完党な指瀺公匏りェブサむト䞊に、我々は唯䞀のハむラむトを芋おいきたす。







最小芁件









远加app/build.gradle



私たちに必芁なAPIぞのアクセス暩を䞎えるであろう、䟝存性のAndroid物事サポヌトラむブラリ、暙準のAndroid SDKの䞀郚ではありたせん。







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



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





各アプリケヌションは、䟋えば掻動、サヌビス、テント、ビュヌ、ボタン、アプリケヌション、のContentProviderなどずいうように暙準クラスでアプリケヌションを構築するための基本的なパッケヌゞをされおいるデフォルトのAndroidラむブラリ、関連付けられおいたす。

しかし、いく぀かのパッケヌゞは、独自のラむブラリです。 アプリケヌションがこれらのパケットのいずれかからのコヌドを䜿甚しおいる堎合、それは圌がこのパッケヌゞず提携したこずを明瀺的に芁求しなければなりたせん。 これは、単䞀の芁玠<甚途・ラむブラリヌ>を介しお行われたす。







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



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





Androidの物事はあなたが同時に䞀぀だけのアプリケヌションをむンストヌルするこずができたすが、私たちのほずんどはそうではありたせん。 この制限のため、宣蚀するこずが可胜である<intent-filter>



掻動、䞡方のためにIOT_LAUCHER



あなたは、起動デバむスですぐにこの掻動を実行するためにデフォルトするこずができたすAndroidManifestアプリケヌションで。 たた、暙準のたた<intent-filter>



アセンブリずdeploya埌に私たちのアプリケヌションを実行するこずができたのAndroid Studioに。







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



のAndroidメヌカヌ- > <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>



" /> <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>



" /> <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>



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



" /> <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>



" /> <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>



" /> <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>





ステップ3ボタン



クロックボタンを接続しおみたしょうスタヌトは、抌されたずき、カメラがワンショットを取りたす。 - 回路が完成され、プランゞャヌが抌されおいるこれは単玔なメカニズムです。 4぀の接点を持぀ボタンは、接続レヌルの2察を含みたす。 プレヌト間の開閉埌の時間の非垞に小さい期間に耇数のスむッチングをトリガするいく぀かのマむクロボタンがありたす。 これは、バりンスず呌ばれおいたす。 詳现情報ボタン 。







raspberry_step1







ボタンは1kの抵抗ずブレッドボヌドを介しお接続されおいたす。 抵抗噚の䞭で迷子にしないようにするために圌らの泚意を払うカラヌコヌディング 。 のは、詳现接続プロセスをペむントしないようにしたしょう。 ただ、ピンラズベリヌ、この少し高いず蚭けられた回路をマッピングしたす。







ボタンは䜿甚統合する準備ができお、ドラむバすでに考慮にバりンスの効果を取り、。 远加の䟝存関係







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



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





ボタンのクラスラッパヌを曞きたす。 おそらく、ラッパヌの実斜を通じお、それは少し過剰に芋えるかもしれたせんが、そのような方法は、我々はボタンのドラむバコヌドでの動䜜をカプセル化し、独自のむンタフェヌスずの察話を䜜成するこずができたす。







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



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



抌されたした{ 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(); } }



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



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





私たちは、私たちの掻動で、このラッパヌを䜿甚しおいたす。 ちょうどそれを回路に接続されおいるラズベリヌPI3䞊のオブゞェクトキヌ名GPIOポヌト「BCM4」、のコンストラクタを䞎えたす。







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



」。 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 ... } }



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





ステップ4カメラ



私たちは、カメラの䜿甚NOIRカメラV2を 。 およそ$ 45あなたは私たちのプロゞェクトのために十分な特性が埗られたす。









カメラは、フラットケヌブルCSIビデオ入力カメラシリアルむンタフェヌスを介しお制埡回路基板に接続されおいたす。 この方法は、USBを接続するための同様のチャンバに比べCPUの負担を枛少させたす。







お䜿いのカメラず、補品がそれを持っおいる必芁があるこずを芁件を䜿甚するようにマニフェストを远加したす。 危険な暩限を含め、必ず蚱可を远加するが、すべおのアクセス暩に同意し、それはあなたがアプリケヌションを再むンストヌルした埌、新たなpermishenaを远加するず、完党にデバむスを再起動する必芁がありたすずいう既知の問題があるアプリケヌションのむンストヌル時に自動的になりたす。







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



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



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





さらにカメラを接続するには、コヌドのこの郚分の基準で玠晎らしい蚘事を曞きたいです。 でそれらを自分で読んでください。このリンク公匏サむトで。 コヌドでは、カメラの蚭定を蚭定したす。 私たちは、すべおの電源を䜿甚しお、480x320の解像床を遞択したせんでした。







ステップ5LED



LED - 電流が流れるこずによっお点灯型ダむオヌド、。 飜和埌、圌自身の抵抗が非垞に小さいです。 あなたは、LEDを通過する電流、たたはちょうど最埌の打撃を制限する必芁な抵抗を接続する堎合。 詳现をLED







私たちは、異なる色の3個のLEDを䜿甚したす。









代わりに3぀のモノクロヌムのシングル䞉色のLEDを䜿甚するこずができたす。 私たちは、それが手元にあったずいう事実を楜しみたした。 図に瀺すように、1オヌムの抵抗を䜿甚しお、我々は、ブレッドボヌドを介しお亀互に私達のLEDを接続したす。







raspberry_step2







LEDで䜿甚するためのラッパヌ実装する堎合はPeripheralManagerService



、あなたのGPIOむンタフェヌスぞのアクセスを提䟛したサヌビスを。 オヌプンな接続ず信号䌝送のためのconfigureそれを。 あなたは抜象クラスの実装を芋れば残念ながら、 com.google.android.things.pio.Gpio



、我々は芋るこずができるコヌル発生させるこずができるほがすべおの方法java.io.IOException



。 簡単にするために、すべおの非衚瀺try-catch



私たちのラッパヌで匏を。







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





個別に各LEDのための私たちの掻動でそれをImplementiruem。







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



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





ステップ6モヌションセンサヌ



たびに、私たちはドアに近づくず退屈、そのドアベルかのように、ボタンを抌しおください。 我々は、䞍必芁な行動から、圓瀟のオフィスのお客様を取り陀くか、男が唯䞀のドアに来るたでも、完党にドアを開けたかったです。 だから我々は、システム党䜓の開始のための䞻なトリガずしお、モヌションセンサヌを䜿甚するこずにしたした。 いずれの堎合においおも、残すボタンであるなどの機胜を耇補したす。 詳现モヌションセンサヌ 。







介しおモヌションセンサヌを接続BCM6



以䞋のスキヌムに埓っおピン。







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



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



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



{ 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

パブリッククラスMainActivityは{アクティビティを拡匵したす







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



」。 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() { ... }
      
      





}







ステップ7サヌボ



サヌボは、私たちのデバむスにおける基本的な機械的な䜜業になりたす。 それは圌が、出力軞180床回転するず、リヌダヌにカヌドをもたらしたした。 さらに詳しい情報に぀いおサヌボ 。







私たちは再び、すでに䜿甚しお既存のドラむバ我々はラッパヌを蚘述れおいるが、。 远加app/build.gradle



䟝存性。







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



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





ドラむブを介しお接続されおいるむンタフェヌスPWM PWM1



䞋蚘スキヌムに埓っお。 䜿甚しPWM



による以前のケヌスずは異なり、代わりに、単にバむナリパルスの、制埡信号を介しお特定の倀を送信する必芁がある、ずいう事実のためにむンタヌフェむスを。 制埡信号 - 䞀定の呚波数ず可倉幅のパルス。 サヌボは、出力軞の回転の特定の角床に倉換、PWM入力信号幅パルスを䜿甚したす。







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



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



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



{ 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

パブリッククラスMainActivityは{アクティビティを拡匵したす







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





}







ステップ8フォトレゞスタ



倜間の䜜業であるため受信画像の無意味な城である顔を怜出するこずがより困難です。 この手段は、システムが䞀時的に無効にするこずができたす。 光䟝存性抵抗LDR - フォトレゞスタを甚いた光センサずしお、この目的のために。 詳现に぀いおはphotoresisto 。







フォトレゞスタ、適切なキヌドラむバで動䜜するように、前述したように。 自然ずメカニックが本圓に䞀臎働くので、これは、論理的です。 app/build.gradle



、既にラむブラリに接続する必芁がありたす。







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



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





ブレッドボヌド同様のスキヌムボタン接続ぞの接続。 唯䞀の違いは、10オヌムの抵抗を䜿甚するこずです。 我々は、ポヌト䜿甚BCM25



。







doorbell_full_scheme_version







類䌌性にもかかわらず、我々は圌のために別々のラッパヌを曞くこずができたす。







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



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



isLighted{ 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); } }



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



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



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





実装のデモンストレヌション



テレビ番組「狂気の手」のすべおの問題を芋盎し、私たちは巧みに叀い携垯電話の䞋から箱に私たちの「怪物」を詰め。 その結果、あなたは、この蚘事の冒頭で芋るこずができたす。







動画デモ









問題



質の悪い怜出噚ずセンサヌ。 トリガヌかどうかは、必芁なずきに誘発し、それが必芁ないずき。 サヌボはスタンバむモヌドにクラックル。







あなたはモヌションセンサヌの原則に粟通しおいる堎合、あなたはそれが機胜しないこずガラスを通しお芋るこずができたした。 だから私はそれをプリントアりトしなければなりたせんでした。







トリプルの内偎から私たちのデザむンを配眮するこずにより、すべおの写真はひどく損なわれたグレヌゞング。 それは光の䞀郚を吞収するように迅速に問題が癜いボックスの衚面からの光の反射であり、窓ガラスを突砎繰り返すこずを実珟し、私たちは、チャンバの呚りに玙の黒シヌトを貌り付けたす。







印刷された写真からの保護なし。







結果



以䞋のためのサンプルコヌドGithubに 。







面癜い、゚キサむティングな、トレンディ、若者。 それは興味深いプロゞェクトであり、その開発のためのアむデアはただそこにありたす。 䟋えば、内偎からドアずは、カヌドを開きたす。 内偎から圌女に来お誰にも扉を開くために - あなたはこの問題を解決するこずができたす。 コメントの䞭で、圓瀟のスマヌトロックのあなたのオプションが完了したこずを瀺唆しおください。 圌らは私たちのようなものであれば、私たちはそれらを実珟したす。







あなたが知っおいる開発のIoTを収益化するためのオプションは䜕ですか コメントであなたの経隓を共有しおいたす。







広告ずしお著者が思い぀いたず自家補ニュヌスリヌダヌ点字実斜しおきた玠晎らしい蚘事、共有したくないBrailleBoxを匱いAndroidのものに芋るために。 ルックスずそれが音ずしおクヌルずしお実装されおいたす。 優秀なプロゞェクトを錓舞。







䟿利なリンク






All Articles