#include "AHTxx.h"

/* Defines */
#define AHT_I2C_ADDR    (uint8_t)0x38

#define CMD_GET_STATUS  (uint8_t)0x71
#define CMD_INIT        (uint8_t)0xbe
#define CMD_MEASURE     (uint8_t)0xac
#define CMD_MEASURE_CTL (uint8_t)0x33
#define CMD_MEASURE_NOP (uint8_t)0x00
#define CMD_SRESET      (uint8_t)0xba
#define CMD_CALIBR1     (uint8_t)0x1b
#define CMD_CALIBR2     (uint8_t)0x1c
#define CMD_CALIBR3     (uint8_t)0x1e

#define STATUS_BUSY     (uint8_t)0x80
#define STATUS_CMD      (uint8_t)0x40
#define STATUS_CYC      (uint8_t)0x20
#define STATUS_CAL      (uint8_t)0x08
#define STATUS_CALB     (uint8_t)0x18

/* Private variables */
static ahtxx_t Data;
static Timer startTimer, getTimer;

/* Private functions prototypes */
static ahtxx_st_t I2CWriteTo(uint8_t DataToSend);
//static uint8_t Calc_CRC8(uint8_t *message, uint8_t Num);
static ahtxx_st_t JH_Reset_REG(uint8_t addr);
void _Init(void);
void _GetData(void);
void _StartMeasure(void);

/**
 * Initialization
 */
void AHTxx_Init(void) {
  // Just powered on, it takes time for the product chip to be ready internally,
  // the delay is 100~500ms, and 500ms is recommended
  auto timer = new AutoDeleteTimer;
  timer->initializeMs<500>(_Init);
  timer->startOnce();
}

void _Init(void) {
  ahtxx_st_t res;
  uint8_t data;

  // When power on, send 0x71 to read the status word for the first time,
  // and judge whether the status word is 0x18.
  Wire.beginTransmission(AHT_I2C_ADDR);
  Wire.write(CMD_GET_STATUS);
  res = (ahtxx_st_t)Wire.endTransmission();
  if (res != St_OK) {
    Data.Error = St_NoSensor;
    return;
  }

  Wire.requestFrom(AHT_I2C_ADDR, 1);
  data = Wire.read();

  if ((data & STATUS_CALB) != STATUS_CALB) {
    //reinitialize registers
    res = JH_Reset_REG(0x1b);
    if (res != St_OK) {
      Data.Error = res;
      return;
    }
    res = JH_Reset_REG(0x1c);
    if (res != St_OK) {
      Data.Error = res;
      return;
    }
    res = JH_Reset_REG(0x1e);
    if (res != St_OK) {
      Data.Error = res;
      return;
    }

    delay(1);
  }

  Data.Error = St_OK;

	// start polling sensors - once per two seconds
	startTimer.initializeMs(2000, _StartMeasure).start();
}

void AHTxx_SoftReset(void) {
  ahtxx_st_t res;
  Data.Error = St_OK;

  res = I2CWriteTo(CMD_SRESET);

  if (res != St_OK) {
    Data.Error = res;
    return;
  }
  startTimer.restart();
}

/**
 * @brief Start Sensor measure.
 */
void _StartMeasure(void) {
  ahtxx_st_t res;
  Data.Error = St_OK;

  Wire.beginTransmission(AHT_I2C_ADDR);
  Wire.write(CMD_MEASURE);
  Wire.write(CMD_MEASURE_CTL);
  Wire.write(CMD_MEASURE_NOP);
  res = (ahtxx_st_t)Wire.endTransmission();
  if (res != St_OK) {
    Data.Error = res;
    return;
  }

	getTimer.initializeMs(600, _GetData).startOnce();
}

void AHTxx_GetData(ahtxx_t * data) {
  data->Error = Data.Error;
  data->Humidity = Data.Humidity;
  data->Temperature = Data.Temperature;
}

void _GetData(void) {
  ahtxx_st_t res;
  uint8_t buf[8] = {0}, i = 0;
  Data.Error = St_OK;

  /* Now read 7 bytes of data */
  Wire.requestFrom(AHT_I2C_ADDR, 7);
  while (Wire.available()) {
    buf[i] = Wire.read();
    i ++;
  }
  if (i < 7) {
    Data.Error = St_NACK_Data;
    return;
  }

  if ((buf[0] & STATUS_BUSY) != 0) {
    Data.Error = St_Timeout;
    return;
  }

  /* Calculate values */
  uint32_t result;

  /* Humidity = Srh * 100% / 2^20 */
  result = buf[1];
  result <<= 8;
  result |= buf[2];
  result <<= 8;
  result |= buf[3];
  result >>= 4;
  result *= 1000;
  result += 524288;
  result /= 256;
  result /= 256;
  result /= 16;
  Data.Humidity = (uint16_t)result; // in xx.x %

  /* Temperature = St * 200 / 2^20 - 50 */
  result = buf[3] & 0xf;
  result <<= 8;
  result |= buf[4];
  result <<= 8;
  result |= buf[5];
  result *= 2000;
  result += 524288;
  result /= 256;
  result /= 256;
  result /= 16;
  Data.Temperature = (int16_t)(result - 500); // in xx.x *C
}

/* Reset register */
static ahtxx_st_t JH_Reset_REG(uint8_t addr) {
  ahtxx_st_t res;
  uint8_t Byte_first, Byte_second, Byte_third;

  Wire.beginTransmission(AHT_I2C_ADDR);
  Wire.write((uint8_t)0x70);
  Wire.write(addr);
  Wire.write((uint8_t)0x00);
  Wire.write((uint8_t)0x00);
  res = (ahtxx_st_t)Wire.endTransmission();
  if (res != St_OK) {
    return res;
  }

  delay(5); //Delay about 5ms
  Wire.beginTransmission(AHT_I2C_ADDR);
  Wire.write((uint8_t)0x71);
  res = (ahtxx_st_t)Wire.endTransmission();
  if (res != St_OK) {
    return res;
  }
  Wire.requestFrom(AHT_I2C_ADDR, 3);
  Byte_first = Wire.read();
  Byte_second = Wire.read();
  Byte_third = Wire.read();

  delay(10); //Delay about 10ms
  Wire.beginTransmission(AHT_I2C_ADDR);
  Wire.write((uint8_t)0x70);
  Wire.write((uint8_t)(0xB0|addr)); //register command
  Wire.write(Byte_second);
  Wire.write(Byte_third);
  res = (ahtxx_st_t)Wire.endTransmission();
  if (res != St_OK) {
    return res;
  }

  return St_OK;
}

static ahtxx_st_t I2CWriteTo(uint8_t DataToSend) {
  Wire.beginTransmission(AHT_I2C_ADDR);
  Wire.write(DataToSend);
  return (ahtxx_st_t)Wire.endTransmission();
}

/**
 * CRC check type: CRC8/MAXIM
 * Polynomial: X8+X5+X4+1
 * Poly: 0011 0001 0x31
 * When the high bit is placed in the back, it becomes 1000 1100 0x8c
 */
/*
static uint8_t Calc_CRC8(uint8_t *message, uint8_t Num)
{
  uint8_t i;
  uint8_t byte;
  uint8_t crc = 0xFF;

  for (byte=0; byte<Num; byte++) {
    crc ^= (message[byte]);
    for (i=8; i>0; --i) {
      if (crc & 0x80) {
        crc = (crc << 1) ^ 0x31;
      } else {
        crc = (crc << 1);
      }
    }
  }

  return crc;
}
*/
