// Copyright 2023 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at

//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @brief This example demonstrates usage of RMT for receiving XJT D16 data
 *
 * The output is the RMT data read and processed
 *
 */

#include <Arduino.h>

//
// Note: This example uses a FrSKY device communication
//          using XJT D16 protocol
//
// ; 0 bit = 6us low/10us high
// ; 1 bit = 14us low/10us high
// ;
// ; --------+       +----------+                 +----------+
// ;         |       |          |                 |          |
// ;         |   0   |          |        1        |          |
// ;         |       |          |                 |          |
// ;         |       |          |                 |          |
// ;         +-------+          +-----------------+          +---------
// ;
// ;         |  6us      10us   |       14us          10us   |
// ;         |-------|----------|-----------------|----------|--------
// ;         |       16us       |            24us            |

//  Typedef of received frame
//
// ; 0x00 - Sync, 0x7E (sync header ID)
// ; 0x01 - Rx ID, 0x?? (receiver ID number, 0x00-0x??)
// ; 0x02 - Flags 1, 0x?? (used for failsafe and binding)
// ; 0x03 - Flags 2, 0x00 (reserved)
// ; 0x04-0x06, Channels 1/9 and 2/10
// ; 0x07-0x09, Channels 3/11 and 4/12
// ; 0x0A-0x0C, Channels 5/13 and 6/14
// ; 0x0D-0x0F, Channels 7/15 and 8/16
// ; 0x10 - 0x00, always zero
// ; 0x11 - CRC-16 High
// ; 0x12 - CRC-16 Low
// ; 0x13 - Tail, 0x7E (tail ID)
typedef union {
  struct {
    uint8_t head;       //0x7E
    uint8_t rxid;       //Receiver Number
    uint8_t flags;      //Range:0x20, Bind:0x01
    uint8_t reserved0;  //0x00
    union {
      struct {
        uint8_t ch0_l;
        uint8_t ch0_h : 4;
        uint8_t ch1_l : 4;
        uint8_t ch1_h;
      };
      uint8_t bytes[3];
    } channels[4];
    uint8_t reserved1;  //0x00
    uint8_t crc_h;
    uint8_t crc_l;
    uint8_t tail;  //0x7E
  };
  uint8_t buffer[20];
} xjt_packet_t;

#define XJT_VALID(i) (i->level0 && !i->level1 && i->duration0 >= 8 && i->duration0 <= 11)

static uint32_t *s_channels;
static uint32_t channels[16];
static uint8_t xjt_flags = 0x0;
static uint8_t xjt_rxid = 0x0;

static bool xjtReceiveBit(size_t index, bool bit) {
  static xjt_packet_t xjt;
  static uint8_t xjt_bit_index = 8;
  static uint8_t xht_byte_index = 0;
  static uint8_t xht_ones = 0;

  if (!index) {
    xjt_bit_index = 8;
    xht_byte_index = 0;
    xht_ones = 0;
  }

  if (xht_byte_index > 19) {
    //fail!
    return false;
  }
  if (bit) {
    xht_ones++;
    if (xht_ones > 5 && xht_byte_index && xht_byte_index < 19) {
      //fail!
      return false;
    }
    //add bit
    xjt.buffer[xht_byte_index] |= (1 << --xjt_bit_index);
  } else if (xht_ones == 5 && xht_byte_index && xht_byte_index < 19) {
    xht_ones = 0;
    //skip bit
    return true;
  } else {
    xht_ones = 0;
    //add bit
    xjt.buffer[xht_byte_index] &= ~(1 << --xjt_bit_index);
  }
  if ((!xjt_bit_index) || (xjt_bit_index == 1 && xht_byte_index == 19)) {
    xjt_bit_index = 8;
    if (!xht_byte_index && xjt.buffer[0] != 0x7E) {
      //fail!
      return false;
    }
    xht_byte_index++;
    if (xht_byte_index == 20) {
      //done
      if (xjt.buffer[19] != 0x7E) {
        //fail!
        return false;
      }
      //check crc?

      xjt_flags = xjt.flags;
      xjt_rxid = xjt.rxid;
      for (int i = 0; i < 4; i++) {
        uint16_t ch0 = xjt.channels[i].ch0_l | ((uint16_t)(xjt.channels[i].ch0_h & 0x7) << 8);
        ch0 = ((ch0 * 2) + 2452) / 3;
        uint16_t ch1 = xjt.channels[i].ch1_l | ((uint16_t)(xjt.channels[i].ch1_h & 0x7F) << 4);
        ch1 = ((ch1 * 2) + 2452) / 3;
        uint8_t c0n = i * 2;
        if (xjt.channels[i].ch0_h & 0x8) {
          c0n += 8;
        }
        uint8_t c1n = i * 2 + 1;
        if (xjt.channels[i].ch1_h & 0x80) {
          c1n += 8;
        }
        s_channels[c0n] = ch0;
        s_channels[c1n] = ch1;
      }
    }
  }
  return true;
}

void parseRmt(rmt_data_t *items, size_t len, uint32_t *channels) {
  bool valid = true;
  rmt_data_t *it = NULL;

  if (!channels) {
    log_e("Please provide data block for storing channel info");
    return;
  }
  s_channels = channels;

  it = &items[0];
  for (size_t i = 0; i < len; i++) {

    if (!valid) {
      break;
    }
    it = &items[i];
    if (XJT_VALID(it)) {
      if (it->duration1 >= 5 && it->duration1 <= 8) {
        valid = xjtReceiveBit(i, false);
      } else if (it->duration1 >= 13 && it->duration1 <= 16) {
        valid = xjtReceiveBit(i, true);
      } else {
        valid = false;
      }
    } else if (!it->duration1 && !it->level1 && it->duration0 >= 5 && it->duration0 <= 8) {
      valid = xjtReceiveBit(i, false);
    }
  }
}

// Change the RMT reading GPIO here:
#define RMT_GPIO 21

void setup() {
  Serial.begin(115200);
  // Initialize the channel to capture up to 64*2 or 48*2 items - 1us tick
  if (!rmtInit(RMT_GPIO, RMT_RX_MODE, RMT_MEM_NUM_BLOCKS_2, 1000000)) {
    Serial.println("init receiver failed\n");
  }
  Serial.println("real tick set to: 1us");
}

void loop() {
  static rmt_data_t data[RMT_MEM_NUM_BLOCKS_2 * RMT_SYMBOLS_PER_CHANNEL_BLOCK];
  static size_t data_symbols = RMT_MEM_NUM_BLOCKS_2 * RMT_SYMBOLS_PER_CHANNEL_BLOCK;

  // Blocking read with timeout of 500ms
  // If data is read, data_symbols will have the number of RMT symbols effectively read
  // to check if something was read and it didn't just timeout, use rmtReceiveCompleted()
  rmtRead(RMT_GPIO, data, &data_symbols, 500);

  // If read something, process the data
  if (rmtReceiveCompleted(RMT_GPIO)) {
    Serial.printf("Got %lu RMT Symbols. Parsing data...\n", (unsigned long)data_symbols);
    parseRmt(data, data_symbols, channels);
  } else {
    Serial.println("No RMT data read...");
  }

  // printout some of the channels every 500ms
  Serial.printf("%04" PRIx32 " %04" PRIx32 " %04" PRIx32 " %04" PRIx32 "\n", channels[0], channels[1], channels[2], channels[3]);
}
