/*!
  * @file      CayenneLpp.c
  *
  * @brief     Implements the Cayenne Low Power Protocol
  *
  * \copyright Revised BSD License, see section \ref LICENSE.
  *
  * \code
  *                ______                              _
  *               / _____)             _              | |
  *              ( (____  _____ ____ _| |_ _____  ____| |__
  *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
  *               _____) ) ____| | | || |_| ____( (___| | | |
  *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
  *              (C)2013-2018 Semtech
  *
  * \endcode
  *
  * \author    Miguel Luis ( Semtech )
  */
/* Includes ------------------------------------------------------------------*/
#include "stm32_mem.h"
#include "CayenneLpp.h"
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

/* External variables ---------------------------------------------------------*/
/* USER CODE BEGIN EV */

/* USER CODE END EV */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
#define CAYENNE_LPP_MAXBUFFER_SIZE                  242
#define LPP_DIGITAL_INPUT       0       /* 1 byte */
#define LPP_DIGITAL_OUTPUT      1       /* 1 byte */
#define LPP_ANALOG_INPUT        2       /* 2 bytes, 0.01 signed */
#define LPP_ANALOG_OUTPUT       3       /* 2 bytes, 0.01 signed */
#define LPP_LUMINOSITY          101     /* 2 bytes, 1 lux unsigned */
#define LPP_PRESENCE            102     /* 1 byte, 1 */
#define LPP_TEMPERATURE         103     /* 2 bytes, 0.1 Celsius degrees signed */
#define LPP_RELATIVE_HUMIDITY   104     /* 1 byte, 0.5% unsigned */
#define LPP_ACCELEROMETER       113     /* 2 bytes per axis, 0.001G */
#define LPP_BAROMETRIC_PRESSURE 115     /* 2 bytes 0.1 hPa Unsigned */
#define LPP_GYROMETER           134     /* 2 bytes per axis, 0.01 degrees/s */
#define LPP_GPS                 136     /* 3 byte lon/lat 0.0001 degrees, 3 bytes alt 0.01m */

/* Data ID + Data Type + Data Size */
#define LPP_DIGITAL_INPUT_SIZE       3
#define LPP_DIGITAL_OUTPUT_SIZE      3
#define LPP_ANALOG_INPUT_SIZE        4
#define LPP_ANALOG_OUTPUT_SIZE       4
#define LPP_LUMINOSITY_SIZE          4
#define LPP_PRESENCE_SIZE            3
#define LPP_TEMPERATURE_SIZE         4
#define LPP_RELATIVE_HUMIDITY_SIZE   3
#define LPP_ACCELEROMETER_SIZE       8
#define LPP_BAROMETRIC_PRESSURE_SIZE 4
#define LPP_GYROMETER_SIZE           8
#define LPP_GPS_SIZE                 11

/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
static uint8_t CayenneLppBuffer[CAYENNE_LPP_MAXBUFFER_SIZE];
static uint8_t CayenneLppCursor = 0;
/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/

/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Exported functions --------------------------------------------------------*/
void CayenneLppInit(void)
{
  CayenneLppCursor = 0;
  /* USER CODE BEGIN CayenneLppCursor */

  /* USER CODE END CayenneLppCursor */
}

void CayenneLppReset(void)
{
  CayenneLppCursor = 0;
  /* USER CODE BEGIN CayenneLppReset */

  /* USER CODE END CayenneLppReset */
}

uint8_t CayenneLppGetSize(void)
{
  /* USER CODE BEGIN CayenneLppGetSize */

  /* USER CODE END CayenneLppGetSize */
  return CayenneLppCursor;
}

uint8_t *CayenneLppGetBuffer(void)
{
  /* USER CODE BEGIN CayenneLppGetBuffer */

  /* USER CODE END CayenneLppGetBuffer */
  return CayenneLppBuffer;
}

uint8_t CayenneLppCopy(uint8_t *dst)
{
  /* USER CODE BEGIN CayenneLppCopy_1 */

  /* USER CODE END CayenneLppCopy_1 */
  UTIL_MEM_cpy_8(dst, CayenneLppBuffer, CayenneLppCursor);
  /* USER CODE BEGIN CayenneLppCopy_2 */

  /* USER CODE END CayenneLppCopy_2 */
  return CayenneLppCursor;
}

uint8_t CayenneLppAddDigitalInput(uint8_t channel, uint8_t value)
{
  /* USER CODE BEGIN CayenneLppAddDigitalInput_1 */

  /* USER CODE END CayenneLppAddDigitalInput_1 */
  if ((CayenneLppCursor + LPP_DIGITAL_INPUT_SIZE) > CAYENNE_LPP_MAXBUFFER_SIZE)
  {
    return 0;
  }
  CayenneLppBuffer[CayenneLppCursor++] = channel;
  CayenneLppBuffer[CayenneLppCursor++] = LPP_DIGITAL_INPUT;
  CayenneLppBuffer[CayenneLppCursor++] = value;
  /* USER CODE BEGIN CayenneLppAddDigitalInput_2 */

  /* USER CODE END CayenneLppAddDigitalInput_2 */
  return CayenneLppCursor;
}

uint8_t CayenneLppAddDigitalOutput(uint8_t channel, uint8_t value)
{
  /* USER CODE BEGIN CayenneLppAddDigitalOutput_1 */

  /* USER CODE END CayenneLppAddDigitalOutput_1 */
  if ((CayenneLppCursor + LPP_DIGITAL_OUTPUT_SIZE) > CAYENNE_LPP_MAXBUFFER_SIZE)
  {
    return 0;
  }
  CayenneLppBuffer[CayenneLppCursor++] = channel;
  CayenneLppBuffer[CayenneLppCursor++] = LPP_DIGITAL_OUTPUT;
  CayenneLppBuffer[CayenneLppCursor++] = value;
  /* USER CODE BEGIN CayenneLppAddDigitalOutput_2 */

  /* USER CODE END CayenneLppAddDigitalOutput_2 */
  return CayenneLppCursor;
}

uint8_t CayenneLppAddAnalogInput(uint8_t channel, float value)
{
  /* USER CODE BEGIN CayenneLppAddAnalogInput_1 */

  /* USER CODE END CayenneLppAddAnalogInput_1 */
  if ((CayenneLppCursor + LPP_ANALOG_INPUT_SIZE) > CAYENNE_LPP_MAXBUFFER_SIZE)
  {
    return 0;
  }

  int16_t val = (int16_t)(value * 100);
  CayenneLppBuffer[CayenneLppCursor++] = channel;
  CayenneLppBuffer[CayenneLppCursor++] = LPP_ANALOG_INPUT;
  CayenneLppBuffer[CayenneLppCursor++] = val >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = val;
  /* USER CODE BEGIN CayenneLppAddAnalogInput_2 */

  /* USER CODE END CayenneLppAddAnalogInput_2 */
  return CayenneLppCursor;
}

uint8_t CayenneLppAddAnalogOutput(uint8_t channel, float value)
{
  /* USER CODE BEGIN CayenneLppAddAnalogOutput_1 */

  /* USER CODE END CayenneLppAddAnalogOutput_1 */
  if ((CayenneLppCursor + LPP_ANALOG_OUTPUT_SIZE) > CAYENNE_LPP_MAXBUFFER_SIZE)
  {
    return 0;
  }
  int16_t val = (int16_t)(value * 100);
  CayenneLppBuffer[CayenneLppCursor++] = channel;
  CayenneLppBuffer[CayenneLppCursor++] = LPP_ANALOG_OUTPUT;
  CayenneLppBuffer[CayenneLppCursor++] = val >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = val;
  /* USER CODE BEGIN CayenneLppAddAnalogOutput_2 */

  /* USER CODE END CayenneLppAddAnalogOutput_2 */
  return CayenneLppCursor;
}

uint8_t CayenneLppAddLuminosity(uint8_t channel, uint16_t lux)
{
  /* USER CODE BEGIN CayenneLppAddLuminosity_1 */

  /* USER CODE END CayenneLppAddLuminosity_1 */
  if ((CayenneLppCursor + LPP_LUMINOSITY_SIZE) > CAYENNE_LPP_MAXBUFFER_SIZE)
  {
    return 0;
  }
  CayenneLppBuffer[CayenneLppCursor++] = channel;
  CayenneLppBuffer[CayenneLppCursor++] = LPP_LUMINOSITY;
  CayenneLppBuffer[CayenneLppCursor++] = lux >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = lux;
  /* USER CODE BEGIN CayenneLppAddLuminosity_2 */

  /* USER CODE END CayenneLppAddLuminosity_2 */
  return CayenneLppCursor;
}

uint8_t CayenneLppAddPresence(uint8_t channel, uint8_t value)
{
  /* USER CODE BEGIN CayenneLppAddPresence_1 */

  /* USER CODE END CayenneLppAddPresence_1 */
  if ((CayenneLppCursor + LPP_PRESENCE_SIZE) > CAYENNE_LPP_MAXBUFFER_SIZE)
  {
    return 0;
  }
  CayenneLppBuffer[CayenneLppCursor++] = channel;
  CayenneLppBuffer[CayenneLppCursor++] = LPP_PRESENCE;
  CayenneLppBuffer[CayenneLppCursor++] = value;
  /* USER CODE BEGIN CayenneLppAddPresence_2 */

  /* USER CODE END CayenneLppAddPresence_2 */
  return CayenneLppCursor;
}

uint8_t CayenneLppAddTemperature(uint8_t channel, float celsius)
{
  /* USER CODE BEGIN CayenneLppAddTemperature_1 */

  /* USER CODE END CayenneLppAddTemperature_1 */
  if ((CayenneLppCursor + LPP_TEMPERATURE_SIZE) > CAYENNE_LPP_MAXBUFFER_SIZE)
  {
    return 0;
  }
  int16_t val = (int16_t)(celsius * 10);
  CayenneLppBuffer[CayenneLppCursor++] = channel;
  CayenneLppBuffer[CayenneLppCursor++] = LPP_TEMPERATURE;
  CayenneLppBuffer[CayenneLppCursor++] = val >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = val;
  /* USER CODE BEGIN CayenneLppAddTemperature_2 */

  /* USER CODE END CayenneLppAddTemperature_2 */
  return CayenneLppCursor;
}

uint8_t CayenneLppAddRelativeHumidity(uint8_t channel, float rh)
{
  /* USER CODE BEGIN CayenneLppAddRelativeHumidity_1 */

  /* USER CODE END CayenneLppAddRelativeHumidity_1 */
  if ((CayenneLppCursor + LPP_RELATIVE_HUMIDITY_SIZE) > CAYENNE_LPP_MAXBUFFER_SIZE)
  {
    return 0;
  }
  CayenneLppBuffer[CayenneLppCursor++] = channel;
  CayenneLppBuffer[CayenneLppCursor++] = LPP_RELATIVE_HUMIDITY;
  CayenneLppBuffer[CayenneLppCursor++] = (uint8_t)(rh * 2);
  /* USER CODE BEGIN CayenneLppAddRelativeHumidity_2 */

  /* USER CODE END CayenneLppAddRelativeHumidity_2 */
  return CayenneLppCursor;
}

uint8_t CayenneLppAddAccelerometer(uint8_t channel, float x, float y, float z)
{
  /* USER CODE BEGIN CayenneLppAddAccelerometer_1 */

  /* USER CODE END CayenneLppAddAccelerometer_1 */
  if ((CayenneLppCursor + LPP_ACCELEROMETER_SIZE) > CAYENNE_LPP_MAXBUFFER_SIZE)
  {
    return 0;
  }
  int16_t vx = (int16_t)(x * 1000);
  int16_t vy = (int16_t)(y * 1000);
  int16_t vz = (int16_t)(z * 1000);

  CayenneLppBuffer[CayenneLppCursor++] = channel;
  CayenneLppBuffer[CayenneLppCursor++] = LPP_ACCELEROMETER;
  CayenneLppBuffer[CayenneLppCursor++] = vx >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = vx;
  CayenneLppBuffer[CayenneLppCursor++] = vy >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = vy;
  CayenneLppBuffer[CayenneLppCursor++] = vz >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = vz;
  /* USER CODE BEGIN CayenneLppAddAccelerometer_2 */

  /* USER CODE END CayenneLppAddAccelerometer_2 */
  return CayenneLppCursor;
}

uint8_t CayenneLppAddBarometricPressure(uint8_t channel, float hpa)
{
  /* USER CODE BEGIN CayenneLppAddBarometricPressure_1 */

  /* USER CODE END CayenneLppAddBarometricPressure_1 */
  if ((CayenneLppCursor + LPP_BAROMETRIC_PRESSURE_SIZE) > CAYENNE_LPP_MAXBUFFER_SIZE)
  {
    return 0;
  }
  int16_t val = (int16_t)(hpa * 10);

  CayenneLppBuffer[CayenneLppCursor++] = channel;
  CayenneLppBuffer[CayenneLppCursor++] = LPP_BAROMETRIC_PRESSURE;
  CayenneLppBuffer[CayenneLppCursor++] = val >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = val;
  /* USER CODE BEGIN CayenneLppAddBarometricPressure_2 */

  /* USER CODE END CayenneLppAddBarometricPressure_2 */
  return CayenneLppCursor;
}

uint8_t CayenneLppAddGyrometer(uint8_t channel, float x, float y, float z)
{
  /* USER CODE BEGIN CayenneLppAddGyrometer_1 */

  /* USER CODE END CayenneLppAddGyrometer_1 */
  if ((CayenneLppCursor + LPP_GYROMETER_SIZE) > CAYENNE_LPP_MAXBUFFER_SIZE)
  {
    return 0;
  }
  int16_t vx = (int16_t)(x * 100);
  int16_t vy = (int16_t)(y * 100);
  int16_t vz = (int16_t)(z * 100);

  CayenneLppBuffer[CayenneLppCursor++] = channel;
  CayenneLppBuffer[CayenneLppCursor++] = LPP_GYROMETER;
  CayenneLppBuffer[CayenneLppCursor++] = vx >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = vx;
  CayenneLppBuffer[CayenneLppCursor++] = vy >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = vy;
  CayenneLppBuffer[CayenneLppCursor++] = vz >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = vz;
  /* USER CODE BEGIN CayenneLppAddGyrometer_2 */

  /* USER CODE END CayenneLppAddGyrometer_2 */
  return CayenneLppCursor;
}

uint8_t CayenneLppAddGps(uint8_t channel, float latitude, float longitude, float meters)
{
  /* USER CODE BEGIN CayenneLppAddGps_1 */

  /* USER CODE END CayenneLppAddGps_1 */
  if ((CayenneLppCursor + LPP_GPS_SIZE) > CAYENNE_LPP_MAXBUFFER_SIZE)
  {
    return 0;
  }
  int32_t lat = (int32_t)(latitude * 10000);
  int32_t lon = (int32_t)(longitude * 10000);
  int32_t alt = (int32_t)(meters * 100);

  CayenneLppBuffer[CayenneLppCursor++] = channel;
  CayenneLppBuffer[CayenneLppCursor++] = LPP_GPS;

  CayenneLppBuffer[CayenneLppCursor++] = lat >> 16;
  CayenneLppBuffer[CayenneLppCursor++] = lat >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = lat;
  CayenneLppBuffer[CayenneLppCursor++] = lon >> 16;
  CayenneLppBuffer[CayenneLppCursor++] = lon >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = lon;
  CayenneLppBuffer[CayenneLppCursor++] = alt >> 16;
  CayenneLppBuffer[CayenneLppCursor++] = alt >> 8;
  CayenneLppBuffer[CayenneLppCursor++] = alt;
  /* USER CODE BEGIN CayenneLppAddGps_2 */

  /* USER CODE END CayenneLppAddGps_2 */
  return CayenneLppCursor;
}

/* USER CODE BEGIN EF */

/* USER CODE END EF */

/* Private Functions Definition -----------------------------------------------*/
/* USER CODE BEGIN PrFD */

/* USER CODE END PrFD */