紅外線遙控技術簡介
ESP32 紅外線遙控(IR Remote)是物聯網專案中最實用的無線控制方式之一,幾乎所有家電(電視、冷氣、投影機、音響)都支援紅外線遙控。ESP32 內建的 RMT(Remote Control)外設能精確產生和解析紅外線訊號,實現智慧家電控制。
紅外線通訊原理
紅外線遙控使用 940nm 波長的紅外光,透過 38kHz(或其他頻率)載波調變來傳輸資料:
- 載波(Carrier):38kHz 方波(duty cycle 1/3),LED 以此頻率閃爍
- 調變:載波的存在(burst)代表邏輯訊號
- 協定:不同廠商使用不同協定(NEC、Sony、RC-5、RC-6 等),最常見的是 NEC 協定
- 接收端:整合式紅外線接收頭(如 VS1838B)解調載波,輸出 TTL 電平訊號

NEC 協定完全解析
NEC 協定是最廣泛使用的紅外線遙控協定,由 NEC 開發,被大量 Android TV 盒子、風扇、燈具採用。
完整的 NEC 訊框包含:
- Leader code(引導碼):9ms HIGH + 4.5ms LOW,標示訊框開始
- Address(位址,8 bits):裝置識別碼(如 0x00)
- ~Address(反相位址,8 bits):用於錯誤檢測
- Command(命令,8 bits):按鍵識別碼(如 0x45 = CH-)
- ~Command(反相命令,8 bits):用於錯誤檢測
- 總長:32 bits + Leader = 約 67.5ms
位元編碼規則非常直觀:
- 邏輯 '0':560μs HIGH + 560μs LOW(總長 1.12ms)
- 邏輯 '1':560μs HIGH + 1.69ms LOW(總長 2.25ms)
- 重複碼(Repeat):若按鍵持續按住,每 110ms 發送一組重複碼(9ms HIGH + 2.25ms LOW + 560μs HIGH)
ESP32 RMT 外設
ESP32 的 RMT(Remote Control) 外設是專為紅外線/無線遙控設計的硬體模組,不需要 CPU 干預就能產生或解析精確的脈衝序列:
- 8 個通道(可任意映射到 GPIO)
- 發送模式:硬體自動輸出脈衝序列,CPU 可同時做其他事
- 接收模式:硬體自動測量脈衝寬度,存入 RAM
- 解析度:12.5ns(80MHz 時脈 ÷ 1 分頻)
- 最大脈衝數:256 組 per transaction
IR 接收:使用 RMT 解碼 NEC 協定
// ESP32 RMT IR 接收 — NEC 協定解碼
#include <driver/rmt.h>
#include // 或使用 Arduino IRremote 函式庫
#define IR_RX_PIN 15 // RMT 通道 0 → GPIO 15
#define RMT_RX_CH 0
// NEC 協定解碼結果
typedef struct {
uint16_t address;
uint16_t command;
bool repeat;
} nec_decode_t;
// RMT 接收配置
rmt_config_t rmt_rx = {
.rmt_mode = RMT_MODE_RX,
.channel = (rmt_channel_t)RMT_RX_CH,
.gpio_num = (gpio_num_t)IR_RX_PIN,
.clk_div = 80, // 80MHz / 80 = 1MHz → 1μs 解析度
.mem_block_num = 1,
.rx_config = {
.idle_threshold = 10000, // 10ms 無訊號視為空閒
.filter_ticks_thresh = 100, // 濾除 < 100μs 的雜訊
.filter_en = true,
}
};
// NEC 協定解碼
bool decodeNEC(rmt_item32_t* items, size_t len, nec_decode_t* result) {
if (len < 34) return false; // Leader(2) + 32 bits // 檢查 Leader code (9ms HIGH + 4.5ms LOW) if (abs((int)items[0].duration0 - 9000) > 1000) return false;
if (abs((int)items[0].duration1 - 4500) > 1000) return false;
// 檢查是否為重複碼
if (len == 2 && abs((int)items[0].duration0 - 9000) < 1000 &&
abs((int)items[0].duration1 - 2250) < 500) { result->repeat = true;
return true;
}
// 解碼 32 bits (items[1] ~ items[32])
uint32_t code = 0;
for (int i = 0; i < 32; i++) { code >>= 1;
if (items[i+1].duration1 > 1000) { // > 1ms = logic '1'
code |= 0x80000000;
}
}
result->address = (code >> 24) & 0xFF;
result->command = (code >> 8) & 0xFF;
result->repeat = false;
return true;
}
void setup() {
Serial.begin(115200);
// 安裝 RMT 接收
rmt_config(&rmt_rx);
rmt_driver_install((rmt_channel_t)RMT_RX_CH, 1000, 0);
Serial.println("IR 接收器就緒,等待紅外線訊號...");
}
void loop() {
RingbufHandle_t rb = NULL;
rmt_get_ringbuf_handle((rmt_channel_t)RMT_RX_CH, &rb);
size_t len = 0;
rmt_item32_t* items = (rmt_item32_t*)xRingbufferReceive(rb, &len, portMAX_DELAY);
if (items && len > 0) {
nec_decode_t result = {0};
if (decodeNEC(items, len / sizeof(rmt_item32_t), &result)) {
if (result.repeat) {
Serial.println("重複碼 (按鍵持續按住)");
} else {
Serial.printf("NEC 解碼成功 | 位址: 0x%02X | 命令: 0x%02X\n",
result.address, result.command);
}
}
vRingbufferReturnItem(rb, (void*)items);
}
}
使用 IRremote 函式庫(更簡單)
// ESP32 IR 接收 — 使用 IRremote 函式庫
#include
#define IR_RX_PIN 15
IRrecv irrecv(IR_RX_PIN);
decode_results results;
void setup() {
Serial.begin(115200);
irrecv.enableIRIn();
Serial.println("IRremote 就緒,按下遙控器...");
}
void loop() {
if (irrecv.decode(&results)) {
Serial.printf("協定: %s | 值: 0x%08X | 位元數: %d\n",
results.decode_type == NEC ? "NEC" :
results.decode_type == SONY ? "SONY" :
results.decode_type == PANASONIC ? "PANASONIC" :
results.decode_type == RC5 ? "RC5" :
results.decode_type == RC6 ? "RC6" : "UNKNOWN",
results.value,
results.bits);
irrecv.resume();
}
}
IR 發送:模擬遙控器
// ESP32 IR 發送 — 使用 IRremote 函式庫
#include
#define IR_TX_PIN 4
IRsend irsend(IR_TX_PIN);
// NEC 命令對照表(以電視為例)
#define NEC_CH_MINUS 0x45
#define NEC_CH 0x46
#define NEC_CH_PLUS 0x47
#define NEC_VOL_DOWN 0x15
#define NEC_VOL_UP 0x16
#define NEC_MUTE 0x0D
#define NEC_POWER 0x44
#define NEC_INPUT 0x40
void setup() {
Serial.begin(115200);
irsend.begin();
Serial.println("IR 發送器就緒");
}
void loop() {
if (Serial.available()) {
char c = Serial.read();
switch (c) {
case 'v': irsend.sendNEC(NEC_VOL_UP, 32); break;
case 'V': irsend.sendNEC(NEC_VOL_DOWN, 32); break;
case 'm': irsend.sendNEC(NEC_MUTE, 32); break;
case 'p': irsend.sendNEC(NEC_POWER, 32); break;
case '0'...'9':
// 以 CH+ 範例,實際需查遙控器編碼
irsend.sendNEC(NEC_CH, 32);
break;
}
}
}
RMT 原生發送(精確控制脈衝)
// ESP32 RMT 原生發送 — 自行組裝 NEC 脈衝
#include <driver/rmt.h>
#define IR_TX_PIN 4
#define RMT_TX_CH 1
rmt_item32_t necItems[34]; // Leader(2) + 32 bits
void buildNECFrame(uint16_t addr, uint16_t cmd, rmt_item32_t* items) {
// Leader code: 9ms HIGH + 4.5ms LOW
items[0].duration0 = 9000;
items[0].level0 = 1;
items[0].duration1 = 4500;
items[0].level1 = 0;
// 32 bits data
uint32_t code = addr | ((uint32_t)~addr << 8) | (cmd << 16) | ((uint32_t)~cmd << 24);
for (int i = 0; i < 32; i++) {
items[i+1].duration0 = 560;
items[i+1].level0 = 1;
if (code & (1 << 31)) {
items[i+1].duration1 = 1690; // logic '1'
} else {
items[i+1].duration1 = 560; // logic '0'
}
items[i+1].level1 = 0;
code <<= 1;
}
}
void setup() {
rmt_config_t rmt_tx = {
.rmt_mode = RMT_MODE_TX,
.channel = (rmt_channel_t)RMT_TX_CH,
.gpio_num = (gpio_num_t)IR_TX_PIN,
.clk_div = 80,
.mem_block_num = 1,
.tx_config = {
.carrier_freq_hz = 38000, // 38kHz 載波
.carrier_level = 1,
.carrier_en = true, // 自動產生 38kHz 載波!
.idle_level = 0,
.loop_en = false,
}
};
rmt_config(&rmt_tx);
rmt_driver_install((rmt_channel_t)RMT_TX_CH, 0, 0);
}
void sendNEC(uint16_t addr, uint16_t cmd) {
buildNECFrame(addr, cmd, necItems);
rmt_write_items((rmt_channel_t)RMT_TX_CH, necItems, 34, true);
Serial.printf("已發送 NEC: 0x%02X 0x%02X\n", addr, cmd);
}
紅外線遙控協定比較

| 特性 | NEC | Sony SIRC | Philips RC-5 | Philips RC-6 |
|---|---|---|---|---|
| 載波頻率 | 38 kHz | 40 kHz | 36 kHz | 36 kHz |
| 資料長度 | 32 bits | 12/15/20 bits | 14 bits | 36 bits |
| 引導碼 | 9ms + 4.5ms | 2.4ms + 0.6ms | 無 | 3ms + 0.89ms |
| 位元 '0' | 560μs + 560μs | 1.2ms + 0.6ms | 889μs + 889μs | 444μs + 444μs |
| 位元 '1' | 560μs + 1.69ms | 2.4ms + 0.6ms | 889μs + 889μs | 444μs + 444μs |
| 重複碼 | Leader + 2.25ms | 無 | Toggle bit | Toggle bit |
| 錯誤檢測 | 反相 + 固定長度 | 固定長度 | 無(Toggle 防重複) | CRC (RC-6 MCE) |
支援多協定的萬用遙控器
// ESP32 萬用遙控器 — 學習 + 發送
#include
#include
實務注意事項
- 接收頭擺放:VS1838B 對正前方 ±45° 靈敏度最佳,側面幾乎無法接收
- NPN 驅動電路:IR LED 需要 NPN 電晶體(如 2N2222)驅動,ESP32 GPIO 無法直接驅動 LED 到足夠亮度
- 限流電阻:IR LED 串聯 100~220Ω 限流電阻,避免燒毀
- 載波頻率匹配:發送端的載波頻率須與接收頭匹配(多數為 38kHz)
- 環境光干擾:陽光或節能燈可能含紅外線成分,導致誤觸發—使用 RMT 的 filter 功能濾除短脈衝
- 多個家電:不同品牌常使用不同協定,萬用遙控器需逐一學習
總結
ESP32 的 RMT 外設讓紅外線遙控的實作變得簡單而精確。從 NEC 協定的位元時序解析,到 RMT 硬體的自動載波產生,ESP32 能同時勝任 IR 接收和發送的角色。
結合 WiFi 和 MQTT,可以做出遠端遙控家電的 IoT 系統;配合之前介紹的 ESP32 Web Server,用手機瀏覽器就能控制電視和冷氣。
文章評論