I2C es un bus serie que permite conectar varios periféricos mediante dos líneas compartidas: una transporta los datos y otra la señal de reloj.
Sensores como el AHT20, el MPU6050 o muchas pantallas OLED intercambian datos mediante protocolos digitales. I2C (Inter-Integrated Circuit) es uno de los más habituales.
En Arduino, esto implicaba buscar una librería específica (a veces había 5 diferentes para el mismo sensor), instalarla, y pelearse con los ejemplos.
En DeviceScript podemos usar un driver que traduzca el protocolo del componente y exponga sus medidas mediante servicios.
Cómo funciona el bus I2C
El protocolo I2C es maravilloso porque solo necesita dos cables (más alimentación) para conectar decenas de sensores:
- SDA (Serial Data): Por donde viajan los datos.
- SCL (Serial Clock): El reloj que marca el ritmo.
Cada sensor tiene una dirección única (como una IP), así que el microcontrolador puede preguntar “¿Sensor 0x38, qué temperatura hace?” sin que el resto de sensores se enteren.
Pines de la placa
Antes de programar, tenemos que conectar los cables. DeviceScript ya sabe qué pines usa tu placa por defecto para I2C, pero conviene repasarlo:
- ESP32 (Genérico): Generalmente SDA: 21, SCL: 22.
- ESP32-C3: Varía según fabricante, pero suele ser SDA: 4, SCL: 5 o similar.
- Raspberry Pi Pico W: SDA: GP4, SCL: GP5 (I2C0).
Al usar startService, DeviceScript configurará automáticamente estos pines por defecto. No hace falta que hagáis Wire.begin(SDA, SCL) como en Arduino, aunque se puede configurar si lo necesitáis.
Usar los drivers disponibles
Aquí viene lo nuevo. En lugar de descargar un .zip, usamos el ecosistema de JavaScript.
DeviceScript agrupa la mayoría de drivers comunes en un paquete llamado @devicescript/drivers. Si seguisteis los pasos de instalación, probablemente ya lo tengáis instalado en vuestro package.json.
Si queréis buscar si vuestro sensor está soportado, no se busca en Google “Arduino library sensor X”, se busca en el catálogo de DeviceScript o directamente en NPM.
Para instalar el paquete de drivers oficial (si no lo tenéis):
- Abrid la terminal en VS Code (
Ctrl + ñ). - Escribid:
npm install @devicescript/driversEsto descargará las definiciones para decenas de sensores (SHT30, AHT20, MPU6050, SSD1306, etc.).
Ejemplo: sensor ambiental
Vamos a conectar un sensor AHT20 (o un SHT30, son muy parecidos). Estos sensores miden temperatura y humedad.
En C++, tendríamos que inicializar el sensor, leer los bytes, convertirlos… En DeviceScript, usamos la función startAHT20.
import { startAHT20 } from "@devicescript/drivers"
// 1. Iniciamos el driver
// Automáticamente busca el sensor en los pines I2C por defecto
const sensor = await startAHT20()
// 2. Usamos los servicios
// El driver AHT20 expone DOS servicios: Temperatura y Humedad
console.log("Sensor iniciado. Esperando datos...")
sensor.temperature.reading.subscribe(temp => {
console.log(`Temperatura: ${temp} °C`)
})
sensor.humidity.reading.subscribe(hum => {
console.log(`Humedad: ${hum} %`)
})¿Qué acaba de pasar?
El código no importa directamente I2C ni especifica la dirección 0x38.
El driver startAHT20:
- Configura el bus I2C.
- Detecta el sensor.
- Nos devuelve un objeto que contiene los servicios estándar
TemperatureyHumidity.
La abstracción permite consumir un servicio de temperatura sin acoplar el resto de la lógica al protocolo interno del sensor.
Ejemplo: acelerómetro
Vamos a subir la apuesta. El MPU6050 es un sensor IMU (Inertial Measurement Unit) que nos da aceleración y giro en 3 ejes (X, Y, Z).
import { startMPU6050 } from "@devicescript/drivers"
// Iniciamos el driver
const imu = await startMPU6050()
// Nos suscribimos a la lectura del acelerómetro
imu.accelerometer.reading.subscribe(async (vector) => {
// vector es un array de 3 números [x, y, z]
const x = vector[0]
const y = vector[1]
const z = vector[2]
// Mostramos datos (usamos toFixed para no llenar la consola de decimales)
console.log(`Aceleración -> X: ${x.toFixed(2)}, Y: ${y.toFixed(2)}, Z: ${z.toFixed(2)}`)
})Al mover el sensor, los valores deberían cambiar en tiempo real.
Si el driver no existe
DeviceScript incluye varios drivers. Si no existe uno para el sensor elegido, podemos hablar con el bus I2C directamente.
Podemos hablar I2C “en crudo” usando el paquete @devicescript/i2c. Es algo más avanzado (similar a usar Wire.h), pero totalmente posible.
import { i2c } from "@devicescript/i2c"
const ADDRESS = 0x23 // Ejemplo BH1750 (Luz)
// Escribir comando
await i2c.write(ADDRESS, Buffer.from([0x10])) // Start measurement
// Leer 2 bytes
const data = await i2c.read(ADDRESS, 2)Sin embargo, casi siempre preferiremos buscar o crear un driver para encapsular esta lógica y exponerla como un servicio bonito.