IR receiver tidak memberikan “kode tombol” universal kepada ESP32. Pada batas elektriknya, receiver menghasilkan urutan carrier burst yang terdeteksi dan jeda di antaranya. Raw capture merepresentasikan interval tersebut sebagai durasi mark dan space yang bergantian, biasanya dalam mikrodetik.

Capture seperti:

uint16_t rawData[] = {
  3570, 1620,
  554, 342,
  498, 1244,
  496, 378,
  // ...
};

adalah deskripsi waveform. Agar replay berhasil, empat hal harus dipertahankan sebagai satu kesatuan: urutan timing, batas frame yang benar, modulation carrier untuk mark, dan output optik IR LED yang cukup.

Perbedaan ini penting ketika ESP32 dipakai sebagai remote AC. Remote AC sering mengirim pesan yang lebih panjang dan berorientasi state dibanding remote appliance sederhana, dan satu kali penekanan tombol fisik dapat menghasilkan lebih dari satu hasil decode jika receiver memisahkan transmisi menjadi beberapa frame.

Raw timing berbeda dari nilai protokol hasil decode

Decoder dapat melaporkan interpretasi protokol sekaligus raw timing:

PANASONIC 400407200000
address = 0x4004
data    = 0x400407200000

Field hasil decode berguna ketika implementasi protokolnya diketahui. Raw array mempunyai fungsi berbeda: ia mencatat durasi mark-space cukup dekat dengan sinyal yang diamati sehingga transmitter dapat mereproduksi waveform tanpa membangun ulang field protokol.

Dengan IRremoteESP8266, replay raw secara umum berbentuk:

IRsend irsend(IR_TX_PIN);

void setup() {
  irsend.begin();
  irsend.sendRaw(rawData, rawDataLength, 38);
}

Argumen terakhir adalah frekuensi carrier dalam kHz. Nilai itu bukan timing tambahan dari array. Raw timing array dan frekuensi carrier merupakan dua bagian sinyal yang berbeda.

Karena itu, menyimpan hanya hasil decode heksadesimal tidak sama dengan menyimpan raw capture. Sender khusus protokol dapat mengubah state hasil decode kembali menjadi waveform jika protokol tersebut didukung, sedangkan sendRaw() hanya memutar ulang urutan timing yang diberikan.

Frame lengkap pertama harus dikenali dengan benar

Receiver dapat menghasilkan output seperti ini dalam satu penekanan remote:

Raw Timing[99]:
+3570, -1620, +554, -342, ...

PANASONIC 400407200000

kemudian diikuti capture lain:

Raw Timing[71]:
+500, -372, +496, -376, ...

UNKNOWN ...

Kedua array itu tidak boleh langsung dianggap sebagai command yang setara. Capture pertama diawali leader panjang yang khas dan dikenali decoder. Capture kedua langsung dimulai dengan mark-space pendek dan bisa merupakan lanjutan, repeat, frame parsial, atau bagian transmisi yang tersegmentasi secara terpisah.

Aturan praktisnya bukan “selalu pilih array paling panjang.” Capture yang berguna adalah frame lengkap yang mewakili transmisi yang dimaksud. Output decoder, capture berulang dari tombol yang sama, dan pola leader yang stabil membantu menentukan batas tersebut.

Validasi yang baik menangkap tombol yang sama beberapa kali. Durasi tidak akan identik secara numerik pada setiap capture karena receiver, interrupt latency, toleransi oscillator, dan demodulasi menambah variasi pengukuran. Namun strukturnya harus tetap dapat dikenali: leader serupa, jumlah interval serupa, serta pola short space dan long space yang sama.

Raw array statis cocok untuk command tetap

Jika command hasil capture tidak akan berubah saat runtime, Preferences, penyimpanan bergaya EEPROM, atau filesystem tidak diperlukan. Array dapat langsung menjadi bagian firmware:

const uint16_t RAW_ON[] = {
  3570, 1620, 554, 342, 498, 1244, 496, 378,
  // remaining timings
};

const uint16_t RAW_ON_LEN =
    sizeof(RAW_ON) / sizeof(RAW_ON[0]);

Command kemudian dapat dikirim langsung:

irsend.sendRaw(RAW_ON, RAW_ON_LEN, 38);

Pendekatan ini mempunyai sifat operasional yang berguna: reboot tidak menghapus command karena array dikompilasi ke dalam firmware.

Penyimpanan runtime cocok untuk desain lain, yaitu ketika perangkat harus mempelajari remote sembarang setelah deployment. Firmware pada desain tersebut perlu menyimpan jumlah timing dan data timing, memvalidasi batas sebelum memuat data, serta mempertahankan metadata sinyal lain yang diperlukan untuk replay.

Static RAW dan learned RAW menyelesaikan masalah yang berbeda. Menambahkan nonvolatile runtime storage untuk command yang selalu tetap hanya menambah state dan failure mode tanpa mengubah waveform.

MQTT sebaiknya memicu command lokal, bukan membawa waveform

Pada remote yang terhubung ke jaringan, MQTT dapat tetap terpisah dari IR encoding. Satu topic command kecil sudah cukup:

topic:   ac/panasonic/cmd
payload: ON

ESP32 memetakan application command tersebut ke raw array lokal:

void callback(char* topic, byte* payload, unsigned int length) {
  String msg;

  for (unsigned int i = 0; i < length; i++) {
    msg += static_cast<char>(payload[i]);
  }

  if (msg == "ON") {
    irsend.sendRaw(RAW_ON, RAW_ON_LEN, 38);
  }
}

Batas ini berguna. MQTT membawa intent; firmware memiliki waveform fisiknya. Client broker tidak perlu mengetahui apakah perangkat memakai raw replay, encoder Panasonic, atau implementasi protokol lain.

Array terpisah dapat mewakili command tetap:

if (msg == "ON") {
  irsend.sendRaw(RAW_ON, RAW_ON_LEN, 38);
} else if (msg == "OFF") {
  irsend.sendRaw(RAW_OFF, RAW_OFF_LEN, 38);
} else if (msg == "COOL") {
  irsend.sendRaw(RAW_COOL, RAW_COOL_LEN, 38);
}

Array tersebut harus benar-benar berisi capture untuk masing-masing state. Membuat RAW_OFF dan RAW_COOL sebagai alias RAW_ON hanya memberikan tiga nama MQTT kepada satu transmisi fisik yang sama.

State AC membuat raw replay sengaja bersifat kaku

Raw replay presisi tetapi tidak fleksibel. Jika remote AC mengirim seluruh state, frame COOL 24 C yang dicapture dapat sekaligus membawa fan speed, posisi swing, field timer, atau setting lain. Replay frame tersebut kemudian akan mengembalikan state apa pun yang tersimpan di dalam capture.

Perilaku ini berguna ketika command yang diinginkan adalah preset tetap. Ia menjadi tidak praktis ketika MQTT membutuhkan command dinamis seperti:

TEMP:18
TEMP:24
FAN:AUTO
FAN:HIGH

Membuat kombinasi tersebut dari raw capture membutuhkan waveform tersimpan yang berbeda untuk setiap state yang diperlukan. Class AC khusus protokol lebih sesuai ketika protokolnya didukung karena firmware dapat mengubah field dan membiarkan encoder membangun frame hasilnya.

Raw replay dan protocol encoding karena itu dapat hidup berdampingan dalam satu desain, tetapi keduanya tidak boleh disamakan. Raw replay berarti “reproduksi waveform ini.” Protocol encoder berarti “bangun waveform untuk state ini.”

IR LED memerlukan driver stage

Timing yang benar belum menjamin indoor unit menerimanya. GPIO ESP32 adalah logic output, sedangkan IR LED merupakan beban optik berbasis arus. Transistor atau MOSFET yang sesuai memungkinkan GPIO melakukan switching arus LED tanpa memaksa GPIO memasok sendiri transmit pulse current.

Susunan low-side NPN yang umum:

                 +5 V
                   |
             current-limit
               resistor
                   |
                IR LED
                   |
                   C
ESP32 GPIO--R_B--B   NPN
                   E
                   |
                  GND

Emitter dan collector tidak dapat dipertukarkan dalam operasi normal. Pinout transistor harus diperiksa untuk part dan package yang benar; perangkat TO-92 dengan bentuk serupa tidak mempunyai urutan E-B-C yang universal.

Kedua resistor juga mempunyai tugas berbeda. Base resistor membatasi arus GPIO ke base. Resistor LED membatasi arus LED. Memakai nilai yang sama pada keduanya hanya karena keduanya resistor bukan aturan desain elektrik.

Sebagai contoh, resistor LED dipilih dari supply voltage, forward voltage LED, voltage drop driver, target pulse current, dan pulse rating LED:

R ≈ (V_supply - V_F - V_driver) / I_LED

Base drive kemudian harus cukup untuk melakukan switching transistor pada collector current tersebut tanpa melewati batas elektrik GPIO. Batas ini ditentukan datasheet komponen, bukan ketersediaan kode warna resistor.

Receiver dan transmitter dapat aktif bersama, tetapi self-reception perlu diperhitungkan

Sebuah transceiver dapat mempertahankan IR receiver pada satu GPIO dan IR LED driver pada GPIO lain:

const uint16_t IR_RX_PIN = 4;
const uint16_t IR_TX_PIN = 9;

IRrecv irrecv(IR_RX_PIN);
IRsend irsend(IR_TX_PIN);

Ketika transmitter memancarkan sinyal, receiver yang berada di dekatnya dapat melihat output IR milik ESP32 sendiri. Firmware yang menangkap setiap frame masuk akibatnya dapat mencatat atau menyimpan transmisinya sendiri seolah-olah berasal dari remote genggam.

Jika mode learning dan sending sama-sama aktif, desain harus menentukan kapan capture diizinkan. Implementasi sederhana dapat menangguhkan atau mengabaikan reception di sekitar transmisi lokal; implementasi yang lebih kompleks dapat membandingkan timestamp atau frame yang diharapkan. Poin pentingnya adalah kedekatan fisik membuat feedback path meskipun object software-nya terpisah.

Raw replay adalah kontrak waveform

Unit penyimpanan yang andal bukan label ramah seperti ON. Yang perlu dipertahankan adalah kontrak sinyal lengkap di balik label tersebut:

command name
    |
    v
complete mark/space array
    +
carrier frequency
    +
known frame boundary
    |
    v
IR transmitter driver
    |
    v
optical signal

Ketika semua bagian itu tetap selaras, MQTT dapat menjadi control layer yang tipis dan ESP32 dapat memutar ulang command hasil capture secara deterministik. Jika satu bagian salah, gejalanya dapat terlihat sama: pesan MQTT diterima, kode memanggil sendRaw(), tetapi AC tetap tidak bereaksi.

Karena itu debugging raw IR paling efektif dipisahkan per boundary. Pastikan lebih dulu frame lengkap yang dimaksud berhasil dicapture. Setelah itu pastikan array tersimpan tidak berubah. Berikutnya verifikasi carrier dan output transmitter. Network trigger baru relevan setelah seluruh layer fisik tersebut bekerja.