LED ma trận MAX7219 và LED 7 đoạn TM1637 với ESP32: chữ chạy 32 × 8 và đồng hồ, không cần thư viện

So sánh LED ma trận MAX7219 và LED 7 đoạn TM1637, nối với ESP32 an toàn mức điện áp, giải thích khung 16 bit và ba lệnh TM1637, hai chương trình chữ chạy và đồng hồ không cần thư viện.

Màn hình LED đỏ có ưu điểm mà OLED, LCD không có: sáng rõ, nhìn được từ xa và cả dưới nắng. Hai loại phổ biến nhất là LED ma trận 8 × 8 dùng chip MAX7219 (ghép thành bảng chữ chạy 32 × 8) và LED 7 đoạn 4 số dùng chip TM1637 (đồng hồ, bộ đếm, hiện nhiệt độ). Bài này giải thích hai chip nhận dữ liệu thế nào, rồi viết cho ESP32 hai chương trình không cần thư viện: chữ chạy "PYWORLD.VN" trên ma trận 32 × 8 và đồng hồ đếm phút:giây trên LED 7 đoạn.

1. Hai loại màn hình LED

MAX7219 ma trận 8 × 8TM1637 LED 7 đoạn 4 số
Hiển thị64 điểm mỗi khối; ghép 4 khối thành 32 × 84 chữ số, dấu hai chấm ở giữa
Hợp vớiChữ chạy, biểu tượng, hình đơn giảnGiờ, số đếm, nhiệt độ
Giao tiếp3 dây kiểu SPI: DIN, CS, CLK2 dây: CLK, DIO (không phải I2C)
Nguồn5 V; 4 khối sáng hết cỡ tới khoảng 1 ADatasheet ghi 5 V; phần lớn module chạy được ở 3,3 V, sáng hơi yếu hơn
Độ sáng16 mức8 mức

Cả hai chip đều tự quét LED liên tục. ESP32 chỉ cần gửi nội dung mới khi muốn đổi, không phải bật tắt từng LED như khi nối LED trực tiếp vào chân GPIO.

2. Linh kiện cần chuẩn bị

3. Sơ đồ nối dây

Sơ đồ nối MAX7219 4 khối với ESP32: VCC vào VIN, DIN vào D23, CS vào D5, CLK vào D18; TM1637: CLK vào D22, DIO vào D21, VCC vào 3V3
MAX7219 lấy 5 V từ VIN; TM1637 lấy 3,3 V để chân DIO không đưa 5 V vào ESP32 (hình minh họa).
MAX7219 (đầu DIN)ESP32TM1637ESP32
VCCVIN (5 V khi cắm USB)VCC3V3
GNDGNDGNDGND
DIND23CLKD22
CSD5DIOD21
CLKD18
  • Module MAX7219 có hai đầu: đầu ghi DIN là đầu vào, đầu ghi DOUT là đầu ra để nối thêm khối. Nối ESP32 vào đầu DIN.
  • Vì sao MAX7219 cấp 5 V được mà TM1637 thì không: ESP32 chỉ gửi tín hiệu sang MAX7219, không đọc ngược lại, nên chân ESP32 không bao giờ nhận 5 V. TM1637 thì có điện trở kéo lên tới VCC trên chân DIO và ESP32 phải đọc chân này (bit ACK), nên cấp 5 V sẽ đưa 5 V vào GPIO. Muốn TM1637 sáng hơn ở 5 V thì cần mạch chuyển mức; xem bài mức logic 3,3 V và 5 V.
  • MAX7219 cấp 5 V cần mức cao tối thiểu khoảng 3,5 V, cao hơn 3,3 V của ESP32 một chút. Phần lớn module vẫn nhận đúng; nếu chữ hiện lộn xộn thì thêm IC chuyển mức 74HCT245.

4. MAX7219 nhận dữ liệu thế nào

Một lần ghi MAX7219 gồm byte địa chỉ thanh ghi và byte dữ liệu 8 điểm của một hàng; bốn khối MAX7219 nối tiếp, dữ liệu đi từ DIN qua từng khối
Mỗi khối nhận 16 bit: địa chỉ thanh ghi và dữ liệu; bốn khối nối tiếp dùng chung CS và CLK (hình minh họa).

Mỗi lần ghi gồm 16 bit: 8 bit đầu là địa chỉ thanh ghi, 8 bit sau là dữ liệu. Với module ma trận phổ biến, thanh ghi 1 đến 8 là 8 hàng, mỗi bit của dữ liệu là một điểm trong hàng. Các thanh ghi cài đặt: 0x09 (giải mã, đặt 0 để tự điều khiển từng điểm), 0x0A (độ sáng 0–15), 0x0B (số hàng quét, đặt 7), 0x0C (bật màn), 0x0F (thử đèn, đặt 0).

Khi nối 4 khối, dữ liệu đi qua từng khối như qua một đường ống: 16 bit gửi trước bị đẩy tới khối xa nhất. Sau khi gửi đủ 64 bit, sườn lên của chân CS làm cả 4 khối cùng nhận phần dữ liệu đang nằm trong chúng.

5. Chương trình chữ chạy MAX7219

// PYWORLD.VN - LED ma trận MAX7219 (module 4 khối 32 x 8) với ESP32: chữ chạy, không cần thư viện
// DIN vào D23, CS vào D5, CLK vào D18; VCC 5 V, GND chung. Chữ ngược, lật hay sai thứ tự khối thì đổi ba cờ DAO_...
// Font 5 x 7 lấy từ bộ chữ chuẩn của thư viện Adafruit GFX (chữ hoa, số, dấu cơ bản)

const int CHAN_DIN = 23;
const int CHAN_CS = 5;
const int CHAN_CLK = 18;
const int SO_KHOI = 4;                 // số module 8 x 8 nối tiếp
const int DO_SANG = 3;                 // 0-15
const bool DAO_COT = true;             // chữ bị lật ngang: đổi cờ này
const bool DAO_HANG = false;           // chữ lộn ngược: đổi cờ này
const bool DAO_KHOI = false;           // các khối hiện sai thứ tự: đổi cờ này
const char *NOI_DUNG = "PYWORLD.VN - ESP32 + MAX7219   ";

// Thanh ghi MAX7219
const uint8_t TG_GIAI_MA = 0x09, TG_DO_SANG = 0x0A, TG_SO_HANG = 0x0B, TG_TAT = 0x0C, TG_THU_DEN = 0x0F;

const uint8_t FONT[][5] = {
  0x00, 0x00, 0x00, 0x00, 0x00,  // dau cach
  0x00, 0x00, 0x5F, 0x00, 0x00,  // !
  0x00, 0x07, 0x00, 0x07, 0x00,  // "
  0x14, 0x7F, 0x14, 0x7F, 0x14,  // #
  0x24, 0x2A, 0x7F, 0x2A, 0x12,  // $
  0x23, 0x13, 0x08, 0x64, 0x62,  // %
  0x36, 0x49, 0x56, 0x20, 0x50,  // &
  0x00, 0x08, 0x07, 0x03, 0x00,  // '
  0x00, 0x1C, 0x22, 0x41, 0x00,  // (
  0x00, 0x41, 0x22, 0x1C, 0x00,  // )
  0x2A, 0x1C, 0x7F, 0x1C, 0x2A,  // *
  0x08, 0x08, 0x3E, 0x08, 0x08,  // +
  0x00, 0x80, 0x70, 0x30, 0x00,  // ,
  0x08, 0x08, 0x08, 0x08, 0x08,  // -
  0x00, 0x00, 0x60, 0x60, 0x00,  // .
  0x20, 0x10, 0x08, 0x04, 0x02,  // /
  0x3E, 0x51, 0x49, 0x45, 0x3E,  // 0
  0x00, 0x42, 0x7F, 0x40, 0x00,  // 1
  0x72, 0x49, 0x49, 0x49, 0x46,  // 2
  0x21, 0x41, 0x49, 0x4D, 0x33,  // 3
  0x18, 0x14, 0x12, 0x7F, 0x10,  // 4
  0x27, 0x45, 0x45, 0x45, 0x39,  // 5
  0x3C, 0x4A, 0x49, 0x49, 0x31,  // 6
  0x41, 0x21, 0x11, 0x09, 0x07,  // 7
  0x36, 0x49, 0x49, 0x49, 0x36,  // 8
  0x46, 0x49, 0x49, 0x29, 0x1E,  // 9
  0x00, 0x00, 0x14, 0x00, 0x00,  // :
  0x00, 0x40, 0x34, 0x00, 0x00,  // ;
  0x00, 0x08, 0x14, 0x22, 0x41,  // <
  0x14, 0x14, 0x14, 0x14, 0x14,  // =
  0x00, 0x41, 0x22, 0x14, 0x08,  // >
  0x02, 0x01, 0x59, 0x09, 0x06,  // ?
  0x3E, 0x41, 0x5D, 0x59, 0x4E,  // @
  0x7C, 0x12, 0x11, 0x12, 0x7C,  // A
  0x7F, 0x49, 0x49, 0x49, 0x36,  // B
  0x3E, 0x41, 0x41, 0x41, 0x22,  // C
  0x7F, 0x41, 0x41, 0x41, 0x3E,  // D
  0x7F, 0x49, 0x49, 0x49, 0x41,  // E
  0x7F, 0x09, 0x09, 0x09, 0x01,  // F
  0x3E, 0x41, 0x41, 0x51, 0x73,  // G
  0x7F, 0x08, 0x08, 0x08, 0x7F,  // H
  0x00, 0x41, 0x7F, 0x41, 0x00,  // I
  0x20, 0x40, 0x41, 0x3F, 0x01,  // J
  0x7F, 0x08, 0x14, 0x22, 0x41,  // K
  0x7F, 0x40, 0x40, 0x40, 0x40,  // L
  0x7F, 0x02, 0x1C, 0x02, 0x7F,  // M
  0x7F, 0x04, 0x08, 0x10, 0x7F,  // N
  0x3E, 0x41, 0x41, 0x41, 0x3E,  // O
  0x7F, 0x09, 0x09, 0x09, 0x06,  // P
  0x3E, 0x41, 0x51, 0x21, 0x5E,  // Q
  0x7F, 0x09, 0x19, 0x29, 0x46,  // R
  0x26, 0x49, 0x49, 0x49, 0x32,  // S
  0x03, 0x01, 0x7F, 0x01, 0x03,  // T
  0x3F, 0x40, 0x40, 0x40, 0x3F,  // U
  0x1F, 0x20, 0x40, 0x20, 0x1F,  // V
  0x3F, 0x40, 0x38, 0x40, 0x3F,  // W
  0x63, 0x14, 0x08, 0x14, 0x63,  // X
  0x03, 0x04, 0x78, 0x04, 0x03,  // Y
  0x61, 0x59, 0x49, 0x4D, 0x43,  // Z
};

uint8_t manHinh[SO_KHOI * 8];          // mỗi phần tử là một cột, bit 0 là hàng trên cùng

// Gửi cùng một cặp (thanh ghi, giá trị) tới mọi khối, hoặc giá trị riêng từng khối nếu giaTri != NULL.
void ghiTatCa(uint8_t thanhGhi, uint8_t chung, const uint8_t *giaTri = nullptr) {
  digitalWrite(CHAN_CS, LOW);
  for (int k = 0; k < SO_KHOI; k++) {   // byte gửi trước bị đẩy tới khối xa nhất
    int khoi = DAO_KHOI ? SO_KHOI - 1 - k : k;
    shiftOut(CHAN_DIN, CHAN_CLK, MSBFIRST, thanhGhi);
    shiftOut(CHAN_DIN, CHAN_CLK, MSBFIRST, giaTri ? giaTri[khoi] : chung);
  }
  digitalWrite(CHAN_CS, HIGH);         // sườn lên của CS: mọi khối chốt dữ liệu cùng lúc
}

void capNhat() {
  uint8_t hang[SO_KHOI];
  for (int r = 0; r < 8; r++) {
    int hangThat = DAO_HANG ? 7 - r : r;
    for (int k = 0; k < SO_KHOI; k++) {
      uint8_t b = 0;
      for (int c = 0; c < 8; c++) {
        if (manHinh[k * 8 + c] >> hangThat & 1) b |= DAO_COT ? (1 << c) : (0x80 >> c);
      }
      hang[k] = b;
    }
    ghiTatCa(r + 1, 0, hang);          // thanh ghi 1-8 là 8 hàng (với module loại phổ biến)
  }
}

// Cột thứ i của dòng chữ (mỗi ký tự 5 cột + 1 cột trống).
uint8_t cotCuaChu(const char *chu, int i) {
  int kyTu = i / 6, cot = i % 6;
  char c = chu[kyTu];
  if (c >= 'a' && c <= 'z') c -= 32;   // font chỉ có chữ hoa
  if (cot == 5 || c < 0x20 || c > 0x5A) return 0;
  return FONT[c - 0x20][cot];
}

void setup() {
  pinMode(CHAN_DIN, OUTPUT);
  pinMode(CHAN_CLK, OUTPUT);
  pinMode(CHAN_CS, OUTPUT);
  digitalWrite(CHAN_CS, HIGH);
  ghiTatCa(TG_THU_DEN, 0);             // tắt chế độ thử đèn
  ghiTatCa(TG_SO_HANG, 7);             // quét đủ 8 hàng
  ghiTatCa(TG_GIAI_MA, 0);             // không giải mã BCD: tự điều khiển từng điểm
  ghiTatCa(TG_DO_SANG, DO_SANG);
  ghiTatCa(TG_TAT, 1);                 // thoát chế độ tắt, bắt đầu hiển thị
}

void loop() {
  static int viTri = 0;
  int tongCot = strlen(NOI_DUNG) * 6;
  for (int x = 0; x < SO_KHOI * 8; x++) manHinh[x] = cotCuaChu(NOI_DUNG, (viTri + x) % tongCot);
  capNhat();
  viTri = (viTri + 1) % tongCot;
  delay(60);                           // tốc độ chạy chữ
}
Ba thời điểm của dòng chữ PYWORLD.VN chạy trên ma trận LED đỏ 32 x 8 điểm, mỗi lần dịch sang trái một cột
Ba thời điểm của dòng chữ, dựng lại theo đúng font và cách dịch cột của chương trình (hình mô phỏng theo code).
  • Font 5 × 7 lấy từ bộ chữ chuẩn của thư viện Adafruit GFX: mỗi ký tự 5 cột, mỗi cột một byte, bit 0 là điểm trên cùng. Bảng chỉ có chữ hoa, số và vài dấu nên chương trình đổi chữ thường thành chữ hoa; chữ có dấu tiếng Việt không hiện được.
  • manHinh[] là bộ đệm 32 cột. loop() chép 32 cột của dòng chữ bắt đầu từ viTri vào bộ đệm, gửi ra, rồi tăng viTri: chữ dịch sang trái mỗi 60 ms.
  • capNhat() ghép 8 cột của mỗi khối thành từng byte hàng rồi gửi 8 lần (8 hàng).
  • Ba cờ DAO_COT, DAO_HANG, DAO_KHOI: các nhà sản xuất nối LED vào MAX7219 theo hướng khác nhau. Nạp xong mà chữ bị lật ngang, lộn ngược hoặc các khối hiện sai thứ tự thì đổi cờ tương ứng rồi nạp lại.

6. TM1637: mã 7 đoạn và ba lệnh

Sơ đồ đặt tên 7 đoạn a đến g, bảng mã số 0 đến 9, màn hình 12:34 và các byte 40 C0 06 DB 4F 66 8C chương trình gửi
Mã từng số theo bit a–g, và các byte chương trình gửi để hiện 12:34 (hình minh họa).

Mỗi chữ số có 7 đoạn đặt tên a đến g. Mỗi đoạn là một bit: bit 0 là đoạn a, bit 6 là đoạn g. Ví dụ số 1 chỉ sáng đoạn b và c nên mã là 0x06. Với module đồng hồ, bit 7 của ô số thứ hai bật dấu hai chấm.

TM1637 dùng 2 dây nhưng không phải I2C: không có địa chỉ, bit thấp gửi trước. Mỗi lần hiển thị gửi ba lệnh: 0x40 (ghi dữ liệu, tự tăng ô), 0xC0 (bắt đầu từ ô 0) kèm 4 byte mã số, và 0x88 cộng độ sáng (bật màn). Sau mỗi byte có xung thứ 9 để chip kéo DIO xuống báo đã nhận (ACK).

7. Chương trình đồng hồ TM1637

// PYWORLD.VN - LED 7 đoạn 4 số TM1637 với ESP32: đồng hồ đếm phút:giây, dấu hai chấm nhấp nháy, không cần thư viện
// CLK vào D22, DIO vào D21; VCC vào 3V3 (module có điện trở kéo lên tới VCC: cấp 5 V sẽ đưa 5 V vào chân ESP32), GND chung
// Bấm nút BOOT để đổi độ sáng 0-7

const int CHAN_CLK = 22;
const int CHAN_DIO = 21;
const int CHAN_BOOT = 0;

// Mã 7 đoạn cho số 0-9: bit 0 = đoạn a ... bit 6 = đoạn g, bit 7 = dấu hai chấm (ở ô số 2)
const uint8_t MA_SO[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F};

// TM1637 không phải I2C: hai chân chỉ kéo xuống thấp hoặc nhả ra để điện trở kéo lên.
void thap(int chan) { pinMode(chan, OUTPUT); digitalWrite(chan, LOW); }
void nha(int chan) { pinMode(chan, INPUT); }
void cho() { delayMicroseconds(5); }

void batDau() {                         // DIO xuống thấp trong lúc CLK đang cao
  nha(CHAN_CLK); nha(CHAN_DIO); cho();
  thap(CHAN_DIO); cho();
}

void ketThuc() {                        // DIO lên cao trong lúc CLK đang cao
  thap(CHAN_CLK); thap(CHAN_DIO); cho();
  nha(CHAN_CLK); cho();
  nha(CHAN_DIO); cho();
}

bool guiByte(uint8_t b) {               // bit thấp gửi trước, xung thứ 9 đọc ACK
  for (int i = 0; i < 8; i++) {
    thap(CHAN_CLK); cho();
    if (b & 1) nha(CHAN_DIO); else thap(CHAN_DIO);
    cho();
    nha(CHAN_CLK); cho();
    b >>= 1;
  }
  thap(CHAN_CLK); nha(CHAN_DIO); cho();
  nha(CHAN_CLK); cho();
  bool coAck = digitalRead(CHAN_DIO) == LOW;  // TM1637 kéo DIO xuống để báo đã nhận
  thap(CHAN_CLK); cho();
  return coAck;
}

bool hienThi(const uint8_t ma[4], uint8_t doSang) {
  batDau();
  bool ok = guiByte(0x40);              // lệnh dữ liệu: ghi, tự tăng địa chỉ
  ketThuc();
  batDau();
  ok &= guiByte(0xC0);                  // lệnh địa chỉ: bắt đầu từ ô số 0
  for (int i = 0; i < 4; i++) ok &= guiByte(ma[i]);
  ketThuc();
  batDau();
  ok &= guiByte(0x88 | (doSang & 7));   // lệnh hiển thị: bật màn, độ sáng 0-7
  ketThuc();
  return ok;
}

void setup() {
  Serial.begin(115200);
  pinMode(CHAN_BOOT, INPUT_PULLUP);
  uint8_t thu[4] = {0x7F | 0x80, 0x7F | 0x80, 0x7F | 0x80, 0x7F | 0x80};
  Serial.println(hienThi(thu, 7) ? "TM1637 tra loi ACK" : "Khong co ACK: kiem tra CLK, DIO, nguon");
  delay(1000);                          // sáng hết các đoạn 1 giây để kiểm tra
}

void loop() {
  static uint8_t doSang = 4;
  static bool nutTruoc = HIGH;
  bool nut = digitalRead(CHAN_BOOT);
  if (nutTruoc == HIGH && nut == LOW) {
    doSang = (doSang + 1) % 8;
    Serial.printf("Do sang %u\n", doSang);
  }
  nutTruoc = nut;

  uint32_t giay = millis() / 1000;
  uint32_t phut = (giay / 60) % 100, s = giay % 60;
  uint8_t ma[4] = {MA_SO[phut / 10], MA_SO[phut % 10], MA_SO[s / 10], MA_SO[s % 10]};
  if ((millis() / 500) % 2 == 0) ma[1] |= 0x80;   // dấu hai chấm sáng nửa giây, tắt nửa giây
  hienThi(ma, doSang);
  delay(50);
}
  • thap(), nha(): chân không bao giờ được đẩy lên cao bằng digitalWrite(HIGH). Muốn mức cao thì chuyển chân sang INPUT để điện trở kéo lên làm việc. Như vậy TM1637 kéo DIO xuống để báo ACK mà không gây chập với ESP32.
  • guiByte() gửi 8 bit (bit thấp trước), rồi tạo xung thứ 9 và đọc DIO: mức thấp là chip đã nhận.
  • Kiểm tra khi khởi động: chương trình bật hết các đoạn trong 1 giây và in ra Serial Monitor có ACK hay không: cách nhanh để biết nối dây đúng chưa.
  • Đồng hồ tính phút, giây từ millis(); dấu hai chấm sáng nửa giây, tắt nửa giây. Bấm nút BOOT để đổi độ sáng.

8. Kết quả trên Serial Monitor

Serial Monitor in TM1637 trả lời ACK và độ sáng khi bấm nút BOOT, hoặc thông báo không có ACK khi đảo dây CLK và DIO
Chương trình TM1637 kiểm tra ACK lúc khởi động và in độ sáng khi bấm BOOT.

Muốn đồng hồ chạy đúng giờ thật thay vì đếm từ lúc bật nguồn, lấy giờ từ module thời gian thực DS3231 (bài cùng loạt) hoặc từ Internet như bài đồng hồ Internet với ESP32 và NTP.

9. Xử lý lỗi thường gặp

Hiện tượngNguyên nhân thường gặpCách xử lý
Ma trận sáng hết hoặc tắt hết, không hiện chữNối vào đầu DOUT; quên nối CS; module ở chế độ thử đènNối vào đầu DIN; kiểm tra dây CS
Chữ bị lật, lộn ngược, sai thứ tự khốiModule nối LED theo hướng khácĐổi các cờ DAO_COT, DAO_HANG, DAO_KHOI
Chữ chập chờn, có điểm lạMức 3,3 V sát ngưỡng, dây dài, nguồn yếuDây ngắn; giảm DO_SANG; thêm IC chuyển mức
ESP32 khởi động lại khi ma trận sáng nhiều4 khối ăn dòng lớn qua cổng USBGiảm độ sáng; cấp 5 V riêng cho module, nối chung GND
TM1637 "Khong co ACK"Đảo CLK với DIO; chưa cấp nguồnĐổi lại hai dây; đo VCC trên module
TM1637 sáng mờCấp 3,3 V; độ sáng thấpBấm BOOT tăng độ sáng; cần sáng hơn thì cấp 5 V kèm chuyển mức cho DIO

10. Xem thêm


Tin tức liên quan

Phát hiện chuyển động bằng sóng Wi-Fi (CSI) trên ESP32: không cần cảm biến
Phát hiện chuyển động bằng sóng Wi-Fi (CSI) trên ESP32: không cần cảm biến

Giải thích Wi-Fi CSI và hướng dẫn dùng ESP32 phát hiện người di chuyển trong phòng chỉ bằng sóng Wi-Fi từ router: lấy dữ liệu CSI, tính điểm chuyển động, đặt ngưỡng và xem trên Serial Plotter.

Màn hình TFT SPI ILI9341 với ESP32: nối dây, lệnh khởi động, vẽ chữ và đồ thị không cần thư viện
Màn hình TFT SPI ILI9341 với ESP32: nối dây, lệnh khởi động, vẽ chữ và đồ thị không cần thư viện

Nối màn hình TFT ILI9341 2.4–2.8 inch với ESP32, các lệnh khởi động và vẽ vùng, màu RGB565, bộ chữ 5x7, chương trình vẽ số lớn và đồ thị điện áp không nhấp nháy, bảng sửa lỗi.

Hướng dẫn dùng màn hình OLED 0.96 inch SSD1306 với Arduino Nano
Hướng dẫn dùng màn hình OLED 0.96 inch SSD1306 với Arduino Nano

Hướng dẫn nối màn hình OLED 0.96 inch SSD1306 (I2C) với Arduino Nano, cài thư viện Adafruit, hiện chữ nhiều cỡ, vẽ thanh mức theo điện áp và lưu ý về bộ nhớ RAM.

Hướng dẫn bắt đầu MicroPython trên ESP32 Lite với Thonny
Hướng dẫn bắt đầu MicroPython trên ESP32 Lite với Thonny

Hướng dẫn cài driver, cài Thonny và nạp firmware MicroPython cho mạch ESP32 Lite, dùng REPL, nháy LED, lưu main.py, đọc nút nhấn, quét I2C và xử lý lỗi thường gặp.

Mạch ESP32 tích hợp OLED 0.96 và pin 18650: sơ đồ chân, sạc pin, đo % pin và chương trình đầu tiên
Mạch ESP32 tích hợp OLED 0.96 và pin 18650: sơ đồ chân, sạc pin, đo % pin và chương trình đầu tiên

Mạch ESP32 tích hợp OLED 0.96 inch và đế pin 18650: chân OLED GPIO5, GPIO4, LED GPIO16, sạc pin, công tắc, gắn cầu phân áp đo phần trăm pin và chương trình hiện lên màn hình.

Đo tốc độ động cơ bằng encoder và bộ đếm xung PCNT của ESP32: vòng/phút và số vòng
Đo tốc độ động cơ bằng encoder và bộ đếm xung PCNT của ESP32: vòng/phút và số vòng

Hướng dẫn đọc encoder Hall hai kênh của động cơ DC bằng bộ đếm xung PCNT của ESP32: đếm 4 cạnh, tính vòng/phút và số vòng, lập bảng tốc độ theo duty trong giờ học STEM.

Hướng dẫn lưu dữ liệu vào bộ nhớ flash của ESP32 bằng thư viện Preferences
Hướng dẫn lưu dữ liệu vào bộ nhớ flash của ESP32 bằng thư viện Preferences

Hướng dẫn dùng thư viện Preferences trên ESP32 với Arduino IDE để lưu số đếm, trạng thái relay và thông số cài đặt vào bộ nhớ flash, không mất khi tắt nguồn.

Tưới cây tự động với ESP32: cảm biến độ ẩm đất, relay và bơm mini, ngưỡng có trễ
Tưới cây tự động với ESP32: cảm biến độ ẩm đất, relay và bơm mini, ngưỡng có trễ

Làm hệ thống tưới cây tự động với ESP32: cảm biến độ ẩm đất điện dung, hiệu chỉnh khô ướt, relay điều khiển bơm mini, ngưỡng có trễ, giới hạn thời gian bơm và thí nghiệm STEM.


Bình luận
Đã thêm vào giỏ hàng