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.

Màn hình TFT màu SPI 2.4 hoặc 2.8 inch (chip điều khiển ILI9341, 240 × 320 điểm ảnh) cho ESP32 hiện được số lớn, nhiều màu, đồ thị, rõ hơn hẳn màn hình OLED nhỏ. Thường người ta dùng thư viện có sẵn, nhưng hiểu màn hình nhận lệnh gì giúp tự sửa được khi gặp màn hình trắng, màu bị đảo hay hình chạy chậm. Bài này giải thích cách nối dây, các lệnh khởi động, màu RGB565, cách vẽ chữ và đồ thị, rồi viết một chương trình vẽ đồ thị điện áp chỉ dùng thư viện SPI có sẵn trong gói esp32.

1. Nối màn hình TFT ILI9341 với ESP32

Sơ đồ nối module TFT ILI9341 SPI với ESP32 DevKit V1: VCC và LED vào 3V3, GND vào GND, CS vào D5, RESET vào TX2 GPIO17, DC vào RX2 GPIO16, MOSI vào D23, SCK vào D18, MISO để trống
Dùng SPI mặc định (VSPI): SCK GPIO18, MOSI GPIO23; CS GPIO5, DC GPIO16, RESET GPIO17 (hình minh họa).
Chân TFTESP32 DevKit V1Vai trò
VCC3V3Nguồn (module có ổn áp thì cấp 5 V vào VIN cũng được)
GNDGND
CSGPIO5Chọn màn hình: 0 là đang nói chuyện với màn hình
RESETGPIO17 (TX2)Đặt lại phần cứng lúc khởi động
DCGPIO16 (RX2)0: byte gửi là mã lệnh; 1: byte gửi là dữ liệu
MOSI (SDI)GPIO23Dữ liệu từ ESP32 sang màn hình
SCKGPIO18Xung nhịp SPI
LED3V3Đèn nền (sáng hết cỡ); muốn chỉnh độ sáng thì nối qua transistor và PWM
MISO (SDO)để trốngChỉ cần khi đọc dữ liệu từ màn hình

Chân tín hiệu của màn hình chạy ở 3,3 V, hợp với ESP32. Phần khe thẻ nhớ (các chân SD_…) trên cùng module là một thiết bị SPI khác, không dùng trong bài. Nếu chưa quen SPI, đọc trước bài SPI là gì: MOSI, MISO, SCK, CS.

2. Màn hình nhận lệnh gì

Bảng lệnh: khởi động gồm 01 đặt lại phần mềm, 11 thoát ngủ, 3A 55 điểm ảnh 16 bit, 36 48 hướng quét và thứ tự BGR, 29 bật hiển thị; vẽ một vùng gồm 2A cột, 2B hàng, 2C ghi bộ nhớ; một điểm ảnh RGB565 màu vàng bằng 0xFE45 chia đỏ 5 bit, lục 6 bit, lam 5 bit
Năm lệnh khởi động, ba lệnh để vẽ một vùng, và cách một điểm ảnh 16 bit chứa màu (hình minh họa).

ILI9341 có bộ nhớ khung hình riêng, 240 × 320 ô, mỗi ô 16 bit. ESP32 chỉ cần ghi vào bộ nhớ đó; chip tự quét ra tấm nền liên tục. Mỗi lần nói chuyện gồm một byte lệnh (DC = 0) và vài byte dữ liệu (DC = 1).

Khởi động chỉ cần 5 lệnh: đặt lại (0x01), thoát ngủ (0x11), chọn 16 bit mỗi điểm (0x3A với dữ liệu 0x55), chọn hướng quét và thứ tự màu (0x36 với 0x48: màn hình dọc, tấm nền xếp màu theo thứ tự lam – lục – đỏ), bật hiển thị (0x29). Các thư viện thường gửi thêm lệnh chỉnh điện áp, gamma; giá trị mặc định sau khi đặt lại đủ dùng cho phần lớn module.

Vẽ luôn theo ba bước: 0x2A chọn cột đầu và cuối, 0x2B chọn hàng đầu và cuối, 0x2C rồi gửi liền một loạt điểm ảnh, màn hình tự điền từ trái sang phải, từ trên xuống dưới trong vùng đã chọn. Tô một hình chữ nhật, vẽ một chữ hay một cột đồ thị đều quy về ba bước này.

Màu dùng dạng RGB565: 5 bit đỏ, 6 bit lục, 5 bit lam ghép thành 16 bit. Mắt người nhạy với màu lục nhất nên lục được nhiều bit hơn. Hàm mau(r, g, b) trong chương trình đổi màu 8 bit mỗi kênh sang dạng này.

3. Vẽ chữ bằng bộ chữ 5 × 7

Chữ số 5 trên lưới 5 cột 7 hàng với các ô vàng là điểm sáng, dưới mỗi cột là byte 0x27, 0x45, 0x45, 0x45, 0x39; bên phải là cùng chữ ở cỡ 1, 2, 3, 5; ghi chú bộ chữ có 43 ký tự
Mỗi ký tự là lưới 5 × 7 điểm; phóng to bằng cách vẽ mỗi điểm thành ô vuông (hình minh họa).

Màn hình không có sẵn chữ; chữ cũng chỉ là các điểm ảnh. Chương trình có một bộ chữ 5 × 7 tự vẽ gồm 43 ký tự (dấu cách, số, chữ in hoa và vài dấu). Mỗi ký tự lưu thành 5 byte, mỗi byte là một cột, bit 0 là hàng trên cùng. Để vẽ cỡ co, mỗi điểm được vẽ thành ô vuông co × co.

Hàm veChu() dựng cả ô 6 × 8 (thêm một cột, một hàng làm khoảng cách) của mỗi ký tự trong bộ nhớ, gồm cả màu nền, rồi gửi một lần. Nhờ có màu nền, chữ mới đè thẳng lên chữ cũ mà không cần xóa trước, nên số đo thay đổi liên tục không bị nhấp nháy. Bộ chữ không có tiếng Việt có dấu; muốn có dấu cần bộ chữ lớn hơn, khi đó nên dùng thư viện.

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

5. Chương trình vẽ đồ thị điện áp

Biến trở nối hai đầu vào 3V3 và GND, chân giữa vào GPIO34. Mỗi 100 ms, chương trình đọc điện áp, vẽ số lớn, giá trị nhỏ nhất, lớn nhất và đồ thị 200 điểm gần nhất (20 giây).

// PYWORLD.VN - Màn hình TFT ILI9341 (240x320, SPI) với ESP32 không cần thư viện: khởi động, tô màu, vẽ chữ 5x7 và đồ thị điện áp
// Nối: SCK GPIO18, MOSI GPIO23, CS GPIO5, DC GPIO16, RESET GPIO17, VCC và LED vào 3V3, GND chung. Biến trở 10 kΩ: hai đầu 3V3, GND, chân giữa GPIO34
// Màn hình hiện điện áp ở GPIO34, nhỏ nhất, lớn nhất và đồ thị 20 giây gần nhất (mỗi 100 ms một điểm)

#include <SPI.h>

const int CHAN_SCK = 18, CHAN_MOSI = 23, CHAN_CS = 5, CHAN_DC = 16, CHAN_RST = 17;
const int CHAN_DO = 34;
const uint32_t TOC_DO_SPI = 40000000;    // 40 MHz; dây dài hoặc hình lỗi thì hạ xuống 20000000

constexpr uint16_t mau(uint8_t r, uint8_t g, uint8_t b) { return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); }
const uint16_t NEN = mau(8, 12, 24), TRANG = mau(255, 255, 255), XAM = mau(150, 160, 175), XANH_DAM = mau(20, 60, 140);
const uint16_t LUOI = mau(40, 50, 70), DUONG = mau(80, 220, 120), VANG = mau(255, 200, 40);

// Bộ chữ 5x7 tự vẽ của bài: mỗi ký tự 5 byte theo cột, bit 0 là hàng trên cùng
const char KY_TU[] = " 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.:-%/=";
const uint8_t PHONG[][5] = {
  {0x00, 0x00, 0x00, 0x00, 0x00},  // ' '
  {0x3E, 0x51, 0x49, 0x45, 0x3E},  // '0'
  {0x00, 0x42, 0x7F, 0x40, 0x00},  // '1'
  {0x42, 0x61, 0x51, 0x49, 0x46},  // '2'
  {0x21, 0x41, 0x45, 0x4B, 0x31},  // '3'
  {0x18, 0x14, 0x12, 0x7F, 0x10},  // '4'
  {0x27, 0x45, 0x45, 0x45, 0x39},  // '5'
  {0x3C, 0x4A, 0x49, 0x49, 0x30},  // '6'
  {0x01, 0x71, 0x09, 0x05, 0x03},  // '7'
  {0x36, 0x49, 0x49, 0x49, 0x36},  // '8'
  {0x06, 0x49, 0x49, 0x29, 0x1E},  // '9'
  {0x7E, 0x09, 0x09, 0x09, 0x7E},  // 'A'
  {0x7F, 0x49, 0x49, 0x49, 0x36},  // 'B'
  {0x3E, 0x41, 0x41, 0x41, 0x22},  // 'C'
  {0x7F, 0x41, 0x41, 0x22, 0x1C},  // 'D'
  {0x7F, 0x49, 0x49, 0x49, 0x41},  // 'E'
  {0x7F, 0x09, 0x09, 0x09, 0x01},  // 'F'
  {0x3E, 0x41, 0x49, 0x49, 0x7A},  // '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, 0x0C, 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'
  {0x46, 0x49, 0x49, 0x49, 0x31},  // 'S'
  {0x01, 0x01, 0x7F, 0x01, 0x01},  // '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, 0x51, 0x49, 0x45, 0x43},  // 'Z'
  {0x00, 0x60, 0x60, 0x00, 0x00},  // '.'
  {0x00, 0x36, 0x36, 0x00, 0x00},  // ':'
  {0x08, 0x08, 0x08, 0x08, 0x08},  // '-'
  {0x23, 0x13, 0x08, 0x64, 0x62},  // '%'
  {0x20, 0x10, 0x08, 0x04, 0x02},  // '/'
  {0x14, 0x14, 0x14, 0x14, 0x14},  // '='
};

// ---- Gửi lệnh và dữ liệu qua SPI ----
void batDau() {
  SPI.beginTransaction(SPISettings(TOC_DO_SPI, MSBFIRST, SPI_MODE0));
  digitalWrite(CHAN_CS, LOW);
}

void ketThuc() {
  digitalWrite(CHAN_CS, HIGH);
  SPI.endTransaction();
}

void guiLenh(uint8_t lenh) {             // DC = 0: byte này là mã lệnh
  digitalWrite(CHAN_DC, LOW);
  SPI.write(lenh);
  digitalWrite(CHAN_DC, HIGH);           // các byte sau (nếu có) là dữ liệu của lệnh
}

void lenhDon(uint8_t lenh, const uint8_t *duLieu = nullptr, int n = 0) {
  batDau();
  guiLenh(lenh);
  for (int i = 0; i < n; i++) SPI.write(duLieu[i]);
  ketThuc();
}

// Chọn vùng hình chữ nhật; các điểm ảnh gửi sau lệnh 0x2C sẽ lấp đầy vùng theo từng hàng.
void datVung(int x0, int y0, int x1, int y1) {
  guiLenh(0x2A);
  SPI.write16(x0);
  SPI.write16(x1);
  guiLenh(0x2B);
  SPI.write16(y0);
  SPI.write16(y1);
  guiLenh(0x2C);
}

void khoiDongTft() {
  digitalWrite(CHAN_RST, LOW);           // đặt lại bằng chân RESET
  delay(20);
  digitalWrite(CHAN_RST, HIGH);
  delay(150);
  lenhDon(0x01);                         // đặt lại phần mềm
  delay(150);
  lenhDon(0x11);                         // thoát chế độ ngủ
  delay(120);
  const uint8_t dinhDang = 0x55, huong = 0x48;
  lenhDon(0x3A, &dinhDang, 1);           // 16 bit mỗi điểm ảnh (RGB565)
  lenhDon(0x36, &huong, 1);              // màn hình dọc 240 x 320, thứ tự màu BGR của tấm nền
  lenhDon(0x29);                         // bật hiển thị
}

// ---- Vẽ ----
void toMau(int x, int y, int w, int h, uint16_t c) {
  static uint16_t dong[240];
  for (int i = 0; i < w; i++) dong[i] = c;
  batDau();
  datVung(x, y, x + w - 1, y + h - 1);
  for (int j = 0; j < h; j++) SPI.writePixels(dong, w * 2);   // writePixels tự gửi byte cao trước
  ketThuc();
}

// Dựng cả ô 6 x 8 của mỗi ký tự (phóng to `co` lần) trong bộ nhớ rồi gửi một lần: chữ mới đè chữ cũ, không nhấp nháy.
void veChu(int x, int y, const char *chuoi, int co, uint16_t mauChu, uint16_t mauNen) {
  static uint16_t o[6 * 5 * 8 * 5];      // đủ cho cỡ 5
  for (; *chuoi; chuoi++) {
    const char *p = strchr(KY_TU, *chuoi);
    const uint8_t *b = PHONG[p ? p - KY_TU : 0];
    int w = 6 * co, h = 8 * co;
    for (int py = 0; py < h; py++) {
      for (int px = 0; px < w; px++) {
        int cot = px / co, hang = py / co;
        bool bat = cot < 5 && hang < 7 && (b[cot] >> hang & 1);
        o[py * w + px] = bat ? mauChu : mauNen;
      }
    }
    batDau();
    datVung(x, y, x + w - 1, y + h - 1);
    SPI.writePixels(o, w * h * 2);
    ketThuc();
    x += w;
  }
}

// ---- Đồ thị ----
const int X0 = 30, Y0 = 136, W = 200, H = 170;
int duLieu[W];                           // mV, cũ bên trái, mới bên phải
int soMau = 0;

int yCua(int mv) { return Y0 + H - 1 - min(H - 1, mv * (H - 1) / 3300); }

void veKhung() {
  toMau(X0 - 1, Y0 - 1, W + 2, 1, XAM);
  toMau(X0 - 1, Y0 + H, W + 2, 1, XAM);
  toMau(X0 - 1, Y0, 1, H, XAM);
  toMau(X0 + W, Y0, 1, H, XAM);
  veChu(6, yCua(1000) - 3, "1V", 1, XAM, NEN);
  veChu(6, yCua(2000) - 3, "2V", 1, XAM, NEN);
  veChu(6, yCua(3000) - 3, "3V", 1, XAM, NEN);
}

// Mỗi cột dựng trong bộ nhớ (nền, lưới, đoạn nối hai điểm liền nhau) rồi gửi một lần.
void veDoThi() {
  static uint16_t cot[H];
  for (int i = 0; i < W; i++) {
    for (int y = 0; y < H; y++) cot[y] = NEN;
    for (int v = 1000; v <= 3000; v += 1000) cot[yCua(v) - Y0] = LUOI;
    int j = i - (W - soMau);
    if (j >= 1) {
      int a = yCua(duLieu[j - 1]) - Y0, b = yCua(duLieu[j]) - Y0;
      for (int y = min(a, b); y <= max(a, b) + 1 && y < H; y++) cot[y] = DUONG;   // nét dày 2 điểm
    }
    batDau();
    datVung(X0 + i, Y0, X0 + i, Y0 + H - 1);
    SPI.writePixels(cot, H * 2);
    ketThuc();
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(CHAN_CS, OUTPUT);
  pinMode(CHAN_DC, OUTPUT);
  pinMode(CHAN_RST, OUTPUT);
  digitalWrite(CHAN_CS, HIGH);
  SPI.begin(CHAN_SCK, -1, CHAN_MOSI, -1);
  khoiDongTft();
  toMau(0, 0, 240, 320, NEN);
  toMau(0, 0, 240, 30, XANH_DAM);
  veChu(8, 8, "DIEN AP GPIO34", 2, TRANG, XANH_DAM);
  veKhung();
  Serial.println("TFT san sang");
}

void loop() {
  static uint32_t lanDo = 0;
  if (millis() - lanDo < 100) return;
  lanDo = millis();

  uint32_t tong = 0;
  for (int i = 0; i < 16; i++) tong += analogReadMilliVolts(CHAN_DO);
  int mv = tong / 16;
  if (soMau < W) {
    duLieu[soMau++] = mv;
  } else {
    memmove(duLieu, duLieu + 1, (W - 1) * sizeof(int));
    duLieu[W - 1] = mv;
  }
  int nho = duLieu[0], lon = duLieu[0];
  for (int i = 1; i < soMau; i++) {
    nho = min(nho, duLieu[i]);
    lon = max(lon, duLieu[i]);
  }

  char chuoi[32];
  snprintf(chuoi, sizeof(chuoi), "%.2f V", mv / 1000.0f);
  veChu(10, 48, chuoi, 5, VANG, NEN);
  snprintf(chuoi, sizeof(chuoi), "MIN %.2f MAX %.2f", nho / 1000.0f, lon / 1000.0f);
  veChu(10, 104, chuoi, 2, XAM, NEN);
  veDoThi();
}
  • batDau(), ketThuc() mở và đóng một giao dịch SPI (40 MHz, chế độ 0) và kéo chân CS; guiLenh() đặt DC = 0 cho byte lệnh rồi trả DC về 1 cho dữ liệu.
  • SPI.writePixels() gửi một mảng điểm ảnh 16 bit, byte cao trước, đúng thứ tự ILI9341 cần.
  • toMau() chọn vùng rồi gửi cùng một màu theo từng hàng; veDoThi() dựng từng cột 170 điểm (nền, đường lưới, đoạn nối hai điểm liền nhau) rồi gửi một lần, không xóa cả vùng nên không nhấp nháy.
  • Dữ liệu đồ thị dồn về bên phải: điểm mới nhất luôn ở cột cuối, đồ thị như đang trôi sang trái.

6. Kết quả mong đợi

Màn hình TFT đỏ hiển thị thanh tiêu đề DIEN AP GPIO34, số lớn 1.65 V màu vàng, dòng MIN 0.43 MAX 2.90, đồ thị đường màu xanh lá đi lên, giữ ngang, rồi xuống và giữ ở khoảng 1,65 V, có nhãn 1V 2V 3V
Điện áp biến trở ở GPIO34: số lớn, nhỏ nhất, lớn nhất và đồ thị 20 giây gần nhất (mô phỏng).

Xoay biến trở, số lớn đổi ngay và đồ thị vẽ lại đường đi của điện áp. Mỗi lần cập nhật gửi khoảng 45 000 điểm ảnh, tức 90 000 byte (số lớn, dòng nhỏ nhất – lớn nhất, 200 cột đồ thị); ở 40 MHz mất khoảng 20 ms, nên cập nhật 10 lần mỗi giây vẫn còn thời gian cho việc khác. ADC của ESP32 đo tới khoảng 3,1 V, nên đầu biến trở sát 3V3 sẽ dừng ở gần 3,1 V.

Khi cần vẽ nhiều kiểu chữ, hình ảnh, nút bấm cảm ứng, dùng thư viện như TFT_eSPI hoặc Adafruit_ILI9341: chúng làm đúng những việc ở trên, kèm nhiều hàm có sẵn. Hiểu các lệnh 0x2A, 0x2B, 0x2C giúp chọn đúng cấu hình cho thư viện và đoán được lỗi khi màn hình không lên.

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

Hiện tượngNguyên nhân thường gặpCách xử lý
Màn hình trắng, chỉ có đèn nềnSai chân CS, DC, RESET; nhầm MOSI với MISOĐối chiếu bảng nối dây; MOSI còn có tên SDI
Hình có sọc, điểm sai rải rácDây dài, tốc độ SPI quá caoDây dưới 15 cm; hạ TOC_DO_SPI xuống 20 MHz
Màu đỏ thành xanh dươngTấm nền xếp màu khácĐổi 0x48 thành 0x40 ở lệnh 0x36
Hình bị lật ngang hoặc chữ ngượcHướng quét khácThử 0x88, 0x28, 0xE8 cho lệnh 0x36
Màu đảo (đen thành trắng), hình lệchMàn hình dùng chip ST7789 hay ST7735, không phải ILI9341Đọc lại tên chip; ST7789 thường cần thêm lệnh 0x21 và có kích thước khác
Số đo nhảy liên tục vài chục mVNhiễu ADC, dây biến trở dàiTăng số lần lấy trung bình; tụ 100 nF từ GPIO34 xuống GND

8. Xem thêm


Tin tức liên quan

GPS NEO-6M với ESP32: đọc câu NMEA, lấy tọa độ, giờ Việt Nam, tốc độ không cần thư viện
GPS NEO-6M với ESP32: đọc câu NMEA, lấy tọa độ, giờ Việt Nam, tốc độ không cần thư viện

GPS làm việc thế nào, module NEO-6M, nối với ESP32 qua UART2, đọc câu NMEA RMC và GGA, kiểm checksum, đổi tọa độ, giờ Việt Nam, tốc độ, tính khoảng cách, không cần TinyGPS.

Làm gateway LoRa sang MQTT bằng ESP32: node cảm biến chạy pin gửi dữ liệu về broker
Làm gateway LoRa sang MQTT bằng ESP32: node cảm biến chạy pin gửi dữ liệu về broker

Hướng dẫn làm hệ thống LoRa hoàn chỉnh với ESP32 và SX1278: node chạy pin đo điện áp, gửi gói rồi ngủ sâu; gateway nhận gói LoRa và publish dữ liệu JSON lên broker MQTT qua Wi-Fi.

ESP32 điều khiển động cơ không chổi than qua ESC: xung 1000–2000 µs, hiệu chuẩn dải ga, thử an toàn
ESP32 điều khiển động cơ không chổi than qua ESC: xung 1000–2000 µs, hiệu chuẩn dải ga, thử an toàn

Dùng ESP32 phát xung ga 1000–2000 µs bằng LEDC để điều khiển ESC và động cơ không chổi than: nối dây với pin LiPo, hiệu chuẩn dải ga, tăng ga từ từ, khóa động cơ và an toàn khi thử.

Bàn phím 4x4 và còi với ESP32: quét ma trận, chống dội, khóa mật khẩu và âm báo không cần thư viện
Bàn phím 4x4 và còi với ESP32: quét ma trận, chống dội, khóa mật khẩu và âm báo không cần thư viện

Cách quét bàn phím ma trận 4x4, chống dội phím, nối còi thụ động qua transistor, chương trình khóa mật khẩu có đổi mật khẩu, khóa tạm khi nhập sai và âm báo không dùng delay.

Xe robot ESP32 điều khiển bằng điện thoại qua Wi-Fi: không cần router, không cần cài ứng dụng
Xe robot ESP32 điều khiển bằng điện thoại qua Wi-Fi: không cần router, không cần cài ứng dụng

Làm xe robot hai bánh với ESP32 và L298N, điều khiển bằng trình duyệt điện thoại qua Wi-Fi do ESP32 phát ra: bố trí, nối dây, code web, tự dừng khi mất sóng, lái vi sai, STEM.

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.

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.

Cài ESP32 vào Arduino IDE 2: thêm bo mạch, chọn ESP32 Dev Module và nạp chương trình đầu tiên
Cài ESP32 vào Arduino IDE 2: thêm bo mạch, chọn ESP32 Dev Module và nạp chương trình đầu tiên

Cài gói esp32 của Espressif vào Arduino IDE 2, chọn bo DOIT ESP32 DEVKIT V1 hoặc ESP32 Dev Module, chọn cổng COM, nạp chương trình nháy LED, in thông tin chip và sửa lỗi lần nạp đầu.


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