#include <math.h>   //necessary for the pow() function
#include <Wire.h>
//#include <Adafruit_NeoPixel.h>
//#include <Adafruit_SleepyDog.h>

#define TIMER_CLR_VOL         60 
#define TIMER_RESET           300 
#define CYCLE_TIMER           1000
#define PKG_HEADER_1          0x3C
#define PKG_HEADER_2          0x32
#define OPCODE_CLEAR_VOLUME   0x5A
#define OPCODE_RESET          0x5D
#define OPCODE_BACK_ACK       0xE5

int  Pkg_DefLen      =32;   
int  Pkg_CurLen      =0; 
int  count,Cmd_Count =0;
byte SendData[7]     ={0x00};  
byte Pkg_Buff[200]   ={0x00};
byte Report_Buff[200]={0x00};

static unsigned long Current_Time, Pre_Time;
static uint8_t  LED_Status = LOW;

void setup() {

  Serial.begin(115200);
  Serial1.begin(2400,SERIAL_8E1);

  Serial.println("System Start!");

}


void loop() 
{
  uint16_t  count;
  uint8_t i, j, k, buffer[64];
  uint8_t show_time = 0;
  
  Current_Time = millis();

  if ((Current_Time - Pre_Time) > CYCLE_TIMER)  show_time=1;  //Show interval

  if (show_time)
  {
      Pre_Time = Current_Time;
      show_time = 0;

      Cmd_Count++;
  }

  //Send Cmd to UFM01 
  if(Cmd_Count==TIMER_CLR_VOL)
  {
    Cmd_Clear_Volume();   //Clear Volume Every 60Sec
    Cmd_Count++;
  }
  else if(Cmd_Count==TIMER_RESET)
  {
    Cmd_Reset();          //Reset Dev Every 300Sec
    Cmd_Count=0;
  }

  count = Serial1.available();
  if(count > 0)
  {
    Serial1.readBytes(buffer, count);

    Recive_UFM01_Pkg(buffer, count);
	
	  Parser_UFM01_Data();
  }
}

/************************************************************************
 * function   : Recive_UFM01_Pkg
 * Description: Get the UFM01 Whole UART Package
 * input      : byte *Buff: Bytes Received from UART    
 *              int Len   : The Length of Rev bytes  
 * 
 * return     :  none
 ************************************************************************/
void Recive_UFM01_Pkg(byte* buff, int Len)
{
  if((Len==1)&&(buff[0]==PKG_HEADER_1))//Only Rec 1Byte and Empty Pkg_Buff
  {
    Pkg_Buff[0]=buff[0];
    Pkg_CurLen=1;
    return;
  }
  else if((Len==1)&&(buff[0]==OPCODE_BACK_ACK))//Ack Cmd
  {
    Pkg_CurLen=0;
    return;
  }
  else if((buff[0]==PKG_HEADER_1)&&(buff[1]==PKG_HEADER_2))//Rec more than 2Bytes
  {
     for(int i=0;i<Len;i++)
     {
        Pkg_Buff[i]=buff[i];
     }
     Pkg_CurLen=Len;

     if(Pkg_CurLen<Pkg_DefLen)
     {
         return;
     }
   }
   else if((Pkg_CurLen==1)&&(buff[0]==PKG_HEADER_2))//Already Rec 1Byte of Header,check if the coming byte is the next Header
   {
      for(int i=0;i<Len;i++)
      {
         Pkg_Buff[i+1]=buff[i];
      }
      Pkg_CurLen=Len+1;

      if(Pkg_CurLen<Pkg_DefLen)
      {
          return;
      }
   }
   else if(Pkg_CurLen>1) //Already Rec 2bytes of Header
   {
      if ((Pkg_CurLen + Len) > 200) //error,exceed the maximum buffer length
      {
          Pkg_CurLen = 0;
          return;
      }

      for (int k = 0; k < Len; k++)
      {
          Pkg_Buff[Pkg_CurLen++] = buff[k];
      }

      if (Pkg_CurLen != Pkg_DefLen)//It's the whole uart package?
      {
          return;
      }
   }
   else
   {
      Pkg_CurLen=0;
      return;     
   }

  Serial.print("\r\n\r\n");
  Serial.print("UFM01:");
  for(int i=0;i<Pkg_DefLen;i++)
  {
    Serial.print(Pkg_Buff[i],HEX);
    Serial.print(" ");
  }
  Serial.print("\r\n");
    
  //Got the whole UART package from UFM01
	Report_Buff[0] = 1;            //data type
	Report_Buff[1] = 1;            //1-New Data; 0-No Updated

	for (int i = 2; i < (Pkg_DefLen - 2); i++)
	{
		Report_Buff[i + 1] = Pkg_Buff[i];
	}

	Pkg_CurLen = 0;
}

/************************************************************************
 * function   : Parser_UFM01_Data
 * Description: Parser the UFM01 Reported Data
 * input      : none
 * return     : none
 ************************************************************************/
void Parser_UFM01_Data(void)
{
	if (Report_Buff[1] == 1)//Data Reported?
  {
    int      Index = 3;
    byte     Value;
    byte     BY1,BY2,BY3,BY4,BY5,BY6;
    char     ItemStr[100]; 
    uint16_t Pkg_Status=0;
    uint32_t Raw_Temp=0;
    uint32_t Pkg_DevID=0,Raw_PosVolume=0,Raw_Flow=0;
    float    Pkg_PosVolume,Pkg_Flow,Pkg_Temp;
  
    Report_Buff[1] = 0;//Marked,data is processed

    Serial.print(Cmd_Count);
    Serial.print("    "); 
    
    //DevID
    BY1= Report_Buff[Index++];   
    BY2= Report_Buff[Index++];
    BY3= Report_Buff[Index++]; 
    BY4= Report_Buff[Index++];
    BY5= Report_Buff[Index++];
    sprintf(ItemStr,"%02X%02X%02X%02X%02X",BY5,BY4,BY3,BY2,BY1);
    Serial.print("ID:");
    Serial.print(ItemStr);
  
    Index++;
    
    //Postive Volumne
    Value = Report_Buff[Index++];
    if (!((Value == 0x0A) || (Value == 0x1A)))
    {
      Serial.print("Error Format!");
      return;
    }

    BY1= Report_Buff[Index++];   
    BY2= Report_Buff[Index++];
    BY3= Report_Buff[Index++]; 
    BY4= Report_Buff[Index++];
    BY5= Report_Buff[Index++];
    BY6= Report_Buff[Index++];

    sprintf(ItemStr,"0x%02X%02X%02X%02X%02X%02X",BY6,BY5,BY4,BY3,BY2,BY1);
    Serial.print("    Vol:");
    Serial.print(ItemStr);
    
    if (Value == 0x0A)//Unit:L
    {
       Serial.print("ml");
    }
    else  //0x1A-Unit:Cubic
    {
       Serial.print("000ml");
    }

    //Flow Rate
    Value = Report_Buff[Index++];
    if (Value != 0x0B)
    {
      Serial.print("Error Format!");
      return;
    }
    
    Raw_Flow = Report_Buff[Index++] + (Report_Buff[Index++]<<8) + (Report_Buff[Index++]<<16) + (Report_Buff[Index++]<<24);
    
    Pkg_Flow = MyParse_hex_string(String(Raw_Flow, HEX));
    Pkg_Flow /= 100.0f;
    
    //Pkg_Flow = Raw_Flow / 100.0f; //Does not work as expected. HEX Value is divided by 100

    Value = Report_Buff[Index++];
    if (Value == 0x80)
    {
      Pkg_Flow = Pkg_Flow * (-1.0f);
    }
    Serial.print("   Flow:");
    Serial.print(String(Pkg_Flow));
    Serial.print("l/h");
    
    //AMP
    Value = Report_Buff[Index++];
    if (Value != 0x0C)
    {
      Serial.print("Error Format!");
      return;
    }
    Index++;
    Index++;

    //Temperature:Degree
    Value = Report_Buff[Index++];
    if (Value != 0x0D)
    {
      Serial.print("Error Format!");
      return;
    }

    // Problem to solve:
    // RAW_Temp is the correct order of strings in HEX
    // BUT it is not possible to divided by 100, because the HEX value is different as the shown strings.

    // Needed: 
    // conversion string_in_HEX --> value
    // then it is possible to divided this value by 100

    Raw_Temp = Report_Buff[Index++] + (Report_Buff[Index++]<<8) + (Report_Buff[Index++]<<16);

    //Pkg_Temp = (float(Raw_Temp) / 100.0f); //this divided the hex-value by 100
    
    Pkg_Temp = MyParse_hex_string(String(Raw_Temp, HEX));
    Pkg_Temp /= 100.0f;
    
    Serial.print("    Temp:");
    Serial.print(Pkg_Temp);
    Serial.print("C");

    //Status
    Pkg_Status = Report_Buff[Index++] + (Report_Buff[Index++] << 8);
    Serial.print("   Error:0x");
    sprintf(ItemStr,"%04X",Pkg_Status);
    Serial.print(ItemStr);
    Serial.print("\r\n");
  } 
}



/************************************************************************
 * function   : Cmd_Clear_Volume
 * Description: Send Command to UFM01 to Clear theWater Volume 
 * input      : none
 * return     : none
 ************************************************************************/
 void Cmd_Clear_Volume(void)
 {
   byte CmdValue=0xFD;
   Send_Cmd(OPCODE_CLEAR_VOLUME, CmdValue);

   Serial.print("Cmd:Clear UFM01 Water Volume");
   Serial.print("\r\n");
 }


/************************************************************************
 * function   : Cmd_Reset
 * Description: Reset the UFM01 Device 
 * input      : none
 * return     : none
 ************************************************************************/
 void Cmd_Reset(void)
 {
   byte CmdValue=0xCB;
   Send_Cmd(OPCODE_RESET, CmdValue);

   Serial.print("Cmd:Reset UFM01 Device");
   Serial.print("\r\n");
 }

/************************************************************************
 * function   : Send_Cmd
 * Description: Send Cmd to UFM01 By UART 
 * input      : byte CmdType-Type of the Command
 *              byte CmdValue-The value of the Command
 * return     : none
 ************************************************************************/
 void Send_Cmd(byte CmdType, byte CmdValue)
 {            
    SendData[0] = 0xFE;                       //Header                                          
    SendData[1] = 0xFE;                       //Header                       
    SendData[2] = 0x11;                       //Start                                   
    SendData[3] = CmdType;                    //Cmd                              
    SendData[4] = CmdValue;                   //Cmd Value
    SendData[5] = (byte)(CmdType + CmdValue); //CRC
    SendData[6] = 0x16;                       //Stop

    Serial1.write(SendData,sizeof(SendData));
 }
 
/************************************************************************
 * function   : Parse_hex_string
 * Description: Converting hex string to number 
 *              Example 0x2622 --> 9762
 * input      : String input - hexadecimal string
 * return     : long int result - Decimal Value
 ************************************************************************/
 long int Parse_hex_string(String input)
 {
  const String hexDigits = "0123456789ABCDEF";
  long int result = 0;

  input.toUpperCase();
  for (int i = 0; i < input.length(); i++) {
    result <<= 4; // shift by 4 bit
    result |= hexDigits.indexOf(input[i]);
  }

  return result;
}

/************************************************************************
 * function   : MyParse_hex_string
 * Description: Converting hex string to number
 *              Example 0x2622 --> 2622
 * input      : String input - hexadecimal string
 * return     : double result - Decimal Value
 ************************************************************************/
 double MyParse_hex_string(String input)
 {
  //input="2622";
  const String hexDigits = "0123456789ABCDEF";
  double result = 0;
  int i_max = input.length()-1; //max index for the string array, e.g. 0...3 with a length (size) of 4

  input.toUpperCase();
  for (int i = 0; i <= i_max; i++) {
    if (hexDigits.indexOf(input[i]) < 10) {
      result += ( hexDigits.indexOf(input[i]) * pow(10, i_max-i) );
    } else {
      // Converting of (A..F) are not possible!
      // result will be not changed
      //Serial.print("Converting of (A..F) are not possible");
    }
    
  }

  return result;
}
