LIR format for the KontroLIR?

For discussions about our KontroLIR - 100% Arduino compatible IR remote control
Post Reply
van0014
Posts: 4
Joined: Sun Dec 10, 2017 3:39 am

LIR format for the KontroLIR?

Post by van0014 »

Hi, i've been using LearnIR and KontroLIR, and wanted to use a format other than RAW for storing codes on the remote with. The LearnIR application is a reduced version of AnalysIR, and doesn't tell me what kind of code is detected. Whether it's NEC, RC5 or any other kind. What it does give is the LIR format, as the most readily useable kind. So i've begun making an array to store codes in LIR format, with or without the carrier. These could be stored in a char array. And while I could try to write some code for this, sending LIR codes, someone might have done this before.

Also, i'm confused about the compression format. It seems to be that compressed sequences start from the list of single bytes, where the double bytes end. Not sure if every remote code has a compressed sequence. The F nibble isn't always near the start of the sequence, so it mustn't be used to mark the start of a sequence. Just the start of a repetition. It's tricky to store these codes in formats other than a string.

Maybe as an unsigned long, leading zeroes can be used to check the size: 0x00xx would be used for sequences without an F. And any data read from after the start of the sequence gets automatically treated as sequence information, rather than data from the pulse pair list

Edit: I've attempted to write a function. It hasn't been tested, and most likely has mistakes. The function takes an unsigned long array and length. 0x00xx is used in place of ASCII spaces, so if the first two nibbles are zero, it's part of the pulse sequence or compressed sequence

Code: Select all

void KIR_sendLIR(const unsigned long* lircode_char, unsigned int arlen)
{
  KIR_sigTime = micros();  // start rolling timer for signal for better accuracy
  unsigned long lirdata[arlen];

  for (unsigned int ik=0; ik<arlen; ik++){
    lirdata[ik] = pgm_read_word_near(&lircode_char[ik]);
  }

  //unsigned long lirdata[] = {
  //  0x0026, 0x228C, 0x11B8, 0x0206, 0x0280, 0x021C, 0x0650, 0x0228, 0x0228, 0x0214, 0xA680, 0x228C, 0x08E4, 0x0001, 0x0011, 0x0022, 0x0021, 0x00F2, 0x0041, 0x0011, 0x0021, 0x0012, 0x0012, 0x0021, 0x0012, 0x0021, 0x0021, 0x0032, 0x0023, 0x0045, 0x001F};

    //arlen = sizeof(lirdata) / sizeof(unsigned long);
  KIRsend.enableIROut(lirdata[0]);

  unsigned int ivar = 1;
  unsigned int seqst = 0;

  for (unsigned int ii=1; ii<arlen; ii++){
    //Check if data is a pulse pair, or part of the sequence
    if (lirdata[ii] <= 0xFF){
      if (seqst == 0){seqst = ii;}
    }
  }

  unsigned int fmark = 0;
  unsigned int pointer = 0;
  unsigned int repeat = 0;
  bool mark = true;
  byte seqdata = 0;
  unsigned long pulsedata = 0;

  for (unsigned int ix=seqst; ix<arlen; ix++){
      for (unsigned int ij=0; ij<2; ij++){
        //Read a nibble
        switch(ij){
            case 0:
            seqdata = (lirdata[ix] & 0xF0) >> 4;
            break;
            case 1:
            seqdata = lirdata[ix] & 0xF;
            break;
          }

        //Send normal mark or space
          if (seqdata == 0xF){
              fmark = 1;
              pointer = 0;
              repeat = 0;

              //If byte = $Fx
              if (((lirdata[ix] & 0xF0) >> 4) == 0xF){
                pointer = lirdata[ix] & 0xF;
                if (ix + 1 < arlen){
                repeat = (lirdata[ix + 1] & 0xF0) >> 4;
                }
                else
                {
                  fmark = 2;
                }
              }
              else
              {
                //if byte = $xF
                if ((ix + 1) < arlen){
                pointer = (lirdata[ix + 1] & 0xF0) >> 4;
                repeat = lirdata[ix + 1] & 0xF;
                }
                else
                {
                  fmark = 2;
                }
              }

              //Pulse repeat
              pulsedata = lirdata[1 + (pointer * 2) + ij];

              for (unsigned int il=0; il<repeat; il++){
                pulsedata = lirdata[1 + (pointer * 2)];
                KIR_mark(pulsedata + ((pulsedata % 2) * 65536));
                if (fmark != 2){
                  pulsedata = lirdata[1 + (pointer * 2) + 1];
                KIR_space(pulsedata + ((pulsedata % 2) * 65536));}
              }
            }

        if (fmark == 0){
          pulsedata = lirdata[1 + (seqdata * 2) + ij];

        switch (mark){
            case false:
            KIR_space(pulsedata + ((pulsedata % 2) * 65536));
            break;
            case true:
            KIR_mark(pulsedata + ((pulsedata % 2) * 65536));
            break;
          }

          mark = !mark;
        }

        fmark = 0;
    }
  }
}
van0014
Posts: 4
Joined: Sun Dec 10, 2017 3:39 am

Re: LIR format for the KontroLIR?

Post by van0014 »

After spending a day on this, and trying to get auto generated code to work, there’s at least something that seems to work to enable lir compressed codes to be copied from analysir and used with kontrolir. I’m hoping to eventually have code that decodes and stores lir format on the kontrolir using the ir demodulator and some button presses, and for an automatic ‘last code received maps to the next kontrolir button pushed’ function, active after mode is entered and stopped after timeout or learn mode exited

Code: Select all

 static int8_t readHexNibble_P(const char *&p) {
  char c = pgm_read_byte(p);
  if (!c) return -1;
  if (c >= '0' && c <= '9') { p++; return c - '0'; }
  if (c >= 'A' && c <= 'F') { p++; return c - 'A' + 10; }
  if (c >= 'a' && c <= 'f') { p++; return c - 'a' + 10; }
  return -1;
}

static int16_t readHexByte_P(const char *&p) {
  int8_t hi = readHexNibble_P(p);
  if (hi < 0) return -1;
  int8_t lo = readHexNibble_P(p);
  if (lo < 0) return -1;
  return (hi << 4) | lo;
}

static int32_t readHexWord_P(const char *&p) {
  int8_t n1 = readHexNibble_P(p);
  if (n1 < 0) return -1;
  int8_t n2 = readHexNibble_P(p);
  if (n2 < 0) return -1;
  int8_t n3 = readHexNibble_P(p);
  if (n3 < 0) return -1;
  int8_t n4 = readHexNibble_P(p);
  if (n4 < 0) return -1;
  return (int32_t(n1) << 12) | (int32_t(n2) << 8) | (int32_t(n3) << 4) | n4;
}

static void skipSpaces_P(const char *&p) {
  char c;
  while ((c = pgm_read_byte(p)) == ' ' || c == '\t' || c == '\r' || c == '\n') {
    p++;
  }
}

//lir format:
//const char JVC_voldn_LIR[] PROGMEM = “26 2328 1194 0232 0232 0232 0698 01 12 F1 52 21 F2 7F 18 F2 61”;
//sendLIR(JVC_voldn_LIR);  // VOL-

void sendLIR(const char *lir_pgm) {
  const char *p = lir_pgm;

  uint8_t  carrier_khz = 0;
  uint16_t marks[15]   = {0};
  uint16_t spaces[15]  = {0};
  uint8_t  num_pairs   = 0;
  uint16_t raw_idx     = 0;

  // --- Carrier: 2 hex digits ---
  skipSpaces_P(p);
  int16_t carr = readHexByte_P(p);
  if (carr < 0) {
    Serial.println(F("LIR: missing carrier"));
    return;
  }
  carrier_khz = (uint8_t)carr;

  // --- Mark/space pairs: 4 hex digits each ---
  while (num_pairs < 15) {
    skipSpaces_P(p);
    char c = pgm_read_byte(p);
    if (!c || !isxdigit(c)) break;

    // Peek ahead to verify 4-digit hex
    const char *peek = p;
    uint8_t count = 0;
    while (count < 4 && pgm_read_byte(peek) && isxdigit(pgm_read_byte(peek))) {
      peek++;
      count++;
    }
    if (count != 4) break;

    int32_t mark_val = readHexWord_P(p);
    if (mark_val < 0) break;
    marks[num_pairs] = (uint16_t)mark_val;

    skipSpaces_P(p);
    int32_t space_val = readHexWord_P(p);
    if (space_val < 0) break;
    spaces[num_pairs] = (uint16_t)space_val;

    // Handle odd-value long signal marker
    if (marks[num_pairs] & 1) marks[num_pairs] += 65536;
    if (spaces[num_pairs] & 1) spaces[num_pairs] += 65536;

    num_pairs++;
  }

  Serial.print(F("Carrier: "));
  Serial.print(carrier_khz);
  Serial.print(F("kHz, Pairs: "));
  Serial.println(num_pairs);

  // --- Pulse train: nibbles with F escape ---
  char pulse_train[128];
  uint8_t pt_len = 0;
  skipSpaces_P(p);
  while (pt_len < 127) {
    char c = pgm_read_byte(p);
    if (!c) break;
    if (c == ' ') {
      p++;
      continue;
    }
    pulse_train[pt_len++] = c;
    p++;
  }
  pulse_train[pt_len] = 0;

  Serial.print(F("Pulse train: "));
  Serial.println(pulse_train);

  // --- Decompress ---
  uint16_t i = 0;
  while (i < pt_len && raw_idx + 2 < MAX_PULSES) {
    char c = pulse_train[i];

    if (c == 'F' || c == 'f') {
      if (i == pt_len - 1) break;
      i++;

      char p_char = pulse_train[i];
      int8_t p_idx = (p_char >= 'A' && p_char <= 'F') ? (p_char - 'A' + 10) :
                     (p_char >= 'a' && p_char <= 'f') ? (p_char - 'a' + 10) :
                     (p_char >= '0' && p_char <= '9') ? (p_char - '0') : -1;
      i++;

      char cnt_char = pulse_train[i];
      int8_t cnt = (cnt_char >= 'A' && cnt_char <= 'F') ? (cnt_char - 'A' + 10) :
                   (cnt_char >= 'a' && cnt_char <= 'f') ? (cnt_char - 'a' + 10) :
                   (cnt_char >= '0' && cnt_char <= '9') ? (cnt_char - '0') : -1;
      i++;

      if (p_idx >= 0 && p_idx < num_pairs && cnt > 0) {
        Serial.print(F("F"));
        Serial.print(p_idx, HEX);
        Serial.print(F(" x"));
        Serial.println(cnt);

        for (uint8_t r = 0; r < cnt && raw_idx + 2 < MAX_PULSES; r++) {
          raw_buffer[raw_idx++] = marks[p_idx];
          raw_buffer[raw_idx++] = spaces[p_idx];
        }
      }
    } else {
      int8_t p_idx = (c >= 'A' && c <= 'F') ? (c - 'A' + 10) :
                     (c >= 'a' && c <= 'f') ? (c - 'a' + 10) :
                     (c >= '0' && c <= '9') ? (c - '0') : -1;
      i++;

      if (p_idx >= 0 && p_idx < num_pairs && raw_idx + 2 < MAX_PULSES) {
        Serial.print(F("pulse["));
        Serial.print(p_idx, HEX);
        Serial.println(F("]"));

        raw_buffer[raw_idx++] = marks[p_idx];
        raw_buffer[raw_idx++] = spaces[p_idx];
      }
    }
  }

// --- Remove trailing space if present (IR always ends on mark) ---
  if (raw_idx > 0 && (raw_idx & 1) == 0) {
    raw_idx--;  // Drop final space
  }

  // --- Debug ---
  Serial.print(F("Total pulses: "));
  Serial.println(raw_idx);
  for (uint16_t j = 0; j < raw_idx; j++) {
    Serial.print(raw_buffer[j]);
    if ((j & 7) == 7) Serial.println();
    else Serial.print(' ');
  }
  Serial.println();

  // --- Send ---
  KIRsend.enableIROut(carrier_khz);
  for (uint16_t n = 0; n < raw_idx; n++) {
    if (n & 1) KIR_space(raw_buffer[n]);
    else       KIR_mark(raw_buffer[n]);
  }
  KIR_space(0);
}
Post Reply