Convert HEX IR code to format for Flipper Zero

Hello!

Please help me to convert IR hex code to format for Flipper Zero
Here is an example:

JVC HRS2901U VHS recorder
Power button
0000 006c 00bc 0000 0140 009f 0014 003c 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 003c 0014 0014 0014 003c 0014 003c 0014 0014 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 037b 0014 003c 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 003c 0014 0014 0014 003c 0014 003c 0014 0014 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 037b 0014 003c 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 003c 0014 0014 0014 003c 0014 003c 0014 0014 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 037b 0014 003c 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 003c 0014 0014 0014 003c 0014 003c 0014 0014 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 037b 0014 003c 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 003c 0014 0014 0014 003c 0014 003c 0014 0014 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 037b 0014 003c 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 003c 0014 0014 0014 003c 0014 003c 0014 0014 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 037b 0014 003c 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 003c 0014 0014 0014 003c 0014 003c 0014 0014 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 037b 0014 003c 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 003c 0014 0014 0014 003c 0014 003c 0014 0014 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 037b 0014 003c 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 003c 0014 0014 0014 003c 0014 003c 0014 0014 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 037b 0014 003c 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 003c 0014 0014 0014 003c 0014 003c 0014 0014 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 037b 0014 003c 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 003c 0014 0014 0014 003c 0014 003c 0014 0014 0014 003c 0014 0014 0014 0014 0014 0014 0014 0014 0014 0014

See also RC: Infrared Hex Code Database: JVC HRS2901U Commands (Page 1)

I donā€™t think youā€™re getting lucky with this source. Even if it will be correct, it is very difficult to translate.

The JVC HRS2901U is a video recorder model, not the remote model. It seems to be very common to write about remotes with the name of the matching device, for me a little confusing.

When I google your VHS recorder, i get the following possible remotes: JVC HRS2901U TV/VCR Combo

At least one of it is already in the Lirc database: http://lirc.sourceforge.net/remotes/jvc/LP20878-009


Right now Iā€™m about to compare
Source 1: https://github.com/logickworkshop/Flipper-IRDB/blob/main/Projectors/Epson/Epson.ir

name: Power
type: parsed
protocol: NECext
address: 83 55 00 00
command: 90 6F 00 00

Source 2: http://lirc.sourceforge.net/remotes/epson/12807990

KEY_POWER                0xC1AA09F6

We see, the address is different. But as a lot of Lirc sources have 2 instead of 4 octet, lets focus on 90 6F vs. 09F6. The values are twisted.

lupus@zoe:~$ echo "ibase=16;obase=2;906F"|bc
1001000001101111
lupus@zoe:~$ echo "ibase=16;obase=2;09F6"|bc
0000100111110110

Iā€™ve added the first 0 in the second line. And the result is mirrored inverse. As Infrared is more or less just a switch 1 to 0, I can imagine the result is the same.


I really hope anybody can explain if Iā€™m right with my assumptions, so far. Than it would be easy to write a converter from already existing Lirc files to flipper .ir files.

1 Like

Maybe someone is able to make sense of the following comparisons:

LIRC Database

KEY_0                    0x8877
KEY_1                    0x20DF
KEY_VOLUMEUP             0xE01F
KEY_VOLUMEDOWN           0xD02F

Samsung remote recorded with Flipper

# 
name: 0
type: parsed
protocol: Samsung32
address: 07 00 00 00
command: 11 00 00 00
# 
name: 1
type: parsed
protocol: Samsung32
address: 07 00 00 00
command: 04 00 00 00
# 
name: vol+
type: parsed
protocol: Samsung32
address: 07 00 00 00
command: 07 00 00 00
# 
name: vol-
type: parsed
protocol: Samsung32
address: 07 00 00 00
command: 0B 00 00 00

Flipper remote recorded with LIRC

KEY_0                    0xE0E08877 0xBEE5A1FC
KEY_1                    0xE0E020DF 0xBEE5A1FC
KEY_VOLUMEUP             0xE0E0E01F 0xBEE5A1FC
KEY_VOLUMEDOWN           0xE0E0D02F 0xBEE5A1FC

The address and command Flipper Zero gives are also in hex format. Iā€™m also interested in finding a way to translate between the two formats, mostly out of morbid curiosity.

Awhile back I had a small project to remote control an in-wall AC unit with a Raspberry Pi 4. I used lircd to send the IR code, and lircd, namely irrecord, to learn those codes. Iā€™m interested in teaching my Flipper to control these AC units, too, and instead of going through and relearning all 11 buttons, I thought thereā€™s be a way to hand craft a .ir file for the Flipper. And here I am.

Unlike the Epson example from @LupusE, there doesnā€™t appear to be any similarities between the codes from lirc and what the Flipper Zero provides.

lirc code:

KEY_POWER   0x10AF8877

Flipper shows me:

address: 08 F5 00 00
command: 11 EE 00 00

Good project.

An AC is difficult, because it owns not send a command, it sends a command package. Because the IR communication is one way the parameter from the display will be send at every key press (there are some exceptions, like power).

If you look at the hex values, it is very hard to understand the format. Die Hex values depends also on the protocol.

Start to convert the command in bin. Than look into the protocol to find the address/command length and if it is MSB/LSB. Search for a start bit and stop bit.

A good base to compare is the flipperzero-firmware at GitHub or the IRMP-org project

I agree that the hex values from Flipper are hard to understand. Though the value from lirc is a hexadecimal representation of the literal IR command. address (2 byte), address inverse (2 byte), command (2 byte), command inverse (2 byte). For an educational tool, it seems an odd choice for Flipper to obfuscate what literal IR command is!

I guess I didnā€™t think the AC remote was that complicated. The only real complicated part of the remote seemed to be the ā€œremote sensingā€ feature, which, as I understand it, lets the AC unit in the wall use the remote as a thermometer for Auto mode.

AC Remote pic

The display simply displays the temperature from a thermometer on the remoteā€™s PCB.

Anyway, itā€™s probably worth starting with something simpler. In which case I think the remote for my input switcher is as simple as it could get. 6 buttons for 5 inputs, and one button I donā€™t know what it does yet.

Filetype: IR signals file
Version: 1
# 
name: Input_1
type: parsed
protocol: NEC
address: 02 00 00 00
command: DE 00 00 00
# 
name: Input_2
type: parsed
protocol: NEC
address: 02 00 00 00
command: DA 00 00 00
# 
name: Input_3
type: parsed
protocol: NEC
address: 02 00 00 00
command: DB 00 00 00
# 
name: Input_4
type: parsed
protocol: NEC
address: 02 00 00 00
command: 55 00 00 00
# 
name: Input_5
type: parsed
protocol: NEC
address: 02 00 00 00
command: DF 00 00 00
# 
name: NEC_D9
type: parsed
protocol: NEC
address: 02 00 00 00
command: D9 00 00 00

With lirc reporting:

Button Command Hex
Input_1 0x40BF7B84
Input_2 0x40BF5BA4
Input_3 0x40BFDB24
Input_4 0x40BFAA55
Input_5 0x40BFFB04

So, this might help get me in the right direction. I started writing an app to help wrap my head around Flipperā€™s IR stuff, though itā€™s very much still a WIP. Basically I wanted a way to see what the Flipper decoded without having to muck about with a remote.

Screenshot-20240402-100919

Iā€™ll keep playing around with this and report back with what, if anything, I learn.

I donā€™t think it is ā€˜obfuscatedā€™. It is exactly the same, but the reversing is handled by the Flipper itself, not written in the config file.

See: flipperzero-firmware/lib/infrared/encoder_decoder/nec/infrared_decoder_nec.c at dev Ā· flipperdevices/flipperzero-firmware Ā· GitHub

    if(decoder->databit_cnt == 32) {
        uint8_t address = decoder->data[0];
        uint8_t address_inverse = decoder->data[1];
        uint8_t command = decoder->data[2];
        uint8_t command_inverse = decoder->data[3];
        uint8_t inverse_command_inverse = (uint8_t)~command_inverse;
        uint8_t inverse_address_inverse = (uint8_t)~address_inverse;
        if((command == inverse_command_inverse) && (address == inverse_address_inverse)) {
            decoder->message.protocol = InfraredProtocolNEC;
            decoder->message.address = address;
            decoder->message.command = command;
            decoder->message.repeat = false;
            result = true;
        } else {
            decoder->message.protocol = InfraredProtocolNECext;
            decoder->message.address = decoder->data[0] | (decoder->data[1] << 8);
            decoder->message.command = decoder->data[2] | (decoder->data[3] << 8);
            decoder->message.repeat = false;
            result = true;
        }

But as i said, look at the bin data ā€¦ Any spreadsheet app (LibreOffice Calc for example) provides =HEX2BIN() and =BIN2HEX()

Adr Cmd
0 2 F D D E 2 1
0000 0010 1111(*) 1101(*) 1101 1110 0010(*) 0001(*)
4 0 B F 7 B 8 4
0100 0000 1011 1111 0111 1011 1000 0100

(*) ā†’ manual added

We see 02 and 04 is switched and reversed. FD and BF are switched/reversed. DE 7B are suspicious similar but inverse and 21 84 are familiar.
This is enough to write an converter. But we want to understand ā€¦

I mentioned to the IRMP project, earlier. IRMP/src/irmpprotocols.h at master Ā· IRMP-org/IRMP Ā· GitHub

/*---------------------------------------------------------------------------------------------------------------------------------------------------
 * NEC & NEC42 & NEC16 & LGAIR & MELINERA:
 *---------------------------------------------------------------------------------------------------------------------------------------------------
 */
#define NEC_START_BIT_PULSE_TIME                9000.0e-6                       // 9000 usec pulse
#define NEC_START_BIT_PAUSE_TIME                4500.0e-6                       // 4500 usec pause
#define NEC_REPEAT_START_BIT_PAUSE_TIME         2250.0e-6                       // 2250 usec pause
#define NEC_PULSE_TIME                           560.0e-6                       //  560 usec pulse
#define NEC_1_PAUSE_TIME                        1690.0e-6                       // 1690 usec pause
#define NEC_0_PAUSE_TIME                         560.0e-6                       //  560 usec pause
#define NEC_FRAME_REPEAT_PAUSE_TIME               40.0e-3                       // frame repeat after 40ms
#define NEC_ADDRESS_OFFSET                       0                              // skip 0 bits
#define NEC_ADDRESS_LEN                         16                              // read 16 address bits
#define NEC_COMMAND_OFFSET                      16                              // skip 16 bits (8 address + 8 /address)
#define NEC_COMMAND_LEN                         16                              // read 16 bits (8 command + 8 /command)
#define NEC_COMPLETE_DATA_LEN                   32                              // complete length
#define NEC_STOP_BIT                            1                               // has stop bit
#define NEC_LSB                                 1                               // LSB...MSB
#define NEC_FLAGS                               0                               // flags

I think the LIRC library does not care about the LSBā€¦MSB. My assumption is, that the LIRC library is saving and sending the signal as the physical waveform is provided and the flipper is educational adding the protocol information ā€¦
But I am not familiar with the LIRC project. It worked for my Kodi home theater a few years back, at this time I was not caring about the how and background. I was a bad person these days.

1 Like

That helps. Much of this stuff is already at the limit of my knowledge, so any amount of ā€œlook into it yourselfā€, while always a good idea, usually involves a non-zero amount of yak shaving for me. I also didnā€™t recognize ā€œbinā€ to mean ā€œbinaryā€, but thatā€™s fine.

I was almost there! I started digging into lib/infrared/encoder_decoder, but Iā€™m no C expert so reading that isnā€™t as easy as Iā€™d like it to be for me - and I think I get better at that by learning to write the language instead of just learning how to read it.

Perhaps Iā€™ll get started on a converterā€¦

It is raining here all day long ā€¦ so Iā€™ve started with a little converter in python.

##
## $ git clone https://git.code.sf.net/p/lirc-remotes/code lirc-remotes-code
##

import re

lircconf_pattern = re.compile(r"""
                ^\s+pre_data\s+0x(?P<address>....).*\n
                ^\s+KEY_POWER\s+0x(?P<key_power>....).*\n
                ^\s+KEY_1\s+0x(?P<key_1>....).*\n
                ^\s+KEY_2\s+0x(?P<key_2>....).*\n
                ^\s+KEY_3\s+0x(?P<key_3>....).*\n
                ^\s+KEY_4\s+0x(?P<key_4>....).*\n
                ^\s+KEY_5\s+0x(?P<key_5>....).*\n
                ^\s+KEY_6\s+0x(?P<key_6>....).*\n
                ^\s+KEY_7\s+0x(?P<key_7>....).*\n
                ^\s+KEY_8\s+0x(?P<key_8>....).*\n
                ^\s+KEY_9\s+0x(?P<key_9>....).*\n
                ^\s+KEY_0\s+0x(?P<key_0>....).*\n
                ^\s+KEY_VOLUMEUP\s+0x(?P<key_volup>....).*\n
                ^\s+KEY_VOLUMEDOWN\s+0x(?P<key_voldn>....).*\n
                ^\s+KEY_MUTE\s+0x(?P<key_mute>....).*\n
                ^\s+KEY_CHANNELUP\s+0x(?P<key_chup>....).*\n
                ^\s+KEY_CHANNELDOWN\s+0x(?P<key_chdn>....).*\n
                ^\s+KEY_UP\s+0x(?P<key_up>....).*\n
                ^\s+KEY_DOWN\s+0x(?P<key_down>....).*\n
                ^\s+KEY_LEFT\s+0x(?P<key_left>....).*\n
                ^\s+KEY_RIGHT\s+0x(?P<key_right>....).*\n
                ^\s+KEY_OK\s+0x(?P<key_ok>....).*\n
                ^\s+KEY_MENU\s+0x(?P<key_menu>....).*\n
                ^\s+KEY_EXIT\s+0x(?P<key_exit>....).*\n
                """, re.VERBOSE | re.MULTILINE | re.DOTALL)

lircdb = '/home/lupus/git/lirc-remotes-code/remotes/lg/AKB69680403.lircd.conf'

readfile = open(lircdb,'r')
lircdata = readfile.read()
readfile.close()

buttons = ["key_power", "key_1", "key_2", "key_3", "key_4", "key_5", "key_6", "key_7", "key_8", "key_9", "key_0"]

for match in lircconf_pattern.finditer(lircdata):
    button_code = []
    address = match.group("address")
    
    for button in buttons:   
        print(button,match.group(button)) 
        if match.group(button) != "":
            button_code.append([button,match.group(button)])

#print(button_code)

## see https://github.com/flipperdevices/flipperzero-firmware/blob/dev/documentation/file_formats/InfraredFileFormats.md
fzirfile = "Filetype: IR signals file" + '\n'
fzirfile += "Version: 1" + '\n'
fzirfile += "#" + '\n'
for fzbutton in button_code:
    fzirfile += "name:" + str(fzbutton[0]) + '\n'
    fzirfile += "type: parsed" + '\n'
    fzirfile += "protocol: NEC" + '\n'
    fzaddress = format(int(format(int(address, 16), "016b")[:8][::-1], 2), '0' + str(len(format(int(address, 16), "016b")[:8][::-1]) // 4) + 'x') + " 00 00 00"
    fzirfile += "address:" + fzaddress + '\n'
    print(address, fzbutton[1])
    bin_fzbutton = format(int(fzbutton[1], 16), "016b")
    fzcommand = format(int(bin_fzbutton[:8][::-1], 2), '0' + str(len(bin_fzbutton[:8][::-1]) // 4) + 'x') + " 00 00 00"
    fzirfile += "command:" + fzcommand  + '\n'
    fzirfile += "#" + '\n'

print(fzirfile)

Not perfect, but a beginning. Now I will make offline tasks, maybe someone has ideas or want to contribute.

Next steps would be to get all needed buttons and map to the Flipper naming convention. This is the easy part: Add at the regex and buttons=[] list.
More difficult will be the protocol detection. Check the header 9044 4436 one 606 1632 zero 606 517 timings against the protocol, check if address and command are reversed and expand the output address/command to 4 bits, if needed (NECext for example).

Maybe helpful for anyone to analyze, two similar remotes in NECext from source and target:

1 Like

Neat, I started something sorta similar in C. My end goal was to maybe write a converter that would run on the Flipper, mostly to figure out how reading writing files on the Flipper (and in C) works. Mine doesnā€™t convert full files yet. Thereā€™s probably even a cleaner way to write this, but Iā€™m still learning my way around C.

void lirc_converter(u_int32_t* address, u_int32_t* command, u_int32_t input) {

    u_int32_t addr = (input & 0xFFFF0000) >> 16;
    u_int32_t cmd = input & 0x0000FFFF;

    u_int32_t mask = ((1 << (16 - 8 + 1)) - 1) << 8;
    u_int32_t address_1 = bit_reversal(addr & 0x00FF);
    u_int32_t address_2 = bit_reversal((addr & mask) >> 8);

    u_int32_t command_1 = bit_reversal(cmd & 0x00FF);
    u_int32_t command_2 = bit_reversal((cmd & mask) >> 8);

    addr = (address_1 << 8) | address_2;
    cmd = (command_1 << 8) | command_2;

    *address = addr;
    *command = cmd;
}

u_int32_t bit_reversal(u_int32_t input) {
    int s = sizeof(input) * 2;
    int i, x, y, p;
    int rtn = 0;

    for(i = 0; i < (s / 2); i++) {
        // extract bit on the left, from MSB
        p = s - i - 1;
        x = input & (1 << p);
        x = x >> p;

        // extract the bit on the right, from LSB
        y = input & (1 << i);
        y = y >> i;

        rtn = rtn | (x << i);
        rtn = rtn | (y << p);
    }
    return rtn;
}

Used like:

u_int32_t address = (u_int32_t)NULL;
u_int32_t command = (u_int32_t)NULL;
lirc_converter(&address, &command, 0x20DF10EF);
printf("Address 0x%04X\n", address);    // 0xFB04
printf("Command: 0x%04X\n", command);   // 0xF708

LIRC doesnā€™t seem to make any important distinction between ā€œNECā€ or ā€œNECextā€, for example. LIRC commands always appear to be 16 bytes long. When I mentioned Flipper seems to be obfuscating things, this is what I meant. The LIRC command appears to be the literal IR pulses, which is what gets sent by the Flipper anyway.

1 Like

You can get literal IR pulses on Flipper by doing ir rx raw in CLI, and they are significantly more complex than LIRC command.