text: // Copyright bakrus
  // Copyright 2017 David Conran
  
  #include "IRrecv.h"
  #include "IRsend.h"
  #include "IRutils.h"
  
  //             CCCCC   OOOOO   OOOOO  LL      IIIII XX    XX
  //            CC    C OO   OO OO   OO LL       III   XX  XX
  //            CC      OO   OO OO   OO LL       III    XXXX
  //            CC    C OO   OO OO   OO LL       III   XX  XX
  //             CCCCC   OOOO0   OOOO0  LLLLLLL IIIII XX    XX
  
  // Coolix A/C / heatpump added by (send) bakrus & (decode) crankyoldgit
  
  // Constants
  // Pulse parms are *50-100 for the Mark and *50+100 for the space
  // First MARK is the one after the long gap
  // pulse parameters in usec
  #define COOLIX_TICK             560U  // Approximately 21 cycles at 38kHz
  #define COOLIX_BIT_MARK_TICKS     1U
  #define COOLIX_BIT_MARK         (COOLIX_BIT_MARK_TICKS * COOLIX_TICK)
  #define COOLIX_ONE_SPACE_TICKS    3U
  #define COOLIX_ONE_SPACE        (COOLIX_ONE_SPACE_TICKS * COOLIX_TICK)
  #define COOLIX_ZERO_SPACE_TICKS   1U
  #define COOLIX_ZERO_SPACE       (COOLIX_ZERO_SPACE_TICKS * COOLIX_TICK)
  #define COOLIX_HDR_MARK_TICKS     8U
  #define COOLIX_HDR_MARK         (COOLIX_HDR_MARK_TICKS * COOLIX_TICK)
  #define COOLIX_HDR_SPACE_TICKS    8U
  #define COOLIX_HDR_SPACE        (COOLIX_HDR_SPACE_TICKS * COOLIX_TICK)
  #define COOLIX_MIN_GAP_TICKS    (COOLIX_HDR_MARK_TICKS + \
                                   COOLIX_ZERO_SPACE_TICKS)
  #define COOLIX_MIN_GAP          (COOLIX_MIN_GAP_TICKS * COOLIX_TICK)
  
  #if SEND_COOLIX
  // Send a Coolix message
  //
  // Args:
  //   data:   Contents of the message to be sent.
  //   nbits:  Nr. of bits of data to be sent. Typically COOLIX_BITS.
  //   repeat: Nr. of additional times the message is to be sent.
  //
  // Status: BETA / Probably works.
  //
  // Ref:
  //   https://github.com/z3t0/Arduino-IRremote/blob/master/ir_COOLIX.cpp
  // TODO(anyone): Verify repeat functionality against a real unit.
  void IRsend::sendCOOLIX(uint64_t data, uint16_t nbits, uint16_t repeat) {
    if (nbits % 8 != 0)
      return;  // nbits is required to be a multiple of 8.
  
    // Set IR carrier frequency
    enableIROut(38);
  
    for (uint16_t r = 0; r <= repeat; r++) {
      // Header
      mark(COOLIX_HDR_MARK);
      space(COOLIX_HDR_SPACE);
  
      // Data
      //   Break data into byte segments, starting at the Most Significant
      //   Byte. Each byte then being sent normal, then followed inverted.
      for (uint16_t i = 8; i <= nbits; i += 8) {
        // Grab a bytes worth of data.
        uint8_t segment = (data >> (nbits - i)) & 0xFF;
        // Normal
        sendData(COOLIX_BIT_MARK, COOLIX_ONE_SPACE,
                 COOLIX_BIT_MARK, COOLIX_ZERO_SPACE,
                 segment, 8, true);
        // Inverted.
        sendData(COOLIX_BIT_MARK, COOLIX_ONE_SPACE,
                 COOLIX_BIT_MARK, COOLIX_ZERO_SPACE,
                 segment ^ 0xFF, 8, true);
      }
  
      // Footer
      mark(COOLIX_BIT_MARK);
      space(COOLIX_MIN_GAP);  // Pause before repeating
    }
  }
  #endif
  
  #if DECODE_COOLIX
  // Decode the supplied Coolix message.
  //
  // Args:
  //   results: Ptr to the data to decode and where to store the decode result.
  //   nbits:   The number of data bits to expect. Typically COOLIX_BITS.
  //   strict:  Flag indicating if we should perform strict matching.
  // Returns:
  //   boolean: True if it can decode it, false if it can't.
  //
  // Status: BETA / Probably working.
  bool IRrecv::decodeCOOLIX(decode_results *results, uint16_t nbits,
                            bool strict) {
    // The protocol sends the data normal + inverted, alternating on
    // each byte. Hence twice the number of expected data bits.
    if (results->rawlen < 2 * 2 * nbits + HEADER + FOOTER - 1)
      return false;  // Can't possibly be a valid COOLIX message.
    if (strict && nbits != COOLIX_BITS)
      return false;  // Not strictly a COOLIX message.
    if (nbits % 8 != 0)  // nbits has to be a multiple of nr. of bits in a byte.
      return false;
  
    uint64_t data = 0;
    uint64_t inverted = 0;
    uint16_t offset = OFFSET_START;
  
    if (nbits > sizeof(data) * 8)
      return false;  // We can't possibly capture a Coolix packet that big.
  
    // Header
    if (!matchMark(results->rawbuf[offset], COOLIX_HDR_MARK)) return false;
    // Calculate how long the common tick time is based on the header mark.
    uint32_t m_tick = results->rawbuf[offset++] * RAWTICK / COOLIX_HDR_MARK_TICKS;
    if (!matchSpace(results->rawbuf[offset], COOLIX_HDR_SPACE)) return false;
    // Calculate how long the common tick time is based on the header space.
    uint32_t s_tick = results->rawbuf[offset++] * RAWTICK /
        COOLIX_HDR_SPACE_TICKS;
  
    // Data
    // Twice as many bits as there are normal plus inverted bits.
    for (uint16_t i = 0; i < nbits * 2; i++, offset++) {
      bool flip = (i / 8) % 2;
      if (!matchMark(results->rawbuf[offset++], COOLIX_BIT_MARK_TICKS * m_tick))
        return false;
      if (matchSpace(results->rawbuf[offset], COOLIX_ONE_SPACE_TICKS * s_tick)) {
        if (flip)
          inverted = (inverted << 1) | 1;
        else
          data = (data << 1) | 1;
      } else if (matchSpace(results->rawbuf[offset],
                            COOLIX_ZERO_SPACE_TICKS * s_tick)) {
        if (flip)
          inverted <<= 1;
        else
          data <<= 1;
      } else {
        return false;
      }
    }
  
    // Footer
    if (!matchMark(results->rawbuf[offset++], COOLIX_BIT_MARK_TICKS * m_tick))
      return false;
    if (offset < results->rawlen &&
        !matchAtLeast(results->rawbuf[offset], COOLIX_MIN_GAP_TICKS * s_tick))
      return false;
  
    // Compliance
    uint64_t orig = data;  // Save a copy of the data.
    if (strict) {
      for (uint16_t i = 0; i < nbits; i += 8, data >>= 8, inverted >>= 8)
        if ((data & 0xFF) != ((inverted & 0xFF) ^ 0xFF))
          return false;
    }
  
    // Success
    results->decode_type = COOLIX;
    results->bits = nbits;
    results->value = orig;
    results->address = 0;
    results->command = 0;
    return true;
  }
  #endif
  