/**
* @file tuya_device.c
* @author www.tuya.com
* @version 0.1
* @date 2022-05-20
*
* @copyright Copyright (c) tuya.inc 2022
*
*/

#include "tuya_iot_config.h"

#if defined(ENABLE_I2C) && (ENABLE_I2C) 
#include "tuya_cloud_types.h"
#include "tal_log.h"
#include "tkl_i2c.h"
#include "tal_thread.h"
#include "tal_system.h"
#include "tkl_pinmux.h"

/***********************************************************
*************************macro define***********************
***********************************************************/
#define I2C_NUM_ID                  TUYA_I2C_NUM_0
#define I2C_SCL_ID                  (g_i2c_idx << 1)
#define I2C_SDA_ID                  ((g_i2c_idx << 1) + 1)

#define SHT3X_ADDR                  0x44
#define SHT3X_REG                   0xE000

#define TASK_IIC_PRIORITY           THREAD_PRIO_1
#define TASK_IIC_SIZE               1024

#define ONE_BYTE                    1u
#define TWO_BYTE                    2u
#define I2C_READ_DATA_SIZE          6u
#define I2C_WRITE_BUFLEN            (256)

/*user pin*/
#define I2C_SCL_PIN                 TUYA_GPIO_NUM_20
#define I2C_SDA_PIN                 TUYA_GPIO_NUM_22

/* CRC result */
#define CRC_OK                      (0)
#define CRC_ERR                     (-1)

/***********************************************************
***********************typedef define***********************
***********************************************************/

/***********************************************************
***********************variable define**********************
***********************************************************/
STATIC TUYA_I2C_NUM_E g_i2c_idx = TUYA_I2C_NUM_MAX;
STATIC UINT8_T g_write_buff[I2C_WRITE_BUFLEN] = {0};

STATIC THREAD_CFG_T example_i2c_task = {
    .priority = TASK_IIC_PRIORITY,
    .stackDepth = TASK_IIC_SIZE,
    .thrdname = "i2c"
};
STATIC THREAD_HANDLE example_i2c_handle;

/*i2c config*/
STATIC TUYA_IIC_BASE_CFG_T example_i2c_cfg = {
    .role = TUYA_IIC_MODE_MASTER,
    .speed = TUYA_IIC_BUS_SPEED_100K,
    .addr_width = TUYA_IIC_ADDRESS_7BIT,
};

/***********************************************************
***********************function define**********************
***********************************************************/
/**
 * @brief get CRC8 value for sht3x
 *
 * @param[in] data: data to be calculated
 * @param[in] len: data length
 *
 * @return CRC8 value
 */
STATIC UCHAR_T __example_sht3x_get_crc8(IN CONST UCHAR_T *data, IN USHORT_T len)
{
    UCHAR_T i;
    UCHAR_T crc = 0xFF;

    while (len--) 
    {
        crc ^= *data;

        for (i = 8; i > 0; --i) 
        {
            if (crc & 0x80)
                crc = (crc << 1) ^ 0x31;
            else
                crc = (crc << 1);
        }
        data++;
    }

    return crc;
}

/**
 * @brief check CRC8
 *
 * @param[in] data: data to be checked
 * @param[in] len: data length
 * @param[in] crc_val: crc value
 *
 * @return check result
 */
STATIC INT_T __example_sht3x_check_crc8(IN CONST UCHAR_T *data, IN CONST USHORT_T len, IN CONST UCHAR_T crc_val)
{
    if (__example_sht3x_get_crc8(data, len) != crc_val)
        return CRC_ERR;

    return CRC_OK;
}

/**
* @brief i2c init
*
* @param[in] clk: clk pin
* @param[in] sda: sda pin
* @param[in] idx: i2c idx
*
* @return error code
*/

STATIC OPERATE_RET __example_i2c_init(UINT8_T clk, UINT8_T sda, TUYA_I2C_NUM_E idx)
{
    if (idx >= TUYA_I2C_NUM_MAX)
    {
        TAL_PR_ERR("%s, input args is invalid!\r\n", __func__);
        return OPRT_INVALID_PARM;
    }

    TAL_PR_INFO("set tp i2c, clk: %d sda: %d, idx: %d\r\n", clk, sda, idx);
    g_i2c_idx = idx;

    tkl_io_pinmux_config(clk, I2C_SCL_ID);
    tkl_io_pinmux_config(sda, I2C_SDA_ID);

    // sw i2c init
    tkl_i2c_init(g_i2c_idx, &example_i2c_cfg);

    return OPRT_OK;
}

/**
* @brief i2c deinit
*
* @return error code
*/
STATIC OPERATE_RET __example_i2c_deinit(void)
{
    if (g_i2c_idx >= TUYA_I2C_NUM_MAX)
    {
        TAL_PR_ERR("%s, TUYA_I2C_NUM is not init!\r\n", __func__);
        return OPRT_RESOURCE_NOT_READY;
    }
    
    OPERATE_RET ret = OPRT_OK;
    ret = tkl_i2c_deinit(g_i2c_idx);
    g_i2c_idx = TUYA_I2C_NUM_MAX;

    return ret;
}

/**
* @brief i2c read 16bit reg value into buf
*
* @param[in] addr: i2c device addr
* @param[in] reg: reg address want to read
* @param[out] buf: the buf to store read data
* @param[in] buf_len: the buf_len to read
*
* @return error code
*/
OPERATE_RET __example_i2c_read_16bit_reg(UINT8_T addr, UINT16_T reg, UINT8_T *buf, UINT16_T buf_len)
{
    UINT8_T write_data[2] = {0};
    UINT8_T wirte_data_len = 0;

    if (!buf || !buf_len)
    {
        TAL_PR_ERR("%s, input args is invalid!\r\n", __func__);
        return OPRT_INVALID_PARM;
    }

    if (g_i2c_idx >= TUYA_I2C_NUM_MAX)
    {
        TAL_PR_ERR("%s, TUYA_I2C_NUM is not init!\r\n", __func__);
        return OPRT_RESOURCE_NOT_READY;
    }


    write_data[0] = (uint8_t)((reg >> 8) & 0xFF);
    write_data[1] = (uint8_t)(reg & 0xFF);
    wirte_data_len = TWO_BYTE;

    /* send reg addr first */
    tkl_i2c_master_send(g_i2c_idx, addr, write_data, wirte_data_len, 1);

    /* read value into buf */
    tkl_i2c_master_receive(g_i2c_idx, addr, buf, buf_len, 0);

    return OPRT_OK;
}

/**
* @brief i2c write buf into 16bit reg
*
* @param[in] addr: i2c device addr
* @param[in] reg: reg address want to write
* @param[in] buf: the buf to store write data
* @param[in] buf_len: the buf_len to write
*
* @return error code
*/
OPERATE_RET __example_i2c_write_16bit_reg(UINT8_T addr, UINT16_T reg, UINT8_T *buf, UINT16_T buf_len)
{
    if (!buf || !buf_len)
    {
        TAL_PR_ERR("%s, input args is invalid!\r\n", __func__);
        return OPRT_INVALID_PARM;
    }

    if (g_i2c_idx >= TUYA_I2C_NUM_MAX)
    {
        TAL_PR_ERR("%s, TUYA_I2C_NUM is not init!\r\n", __func__);
        return OPRT_RESOURCE_NOT_READY;
    }

    UINT8_T reg_len = TWO_BYTE;
    UINT8_T write_data_len = reg_len + buf_len;

    if (write_data_len > I2C_WRITE_BUFLEN)
    {
        TAL_PR_ERR("%s, write_data_len(%d+%d) is overflow the limit(%d)\r\n", __func__, buf_len, reg_len, I2C_WRITE_BUFLEN);
        return OPRT_EXCEED_UPPER_LIMIT;
    }

    memcpy(g_write_buff + reg_len, buf, buf_len);

    g_write_buff[0] = (uint8_t)((reg >> 8) & 0xFF);
    g_write_buff[1] = (uint8_t)(reg & 0xFF);

    /* send reg + buf */
    tkl_i2c_master_send(g_i2c_idx, addr, g_write_buff, write_data_len, 0);

    return OPRT_OK;
}

/**
* @brief i2c task
*
* @param[in] param:Task parameters
* @return none
*/
VOID __i2c_task(VOID* param)
{
    OPERATE_RET rt = OPRT_OK;
    UCHAR_T read_buf[I2C_READ_DATA_SIZE] = {0};
    USHORT_T temper = 0, humi = 0;

    /*i2c init*/
    TUYA_CALL_ERR_GOTO(__example_i2c_init(I2C_SCL_PIN, I2C_SDA_PIN, I2C_NUM_ID), __EXIT);

    do
    {
        __example_i2c_read_16bit_reg(SHT3X_ADDR, SHT3X_REG, read_buf, I2C_READ_DATA_SIZE);

        /*check read data*/
        if ((CRC_ERR == __example_sht3x_check_crc8(read_buf,   2, read_buf[2])) ||\
            (CRC_ERR == __example_sht3x_check_crc8(read_buf+3, 2, read_buf[5]))) {
            TAL_PR_ERR("[SHT3x] The received temp_humi data can't pass the CRC8 check.");
        }else {
            temper = ((USHORT_T)read_buf[0] << 8) | read_buf[1];
            humi   = ((USHORT_T)read_buf[3] << 8) | read_buf[4];

            TAL_PR_NOTICE("temper = %d humi = %d", temper, humi);
        }

        tal_system_sleep(2000);
    } while(1);

__EXIT:
    __example_i2c_deinit();
}

/**
* @brief examples_adc_init_and_read
*
* @return none
*/
VOID example_i2c(INT_T argc, CHAR_T *argv[])
{
    OPERATE_RET rt = OPRT_OK;
    TUYA_CALL_ERR_LOG(tal_thread_create_and_start(&example_i2c_handle, NULL, NULL, __i2c_task, NULL, &example_i2c_task));
    return;
}

#endif
