/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */

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

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */

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

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/

/* USER CODE BEGIN PV */
/*
 * BASE CLOCK 12MHz
 * Desired interrupt period 60s
 */
const uint16_t tim21_prescaler = 60000-1; // 100Hz
//const uint16_t tim21_period = 12000-1; // 60s
//const uint16_t tim21_period = 1200-1; // 6s
const uint16_t tim21_period = MEASUREMENT_PERIOD_S * (SYSTEM_CLOCK_HZ / tim21_prescaler) - 1;
//const uint16_t tim21_period = 200-1; // 1s

/* Input register memory map
 * (All registers are 16-bit)
 * -------------------------
 *
 * 30010 : CO2  [ppm]        Unsigned value in range [0,40000]
 * 30011 : T    [deg_C * 10] From SHT4x; unsigned value in range [0; 1250]; e.g. 21.5 C => 215
 * 30012 : RH   [%]          From SHT4x; unsigned value in range [0; 100]
 *
 * Backup T and RH sensor:
 * 30013 : T    [deg_C * 10] From SCD4x; unsigned value in range [0; 600]; e.g. 21.5 C => 215
 * 30014 : RH   [%]          From SCD4x; unsigned value in range [0; 100]
 *
 * Extended temperature range (signed values):
 * 30015 : T    [deg_C * 10] From SHT4x; signed value (two's complement) in range [-400;1250]
 * 30016 : T    [deg_C * 10] From SCD4x; signed value (two's complement) in range [-100;600]; e.g. -12.3 C => -123
 *
 */
/* Input registers memory map implementation */
enum
{
	REGISTER_NUM_SERIAL_NUMBER_1 = 30001,
	REGISTER_NUM_SERIAL_NUMBER_2 = 30002,
	REGISTER_NUM_T = 30003, /* deg C */
	REGISTER_NUM_T_F = 30004, /* deg F */
	REGISTER_NUM_RH = 30005, /* %, from SHT4x */
	REGISTER_NUM_LIGHT_INTENSITY_0 = 30006, /* ticks, from LTR329 */ /*TODO: Find out what it is */
	REGISTER_NUM_LIGHT_INTENSITY_1 = 30007, /* ticks, from LTR329 */ /*TODO: Find out what it is */
	REGISTER_NUM_ERROR_T_RH	= 30008, /* 0 = OK, 1 I2C = ERROR, 2 = CRC ERROR */
	REGISTER_NUM_ERROR_LIGHT = 30009 /* 0 = OK, 1 = I2C ERROR */
} data_registers_numbers;

enum
{
	REGISTER_NUM_MODBUS_ADDR = 40001,
	REGISTER_NUM_BAUDRATE = 40002,
	REGISTER_NUM_LTR329_GAIN = 40003,
	REGISTER_NUM_LTR329_MEAS_RATE = 40004,
	REGISTER_NUM_LTR329_INTEGRATION_TIME = 40005,
	REGISTER_NUM_LTR329_MODE = 40006,
	REGISTER_NUM_RESET_DEVICE = 49999
	/* TODO: Change registers to start with the modbus specific settings? */
	/* TODO: Seal the gap */
} config_registers_numbers;

/* Variables to store the measured data */
int16_t T_SHT4x, TF_SHT4x;
uint16_t RH_SHT4x;
uint16_t light_ch0, light_ch1;
uint16_t error_sht, error_ltr;
/* Struct to store the sensor config */
config_t sensor_config;
/* Device ID struct */
modbus_device_id_t device_id;
uint8_t sensor_config_pending_write = 0;
uint8_t baudrate_changed = 0;
uint8_t ltr329_config_changed = 0;
uint8_t modbus_address_changed = 0;


/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_I2C1_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_TIM21_Init(void);
static void MX_IWDG_Init(void);
/* USER CODE BEGIN PFP */
void USART2_TX_Buffer(uint8_t* buffer_tx, uint16_t buffer_tx_len);
/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

int8_t sht4x_i2c_transmit(uint8_t address, uint8_t *buffer, int len)
{
	return i2c_transmit(address, buffer, len);
}

int8_t sht4x_i2c_receive(uint8_t address, uint8_t *buffer, int len)
{
	return i2c_receive(address, buffer, len);
}

uint8_t sensirion_crc8_calculate(const uint8_t *data, uint16_t count)
{
	return crc8_calculate(data, count);
}

int8_t sht4x_disable_interrupts(void)
{
	return uart_disable_interrupts();
}

int8_t sht4x_enable_interrupts(void)
{
	return uart_enable_interrupts();
}

int8_t ltr329_i2c_transmit(uint8_t address, uint8_t *buffer, int len)
{
	return i2c_transmit(address, buffer, len);
}
int8_t ltr329_i2c_receive(uint8_t address, uint8_t *buffer, int len)
{
	return i2c_receive(address, buffer, len);
}

int8_t ltr329_disable_interrupts(void)
{
	return uart_disable_interrupts();
}

int8_t ltr329_enable_interrupts(void)
{
	return uart_enable_interrupts();
}

void delay_ms(int delay_ms)
{
	LL_mDelay(delay_ms);
}
/* USER CODE END 0 */

/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */

  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG);
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR);

  /* System interrupt init*/
  /* SysTick_IRQn interrupt configuration */
  NVIC_SetPriority(SysTick_IRQn, 3);

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
  /* Read config from EEPROM - if unsuccessful, set the default values*/
  int8_t config_read_status = config_read(&sensor_config);
  if (config_read_status != CONFIG_OK)
  {
	  sensor_config.modbus_addr = MODBUS_DEFAULT_SLAVE_ADDRESS;
	  sensor_config.baudrate_index = CONFIG_DEFAULT_BAUDRATE_INDEX;
  }
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_I2C1_Init();
  MX_USART2_UART_Init();
  MX_TIM21_Init();
  MX_IWDG_Init();
  /* USER CODE BEGIN 2 */

  /* Turn on MAGENTA LED to signal startup state */
//  LL_GPIO_ResetOutputPin(LED_R_GPIO_Port, LED_R_Pin);
//  LL_GPIO_ResetOutputPin(LED_G_GPIO_Port, LED_G_Pin);
//  LL_GPIO_ResetOutputPin(LED_B_GPIO_Port, LED_B_Pin);

  /* Enable I2C for sensors */
  LL_I2C_Enable(I2C1);


  /* Set the modbus address */
  modbus_slave_set_address(sensor_config.modbus_addr);

  /* Enable UART for RS485 */
  LL_USART_Enable(USART2);

  /* Start the timer for measurement triggering */
  LL_TIM_EnableCounter(TIM21);
  LL_TIM_EnableIT_UPDATE(TIM21);

  /* I2C context init (for SHT4x and SCD4x) */
  i2c_context_t i2c_context;
  i2c_context.i2c = I2C1;
  i2c_init(&i2c_context);

  /* Fill device ID struct */
  device_id.object_name.VendorName         = "Veles Labs";
  device_id.object_name.ProductCode        = "124C41";
  device_id.object_name.MajorMinorRevision = "1.2";
  device_id.object_name.VendorUrl          = "https://veles-labs.com";
  device_id.object_name.ProductName        = "RHT_Wired";
  device_id.object_name.ModelName          = "Hugo";
  modbus_slave_init_device_id(&device_id);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */

  /* Wait 1000ms for sensors initialization */
  /* SHT4x Init Time:  max 1 	ms 	(datasheet pg. 8) */
  /* LTR329 Init Time: max 100 	ms 	(datasheet pg. 9) */
  LL_mDelay(1000);

  /* Initiualize LTR329 */
  int8_t ltr_ret;
  do
  {
	  ltr_ret = ltr329_write_settings(LTR329_GAIN_48X, LTR329_MODE_ACTIVE, LTR329_INTEGRATION_100MS, LTR329_MEAS_RATE_100MS);
	  /* TODO: Check register status */
  } while (ltr_ret != 0);
  /* LTR329 Innitialized */
  ltr329_gain_t ltr329_gain;
  ltr329_als_mode_t ltr329_mode;
  ltr329_integration_time_t ltr329_int_time;
  ltr329_measurement_rate_t ltr329_meas_rate;
  ltr329_read_settings(&ltr329_gain, &ltr329_mode, &ltr329_int_time, &ltr329_meas_rate);
  sensor_config.ltr329_gain = ltr329_gain;
  sensor_config.ltr329_mode = ltr329_mode;
  sensor_config.ltr329_integ_time = ltr329_int_time;
  sensor_config.ltr329_meas_rate = ltr329_meas_rate;

  /*static uint32_t new_baud;*/
  /* Enter the main loop */
  while (1)
  {
	  /* Watchdog is set to 28 s (37 kHz, prescaler 256, 4095 reload value) */
	  LL_IWDG_ReloadCounter(IWDG);
	  if (usart2_rx_done == 1)
	  {
	  /* Process the message */
		  if (usart2_rx_message_too_long)
		  {
			  /* Do nothing, just delete the buffer and set the flag back to zero*/
			  usart2_rx_message_too_long = 0;
		  } else
		  {
			  /* Process the message:
			   * message is stored in modbus_buffer[], no copying necessary;
			   * but we need to make sure that modbus_buffer[] will not be used while
			   * processing the message: this can be done by disabling RX interrupt */
			  LL_USART_DisableIT_RXNE(USART2);
			  modbus_slave_process_msg(modbus_buffer, usart2_rx_message_len);
			  /* Reset the RX DONE flag */
			  usart2_rx_done = 0;
			  LL_USART_EnableIT_RXNE(USART2);
		  }
		  /* Reset the RX DONE flag */
		  usart2_rx_done = 0;
	  }
	  /* if config changed (MODBUS write), reflect changes to EEPROM */
	  if (sensor_config_pending_write) {
		  uart_disable_interrupts();
		  config_write(&sensor_config);
		  sensor_config_pending_write = 0;
		  uart_enable_interrupts();
	  }
	  if (modbus_address_changed)
	  {
		  modbus_slave_set_address(sensor_config.modbus_addr);
		  modbus_address_changed = 0;
	  }
	  if (baudrate_changed)
	  {
		  while (!LL_USART_IsActiveFlag_TXE(USART2));
		  uart_disable_interrupts();
		  LL_USART_SetBaudRate(USART2, SYSTICK_FREQ_HZ, LL_USART_OVERSAMPLING_16,
				  	  	  	   config_baudrates[sensor_config.baudrate_index]);
		  LL_USART_EnableDirectionRx(USART2);
		  LL_USART_EnableDirectionTx(USART2);
		  baudrate_changed = 0;
		  ltr329_config_changed = 0;
		  uart_enable_interrupts();

		  /*new_baud = LL_USART_GetBaudRate(USART2, SYSTICK_FREQ_HZ, LL_USART_OVERSAMPLING_16);*/
	  }
	  if(ltr329_config_changed)
	  {
		  uart_disable_interrupts();
		  ltr329_write_settings(sensor_config.ltr329_gain,
				  	  	  	  	sensor_config.ltr329_mode,
								sensor_config.ltr329_integ_time,
								sensor_config.ltr329_meas_rate);
		  ltr329_config_changed = 0;
		  uart_enable_interrupts();
	  }
	  /* It is time for measurement */
	  if (tim21_elapsed_period == 1)
	  {
		  /* TODO: Check the data */
		  /* Read SHT4x data (always connected) */
		  int8_t sht_read_ret = sht4x_measure(&T_SHT4x, &RH_SHT4x);
		  TF_SHT4x = CELSIUS_TO_FAHRENHEIT(T_SHT4x);
		  if (sht_read_ret == SHT4X_OK)
		  {
			  error_sht = 0;
		  } else
		  {
			  /* TODO: Do something */
			  if (sht_read_ret == SHT4X_ERROR)
			  {
				  error_sht = 1;
			  } else if (sht_read_ret == SHT4X_CRC8_ERROR)
			  {
				  error_sht = 2;
			  }
		  }

		  /* Red LTR329 values */
		  uint8_t data_valid, new_data;
		  ltr329_gain_t set_gain;
		  ltr_ret = ltr329_read_status_register(&data_valid, &new_data, &set_gain);
		  /* TODO: Do something with the flags */
		  if (ltr_ret == LTR329_OK)
		  {
			  if (new_data == 1)
			  {
				  ltr_ret = ltr329_measure(&light_ch0, &light_ch1);
				  if (ltr_ret == LTR329_OK)
				  {
					  error_ltr = 0;
				  } else
				  {
					  error_ltr = 1;
				  }
			  }
		  } else
		  {
			  error_ltr = 1;
		  }

		  /* Reset the TIM21 Elapsed Period Flag */
		  tim21_elapsed_period = 0;
	  }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  LL_FLASH_SetLatency(LL_FLASH_LATENCY_0);
  while(LL_FLASH_GetLatency()!= LL_FLASH_LATENCY_0)
  {
  }
  LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1);
  LL_RCC_HSI_Enable();

   /* Wait till HSI is ready */
  while(LL_RCC_HSI_IsReady() != 1)
  {

  }
  LL_RCC_HSI_SetCalibTrimming(16);
  LL_RCC_LSI_Enable();

   /* Wait till LSI is ready */
  while(LL_RCC_LSI_IsReady() != 1)
  {

  }
  LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLL_MUL_3, LL_RCC_PLL_DIV_4);
  LL_RCC_PLL_Enable();

   /* Wait till PLL is ready */
  while(LL_RCC_PLL_IsReady() != 1)
  {

  }
  LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1);
  LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1);
  LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1);
  LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL);

   /* Wait till System clock is ready */
  while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL)
  {

  }

  LL_Init1msTick(12000000);

  LL_SetSystemCoreClock(12000000);
  LL_RCC_SetUSARTClockSource(LL_RCC_USART2_CLKSOURCE_PCLK1);
  LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1);
}

/**
  * @brief I2C1 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2C1_Init(void)
{

  /* USER CODE BEGIN I2C1_Init 0 */

  /* USER CODE END I2C1_Init 0 */

  LL_I2C_InitTypeDef I2C_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
  /**I2C1 GPIO Configuration
  PA9   ------> I2C1_SCL
  PA10   ------> I2C1_SDA
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_9;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = LL_GPIO_PIN_10;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1);

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  /** I2C Initialization
  */
  LL_I2C_EnableAutoEndMode(I2C1);
  LL_I2C_DisableOwnAddress2(I2C1);
  LL_I2C_DisableGeneralCall(I2C1);
  LL_I2C_EnableClockStretching(I2C1);
  I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C;
  I2C_InitStruct.Timing = 0x40000A0B;
  I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE;
  I2C_InitStruct.DigitalFilter = 0;
  I2C_InitStruct.OwnAddress1 = 0;
  I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK;
  I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT;
  LL_I2C_Init(I2C1, &I2C_InitStruct);
  LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK);
  /* USER CODE BEGIN I2C1_Init 2 */
  /* USER CODE END I2C1_Init 2 */

}

/**
  * @brief IWDG Initialization Function
  * @param None
  * @retval None
  */
static void MX_IWDG_Init(void)
{

  /* USER CODE BEGIN IWDG_Init 0 */

  /* USER CODE END IWDG_Init 0 */

  /* USER CODE BEGIN IWDG_Init 1 */

  /* USER CODE END IWDG_Init 1 */
  LL_IWDG_Enable(IWDG);
  LL_IWDG_EnableWriteAccess(IWDG);
  LL_IWDG_SetPrescaler(IWDG, LL_IWDG_PRESCALER_256);
  LL_IWDG_SetReloadCounter(IWDG, 4095);
  while (LL_IWDG_IsReady(IWDG) != 1)
  {
  }

  LL_IWDG_ReloadCounter(IWDG);
  /* USER CODE BEGIN IWDG_Init 2 */

  /* USER CODE END IWDG_Init 2 */

}

/**
  * @brief TIM21 Initialization Function
  * @param None
  * @retval None
  */
static void MX_TIM21_Init(void)
{

  /* USER CODE BEGIN TIM21_Init 0 */

  /* USER CODE END TIM21_Init 0 */

  LL_TIM_InitTypeDef TIM_InitStruct = {0};

  /* Peripheral clock enable */
  LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM21);

  /* TIM21 interrupt Init */
  NVIC_SetPriority(TIM21_IRQn, 0);
  NVIC_EnableIRQ(TIM21_IRQn);

  /* USER CODE BEGIN TIM21_Init 1 */

  /* USER CODE END TIM21_Init 1 */
  TIM_InitStruct.Prescaler = tim21_prescaler;
  TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  TIM_InitStruct.Autoreload = tim21_period;
  TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  LL_TIM_Init(TIM21, &TIM_InitStruct);
  LL_TIM_EnableARRPreload(TIM21);
  LL_TIM_SetClockSource(TIM21, LL_TIM_CLOCKSOURCE_INTERNAL);
  LL_TIM_SetTriggerOutput(TIM21, LL_TIM_TRGO_RESET);
  LL_TIM_DisableMasterSlaveMode(TIM21);
  /* USER CODE BEGIN TIM21_Init 2 */

  /* USER CODE END TIM21_Init 2 */

}

/**
  * @brief USART2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART2_UART_Init(void)
{

  /* USER CODE BEGIN USART2_Init 0 */

  /* USER CODE END USART2_Init 0 */

  LL_USART_InitTypeDef USART_InitStruct = {0};

  LL_GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* Peripheral clock enable */
  LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2);

  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);
  /**USART2 GPIO Configuration
  PA1   ------> USART2_DE
  PA2   ------> USART2_TX
  PA3   ------> USART2_RX
  */
  GPIO_InitStruct.Pin = LL_GPIO_PIN_1;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = LL_GPIO_PIN_2;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  GPIO_InitStruct.Pin = LL_GPIO_PIN_3;
  GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
  GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
  GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  GPIO_InitStruct.Alternate = LL_GPIO_AF_4;
  LL_GPIO_Init(GPIOA, &GPIO_InitStruct);

  /* USART2 interrupt Init */
  NVIC_SetPriority(USART2_IRQn, 0);
  NVIC_EnableIRQ(USART2_IRQn);

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  USART_InitStruct.BaudRate = config_baudrates[sensor_config.baudrate_index];
  USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_9B;
  USART_InitStruct.StopBits = LL_USART_STOPBITS_1;
  USART_InitStruct.Parity = LL_USART_PARITY_EVEN;
  USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX;
  USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
  USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16;
  LL_USART_Init(USART2, &USART_InitStruct);
  LL_USART_EnableDEMode(USART2);
  LL_USART_SetDESignalPolarity(USART2, LL_USART_DE_POLARITY_HIGH);
  LL_USART_SetDEAssertionTime(USART2, 0);
  LL_USART_SetDEDeassertionTime(USART2, 0);
  LL_USART_ConfigAsyncMode(USART2);
  LL_USART_Enable(USART2);
  /* USER CODE BEGIN USART2_Init 2 */

  /* Enable IDLE Interrupt */
  LL_USART_EnableIT_IDLE(USART2);

  /* Enable RX Not Empty Interrupt */
  LL_USART_EnableIT_RXNE(USART2);

  LL_USART_EnableDirectionRx(USART2);
  LL_USART_EnableDirectionTx(USART2);
  LL_USART_Enable(USART2);
  /* USER CODE END USART2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);

}

/* USER CODE BEGIN 4 */
void USART2_TX_Buffer(uint8_t* buffer_tx, uint16_t buffer_tx_len)
{
	__disable_irq();
	for (uint16_t i = 0; i < buffer_tx_len; i++)
	{
		  LL_USART_TransmitData9(USART2, buffer_tx[i]);
		  while (!LL_USART_IsActiveFlag_TXE(USART2));
	}
	__enable_irq();
}

int8_t uart_disable_interrupts(void)
{
	LL_USART_Disable(USART2);
	LL_USART_DisableIT_IDLE(USART2);
	LL_USART_EnableIT_RXNE(USART2);

	return 0;
}

int8_t uart_enable_interrupts(void)
{
	LL_USART_Enable(USART2);
	LL_USART_EnableIT_IDLE(USART2);
	LL_USART_EnableIT_RXNE(USART2);

	return 0;
}

int8_t modbus_slave_callback(modbus_transaction_t *transaction)
{
	uint16_t register_number = transaction->register_number;
	switch (transaction->function_code)
	{
		case MODBUS_READ_INPUT_REGISTERS:
			for (int i = 0; i < transaction->register_count; i++, register_number++)
			{
				switch (register_number)
				{
					case REGISTER_NUM_T:
						transaction->input_registers_signed[i] = (int16_t)T_SHT4x;
						break;
					case REGISTER_NUM_T_F:
						transaction->input_registers_signed[i] = (int16_t)TF_SHT4x;
						break;
					case REGISTER_NUM_RH:
						transaction->input_registers[i] = (uint16_t)RH_SHT4x;
						break;
					case REGISTER_NUM_LIGHT_INTENSITY_0:
						transaction->input_registers[i] = (uint16_t)light_ch0;
						break;
					case REGISTER_NUM_LIGHT_INTENSITY_1:
						transaction->input_registers[i] = (uint16_t)light_ch1;
						break;
					case REGISTER_NUM_ERROR_T_RH:
						transaction->input_registers[i] = (uint16_t)error_sht;
						break;
					case REGISTER_NUM_ERROR_LIGHT:
						transaction->input_registers[i] = (uint16_t)error_ltr;
						break;
					default:
						return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED;
				}
			}
			return MODBUS_OK;
		case MODBUS_READ_HOLDING_REGISTERS:
			for (int i = 0; i < transaction->register_count; i++, register_number++)
			{
				switch (register_number)
				{
					case REGISTER_NUM_MODBUS_ADDR:
						transaction->holding_registers[i] = (uint16_t)(sensor_config.modbus_addr);
						break;
					case REGISTER_NUM_BAUDRATE:
						transaction->holding_registers[i] = (uint16_t)(sensor_config.baudrate_index);
						break;
					case REGISTER_NUM_LTR329_GAIN:
						transaction->holding_registers[i] = (uint16_t)(sensor_config.ltr329_gain);
						break;
					case REGISTER_NUM_LTR329_INTEGRATION_TIME:
						transaction->holding_registers[i] = (uint16_t)(sensor_config.ltr329_integ_time);
						break;
					case REGISTER_NUM_LTR329_MEAS_RATE:
						transaction->holding_registers[i] = (uint16_t)(sensor_config.ltr329_meas_rate);
						break;
					case REGISTER_NUM_LTR329_MODE:
						transaction->holding_registers[i] = (uint16_t)(sensor_config.ltr329_mode);
						break;
					default:
						return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED;
				}
			}
			return MODBUS_OK;
		case MODBUS_WRITE_SINGLE_REGISTER:
			transaction->register_count = 1;
		case MODBUS_WRITE_MULTIPLE_REGISTERS:
			for (int i = 0; i < transaction->register_count; i++, register_number++)
			{
				switch (register_number)
				{
					case REGISTER_NUM_MODBUS_ADDR:
						/* allowed values: [1, 247] (from 1 to 247) as given in section 2.1 of document
						 * MODBUS over Serial Line: Specification and Implementation Guide (V1.02) */
						if (transaction->holding_registers[i] < 1 || transaction->holding_registers[i] > 247) {
							return MODBUS_ERROR_OUT_OF_BOUNDS;
						}
						sensor_config.modbus_addr = (uint16_t) transaction->holding_registers[i];
						modbus_address_changed = 1;
						break;
					case REGISTER_NUM_BAUDRATE:
						/* allowed values: [0, 9] (from 0 to 9, where 9 == config_baudrates_length) */
						if (transaction->holding_registers[i] >= config_baudrates_length) {
							return MODBUS_ERROR_OUT_OF_BOUNDS;
						}
						sensor_config.baudrate_index = (uint16_t) (transaction->holding_registers[i]);
						baudrate_changed = 1;
						break;
					case REGISTER_NUM_RESET_DEVICE:
						if (transaction->holding_registers[i] == 0xABCD) {
							/* software reset */
							NVIC_SystemReset();
						} else {
							return MODBUS_ERROR_OUT_OF_BOUNDS;
						}
						break;
					case REGISTER_NUM_LTR329_GAIN:
						if (transaction->holding_registers[i] != LTR329_GAIN_1X ||
							transaction->holding_registers[i] != LTR329_GAIN_2X ||
							transaction->holding_registers[i] != LTR329_GAIN_4X ||
							transaction->holding_registers[i] != LTR329_GAIN_8X ||
							transaction->holding_registers[i] != LTR329_GAIN_48X ||
							transaction->holding_registers[i] != LTR329_GAIN_96X)
						{
							return MODBUS_ERROR_OUT_OF_BOUNDS;
						}
						sensor_config.ltr329_gain = (ltr329_gain_t) (transaction->holding_registers[i]);
						ltr329_config_changed = 1;
						break;
					case REGISTER_NUM_LTR329_INTEGRATION_TIME:
						if (transaction->holding_registers[i] != LTR329_INTEGRATION_50MS  ||
							transaction->holding_registers[i] != LTR329_INTEGRATION_100MS ||
							transaction->holding_registers[i] != LTR329_INTEGRATION_150MS ||
							transaction->holding_registers[i] != LTR329_INTEGRATION_200MS ||
							transaction->holding_registers[i] != LTR329_INTEGRATION_250MS ||
							transaction->holding_registers[i] != LTR329_INTEGRATION_300MS ||
							transaction->holding_registers[i] != LTR329_INTEGRATION_350MS ||
							transaction->holding_registers[i] != LTR329_INTEGRATION_400MS)
						{
							return MODBUS_ERROR_OUT_OF_BOUNDS;
						}
						sensor_config.ltr329_integ_time = (ltr329_integration_time_t) (transaction->holding_registers[i]);
						ltr329_config_changed = 1;
						break;
					case REGISTER_NUM_LTR329_MEAS_RATE:
						if (transaction->holding_registers[i] != LTR329_MEAS_RATE_50MS   ||
							transaction->holding_registers[i] != LTR329_MEAS_RATE_100MS  ||
							transaction->holding_registers[i] != LTR329_MEAS_RATE_200MS  ||
							transaction->holding_registers[i] != LTR329_MEAS_RATE_500MS  ||
							transaction->holding_registers[i] != LTR329_MEAS_RATE_1000MS ||
							transaction->holding_registers[i] != LTR329_MEAS_RATE_2000MS)
						{
							return MODBUS_ERROR_OUT_OF_BOUNDS;
						}
						sensor_config.ltr329_meas_rate = (ltr329_measurement_rate_t) (transaction->holding_registers[i]);
						ltr329_config_changed = 1;
						break;
					case REGISTER_NUM_LTR329_MODE:
						if (transaction->holding_registers[i] != LTR329_MODE_STAND_BY   ||
							transaction->holding_registers[i] != LTR329_MODE_ACTIVE)
						{
							return MODBUS_ERROR_OUT_OF_BOUNDS;
						}
						sensor_config.ltr329_mode = (ltr329_als_mode_t) (transaction->holding_registers[i]);
						ltr329_config_changed = 1;
						break;
					default:
						return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED;
				}
			}
			sensor_config_pending_write = 1;
			return MODBUS_OK;
		default:
			return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED;
	}
	/* Catch-all error */
	return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED; /* nothing implemented yet! TODO */
}

int8_t modbus_transmit_function(uint8_t *buffer, uint16_t data_len)
{
	 /* TODO */
	USART2_TX_Buffer(buffer, data_len);
	return MODBUS_OK;
}
/* USER CODE END 4 */

/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}

#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

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