使用 JavaScript 访问 I2C 外设
约 1260 字大约 4 分钟
2026-03-23
I2C 是一种两线串行通信协议,常用于连接传感器、显示屏、EEPROM 等外设。下面以三种常见外设为例,介绍 I2C 的使用方法。
详细 API 请参考 I2C 模块 API。
基础代码
C++ 初始化
#include <BeShell.hpp>
using namespace be ;
BeShell beshell ;
void app_main() {
// 应用 I2C 和文件系统模块
beshell.use<Serial>() ;
beshell.use<FS>() ;
// 挂载分区
FS::mount("/", new LittleFS("fsroot", true)) ;
// 启动 BeShell
beshell.setup() ;
// 自动运行 main.js
beshell.engine->evalScript("/main.js") ;
beshell.run() ;
}JS 初始化
import { i2c0 } from "serial"
// 配置 I2C 总线
i2c0.setup({
sda: 21, // SDA 引脚
scl: 22, // SCL 引脚
freq: 400000 // 400kHz
})
// 扫描总线上的设备
i2c0.scan()检测 I2C 设备
扫描所有设备
使用 scan() 方法扫描总线上的所有 I2C 设备:
import { i2c0 } from "serial"
i2c0.setup({ sda: 21, scl: 22, freq: 400000 })
// 扫描总线,打印所有发现的设备地址
i2c0.scan()
// 输出示例:Found device at 0x3C
// Found device at 0x50检测特定设备
使用 ping() 方法检测指定地址的设备是否存在:
import { i2c0 } from "serial"
i2c0.setup({ sda: 21, scl: 22, freq: 400000 })
// 检测特定地址的设备
const hasSSD1306 = i2c0.ping(0x3C)
console.log("SSD1306 exists:", hasSSD1306)
const hasAT24C02 = i2c0.ping(0x50)
console.log("AT24C02 exists:", hasAT24C02)
const hasAHT20 = i2c0.ping(0x38)
console.log("AHT20 exists:", hasAHT20)常用 I2C 设备地址
| 设备 | 地址 |
|---|---|
| SSD1306 OLED | 0x3C |
| AT24C02 EEPROM | 0x50 |
| AHT20 温湿度传感器 | 0x38 |
| MPU6050 陀螺仪 | 0x68 |
| BMP280 气压传感器 | 0x76 |
SSD1306 OLED 显示
SSD1306 是一款常用的 128x64 单色 OLED 显示屏。
接线
| ESP32 | SSD1306 |
|---|---|
| 3.3V | VCC |
| GND | GND |
| GPIO 21 | SDA |
| GPIO 22 | SCL |
JS 示例
import { i2c0 } from "serial"
const SSD1306_ADDR = 0x3C
// 初始化 I2C
i2c0.setup({ sda: 21, scl: 22, freq: 400000 })
// SSD1306 初始化命令
const initCmds = [
0xAE, // 关闭显示
0xD5, 0x80, // 设置时钟分频
0xA8, 0x3F, // 设置驱动路数
0xD3, 0x00, // 设置显示偏移
0x40, // 设置显示开始行
0x8D, 0x14, // 开启电荷泵
0x20, 0x00, // 设置内存模式
0xA1, // 段重映射
0xC8, // COM 扫描方向
0xDA, 0x12, // 设置 COM 引脚
0x81, 0xCF, // 设置对比度
0xD9, 0xF1, // 设置预充电周期
0xDB, 0x40, // 设置 VCOMH 检测级别
0xA4, // 全局显示开启
0xA6, // 正常显示
0xAF // 开启显示
]
// 发送命令到 SSD1306
function ssd1306_sendCmd(cmd) {
i2c0.write8(SSD1306_ADDR, 0x80, cmd)
}
// 发送数据到 SSD1306
function ssd1306_sendData(data) {
i2c0.write8(SSD1306_ADDR, 0x40, data)
}
// 清屏
function ssd1306_clear() {
for (let i = 0; i < 128 * 8; i++) {
ssd1306_sendData(0x00)
}
}
// 显示字符串(8x16 字体)
function ssd1306_drawString(str, x, y) {
// 简化的字符映射,实际需要完整的字库
for (let i = 0; i < str.length; i++) {
const charCode = str.charCodeAt(i)
// 每个字符占 8 列,16 行
for (let col = 0; col < 8; col++) {
ssd1306_sendData(0x00) // 示例
}
}
}
// 初始化屏幕
for (const cmd of initCmds) {
ssd1306_sendCmd(cmd)
}
ssd1306_clear()
console.log("SSD1306 initialized")AT24C02 EEPROM 存储
AT24C02 是一款 2Kbit (256字节) 的 EEPROM,用于持久存储数据。
接线
| ESP32 | AT24C02 |
|---|---|
| 3.3V | VCC |
| GND | GND |
| GPIO 21 | SDA |
| GPIO 22 | SCL |
JS 示例
import { i2c0 } from "serial"
const AT24C02_ADDR = 0x50
// 初始化 I2C
i2c0.setup({ sda: 21, scl: 22, freq: 100000 })
// 写一个字节到指定地址
function at24c02_writeByte(addr, data) {
i2c0.write8(AT24C02_ADDR, addr, data)
}
// 读取指定地址的一个字节
function at24c02_readByte(addr) {
return i2c0.readU8(AT24C02_ADDR, addr)
}
// 写入一页(8字节)到指定页地址
function at24c02_writePage(pageAddr, data) {
for (let i = 0; i < 8; i++) {
i2c0.write8(AT24C02_ADDR, pageAddr * 8 + i, data[i])
}
}
// 写入测试
at24c02_writeByte(0x00, 0xAB)
console.log("Write:", 0xAB)
// 读取验证
const value = at24c02_readByte(0x00)
console.log("Read:", value)
// 写入字符串
const str = "Hello BeShell"
for (let i = 0; i < str.length; i++) {
at24c02_writeByte(0x10 + i, str.charCodeAt(i))
}
console.log("String saved")
// 读取字符串
let result = ""
for (let i = 0; i < str.length; i++) {
result += String.fromCharCode(at24c02_readByte(0x10 + i))
}
console.log("String read:", result)AHT20 温湿度传感器
AHT20 是一款高精度温湿度传感器,I2C 接口。
接线
| ESP32 | AHT20 |
|---|---|
| 3.3V | VCC |
| GND | GND |
| GPIO 21 | SDA |
| GPIO 22 | SCL |
JS 示例
import { i2c0 } from "serial"
const AHT20_ADDR = 0x38
// 初始化 I2C
i2c0.setup({ sda: 21, scl: 22, freq: 100000 })
// AHT20 命令
const CMD_INIT = [0xBE, 0x08, 0x00] // 初始化命令
const CMD_MEASURE = [0xAC, 0x33, 0x00] // 触发测量
// 初始化传感器
function aht20_init() {
i2c0.send(AHT20_ADDR, CMD_INIT)
console.log("AHT20 initialized")
}
// 读取温湿度
function aht20_read() {
// 触发测量
i2c0.send(AHT20_ADDR, CMD_MEASURE)
// 等待测量完成(80ms)
delay(80)
// 读取 6 字节数据
const data = i2c0.recv(AHT20_ADDR, 6)
if (!data) {
return null
}
const bytes = new Uint8Array(data)
// 解析数据
// 湿度 = (bytes[1] << 12 | bytes[2] << 4 | bytes[3] >> 4) / 2^20 * 100%
const hum = ((bytes[1] << 12) | (bytes[2] << 4) | (bytes[3] >> 4)) / 1048576 * 100
// 温度 = ((bytes[3] & 0x0F) << 16 | bytes[4] << 8 | bytes[5]) / 2^20 * 200 - 50
const temp = (((bytes[3] & 0x0F) << 16) | (bytes[4] << 8) | bytes[5]) / 1048576 * 200 - 50
return { temperature: temp, humidity: hum }
}
// 简单延时函数
function delay(ms) {
const end = Date.now() + ms
while (Date.now() < end) {}
}
// 初始化
aht20_init()
// 读取并输出温湿度
const sensorData = aht20_read()
if (sensorData) {
console.log("Temperature:", sensorData.temperature.toFixed(2), "C")
console.log("Humidity:", sensorData.humidity.toFixed(2), "%")
}
// 定时读取
setInterval(() => {
const data = aht20_read()
if (data) {
console.log(`Temp: ${data.temperature.toFixed(2)}C, Hum: ${data.humidity.toFixed(2)}%`)
}
}, 2000)常见问题
总线扫描不到设备
- 检查接线是否正确(SDA/SCL)
- 检查设备电源是否正常
- 确认 I2C 地址是否正确
- 尝试降低通信频率(如 100000)
读写失败
- 确保设备已通过
addDevice添加到总线 - 检查设备是否支持寄存器访问模式
- 确认寄存器地址位数配置正确
