/**
  ******************************************************************************
  * @file    sensors_data.c
  * @author  MCD Application Team
  * @version V1.1.0
  * @date    04-September-2017
  * @brief   Manage sensors of STM32L475 IoT board.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2017 STMicroelectronics International N.V.
  * All rights reserved.</center></h2>
  *
  * Redistribution and use in source and binary forms, with or without 
  * modification, are permitted, provided that the following conditions are met:
  *
  * 1. Redistribution of source code must retain the above copyright notice, 
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. Neither the name of STMicroelectronics nor the names of other 
  *    contributors to this software may be used to endorse or promote products 
  *    derived from this software without specific written permission.
  * 4. This software, including modifications and/or derivative works of this 
  *    software, must execute solely and exclusively on microcontroller or
  *    microprocessor devices manufactured by or for STMicroelectronics.
  * 5. Redistribution and use of this software other than as permitted under 
  *    this license is void and will automatically terminate your rights under 
  *    this license. 
  *
  * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" 
  * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT 
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
  * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
  * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT 
  * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "stm32l4xx_hal.h"
#include "msg.h"
#include "sensors_data.h"

#include "x_nucleo_iks01a2.h"
#include "x_nucleo_iks01a2_accelero.h"
#include "x_nucleo_iks01a2_gyro.h"
#include "x_nucleo_iks01a2_magneto.h"
#include "x_nucleo_iks01a2_pressure.h"
#include "x_nucleo_iks01a2_temperature.h"
#include "x_nucleo_iks01a2_humidity.h"

/* Private typedef -----------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/
#define VDDA_APPLI                     ((uint32_t) 3300)    /* Value of analog voltage supply Vdda (unit: mV) */
/* Internal temperature sensor: constants data used for indicative values in  */
/* this example. Refer to device datasheet for min/typ/max values.            */
/* For more accurate values, device should be calibrated on offset and slope  */
/* for application temperature range.                                         */
#define INTERNAL_TEMPSENSOR_V30        ((int32_t)760)           /* Internal temperature sensor, parameter V25 (unit: mV). Refer to device datasheet for min/typ/max values. */
#define INTERNAL_TEMPSENSOR_AVGSLOPE   ((int32_t)2500)          /* Internal temperature sensor, parameter Avg_Slope (unit: uV/DegCelsius). Refer to device datasheet for min/typ/max values. */
#define TEMP30_CAL_ADDR   ((uint16_t*) ((uint32_t)0x1FFF75A8))  /* Internal temperature sensor, parameter TS_CAL1: TS ADC raw data acquired at a temperature of 30 DegC (+-5 DegC) */
#define TEMP110_CAL_ADDR  ((uint16_t*) ((uint32_t)0x1FFF75CA))  /* Internal temperature sensor, parameter TS_CAL2: TS ADC raw data acquired at a temperature of  110 DegC (+-5 DegC) */
#define VDDA_TEMP_CAL                  ((uint32_t)3000)        /* Vdda value with which temperature sensor has been calibrated in production (+-10 mV). */

/* Private macros ------------------------------------------------------------*/
/**
  * @brief  Computation of temperature (unit: degree Celsius) from the internal
  *         temperature sensor measurement by ADC.
  *         Computation is using temperature sensor calibration values done
  *         in production.
  *         Computation formula:
  *         Temperature = (TS_ADC_DATA - TS_CAL1) * (110degC - 30degC)
  *                       / (TS_CAL2 - TS_CAL1) + 30degC
  *         with TS_ADC_DATA = temperature sensor raw data measured by ADC
  *              Avg_Slope = (TS_CAL2 - TS_CAL1) / (110 - 30)
  *              TS_CAL1 = TS_ADC_DATA @30degC (calibrated in factory)
  *              TS_CAL2 = TS_ADC_DATA @110degC (calibrated in factory)
  *         Calculation validity conditioned to settings: 
  *          - ADC resolution 12 bits (need to scale conversion value 
  *            if using a different resolution).
  *          - Power supply of analog voltage set to literal VDDA_APPLI
  *            (need to scale value if using a different value of analog
  *            voltage supply).
  * @param TS_ADC_DATA: Temperature sensor digital value measured by ADC
  * @retval None
  */
#define COMPUTATION_TEMPERATURE_TEMP30_TEMP110(TS_ADC_DATA)                    \
  (((( ((int32_t)((TS_ADC_DATA * VDDA_APPLI) / VDDA_TEMP_CAL)                  \
        - (int32_t) *TEMP30_CAL_ADDR)                                          \
     ) * (int32_t)(110 - 30)                                                   \
    ) / (int32_t)(*TEMP110_CAL_ADDR - *TEMP30_CAL_ADDR)                        \
   ) + 30                                                                      \
  )

/* Private variables ---------------------------------------------------------*/
extern ADC_HandleTypeDef    hadc1;

void *HandleAccSensor = NULL;
void *HandleGyroSensor = NULL;
void *HandleMagSensor = NULL;

void *HandlePressSensor = NULL;
void *HandleTempSensor = NULL;
void *HandleHumSensor = NULL;

/* Global variables ----------------------------------------------------------*/
static SensorAxes_t ACC_Value;
static SensorAxes_t GYR_Value;
static SensorAxes_t MAG_Value;

static float    PRESSURE_Value;
static float    TEMPERATURE_Value;
static float    HUMIDITY_Value;



/* Private function prototypes -----------------------------------------------*/
static uint32_t temp_sensor_getValue();

/* Functions Definition ------------------------------------------------------*/
/**
  * @brief  Get JunctionTemp level in 12 bits.
  * @retval JunctionTemp level(0..0xFFF) / 0xFFFFFFFF : Error
  */
static uint32_t temp_sensor_getValue()
{   
  static uint32_t   wTemperature_DegreeCelsius = 0;
  
  if(HAL_ADC_PollForConversion(&hadc1, 1000)== HAL_OK)
  {
    wTemperature_DegreeCelsius = COMPUTATION_TEMPERATURE_TEMP30_TEMP110(HAL_ADC_GetValue(&hadc1) );
    
    return (wTemperature_DegreeCelsius);
  } 
  
  return  0xFFFFFFFF;
}

/**
  * @brief  init_sensors
  * @param  none
  * @retval 0 in case of success
  *         -1 in case of failure
  */
int init_sensors(void)
{
  int ret = 0;
  
  HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
  HAL_ADC_Start(&hadc1) ;
  
  if (COMPONENT_OK != BSP_ACCELERO_Init( LSM6DSL_X_0, &HandleAccSensor ))
  {
    msg_error("BSP_ACCELERO_Init() returns %d\n", ret);
    ret = -1;
  }
  
  if (COMPONENT_OK != BSP_ACCELERO_Sensor_Enable( HandleAccSensor ))
  {
    msg_error("BSP_ACCELERO_Sensor_Enable() returns %d\n", ret);
    ret = -1;
  }

  if (COMPONENT_OK != BSP_GYRO_Init( LSM6DSL_G_0, &HandleGyroSensor ))
  {
    msg_error("BSP_GYRO_Init() returns %d\n", ret);
    ret = -1;
  }

  BSP_GYRO_Sensor_Enable( HandleGyroSensor );  

  if (COMPONENT_OK != BSP_MAGNETO_Init( LSM303AGR_M_0, &HandleMagSensor ))
  {
    msg_error("BSP_MAGNETO_Init() returns %d\n", ret);
    ret = -1;
  }
  
  BSP_MAGNETO_Sensor_Enable( HandleMagSensor );

  if (COMPONENT_OK != BSP_PRESSURE_Init( LPS22HB_P_0, &HandlePressSensor ))
  {
    msg_error("BSP_PRESSURE_Init() returns %d\n", ret);
    ret = -1;
  }
  
  BSP_PRESSURE_Sensor_Enable( HandlePressSensor );

  if (COMPONENT_OK != BSP_TEMPERATURE_Init( HTS221_T_0, &HandleTempSensor ))
  {
    msg_error("BSP_TEMPERATURE_Init() returns %d\n", ret);
    ret = -1;
  }
  
  BSP_TEMPERATURE_Sensor_Enable( HandleTempSensor );
  
  if (COMPONENT_OK != BSP_HUMIDITY_Init( HTS221_H_0, &HandleHumSensor ))
  {
    msg_error("BSP_HUMIDITY_Init() returns %d\n", ret);
    ret = -1;
  }

  BSP_HUMIDITY_Sensor_Enable( HandleHumSensor );  
  
  return ret;
}

/**
  * @brief  fill the payload with the sensor values
  * @param  none
  * @param PayloadBuffer is the char pointer for the Payload buffer to be filled
  * @param PayloadSize size of the above buffer
  * @retval 0 in case of success
  *         -1 in case of failure
  */
int prepare_mqtt_payload(char * payloadBuffer, int payloadSize, char * deviceID)
{
  char * buff = payloadBuffer;
  int buffSize = payloadSize;
  int snprintfreturn = 0;

  uint32_t tempValue = temp_sensor_getValue();

  /* Read the Acc values */
  BSP_ACCELERO_Get_Axes(HandleAccSensor,&ACC_Value);

  /* Read the Gyro values */
  BSP_GYRO_Get_Axes(HandleGyroSensor,&GYR_Value);  
  
  /* Read the Magneto values */
  BSP_MAGNETO_Get_Axes(HandleMagSensor,&MAG_Value);
  
  BSP_PRESSURE_Get_Press(HandlePressSensor,(float *)&PRESSURE_Value);
  
  BSP_TEMPERATURE_Get_Temp(HandleTempSensor,(float *)&TEMPERATURE_Value);
  
  BSP_HUMIDITY_Get_Hum(HandleHumSensor,(float *)&HUMIDITY_Value);
  
  if (deviceID != NULL)
  {
    snprintfreturn = snprintf( buff, buffSize, "{\"deviceId\":\"%s\","
             "\"temperature\": %d"
             "}",
             deviceID,
             tempValue );
  }
  else
  { 
    snprintfreturn = snprintf( buff, buffSize, "{\n \"state\": {\n  \"reported\": {\n"
           "   \"environmental_temperature\": %.2f,\n   \"humidity\": %.2f,\n   \"pressure\": %.2f,\n   \"mcu_temperature\": %d,\n"
           "   \"acc_x\": %d, \"acc_y\": %d, \"acc_z\": %d,\n"
           "   \"gyr_x\": %d, \"gyr_y\": %d, \"gyr_z\": %d,\n"
           "   \"mag_x\": %d, \"mag_y\": %d, \"mag_z\": %d\n"
           "  }\n }\n}",
           TEMPERATURE_Value, HUMIDITY_Value, PRESSURE_Value, tempValue,
           ACC_Value.AXIS_X, ACC_Value.AXIS_Y, ACC_Value.AXIS_Z,
           GYR_Value.AXIS_X, GYR_Value.AXIS_Y, GYR_Value.AXIS_Z,
           MAG_Value.AXIS_X, MAG_Value.AXIS_Y, MAG_Value.AXIS_Z );  
  }

  /* Check total size to be less than buffer size
            * if the return is >=0 and <n, then
            * the entire string was successfully formatted; if the return is
            * >=n, the string was truncated (but there is still a null char
            * at the end of what was written); if the return is <0, there was
            * an error.
            */
  if (snprintfreturn >= 0 && snprintfreturn < payloadSize)
  {
      return 0;
  }
  else if(snprintfreturn >=payloadSize)
  {
      msg_error("Data Pack truncated\n");
      return 0;
  }
  else
  {
      msg_error("Data Pack Error\n");
      return -1;
  }
}

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
