Chuông cửa ESP32-CAM gửi ảnh qua Telegram: bấm chuông là chụp, đèn flash ban đêm, lưu thẻ nhớ

Làm chuông cửa bằng ESP32-CAM: bấm nút thì chụp ảnh gửi về điện thoại qua Telegram kèm giờ, tự bật đèn flash ban đêm, lưu ảnh vào thẻ nhớ, nhắn /anh để xem cửa bất cứ lúc nào.

Chuông cửa có camera giúp biết ai đang đứng trước cửa ngay cả khi không ở nhà. Bài này dùng mạch ESP32-CAM (AI Thinker) làm chuông cửa: khách bấm nút thì ESP32-CAM chụp ảnh, gửi về điện thoại qua Telegram kèm giờ bấm chuông, đồng thời lưu bản sao vào thẻ nhớ. Buổi tối đèn flash trên bo tự bật khi chụp. Không cần máy chủ riêng hay dịch vụ trả phí: ESP32-CAM gọi thẳng Bot API của Telegram.

1. Chuông cửa hoạt động thế nào

Luồng hoạt động: bấm chuông ở GPIO13, ngắt GPIO đặt cờ, chụp ảnh 800x600 JPEG, lưu thẻ nhớ, gửi sendPhoto qua HTTPS, máy chủ Telegram chuyển tin tới điện thoại; từ 18 giờ tới 6 giờ bật đèn flash
Từ lúc bấm chuông tới khi ảnh hiện trên điện thoại.
  • Nút chuông nối GPIO13 xuống GND. Chương trình dùng ngắt để không bỏ sót lần bấm trong lúc ESP32-CAM đang bận hỏi tin nhắn qua mạng.
  • Chụp ảnh 800 × 600 điểm, nén JPEG ngay trong camera OV2640, mỗi ảnh khoảng vài chục KB.
  • Đèn flash: giờ lấy từ Internet (NTP) theo múi giờ Việt Nam; từ 18 giờ tới 6 giờ sáng thì bật đèn lúc chụp.
  • Gửi ảnh bằng phương thức sendPhoto của Telegram; bấm liên tục thì 10 giây mới gửi một ảnh.
  • Lệnh từ điện thoại: nhắn /anh để xem cửa bất cứ lúc nào, /anhden để chụp có đèn, /trangthai để xem tình trạng.

2. Chuẩn bị

  • Mạch ESP32-CAM AI Thinker (có PSRAM, camera OV2640) và đế nạp ESP32-CAM-MB, hoặc mạch chuyển USB-TTL
  • Dây USB để nạp chương trình
  • Nút nhấn (nút chuông cửa loại thường đóng mở bằng tiếp điểm), tụ 100 nF, điện trở 10 kΩ
  • Nguồn 5 V từ 2 A. Nếu đã có adapter 12 V ở cổng thì hạ xuống 5 V bằng mạch hạ áp LM2596.
  • Thẻ nhớ micro SD (không bắt buộc), định dạng FAT32
  • Breadboard và dây cắm để thử trên bàn
  • Thư viện ArduinoJson (bản 7) cài qua Library Manager; camera, thẻ nhớ, HTTPS có sẵn trong gói esp32.

3. Nối dây

Mạch ESP32-CAM AI Thinker: chân 5V và GND nối nguồn 5 V từ 2 A; nút chuông một chân nối IO13, chân kia nối GND; thẻ nhớ chạy chế độ 1 bit dùng IO14, IO15, IO2; nạp chương trình qua đế ESP32-CAM-MB hoặc mạch USB-TTL với IO0 nối GND
Chỉ cần nguồn 5 V và một nút nhấn ở IO13.
  • ESP32-CAM có ít chân trống. Thẻ nhớ chạy chế độ 1 bit (chỉ dùng IO14, IO15, IO2) nên IO4, IO12, IO13 được để trống. IO4 nối sẵn đèn flash; IO12 là chân cấu hình điện áp flash lúc khởi động, nối nút nhấn có điện trở kéo lên vào đây có thể làm mạch không khởi động. Vì vậy nút chuông dùng IO13.
  • Nguồn là chỗ hay hỏng nhất: lúc phát Wi-Fi và bật đèn flash, ESP32-CAM kéo dòng tăng vọt. Nguồn yếu hoặc dây mảnh dài sẽ làm mạch tự khởi động lại (Serial báo "Brownout detector was triggered"). Dùng nguồn 5 V từ 2 A, dây ngắn, thêm tụ hóa 470–1000 µF ở chân 5V nếu cần.
  • Dây nút chuông kéo ra ngoài cửa dài vài mét dễ bắt nhiễu: thêm tụ 100 nF song song với nút và điện trở 10 kΩ từ IO13 lên 3V3. Không đưa 5 V vào IO13.

4. Tạo bot Telegram

  1. Trong Telegram, nhắn cho tài khoản BotFather lệnh /newbot, đặt tên cho bot. BotFather trả về token dạng 123456789:ABC…: chép vào BOT_TOKEN.
  2. Nạp chương trình lần đầu với CHAT_ID = 0, mở Serial Monitor.
  3. Mở cuộc trò chuyện với bot vừa tạo, nhắn /start. Serial in ra Tin tu chat id … (bo qua): chép số đó vào CHAT_ID rồi nạp lại.

Token là chìa khóa điều khiển bot: ai có token đều gửi tin thay bot được. Không đăng token lên mạng, không chụp màn hình có token. Chương trình chỉ làm theo tin nhắn từ đúng CHAT_ID, nên người lạ tìm thấy bot cũng không ra lệnh chụp ảnh được. Muốn cả nhà cùng nhận ảnh, tạo một nhóm Telegram, thêm bot vào nhóm và dùng chat id của nhóm (số âm).

5. Chương trình

Trong Arduino IDE chọn bo AI Thinker ESP32-CAM. Khi nạp bằng mạch USB-TTL thì nối IO0 xuống GND, bấm nút reset, nạp xong tháo IO0 ra rồi reset lại; đế ESP32-CAM-MB làm việc này tự động.

// PYWORLD.VN - Chuông cửa ESP32-CAM: bấm chuông thì chụp ảnh, gửi qua Telegram kèm giờ, lưu bản sao vào thẻ nhớ
// PYWORLD.VN - Bo AI Thinker ESP32-CAM; nút chuông nối GPIO13 xuống GND; từ 18 giờ tới 6 giờ bật đèn flash GPIO4 khi chụp
// PYWORLD.VN - Lệnh Telegram: /anh chụp ngay, /anhden chụp có đèn, /trangthai; đọc tin nhắn bằng ArduinoJson 7

#include "esp_camera.h"
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>
#include <ArduinoJson.h>
#include "FS.h"
#include "SD_MMC.h"
#include <time.h>

const char *TEN_WIFI = "TEN_WIFI";
const char *MAT_KHAU_WIFI = "MAT_KHAU_WIFI";
const char *BOT_TOKEN = "123456789:MA_TOKEN_CUA_BOT";  // lấy từ BotFather
const long long CHAT_ID = 0;   // để 0 lúc đầu: Serial in chat id của người nhắn, chép vào đây rồi nạp lại

const int CHAN_NUT = 13;                   // nút chuông nối xuống GND
const int CHAN_DEN_FLASH = 4;
const int CHAN_LED_DO = 33;                // LED đỏ nhỏ trên bo, sáng khi mức thấp
const uint32_t CHO_GIUA_HAI_LAN_MS = 10000;  // bấm liên tục thì 10 giây mới gửi một ảnh
const uint32_t CHU_KY_HOI_MS = 3000;
const int GIO_BAT_DEN = 18, GIO_TAT_DEN = 6;

// Chân camera OV2640 trên mạch AI Thinker ESP32-CAM
#define CHAN_PWDN 32
#define CHAN_RESET -1
#define CHAN_XCLK 0
#define CHAN_SIOD 26
#define CHAN_SIOC 27
#define CHAN_Y9 35
#define CHAN_Y8 34
#define CHAN_Y7 39
#define CHAN_Y6 36
#define CHAN_Y5 21
#define CHAN_Y4 19
#define CHAN_Y3 18
#define CHAN_Y2 5
#define CHAN_VSYNC 25
#define CHAN_HREF 23
#define CHAN_PCLK 22

WiFiClientSecure tls;
bool coTheNho = false, daBaoKhoiDong = false;
uint32_t soAnh = 0, lanHoi = 0, lanBao = 0, lanChuong = 0;
int soLanChuong = 0;
long long updateTiep = 0;
volatile bool coBam = false;
volatile uint32_t lucBam = 0;

bool khoiDongCamera() {
  camera_config_t c = {};
  c.pin_pwdn = CHAN_PWDN;
  c.pin_reset = CHAN_RESET;
  c.pin_xclk = CHAN_XCLK;
  c.pin_sccb_sda = CHAN_SIOD;
  c.pin_sccb_scl = CHAN_SIOC;
  c.pin_d7 = CHAN_Y9;
  c.pin_d6 = CHAN_Y8;
  c.pin_d5 = CHAN_Y7;
  c.pin_d4 = CHAN_Y6;
  c.pin_d3 = CHAN_Y5;
  c.pin_d2 = CHAN_Y4;
  c.pin_d1 = CHAN_Y3;
  c.pin_d0 = CHAN_Y2;
  c.pin_vsync = CHAN_VSYNC;
  c.pin_href = CHAN_HREF;
  c.pin_pclk = CHAN_PCLK;
  c.xclk_freq_hz = 20000000;
  c.ledc_timer = LEDC_TIMER_0;
  c.ledc_channel = LEDC_CHANNEL_0;
  c.pixel_format = PIXFORMAT_JPEG;
  c.frame_size = FRAMESIZE_SVGA;            // 800 x 600: đủ nhìn mặt người, ảnh khoảng vài chục KB
  c.jpeg_quality = 12;
  c.fb_count = psramFound() ? 2 : 1;
  c.fb_location = psramFound() ? CAMERA_FB_IN_PSRAM : CAMERA_FB_IN_DRAM;
  c.grab_mode = CAMERA_GRAB_LATEST;
  esp_err_t loi = esp_camera_init(&c);
  if (loi != ESP_OK) {
    Serial.printf("Loi khoi dong camera 0x%x: kiem tra cap camera, nguon 5V\n", loi);
    return false;
  }
  return true;
}

uint32_t soAnhLonNhat() {
  uint32_t lonNhat = 0;
  File thuMuc = SD_MMC.open("/");
  for (File f = thuMuc.openNextFile(); f; f = thuMuc.openNextFile()) {
    unsigned so;
    if (sscanf(f.name(), "chuong_%u.jpg", &so) == 1 && so > lonNhat) lonNhat = so;
    f.close();
  }
  return lonNhat;
}

// Ngắt nút chuông: chỉ đặt cờ, bỏ qua các lần dội trong 200 ms.
void IRAM_ATTR ngatNut() {
  uint32_t bay = millis();
  if (bay - lucBam > 200) {
    lucBam = bay;
    coBam = true;
  }
}

bool laBanDem(const struct tm &t) {
  return t.tm_hour >= GIO_BAT_DEN || t.tm_hour < GIO_TAT_DEN;
}

camera_fb_t *chupAnh(bool den) {
  if (den) {
    digitalWrite(CHAN_DEN_FLASH, HIGH);
    delay(150);                              // chờ cảm biến chỉnh lại độ sáng
  }
  camera_fb_t *cu = esp_camera_fb_get();     // bỏ khung chụp trước lúc bấm
  if (cu) esp_camera_fb_return(cu);
  camera_fb_t *khung = esp_camera_fb_get();
  digitalWrite(CHAN_DEN_FLASH, LOW);
  return khung;
}

void luuThe(camera_fb_t *khung) {
  if (!coTheNho) return;
  char ten[24];
  snprintf(ten, sizeof(ten), "/chuong_%04lu.jpg", (unsigned long)++soAnh);
  File f = SD_MMC.open(ten, FILE_WRITE);
  bool duoc = f && f.write(khung->buf, khung->len) == khung->len;
  if (f) f.close();
  Serial.printf(duoc ? "Da luu %s\n" : "Loi ghi %s\n", ten);
}

bool guiTin(const String &noiDung) {
  if (WiFi.status() != WL_CONNECTED || CHAT_ID == 0) return false;
  HTTPClient http;
  http.begin(tls, String("https://api.telegram.org/bot") + BOT_TOKEN + "/sendMessage");
  http.addHeader("Content-Type", "application/json");
  JsonDocument doc;
  doc["chat_id"] = CHAT_ID;
  doc["text"] = noiDung;
  String than;
  serializeJson(doc, than);
  int ma = http.POST(than);
  http.end();
  Serial.printf("Gui tin Telegram: HTTP %d\n", ma);
  return ma == 200;
}

// Gửi ảnh bằng sendPhoto: thân yêu cầu dạng multipart/form-data gồm chat_id, caption và file ảnh.
bool guiAnh(camera_fb_t *khung, const String &chuThich) {
  if (WiFi.status() != WL_CONNECTED || CHAT_ID == 0) return false;
  WiFiClientSecure k;
  k.setInsecure();
  k.setTimeout(20000);
  if (!k.connect("api.telegram.org", 443)) {
    Serial.println("Khong ket noi duoc api.telegram.org");
    return false;
  }
  const char *BIEN = "PyworldBienAnh";
  char chat[24];
  snprintf(chat, sizeof(chat), "%lld", CHAT_ID);
  String dau = String("--") + BIEN + "\r\nContent-Disposition: form-data; name=\"chat_id\"\r\n\r\n" + chat + "\r\n"
               + "--" + BIEN + "\r\nContent-Disposition: form-data; name=\"caption\"\r\n\r\n" + chuThich + "\r\n"
               + "--" + BIEN + "\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"chuong_cua.jpg\"\r\n"
               + "Content-Type: image/jpeg\r\n\r\n";
  String cuoi = String("\r\n--") + BIEN + "--\r\n";
  size_t tong = dau.length() + khung->len + cuoi.length();
  k.printf("POST /bot%s/sendPhoto HTTP/1.1\r\nHost: api.telegram.org\r\n", BOT_TOKEN);
  k.printf("Content-Type: multipart/form-data; boundary=%s\r\nContent-Length: %u\r\nConnection: close\r\n\r\n", BIEN, (unsigned)tong);
  k.print(dau);
  for (size_t i = 0; i < khung->len; i += 2048) k.write(khung->buf + i, min((size_t)2048, khung->len - i));
  k.print(cuoi);
  String dong = k.readStringUntil('\n');     // ví dụ "HTTP/1.1 200 OK"
  k.stop();
  int ma = dong.length() > 12 ? dong.substring(9, 12).toInt() : -1;
  Serial.printf("Gui anh %u byte: HTTP %d\n", (unsigned)khung->len, ma);
  return ma == 200;
}

void chupVaGui(bool den, const String &chuThich) {
  digitalWrite(CHAN_LED_DO, LOW);
  camera_fb_t *khung = chupAnh(den);
  if (!khung) {
    Serial.println("Chup anh loi");
    guiTin(chuThich + " (chụp ảnh lỗi)");
  } else {
    luuThe(khung);
    if (!guiAnh(khung, chuThich)) guiTin(chuThich + " (gửi ảnh lỗi)");
    esp_camera_fb_return(khung);
  }
  digitalWrite(CHAN_LED_DO, HIGH);
}

void baoChuong() {
  struct tm t;
  bool coGio = getLocalTime(&t, 0);
  bool den = coGio && laBanDem(t);
  char gio[24] = "";
  if (coGio) strftime(gio, sizeof(gio), "%H:%M:%S %d/%m/%Y", &t);
  soLanChuong++;
  Serial.printf("Co nguoi bam chuong (lan %d) %s%s\n", soLanChuong, gio, den ? ", bat den" : "");
  chupVaGui(den, String("Có người bấm chuông") + (coGio ? String(" lúc ") + gio : String("")));
}

void xuLyLenh(String lenh) {
  lenh.trim();
  lenh.toLowerCase();
  Serial.printf("Lenh: %s\n", lenh.c_str());
  if (lenh == "/anh" || lenh == "/anhden") {
    chupVaGui(lenh == "/anhden", "Ảnh chụp theo yêu cầu");
  } else if (lenh == "/trangthai") {
    String s = "Chuông cửa đang chạy " + String(millis() / 60000) + " phút\n";
    s += "Số lần bấm chuông từ lúc chạy: " + String(soLanChuong) + "\n";
    s += "Thẻ nhớ: " + String(coTheNho ? "có, đã lưu " + String(soAnh) + " ảnh" : "không có") + "\n";
    s += "Wi-Fi: " + String(WiFi.RSSI()) + " dBm";
    guiTin(s);
  } else {
    guiTin("Lệnh dùng được:\n/anh: chụp ảnh ngay\n/anhden: chụp có bật đèn\n/trangthai: xem trạng thái");
  }
}

void docTinMoi() {
  HTTPClient http;
  char so[24];
  snprintf(so, sizeof(so), "%lld", updateTiep);
  http.begin(tls, String("https://api.telegram.org/bot") + BOT_TOKEN + "/getUpdates?timeout=0&limit=5&offset=" + so);
  int ma = http.GET();
  if (ma != 200) {
    Serial.printf("getUpdates loi: %d\n", ma);
    http.end();
    return;
  }
  String than = http.getString();
  http.end();
  JsonDocument doc;
  if (deserializeJson(doc, than)) return;
  for (JsonObject u : doc["result"].as<JsonArray>()) {
    updateTiep = u["update_id"].as<long long>() + 1;
    long long chat = u["message"]["chat"]["id"].as<long long>();
    const char *chu = u["message"]["text"] | "";
    if (CHAT_ID == 0 || chat != CHAT_ID) {
      Serial.printf("Tin tu chat id %lld (bo qua): %s\n", chat, chu);
      continue;
    }
    xuLyLenh(String(chu));
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(CHAN_DEN_FLASH, OUTPUT);
  digitalWrite(CHAN_DEN_FLASH, LOW);
  pinMode(CHAN_LED_DO, OUTPUT);
  digitalWrite(CHAN_LED_DO, HIGH);
  pinMode(CHAN_NUT, INPUT_PULLUP);
  if (!khoiDongCamera()) return;
  coTheNho = SD_MMC.begin("/sdcard", true);  // chế độ 1 bit: GPIO4 (flash) và GPIO13 (nút) không dính tới thẻ
  if (coTheNho) soAnh = soAnhLonNhat();
  Serial.println(coTheNho ? "Da gan the nho" : "Khong thay the nho (van gui anh duoc)");
  attachInterrupt(digitalPinToInterrupt(CHAN_NUT), ngatNut, FALLING);
  tls.setInsecure();                         // có mã hóa nhưng không kiểm tra chứng chỉ (như bài Telegram)
  WiFi.mode(WIFI_STA);
  WiFi.begin(TEN_WIFI, MAT_KHAU_WIFI);
  configTzTime("ICT-7", "pool.ntp.org", "time.google.com");   // giờ Việt Nam UTC+7
  Serial.println("Dang ket noi Wi-Fi...");
}

void loop() {
  if (coBam) {
    coBam = false;
    if (lanChuong && millis() - lanChuong < CHO_GIUA_HAI_LAN_MS) {
      Serial.println("Bam lai trong 10 giay: bo qua");
    } else {
      lanChuong = millis();
      baoChuong();
    }
  }
  if (WiFi.status() != WL_CONNECTED) return;
  if (!daBaoKhoiDong && (lanBao == 0 || millis() - lanBao > 10000)) {
    lanBao = millis();
    Serial.printf("Wi-Fi OK, IP %s\n", WiFi.localIP().toString().c_str());
    if (CHAT_ID == 0) Serial.println("Chua co CHAT_ID: nhan /start cho bot roi xem chat id in ra o day");
    daBaoKhoiDong = guiTin("Chuông cửa đã sẵn sàng.") || CHAT_ID == 0;
  }
  if (millis() - lanHoi >= CHU_KY_HOI_MS) {
    lanHoi = millis();
    docTinMoi();
  }
}

Giải thích

  • ngatNut() chạy ngay khi IO13 xuống thấp, chỉ ghi lại thời điểm và đặt cờ coBam; các lần dội trong 200 ms bị bỏ qua. Mọi việc nặng (chụp, gửi) làm trong loop(), không làm trong hàm ngắt.
  • chupAnh() bật đèn trước 150 ms để cảm biến kịp chỉnh độ sáng, bỏ một khung cũ còn trong bộ đệm, rồi lấy khung mới.
  • guiAnh() mở kết nối HTTPS tới api.telegram.org và tự ghép yêu cầu multipart/form-data: phần chat_id, phần caption và phần file ảnh. Ảnh gửi từng đoạn 2048 byte thẳng từ bộ đệm camera.
  • docTinMoi() hỏi tin nhắn mới mỗi 3 giây bằng getUpdates, giống bài Telegram cảnh báo trong loạt ESP32 với điện thoại; offset tăng dần để mỗi tin chỉ xử lý một lần.
  • tls.setInsecure(): kết nối vẫn mã hóa nhưng không kiểm tra chứng chỉ máy chủ. Cách này đơn giản và đủ cho chuông cửa; muốn chặt hơn thì nạp chứng chỉ gốc của máy chủ Telegram bằng setCACert().
Cấu trúc yêu cầu sendPhoto: dòng POST tới bot token, Host api.telegram.org, Content-Type multipart/form-data có boundary, Content-Length 48554; ba phần chat_id, caption và photo kèm ảnh JPEG 48213 byte, kết thúc bằng boundary có hai gạch
Thân yêu cầu gửi ảnh và cách tính Content-Length (số liệu ví dụ).

6. Kết quả

Serial Monitor: lần đầu in chat id của người nhắn /start; sau khi điền CHAT_ID, gửi tin sẵn sàng, có người bấm chuông lúc 19 giờ 42 bật đèn, lưu chuong_0001.jpg, gửi ảnh HTTP 200, bấm lại trong 10 giây bị bỏ qua, nhắn /anh thì chụp và gửi ảnh thứ hai
Serial Monitor khi lấy chat id và khi có người bấm chuông.
Màn hình điện thoại: tin chuông cửa đã sẵn sàng, ảnh chụp ở cửa kèm dòng có người bấm chuông lúc 19:42:05, người dùng nhắn /trangthai, bot trả lời số phút đã chạy, số lần bấm chuông, thẻ nhớ, Wi-Fi
Tin nhắn trên điện thoại (giao diện vẽ kiểu chung).

Từ lúc bấm chuông tới khi điện thoại báo tin thường mất vài giây, tùy tốc độ mạng và độ lớn ảnh. Muốn nhanh hơn thì giảm cỡ ảnh xuống FRAMESIZE_VGA (640 × 480) hoặc tăng jpeg_quality (số lớn hơn là nén mạnh hơn).

7. Lắp đặt

  • Đặt ESP32-CAM trong hộp kín có ô kính trước ống kính, che mưa nắng; hơi nước đọng trên kính làm ảnh mờ.
  • Kiểm tra sóng Wi-Fi ở cổng bằng lệnh /trangthai: dưới khoảng −75 dBm thì gửi ảnh chậm, hay lỗi. Bản ESP32-CAM có đầu nối anten ngoài giúp ích ở chỗ xa bộ phát.
  • Hướng camera vào khu vực trước cửa nhà mình, tránh quay sang nhà hàng xóm hay lòng đường.
  • Chuông cửa này báo qua điện thoại; muốn có tiếng chuông trong nhà thì giữ chuông điện cũ song song, hoặc làm thêm một mạch ESP32 trong nhà nhận tin và phát tiếng.

8. Lỗi hay gặp

Hiện tượngNguyên nhân thường gặpCách xử lý
"Loi khoi dong camera 0x105" hoặc 0x20001Cáp camera cắm lỏng, chọn sai bo, nguồn yếuMở khóa đầu nối, cắm lại cáp; chọn đúng AI Thinker ESP32-CAM
Mạch tự khởi động lại khi gửi ảnhSụt áp nguồn (brownout)Nguồn 5 V từ 2 A, dây ngắn, thêm tụ hóa ở chân 5V
Serial báo "Gui anh … HTTP 400"Sai CHAT_IDLấy lại chat id bằng cách nhắn /start
"HTTP 401" hoặc 404Sai tokenChép lại token từ BotFather, không thừa dấu cách
Bấm chuông không có phản ứngNút nối nhầm chân, dây dài bị nhiễuĐo IO13 xuống 0 V khi bấm; thêm tụ 100 nF và điện trở 10 kΩ
Ban đêm ảnh tối đenChưa lấy được giờ từ Internet nên không bật đènKiểm tra Wi-Fi; nhắn /anhden để chụp có đèn
Ảnh có sọc hoặc ám màuẢnh đầu tiên sau khi bật camera chưa cân bằng sángBình thường ở ảnh đầu; các ảnh sau sẽ ổn

9. Xem thêm


Tin tức liên quan

PZEM-004T với ESP32: đo điện áp, dòng, công suất, điện năng 220 V qua Modbus không cần thư viện
PZEM-004T với ESP32: đo điện áp, dòng, công suất, điện năng 220 V qua Modbus không cần thư viện

Cấu tạo PZEM-004T v3, đấu an toàn phía 220 V và vòng CT, nối TTL với ESP32 có cầu phân áp, giải nghĩa khung Modbus và chương trình đọc điện năng không cần thư viện.

Khóa cửa ESP32 mở bằng thẻ RFID hoặc mật khẩu: RC522, bàn phím 4x4, còi, khóa chốt điện từ 12 V
Khóa cửa ESP32 mở bằng thẻ RFID hoặc mật khẩu: RC522, bàn phím 4x4, còi, khóa chốt điện từ 12 V

Làm khóa cửa ESP32 mở bằng thẻ RFID RC522 hoặc mật khẩu bàn phím 4x4: nối dây, nguồn và diode cho khóa chốt 12 V, thêm xóa thẻ, đổi mật khẩu, tạm khóa khi sai 3 lần.

Mạch hạ áp LM2596 và MP1584: cách chọn, chỉnh điện áp và cấp nguồn ESP32
Mạch hạ áp LM2596 và MP1584: cách chọn, chỉnh điện áp và cấp nguồn ESP32

So sánh mạch hạ áp LM2596 và MP1584, cách chỉnh điện áp ra bằng đồng hồ vạn năng trước khi nối tải, cấp nguồn 5 V cho ESP32 DevKit V1 từ nguồn 12 V và xử lý lỗi thường gặp.

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.

Đế ra chân ESP32 30 chân: cấp nguồn DC 6,5–16 V, hàng chân G V S và jumper chọn 5 V hay 3,3 V
Đế ra chân ESP32 30 chân: cấp nguồn DC 6,5–16 V, hàng chân G V S và jumper chọn 5 V hay 3,3 V

Cách dùng đế ra chân ESP32 30 chân: cấu tạo, cắm ESP32 đúng chiều, cấp nguồn jack DC 6,5–16 V và tính nhiệt IC ổn áp, hàng G V S, jumper 5V/3.3V, ví dụ relay và nút nhấn.

Hướng dẫn đọc ADC trên ESP32 và đo điện áp pin 18650 bằng cầu phân áp
Hướng dẫn đọc ADC trên ESP32 và đo điện áp pin 18650 bằng cầu phân áp

Hướng dẫn đọc điện áp analog bằng ADC của ESP32 trên Arduino IDE, dùng analogReadMilliVolts, làm cầu phân áp đo pin Li-ion 18650 và ước lượng phần trăm pin còn lại.

ESP32 gửi cảnh báo Telegram, nhận lệnh bật tắt relay: tạo bot, lấy chat id, không cần thư viện bot
ESP32 gửi cảnh báo Telegram, nhận lệnh bật tắt relay: tạo bot, lấy chat id, không cần thư viện bot

Tạo bot Telegram, lấy token và chat id, cho ESP32 gửi cảnh báo cửa mở, báo có điện lại và nhận lệnh /bat, /tat relay bằng HTTPClient và ArduinoJson.

Từ breadboard đến thiết bị dùng thật: đưa ESP32 vào hộp, khoan lỗ jack Type-C, GX16, jack DC
Từ breadboard đến thiết bị dùng thật: đưa ESP32 vào hộp, khoan lỗ jack Type-C, GX16, jack DC

Đưa dự án ESP32 từ breadboard vào hộp: cấp nguồn USB 5 V hoặc adapter 12 V qua LM2596, bố trí linh kiện, khoan lỗ jack Type-C, jack DC, GX16 và chương trình hộp relay có nút nhấn.


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