Android Things UserSensor.Builder - 无法创建距离传感器驱动程序

Android Things UserSensor.Builder - unable to create distance sensor driver

我正在尝试为标准 HC-SR04 超声波传感器创建 Android Things 驱动程序。我相信我的事件顺序是正确的:see footer,但无法将其注册为 UserSensor。

userSensor = UserSensor.Builder()
    .setName("HC-SR04 Ultrasonic Distance Sensor")
    .setVersion(1)
    // If boolean "on face or not," should I use something linear like TYPE_LIGHT
    .setType(Sensor.TYPE_PROXIMITY) 
    .setDriver(this) // UserSensorDriver  
    .build()

至此,用UserDriverManager注册UserSensor(完成)和用SensorManager注册有什么区别?有什么东西阻止它出现在传感器列表中吗?我需要等到传感器像 sensorManager.registerDynamicSensorCallback 一样 "ready" 吗?

val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
sensorManager.registerListener(this, // SensorEventListener.onSensorChanged
    sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY),
    SensorManager.SENSOR_DELAY_NORMAL)

无论我尝试什么,我都会得到 "E/SensorManager: sensor or listener is null"(这在 Kotlin 中更令人惊讶,因为 null 不应该偷偷溜进来)


我的传感器/ also a gist

/** Callback for when the distance changes "enough to care" */
interface SignificantDistanceChangeListener {
    fun onDistanceChanged(distanceCm: Float)
}

/**
 * User Sensor - Ultrasonic range finder
 */
class HCSR04(context: Context, val sdcl: SignificantDistanceChangeListener) : UserSensorDriver(), SensorEventListener, AutoCloseable {
    private val LOG = Logger.getLogger(this.javaClass.name)
    private val gpio = PeripheralManagerService().openGpio("BCM23")
    private val distanceReading: BlockingQueue<Float> = ArrayBlockingQueue(1)
    // Choreography of each ping
    private val scheduler: ScheduledExecutorService = Executors.newScheduledThreadPool(1)
    private val userSensor: UserSensor

    init {
        userSensor = UserSensor.Builder()
                .setName("HC-SR04 Ultrasonic Distance Sensor")
                .setVersion(1)
                .setType(Sensor.TYPE_PROXIMITY) // Could this be something more linear like TYPE_LIGHT
                .setDriver(this)
                .build()
        UserDriverManager.getManager().registerSensor(userSensor)

        val sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
        LOG.info("ALL Sensors: ${sensorManager.getSensorList(Sensor.TYPE_ALL)}")

        sensorManager.registerDynamicSensorCallback(object : SensorManager.DynamicSensorCallback() {
            override fun onDynamicSensorConnected(sensor: Sensor) {
                LOG.info("onDynamicSensorConnected")
                if (sensor.type == Sensor.TYPE_PROXIMITY) {
                    sensorManager.registerListener(
                            this@HCSR04,
                            sensor,
                            SensorManager.SENSOR_DELAY_NORMAL
                    )
                }
            }
        })

    }

    val gpioEdgeCallback = object : GpioCallback() {
        // Track the reply rise/fall
        private val startMs = AtomicLong()
        private val startValid = AtomicBoolean(false)

        private fun calculate() {
            val elapsed = (System.nanoTime() / 1000) - startMs.get()
            if (startValid.get() && elapsed > 0) {
                distanceReading.put(elapsed * 34000 / 2f)
            } else {
                LOG.warning("Discarding edge callback ${startMs.get()} ${startValid.get()} $elapsed")
            }
            startValid.set(false)
        }

        override fun onGpioEdge(gpio: Gpio?): Boolean {
            if (gpio != null) {
                if (gpio.value) {
                    startMs.set(System.nanoTime() / 1000)
                    startValid.set(true)
                } else {
                    calculate()
                }
                LOG.finer("GPIO input edge: ${System.nanoTime() / 1000} ${gpio.value}")
            }
            return true
        }

        override fun onGpioError(gpio: Gpio?, error: Int) = LOG.severe("$gpio Error event $error")
    }

    /** Launch a new thread to get the distance, then block until we have a result */
    override fun read(): UserSensorReading {
        distanceReading.clear()

        gpio.setDirection(Gpio.DIRECTION_OUT_INITIALLY_LOW)
        gpio.setActiveType(Gpio.ACTIVE_HIGH)
        gpio.value = false

        scheduler.schedule({ gpio.value = true }, 1, TimeUnit.MICROSECONDS)
        scheduler.schedule({ gpio.value = false }, 11, TimeUnit.MICROSECONDS)
        scheduler.schedule({
            gpio.setDirection(Gpio.DIRECTION_IN)
            gpio.setActiveType(Gpio.ACTIVE_HIGH) // redundant?
            gpio.setEdgeTriggerType(Gpio.EDGE_BOTH)
            gpio.registerGpioCallback(gpioEdgeCallback)
        }, 12, TimeUnit.MICROSECONDS)

        val distanceCm = distanceReading.take()
        gpio.unregisterGpioCallback(gpioEdgeCallback)
        LOG.info("New distance reading: $distanceCm")
        return UserSensorReading(floatArrayOf(distanceCm))
    }

    /** from @SensorEventListener */
    override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) = LOG.info("$sensor accuracy change: $accuracy")

    /**
     * from @SensorEventListener
     */
    override fun onSensorChanged(event: SensorEvent) = sdcl.onDistanceChanged(event.values[0])

    /** from @AutoCloseable */
    override fun close() {
        LOG.warning("Closing Sensor HCSR04")
        UserDriverManager.getManager().unregisterSensor(userSensor)
        gpio.close()
        scheduler.shutdownNow()
    }
}

您可能会考虑的一件事是更改传感器类型。 TYPE_PROXIMITY 是on-change 传感器,当前预览支持。然而,它也是一个唤醒传感器,可能尚未得到完全支持。您可以尝试修改传感器定义以改用自定义类型:

userSensor = UserSensor.Builder()
        .setName("HC-SR04 Ultrasonic Distance Sensor")
        .setVersion(1)
        .setCustomType(Sensor.TYPE_DEVICE_PRIVATE_BASE,
                "com.example.ultrasonic",
                Sensor.REPORTING_MODE_CONTINUOUS)
        .setDriver(this)
        .build()

at this point, what is the difference between registering the UserSensor with the UserDriverManager (done), and registering it with the SensorManager?

您不能直接用 SensorManager 注册 UserSensor。 Android SensorManager API 的存在是为了使客户端应用程序能够从设备内置的传感器读取数据。 UserDriverManager API 的存在是为了让 Android Things 开发人员能够向系统添加新的传感器,您可能希望使用相同 SensorManager API.

换句话说,您构建一个 UserSensor 以通过 UserDriverManager 将自定义传感器数据注入框架。您使用 SensorManager 提取提供给框架的数据并在客户端应用程序中使用它。

Is there anything that is preventing it from showing up in the list of Sensors?

您应该能够在传感器回调触发后使用 SensorManager.getDynamicSensorList()(与 getSensorList() 方法不同)对此进行测试。

Do I need to wait until the sensor is "ready" like with sensorManager.registerDynamicSensorCallback?

动态回调告诉您新驱动程序何时成功注册到框架。在调用 onDynamicSensorConnected() 之前,您将无法附加侦听器或查询传感器本身。