VS1838B là IC nhận hồng ngoại tích hợp — bên trong không chỉ là photodiode mà còn có toàn bộ mạch demodulate, lọc nhiễu, và khuếch đại tự động. Kết nối với MCU chỉ cần 1 chân digital và vài dòng code. Bài này phân tích cách VS1838B lọc tín hiệu 38kHz ra khỏi nhiễu môi trường, giải thích NEC protocol, và code decode remote bất kỳ.
Nguyên Lý Hoạt Động
1. Tại Sao Dùng 38kHz — Tránh Nhiễu Môi Trường
Ánh sáng môi trường (đèn, nắng) cũng chứa IR nhưng không dao động ở 38kHz. Remote điều khiển từ xa điều chế (modulate) tín hiệu: LED IR bật tắt 38.000 lần/giây (38kHz).
Remote TV gửi:
Bit "1" = [ 38kHz burst 562µs ] + [ không phát 1687µs ]
Dạng sóng thực tế:
──┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌┐┌──────────────────
└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└┘└
←────── 38kHz carrier ──────────→←── không phát ──→
562µs 1687µs
Ánh sáng nền (đèn huỳnh quang, nắng):
DC IR không điều chế hoặc 50/100Hz — hoàn toàn khác 38kHz
→ Bộ lọc bandpass loại bỏ hoàn toàn
38kHz là chuẩn chung: Ngoài ra còn 36kHz (Sony), 40kHz, 56kHz — nhưng 38kHz là phổ biến nhất.
2. Bên Trong VS1838B — 4 Khối Chức Năng
IR Light (modulated 38kHz)
↓
┌──────────────────────┐
│ PIN Photodiode │ ← Thu IR, tạo dòng
└──────────┬───────────┘
↓
┌──────────────────────┐
│ Bandpass Filter │ ← Chỉ cho 38kHz qua, lọc nhiễu DC và tần số khác
│ (centered at 38kHz) │
└──────────┬───────────┘
↓
┌──────────────────────┐
│ AGC │ ← Automatic Gain Control: điều chỉnh khuếch đại
│ (Automatic Gain │ để nhận tín hiệu từ remote xa/gần đều OK
│ Control) │
└──────────┬───────────┘
↓
┌──────────────────────┐
│ Comparator + Output │ ← Xuất digital (active LOW)
└──────────┬───────────┘
↓
OUT pin (active LOW)
Ngõ ra active LOW (đảo):
- Remote gửi burst 38kHz → VS1838B phát hiện → OUT = LOW
- Remote không gửi → OUT = HIGH (nhờ pull-up nội bộ trong VS1838B)
Vì vậy, sóng dạng tại OUT pin là đảo ngược so với tín hiệu remote gốc:
Remote LED IR: ──┐ ┌──┐ ┌──────
└───┘ └──┘
VS1838B OUT: ──┘ └──┘ └────── ← Đảo ngược
┌───┐ ┌──┐
Library IRremote đã xử lý việc đảo ngược này tự động.
3. NEC Protocol — Chuẩn Phổ Biến Nhất
Phần lớn remote tivi, điều hòa, quạt Asia dùng NEC protocol.
Cấu trúc một lần gửi lệnh (frame):
[ 9ms AGC burst ] [ 4.5ms space ] [ 32 bits data ] [ final burst 562µs ]
32 bits = [ Address 8-bit ] [ ~Address 8-bit ] [ Command 8-bit ] [ ~Command 8-bit ]
└── Địa chỉ thiết bị ──┘ └── Lệnh cụ thể ─────────────────┘
└── Kiểm tra (inverted) ──────────────────────────────────────────────┘
~Address = bitwise NOT của Address (dùng để kiểm tra lỗi)
Mã hóa bit:
Bit "0": [ 562µs burst ] [ 562µs space ] = tổng 1.125ms
Bit "1": [ 562µs burst ] [ 1687µs space ] = tổng 2.25ms
MCU đo khoảng thời gian giữa các xung:
~562µs = bit 0
~1687µs = bit 1
Repeat code (giữ nút):
[ 9ms burst ] [ 2.25ms space ] [ 562µs burst ]
Cho biết nút đang được giữ nhưng không gửi lại địa chỉ/lệnh.
Giá trị kết quả decode (HEX):
Kết quả results.value từ IRremote library là 32-bit: 0xAABBCCDD
- AA = Address (hex)
- BB = ~Address
- CC = Command
- DD = ~Command
Ví dụ nút “1” trên remote điều hòa: 0x00FF30CF → Address=0x00, Command=0x30.
Thông Số Kỹ Thuật
| Thông số | Giá trị |
|---|---|
| Điện áp hoạt động | 2.7V – 5.5V |
| Dòng tiêu thụ (standby) | ~0.4mA |
| Tần số trung tâm | 38kHz |
| Dải tần chấp nhận | 36.7kHz – 39kHz |
| Bước sóng IR nhạy nhất | 940nm |
| Góc nhận | ±45° |
| Khoảng cách tối đa | ~18m (trong điều kiện tốt) |
| Ngõ ra | Digital, active LOW |
| Thời gian đáp ứng | <50µs |
Sơ Đồ Chân (Pinout)
[Mắt nhận IR]
┌─────────────────┐
│ VS1838B │
└─────────────────┘
OUT GND VCC
1 2 3
Nhìn từ phía trước (mặt lồi hướng về bạn):
Chân 1 (trái) = OUT
Chân 2 (giữa) = GND
Chân 3 (phải) = VCC
QUAN TRỌNG: Thứ tự chân của VS1838B và TSOP1838 có thể khác nhau giữa các nhà sản xuất. Luôn kiểm tra datasheet hoặc nhìn dấu in trên linh kiện. Cắm sai VCC và OUT → có thể gây khói hoặc hỏng.
Module trong kit 37 module (KY-022):
┌────────────────────────────────┐
│ [VS1838B] module board │
└────────────────────────────────┘
GND VCC S (Signal/OUT)
Module đã có điện trở bảo vệ và tụ lọc nguồn — dễ dùng hơn dùng linh kiện rời.
So Sánh Các IR Protocol Phổ Biến
| Protocol | Tần số | Cấu trúc | Nhà sản xuất |
|---|---|---|---|
| NEC | 38kHz | 9ms+4.5ms start, 32-bit | Generic, nhiều hãng Asia |
| Sony SIRC | 40kHz | 2.4ms+0.6ms start, 12/15/20-bit | Sony |
| Philips RC5 | 36kHz | Biphase Manchester, 14-bit | Philips |
| Philips RC6 | 36kHz | Mode bits, 20-32-bit | Philips |
| Samsung | 38kHz | 4.5ms+4.5ms start, 32-bit | Samsung |
IRremote library hỗ trợ tất cả các protocol trên tự động.
Kết Nối Phần Cứng
VS1838B Rời với ESP32 DevKit V1
ESP32 DevKit V1 VS1838B (3-pin rời)
───────────────────── ────────────────────
3V3 ─────────────────→ VCC (Chân 3 — phải)
GND ─────────────────→ GND (Chân 2 — giữa)
GPIO4 (Input) ←────────── OUT (Chân 1 — trái) [active LOW]
Thêm tụ lọc 100nF giữa VCC và GND gần VS1838B để tránh nhiễu nguồn (quan trọng với linh kiện rời, không cần với module).
Module KY-022 với Arduino Uno
Arduino Uno KY-022 Module
───────────────────── ─────────────────
5V ─────────────────→ VCC (hoặc middle pin)
GND ─────────────────→ GND
Pin 11 (Input) ←───────── S (Signal/OUT)
Pin 11 là pin mặc định của IRremote library trên Arduino Uno.
Code Arduino IDE
Cài Library
IRremote của Arduino-IRremote (tác giả: shirriff, z3t0, ArminJo) — tìm trong Library Manager.
Lưu ý phiên bản: IRremote v3+ thay đổi API so với v2. Code dưới đây dùng v3+.
Code Đọc Remote Bất Kỳ — Arduino Uno
/*
* IR Receiver VS1838B — Đọc và hiển thị mã HEX từ remote bất kỳ
* Board: Arduino Uno
* Library: IRremote (v3+) — tác giả Arduino-IRremote
* Kết nối: VCC→5V, GND→GND, S→Pin11
*
* Dùng code này để "học" mã HEX của từng nút remote
* Sau đó ghi lại và dùng trong code điều khiển thực tế
*/
#include <IRremote.hpp> // IRremote v3+ dùng .hpp không phải .h
const int IR_RECEIVE_PIN = 11;
void setup() {
Serial.begin(9600);
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
// ENABLE_LED_FEEDBACK: LED onboard Arduino nhấp nháy khi nhận IR
Serial.println("=== IR Receiver - Học Mã Remote ===");
Serial.println("Nhấn từng nút remote rồi xem mã HEX bên dưới:");
}
void loop() {
if (IrReceiver.decode()) {
// Nhận được tín hiệu hợp lệ
Serial.print("Protocol: ");
Serial.println(IrReceiver.getProtocolString());
Serial.print("Mã HEX: 0x");
Serial.println(IrReceiver.decodedIRData.decodedRawData, HEX);
Serial.print("Address: 0x");
Serial.print(IrReceiver.decodedIRData.address, HEX);
Serial.print(" | Command: 0x");
Serial.println(IrReceiver.decodedIRData.command, HEX);
// Kiểm tra nút đang được giữ
if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT) {
Serial.println("(Repeat — đang giữ nút)");
}
Serial.println("---");
// Quan trọng: phải gọi resume() để nhận tín hiệu tiếp theo
IrReceiver.resume();
}
}
Code Điều Khiển LED Theo Nút Remote — Arduino Uno
/*
* IR Receiver — Điều khiển LED bằng remote
* Board: Arduino Uno
* Library: IRremote (v3+)
* Kết nối: VCC→5V, GND→GND, S→Pin11; LED→Pin13
*
* BƯỚC 1: Dùng code "Học Mã Remote" ở trên để tìm HEX của các nút cần dùng
* BƯỚC 2: Điền vào MAP bên dưới
*/
#include <IRremote.hpp>
const int IR_RECEIVE_PIN = 11;
const int LED_PIN = 13; // LED onboard
// Mã HEX của từng nút — thay bằng mã thực của remote bạn
// Đây là ví dụ cho remote hồng ngoại mini phổ biến
#define BTN_ON 0xFFA25D // Nút "ON"
#define BTN_OFF 0xFF629D // Nút "OFF"
#define BTN_UP 0xFF22DD // Nút tăng
#define BTN_DOWN 0xFF02FD // Nút giảm
int brightness = 128; // Độ sáng hiện tại (0-255)
void setup() {
Serial.begin(9600);
IrReceiver.begin(IR_RECEIVE_PIN, ENABLE_LED_FEEDBACK);
pinMode(LED_PIN, OUTPUT);
analogWrite(LED_PIN, brightness);
Serial.println("=== Điều Khiển LED Bằng Remote ===");
}
void loop() {
if (IrReceiver.decode()) {
// Bỏ qua tín hiệu repeat (giữ nút) — chỉ xử lý nhấn mới
if (!(IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT)) {
uint32_t code = IrReceiver.decodedIRData.decodedRawData;
Serial.print("Nhận: 0x"); Serial.println(code, HEX);
switch (code) {
case BTN_ON:
analogWrite(LED_PIN, 255);
brightness = 255;
Serial.println("LED: Bật tối đa");
break;
case BTN_OFF:
analogWrite(LED_PIN, 0);
brightness = 0;
Serial.println("LED: Tắt");
break;
case BTN_UP:
brightness = min(255, brightness + 30);
analogWrite(LED_PIN, brightness);
Serial.print("LED: Tăng sáng → "); Serial.println(brightness);
break;
case BTN_DOWN:
brightness = max(0, brightness - 30);
analogWrite(LED_PIN, brightness);
Serial.print("LED: Giảm sáng → "); Serial.println(brightness);
break;
default:
Serial.print("Nút không rõ: 0x"); Serial.println(code, HEX);
break;
}
}
IrReceiver.resume(); // Tiếp tục nhận tín hiệu
}
}
Code ESP32 — Điều Khiển PWM LED Bằng Remote
/*
* IR Receiver VS1838B — ESP32, điều khiển LED PWM bằng remote
* Board: ESP32 DevKit V1
* Library: IRremote (v3+)
* Kết nối: VCC→3V3, GND→GND, OUT→GPIO4
* LED: GPIO5 (PWM)
*
* Thay BTN_* bằng mã HEX thực từ remote của bạn
*/
#include <IRremote.hpp>
const int IR_RECEIVE_PIN = 4;
const int LED_PIN = 5; // PWM GPIO
// Ví dụ mã NEC từ remote mini phổ biến — thay bằng mã thực của bạn
#define BTN_1 0xFF30CF // Nút 1
#define BTN_2 0xFF18E7 // Nút 2
#define BTN_3 0xFF7A85 // Nút 3
#define BTN_OK 0xFF38C7 // Nút OK
void setup() {
Serial.begin(115200);
IrReceiver.begin(IR_RECEIVE_PIN, DISABLE_LED_FEEDBACK);
// DISABLE_LED_FEEDBACK: ESP32 không có LED onboard tiêu chuẩn
// Cấu hình PWM LED
ledcAttachPin(LED_PIN, 0);
ledcSetup(0, 5000, 8); // Channel 0, 5kHz, 8-bit
Serial.println("=== IR Remote ESP32 ===");
}
void loop() {
if (IrReceiver.decode()) {
if (!(IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT)) {
uint32_t code = IrReceiver.decodedIRData.decodedRawData;
Serial.printf("Code: 0x%08X | Protocol: %s\n",
code, IrReceiver.getProtocolString());
switch (code) {
case BTN_1:
ledcWrite(0, 64); // 25% brightness
Serial.println("LED: 25%");
break;
case BTN_2:
ledcWrite(0, 128); // 50% brightness
Serial.println("LED: 50%");
break;
case BTN_3:
ledcWrite(0, 255); // 100% brightness
Serial.println("LED: 100%");
break;
case BTN_OK:
ledcWrite(0, 0); // Tắt
Serial.println("LED: Tắt");
break;
default:
Serial.printf("Không rõ: 0x%08X\n", code);
break;
}
}
IrReceiver.resume();
}
}
Kết Quả Mong Đợi (Khi Học Mã)
=== IR Receiver - Học Mã Remote ===
Nhấn từng nút remote rồi xem mã HEX bên dưới:
Protocol: NEC
Mã HEX: 0xFF30CF
Address: 0xFF | Command: 0x30
---
Protocol: NEC
Mã HEX: 0xFF18E7
Address: 0xFF | Command: 0x18
---
Protocol: NEC
Mã HEX: 0xFF30CF
(Repeat — đang giữ nút)
---
Ứng Dụng Thực Tế
| Ứng dụng | Chi tiết |
|---|---|
| Điều khiển đèn/quạt bằng remote TV cũ | Map nút remote → relay/dimmer |
| Robot điều khiển từ xa | Remote → di chuyển tiến/lùi/rẽ |
| Điều khiển âm lượng | Remote → tăng/giảm PWM loa |
| Home automation đơn giản | Remote → bật/tắt nhiều thiết bị khác nhau |
| Điều khiển màn hình OLED | Remote → chuyển màn hình, điều hướng menu |
| Emulator remote | Ghi mã → phát lại bằng LED IR để điều khiển TV/điều hòa |
Lưu Ý Khi Sử Dụng
1. Gọi IrReceiver.resume() sau mỗi lần decode
Nếu không gọi resume(), receiver sẽ không nhận tín hiệu tiếp theo. Đây là lỗi phổ biến nhất khi mới dùng IRremote — code chỉ nhận được nút đầu tiên rồi không phản ứng nữa.
2. Kiểm tra thứ tự chân VS1838B thực tế
Chân OUT/GND/VCC có thể khác nhau giữa nhà sản xuất và phiên bản (VS1838, TSOP1838, TSOP38238). Không giả định thứ tự — kiểm tra datasheet hoặc marking trên linh kiện. Cắm ngược → VS1838B nóng ngay lập tức → hỏng.
3. Thêm tụ 100nF giữa VCC và GND
Đặc biệt với linh kiện rời VS1838B: nhiễu nguồn cao → nhận sai hoặc không nhận. Tụ 100nF đặt gần nhất có thể cạnh chân VCC-GND của VS1838B.
4. IRremote v2 và v3 API khác nhau
#include (v2) vs #include (v3). Cú pháp decode, cách đọc value, cách gọi resume đều thay đổi. Code mẫu trên dùng v3. Kiểm tra phiên bản library đã cài.
5. Nhiễu đèn huỳnh quang 50Hz
Đèn huỳnh quang chớp 50Hz (hoặc 100Hz harmonic) có thể tạo nhiễu. Thường không đủ mạnh ở 38kHz để gây vấn đề, nhưng nếu nhận tín hiệu giả liên tục trong phòng đèn huỳnh quang: che góc nhìn sensor hoặc giảm độ nhạy bằng resistor nối tiếp với OUT pin.


