0

我正在尝试为标准 HC-SR04 超声波传感器创建一个 Android Things 驱动程序。我相信我的事件顺序是正确的:请参阅页脚,但无法将其注册为 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()

此时,将 UserSensor 注册到 UserDriverManager(完成)和将其注册到 SensorManager 有什么区别?是否有任何东西阻止它出现在传感器列表中?我是否需要像 sensorManager.registerDynamicSensorCallback 一样等到传感器“准备好”?

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 不应该潜入太多)


我的传感器/也是一个要点

/** 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()
    }
}
4

1 回答 1

0

您可能会考虑的一件事是更改传感器类型。TYPE_PROXIMITY是一个变化传感器,在当前预览中受支持。但是,它也是一个唤醒传感器,可能尚未完全支持。您可以尝试修改传感器定义以使用自定义类型:

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

此时,将 UserSensor 注册到 UserDriverManager(完成)和将其注册到 SensorManager 有什么区别?

您不能UserSensor直接使用SensorManager. Android SensorManagerAPI 的存在是为了使客户端应用程序能够从设备内置的传感器读取数据。该UserDriverManagerAPI 的存在是为了让Android Things开发人员能够将新传感器添加到系统中,您可能希望使用相同的SensorManagerAPI 在代码的其他地方读取这些传感器。

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

是否有任何东西阻止它出现在传感器列表中?

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

我是否需要像 sensorManager.registerDynamicSensorCallback 一样等到传感器“准备好”?

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

于 2017-05-25T04:12:51.303 回答