Arduino-IRremote separates the representation used while receiving an infrared frame from the representation consumed by sendRaw(). The receiver records sampled durations, tracks the gap before the frame separately, and can compensate timing distortion introduced by a demodulating IR receiver. A transmitter, by contrast, expects an array that starts with the first mark and alternates mark and space durations.
That boundary matters for AC remotes because their frames can be long and are frequently replayed as RAW data when no protocol-specific encoder is available.
printIRResultRawFormatted() is diagnostic output
A receiver loop often contains:
if (IrReceiver.decode()) {
IrReceiver.printIRResultShort(&Serial);
IrReceiver.printIRResultRawFormatted(&Serial, true);
IrReceiver.resume();
}The second argument to printIRResultRawFormatted() requests microseconds. The function is useful for inspecting the captured waveform, but it prints a human-readable formatted dump. It is not the API specifically intended to emit a C array for copy-paste replay.
For that purpose, current Arduino-IRremote provides:
IrReceiver.printIRResultAsCArray(&Serial, true);With true, the generated values are expressed in microseconds. They can be stored in a uint16_t array and passed to the microsecond overload of sendRaw().
A capture sketch can therefore print both views:
if (IrReceiver.decode()) {
IrReceiver.printIRResultShort(&Serial);
Serial.println("Formatted timing:");
IrReceiver.printIRResultRawFormatted(&Serial, true);
Serial.println("C array for replay:");
IrReceiver.printIRResultAsCArray(&Serial, true);
IrReceiver.resume();
}The distinction prevents a common copy-paste error: treating diagnostic text as though it were already the transmitter array.
The receive buffer stores timing ticks
Arduino-IRremote samples the receiver input periodically. With the default configuration, MICROS_PER_TICK is 50 microseconds. The internal receive buffer stores counts of those ticks rather than storing every interval directly as a uint16_t microsecond value.
Conceptually:
IR receiver output
|
v
50 us sampling
|
v
rawbuf[] tick countsA raw buffer value of 11 therefore represents approximately:
11 * 50 us = 550 usCurrent Arduino-IRremote normally uses an 8-bit timing buffer unless USE_16_BIT_TIMING_BUFFER is enabled. At 50 microseconds per tick, an 8-bit entry can represent up to 12.75 ms before clipping.
The internal rawbuf is consequently a receive-side structure, not a ready-made microsecond transmit array.
The initial receive gap is not the first transmit mark
A receiver needs the idle gap before a frame to decide where the frame begins. Arduino-IRremote stores that value as initialGapTicks. Since version 4.4, the gap is kept separately instead of occupying the first timing entry in rawbuf.
The raw receive structure also keeps its first buffer entry unused for backward compatibility. The transmit API has a different contract: raw data starts with a mark and has no leading receive gap.
The 16-bit overload is:
IrSender.sendRaw(rawData, rawLength, frequencyKHz);and interprets the array as:
rawData[0] first mark
rawData[1] first space
rawData[2] next mark
rawData[3] next space
...Passing an internal receive buffer without converting its indexing and units can therefore produce a waveform with the wrong boundary even though the sketch compiles.
sendRaw() uses the C++ array type as part of the contract
Arduino-IRremote provides separate raw overloads.
A uint16_t array contains microseconds:
const uint16_t rawData[] = {
9000, 4500, 560, 560, 560, 1690
};
IrSender.sendRaw(
rawData,
sizeof(rawData) / sizeof(rawData[0]),
38
);An 8-bit array contains timing ticks:
const uint8_t rawTicks[] = {
180, 90, 11, 11, 11, 34
};
IrSender.sendRaw(
rawTicks,
sizeof(rawTicks) / sizeof(rawTicks[0]),
38
);With the default 50 microsecond tick, both examples describe approximately the same intervals. The value 180 means 180 microseconds in the first overload but 180 ticks, or about 9000 microseconds, in the second.
The carrier frequency is separate from both arrays. In these examples, the final argument 38 means 38 kHz.
In-memory capture and replay should use the conversion helper
Arduino-IRremote’s ReceiveAndSend example handles unknown or generic pulse-width/pulse-distance data by copying the capture through:
IrReceiver.compensateAndStoreIRResultInArray(rawCode);The destination is an 8-bit tick array, which is then sent with the 8-bit sendRaw() overload.
A minimal form is:
#include <Arduino.h>
#include <IRremote.hpp>
const uint8_t IR_RECEIVE_PIN = 4;
const uint8_t IR_SEND_PIN = 5;
uint8_t rawCode[RAW_BUFFER_LENGTH];
uint16_t rawCodeLength;
void setup() {
Serial.begin(115200);
IrReceiver.begin(IR_RECEIVE_PIN, DISABLE_LED_FEEDBACK);
IrSender.begin(IR_SEND_PIN, DISABLE_LED_FEEDBACK);
}
void loop() {
if (!IrReceiver.decode()) {
return;
}
if (IrReceiver.decodedIRData.flags & IRDATA_FLAGS_WAS_OVERFLOW) {
Serial.println("RAW buffer overflow");
IrReceiver.resume();
return;
}
rawCodeLength = IrReceiver.decodedIRData.rawlen - 1;
IrReceiver.compensateAndStoreIRResultInArray(rawCode);
IrReceiver.resume();
IrSender.sendRaw(rawCode, rawCodeLength, 38);
}This is safer than copying irparams.rawbuf directly. The helper removes the receive-side offset and applies the library’s mark/space compensation before storing replay data.
The compensation exists because demodulating IR receiver modules can shift mark and space edges. Arduino-IRremote uses MARK_EXCESS_MICROS for this correction; the current default is 20 microseconds unless configured otherwise.
Long AC frames must be checked for overflow
AC remotes can exceed the timing count required by ordinary TV-style protocols. Arduino-IRremote sets:
IRDATA_FLAGS_WAS_OVERFLOWwhen the captured frame does not fit in RAW_BUFFER_LENGTH.
An overflowed capture is incomplete and should not be replayed as though it represented a complete command. The library selects different default buffer sizes according to available RAM, and a sketch can define a larger RAW_BUFFER_LENGTH before including IRremote.hpp when necessary.
RECORD_GAP_MICROS is another frame-boundary parameter. It must be longer than valid spaces inside the protocol but shorter than the gap that separates transmissions. A value that is too small can split a long AC transmission into multiple captures.
RAW replay requires timing, boundary, and carrier to agree
A replayable capture consists of three separate properties:
timing representation
+
correct frame boundary
+
carrier frequencyFor copy-paste workflows, printIRResultAsCArray(&Serial, true) produces the representation intended for a microsecond uint16_t array. For capture and replay inside the same firmware, compensateAndStoreIRResultInArray() followed by the 8-bit sendRaw() overload preserves the library’s intended conversion path.
The distinction is small at the API surface but decisive on the wire: the IR LED can visibly flash while the appliance still ignores the command if the array has the wrong units, starts at the wrong entry, is truncated, or is sent with an unsuitable carrier frequency.