devicescript-crear-driver-i2c-raw

Cómo crear un driver I2C en DeviceScript

  • 2 min

Un driver I2C es la capa que traduce el protocolo de un dispositivo a una interfaz que el programa puede utilizar. Para escribirlo necesitamos el datasheet y acceso al bus.

Si un sensor habla I2C (o SPI), podemos hablar con él. Solo necesitamos dos cosas:

  1. Su Datasheet (Hoja de datos).
  2. Las funciones de bajo nivel i2c.write y i2c.read.

Hoy vamos a aprender a crear un “Mini-Driver”. No será una especificación completa de Jacdac (eso es para nota), pero será una clase o función que nos permitirá usar el sensor en nuestro proyecto de forma limpia.

Leer el datasheet

Antes de escribir código, tenemos que saber qué decirle al sensor. Para este ejemplo, vamos a implementar un driver para el BH1750, un sensor de luz muy común.

Si abrimos su datasheet, encontraremos una tabla de comandos como esta:

InstrucciónOpcode (Hex)Descripción
Power Down0x00Apaga el sensor
Power On0x01Enciende el sensor
Continuos H-Res0x10Empieza a medir en alta resolución

Y nos dice que, tras enviar el comando de medición, debemos esperar 120ms y luego leer 2 bytes (parte alta y parte baja) que forman el valor de luxes.

¡Ya tenemos el plan!

  1. Enviar 0x10 (Start Measurement).
  2. Esperar.
  3. Leer 2 bytes.
  4. Unirlos matemáticamente.

Usar I2CSensorDriver

El paquete @devicescript/i2c ofrece acceso directo al bus y operaciones sobre registros. Para un driver completo resulta más cómodo heredar de I2CSensorDriver, que conserva la dirección y proporciona writeBuf y readBuf.

Vamos a implementar la lectura en una clase. El literal hex`10` crea el búfer con el comando de medida sin convertirlo a texto.

import { sleep } from "@devicescript/core"
import { I2CSensorDriver } from "@devicescript/drivers"

export class BH1750Driver extends I2CSensorDriver<{ lux: number }> {
    constructor(address = 0x23) {
        super(address, { readCacheTime: 200 })
    }

    override async initDriver() {
        await this.writeBuf(hex`01`) // Power On
    }

    override async readData() {
        await this.writeBuf(hex`10`) // Continuous H-Resolution Mode
        await sleep(180)

        const data = await this.readBuf(2)
        const raw = (data[0] << 8) | data[1]

        return { lux: raw / 1.2 }
    }
}
Copied!

Ahora inicializamos el driver y leemos el valor. read() ejecuta readData() y devuelve el objeto con la medida.

const miSensor = new BH1750Driver(0x23)
await miSensor.init()

setInterval(async () => {
    const { lux } = await miSensor.read()
    console.log(`Luz: ${lux.toFixed(1)} lx`)
}, 1000)
Copied!

Este ejemplo se centra en la comunicación. Un driver reutilizable debería validar respuestas, documentar el modo de medida y exponer un servicio estándar, por ejemplo LightLevel.