Skip to content

Protocol

Alejandro Mora edited this page May 8, 2026 · 1 revision

Protocol Specification

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.


IR Transport Layer

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.


Frame Format

Each IR message carries one framing byte (address) and one payload byte (command).

Address Byte Layout

 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)

Frame Types

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)

Pass Number (Bits 1-0)

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).


Message Sequence

Flipper -> Daemon (Credential Transmission)

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>

Timing (RC-6)

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

Timing (NEC)

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

Payload Format

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).


CRC-8 Integrity Check

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.


ACK Response (Daemon -> Flipper)

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.


Constants Reference

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         63

Decoder State Machine

The 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.

Clone this wiki locally