Android Things: барометр/термометр з Raspberry Pi 3 та клієнт на Android для нього
Відразу після анонсу Android Things мені закортіло отримати один з девайсів для тестування. Я вирішив зробити кімнатний барометр/термометр з можливістю віддаленого перегляду даних зі смартфону. Спочатку була ідея написати невеликий сервер для зберігання даних, але потім я згадав про існування Firebase Realtime Database. :)
Мною був обраний одноплатний комп’ютер Raspberry Pi 3. Крім нього був придбаний корпус, блок живлення, радіатори, а також барометр MP180.
Так це виглядає у зібраному вигляді:
Після монтажу можна приступати до написання коду. Для того, щоб розробляти під Android Things потрібно додати залежність до build.gradle:
provided 'com.google.android.things:androidthings:0.1-devpreview'
В AndroidManifest.xml в тегу Application потрібно вказати:
<uses-library android:name="com.google.android.things"/>
І також налаштувати intent-filter для вашої основної Activity. Це дозволить запускатись вашій аплікації разом з девайсом:
<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>
Для того, щоб підключити і працювати зі сторонніми девайсами, у Google розробили концепт User-Space Drivers, який дозволяє самостійно написати драйвер.
Для деяких девайсів вже є готові написані драйвери, знайти ви можете на GitHub Android Things.
На жаль, під мій датчик, на момент написання статті, готового драйвера не було, а писати самому було лінь. Але на GitHub я знайшов драйвер сторонньої реалізації, він і був використаний у проекті, за що і хочу подякувати автору.
Для написання був обраний Kotlin. По суті вся моя аплікація для Android Things складається з однієї Activity, в якій раз на 10 хвилин беруться покази з датчика і пишуться у Firebase для того, щоб потім їх зміг забрати мобільний Android клієнт. Для зручної реалізації таймера була використана RXJava.
private fun startSensorPolling() { disposable = getSensorDataAsFlowable() .repeatWhen { it.delay(10, TimeUnit.MINUTES) } .retryWhen { it.delay(10, TimeUnit.MINUTES) } .subscribe({ storeToDB(it) }, { Log.e(TAG, "Can't read data from sensor:", it) }) } private fun storeToDB(data: Bmp180Data?) { val firebase = FirebaseDatabase.getInstance() val reference: DatabaseReference = firebase.getReference(SENSOR_DATA_REFERENCE) reference.push().setValue(data) Log.d(TAG, "${data.toString()} saved into firebase") } private fun getSensorDataAsFlowable(): Flowable<Bmp180Data> { return Flowable.fromCallable { getSensorData() } } private fun getSensorData(): Bmp180Data { val temp = mBmp180.readTemperature() val press = mBmp180.readPressure() val alt = mBmp180.readAltitude() return Bmp180Data(temp.toInt(), press, alt.toInt()) }
Далі напишемо клієнт для телефону. Для того щоб отримувати останній показ датчика, зареєструємо Firebase ChildEventListener і вкажемо ліміт
private val bmp180Reference: DatabaseReference by lazy { fireBase.getReference(CONNECTION_DATA_REFERENCE) }
private fun registerFirebaseListener() { bmp180Reference.orderByChild("date").limitToLast(1).addChildEventListener(this) }
І тільки з’являться нові дані у Firebase ми зможемо отримати їх у колбеці
override fun onChildAdded(dataSnapshot: DataSnapshot?, p1: String?) { showContent() fillUI(dataSnapshot?.getValue(Bmp180Data::class.java)) }
Ось і результат:
Код проекту доступний на GitHub. На зауваження, пропозиції з радістю дам відповіді у коментарях.