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. На зауваження, пропозиції з радістю дам відповіді у коментарях.