Thư viện Wire.h: API I2C đầy đủ với ví dụ
Wire.h là thư viện I2C tích hợp sẵn trong Arduino IDE, không cần cài thêm. Bài viết này liệt kê toàn bộ API kèm ví dụ thực tế — từ master scan bus đến slave responder, để bạn dùng làm tham khảo nhanh.
1. Khởi tạo
#include
Wire.begin(); // Master mode
Wire.begin(addr); // Slave mode với địa chỉ 7-bit
Wire.begin(sda, scl); // ESP32 / ESP8266: chỉ định chân
Wire.setClock(400000); // 100k (chuẩn), 400k (fast), 1M (fast+, ESP32)
Wire.end(); // Giải phóng bus (ESP32)
2. Master — gửi dữ liệu
Wire.beginTransmission(0x68); // chọn slave
Wire.write(0x1A); // địa chỉ thanh ghi
Wire.write(0xFF); // giá trị
uint8_t err = Wire.endTransmission();
// Trả về:
// 0 = thành công
// 1 = buffer overflow
// 2 = NACK trên địa chỉ (slave không tồn tại)
// 3 = NACK trên data
// 4 = lỗi khác
Buffer mặc định 32 byte trên AVR — gửi nhiều hơn sẽ truncate. Trên ESP32 buffer 128 byte.
3. Master — đọc dữ liệu
uint8_t n = Wire.requestFrom(0x68, (uint8_t)6);
while (Wire.available()) {
uint8_t b = Wire.read();
Serial.println(b, HEX);
}
// requestFrom() có overload thứ 3:
Wire.requestFrom(addr, len, true); // stop sau đọc (mặc định)
Wire.requestFrom(addr, len, false); // restart, không thả bus
4. Pattern "chọn thanh ghi rồi đọc" — chuẩn nhất
uint8_t readReg(uint8_t addr, uint8_t reg) {
Wire.beginTransmission(addr);
Wire.write(reg);
Wire.endTransmission(false); // restart, GIỮ bus
Wire.requestFrom(addr, (uint8_t)1);
return Wire.read();
}
void writeReg(uint8_t addr, uint8_t reg, uint8_t val) {
Wire.beginTransmission(addr);
Wire.write(reg);
Wire.write(val);
Wire.endTransmission();
}
5. Đọc 16-bit (cảm biến nhiệt, áp suất...)
int16_t readReg16(uint8_t addr, uint8_t reg) {
Wire.beginTransmission(addr);
Wire.write(reg);
Wire.endTransmission(false);
Wire.requestFrom(addr, (uint8_t)2);
int16_t hi = Wire.read();
int16_t lo = Wire.read();
return (hi << 8) | lo;
}
Tuỳ chip, có thể little-endian (đảo thứ tự).
6. Slave mode — Arduino làm slave
const uint8_t MY_ADDR = 0x42;
volatile uint8_t lastCmd = 0;
void onReceive(int n) {
while (Wire.available()) lastCmd = Wire.read();
}
void onRequest() {
Wire.write(millis() & 0xFF); // gửi 1 byte timestamp
}
void setup() {
Wire.begin(MY_ADDR);
Wire.onReceive(onReceive);
Wire.onRequest(onRequest);
}
void loop() {}
7. I2C Scanner — code mẫu
void scanI2C() {
Serial.println("Scan...");
for (uint8_t a = 1; a < 127; a++) {
Wire.beginTransmission(a);
if (Wire.endTransmission() == 0) {
Serial.print(" Found 0x");
Serial.println(a, HEX);
}
}
}
8. Đa bus I2C trên ESP32
ESP32 có 2 bus I2C phần cứng (Wire và Wire1):
TwoWire I2C_A = TwoWire(0);
TwoWire I2C_B = TwoWire(1);
void setup() {
I2C_A.begin(21, 22, 400000); // bus 0
I2C_B.begin(25, 26, 400000); // bus 1
}
Dùng khi 2 slave trùng địa chỉ (vd 2 cảm biến MPU-6050 cùng 0x68).
9. Bảng nhanh các API
| Hàm | Vai trò |
|---|---|
begin([addr]) | Khởi tạo master / slave |
beginTransmission(addr) | Bắt đầu gửi (master) |
write(b) / write(buf, n) | Đẩy byte vào buffer |
endTransmission([stop]) | Gửi đi, nhận ACK/NACK |
requestFrom(addr, n) | Yêu cầu n byte từ slave |
available() | Số byte còn trong buffer nhận |
read() | Đọc byte tiếp theo |
onReceive(handler) | Callback khi slave nhận |
onRequest(handler) | Callback khi master xin data |
setClock(hz) | Đổi tốc độ bus |
10. Lỗi thường gặp
- endTransmission trả 2 — slave không tồn tại (sai địa chỉ, chưa cấp nguồn, dây đứt).
- available() luôn 0 — quên requestFrom hoặc slave không trả lời.
- Treo cả vòng loop — bus bị slave "giữ" SDA mãi LOW. Reset module hoặc tự bit-bang SCL 9 xung để giải phóng.
- Đọc ra rác — sai endian hoặc sai địa chỉ thanh ghi.
Liên quan
Đọc bài I2C trên Arduino: Wire.h căn bản nếu bạn mới làm quen. Để dùng OLED, xem Adafruit SSD1306: vẽ pixel, text, hình.