-
-
Notifications
You must be signed in to change notification settings - Fork 1
Protocol
InfraFi uses a custom framing protocol on top of standard RC-6 (or NEC) IR signals. This page documents the complete protocol: frame layout, message sequence, payload format, CRC, and the ACK response.
InfraFi relies on kernel-decoded IR scancodes, not raw pulse timing. Each RC-6 message is decoded by the kernel (or Arduino's IRremote library) into a 16-bit scancode comprised of an 8-bit address and an 8-bit command.
The InfraFi protocol encodes one byte of application payload per IR message, using the address byte for framing metadata and the command byte for data.
Why RC-6?
The ITE8708 CIR chip (found in Intel NUCs) is designed for RC-6 at 36kHz. Using LIRC_MODE_SCANCODE lets the kernel decode RC-6 natively, avoiding the hardware FIFO overflow that occurs with raw timing mode and custom protocols.
NEC support: RC-6 is the default, but the Flipper app also supports NEC for compatibility with other receivers. NEC frames are longer (~67ms each) and use wider inter-message gaps.
Each IR message carries one framing byte (address) and one payload byte (command).
7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+
| Magic (0xA) | Type | Pass |
+---+---+---+---+---+---+---+---+
Bits 7-4: 0xA (InfraFi magic, identifies our frames)
Bits 3-2: Frame type
Bits 1-0: Pass number (retransmission index)
| Bits 3-2 | Value | Constant | Direction | Description |
|---|---|---|---|---|
00 |
0x00 |
WFR_FRAME_TYPE_START |
Flipper -> Daemon | Start of transmission, command = payload length |
01 |
0x04 |
WFR_FRAME_TYPE_DATA |
Flipper -> Daemon | Data byte, command = one byte of payload |
10 |
0x08 |
WFR_FRAME_TYPE_END |
Flipper -> Daemon | End of transmission, command = CRC-8 of payload |
11 |
0x0C |
WFR_FRAME_TYPE_ACK |
Daemon -> Flipper | ACK response frame (same framing, different payload) |
InfraFi retransmits the full sequence to improve reliability. The pass number (0-3) identifies which retransmission attempt a frame belongs to. The daemon reassembles each pass independently and accepts the first complete pass with a valid CRC.
Default retransmit count: 1 (two passes total: pass 0 and pass 1).
START addr=0xA0|pass cmd=<payload_length>
DATA addr=0xA4|pass cmd=<byte_0>
DATA addr=0xA4|pass cmd=<byte_1>
...
DATA addr=0xA4|pass cmd=<byte_N-1>
END addr=0xA8|pass cmd=<crc8>
[pause WFR_RC6_RETRANSMIT_GAP_MS]
START addr=0xA1 cmd=<payload_length> (pass 1)
DATA addr=0xA5 cmd=<byte_0>
...
END addr=0xA9 cmd=<crc8>
| Constant | Value | Description |
|---|---|---|
WFR_RC6_INTER_MSG_MS |
20ms | Delay between individual IR messages within one pass |
WFR_RC6_RETRANSMIT_GAP_MS |
200ms | Pause between retransmission passes |
| Constant | Value | Description |
|---|---|---|
WFR_NEC_INTER_MSG_MS |
50ms | Delay between messages (NEC frames are longer) |
WFR_NEC_RETRANSMIT_GAP_MS |
200ms | Pause between passes |
The payload is a WiFi QR string (meCard-style format used by most QR code WiFi scanners):
WIFI:T:<security>;S:<ssid>;P:<password>;H:<hidden>;;
| Field | Values | Description |
|---|---|---|
T |
nopass, WEP, WPA, SAE
|
Security type |
S |
String | SSID (network name) |
P |
String | Password (empty for open networks) |
H |
true / false
|
Hidden SSID |
Security type mapping:
| Code | String | Description |
|---|---|---|
WFR_SEC_OPEN (0) |
nopass |
Open network |
WFR_SEC_WPA (1) |
WPA |
WPA/WPA2-PSK |
WFR_SEC_WEP (2) |
WEP |
WEP (legacy) |
WFR_SEC_SAE (3) |
SAE |
WPA3 (SAE) |
Example payload:
WIFI:T:WPA;S:MyNetwork;P:hunter2;H:false;;
Maximum payload length: 255 bytes (WFR_MAX_TOTAL_PAYLOAD).
The END frame carries a CRC-8 checksum of the entire payload string.
Algorithm: CRC-8/CCITT
- Polynomial:
0x07 - Initial value:
0x00 - No input/output reflection
- No final XOR
The daemon computes the CRC over the reassembled payload bytes after receiving the END frame. If the CRC does not match, the pass is discarded. If all passes fail CRC, the transmission is dropped silently.
After a connection attempt, the daemon optionally sends an ACK back to the Flipper using the same START/DATA/END framing with WFR_FRAME_TYPE_ACK in bits 3-2 of the address byte.
ACK payload strings:
| Result | Payload | Example |
|---|---|---|
| Success | OK:<ip_address> |
OK:192.168.1.102 |
| Failure | FAIL |
FAIL |
ACK timing: The daemon waits 1 second after connection attempt before sending the ACK (to give the Flipper time to switch to receive mode).
Flipper ACK timeout: 30 seconds (WFR_ACK_TIMEOUT_SEC). The Flipper switches to IR receive mode and waits. If no valid ACK is received within the timeout, it displays a timeout error.
All protocol constants are defined in flipper/protocol/wfr_protocol.h, which is shared between the Flipper app and the Linux daemon.
#define WFR_FRAME_MAGIC 0xA0
#define WFR_FRAME_MAGIC_MASK 0xF0
#define WFR_FRAME_TYPE_MASK 0x0C
#define WFR_FRAME_PASS_MASK 0x03
#define WFR_FRAME_TYPE_START 0x00
#define WFR_FRAME_TYPE_DATA 0x04
#define WFR_FRAME_TYPE_END 0x08
#define WFR_FRAME_TYPE_ACK 0x0C
#define WFR_RC6_INTER_MSG_MS 20
#define WFR_RC6_RETRANSMIT_GAP_MS 200
#define WFR_RETRANSMIT_COUNT 1
#define WFR_NEC_INTER_MSG_MS 50
#define WFR_NEC_RETRANSMIT_GAP_MS 200
#define WFR_ACK_TIMEOUT_SEC 30
#define WFR_MAX_TOTAL_PAYLOAD 255
#define WFR_SSID_MAX_LEN 32
#define WFR_PASS_MAX_LEN 63The daemon's wfr_decode module implements a simple state machine per pass:
IDLE
-> START received: record payload length, advance to COLLECTING
COLLECTING
-> DATA received: append command byte to buffer
-> END received when buffer full: verify CRC
-> CRC OK: deliver payload to caller
-> CRC fail: discard pass, return to IDLE
-> Unexpected frame or wrong pass: reset state
Each pass is tracked independently. The decoder accepts the first pass that produces a CRC-valid payload and ignores subsequent passes for the same transmission.
Getting Started
Components
Reference