/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2026 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "adc.h"
#include "i2c.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "ads1015_driver.h"
#include "digital_outputs_driver.h"
#include "ad5934_driver.h"
#include "rs485_driver.h"
#include "flash_manager.h"


#include <string.h>
/* USER CODE END Includes */

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

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define AVG_SLOPE (4.3F)
#define V_AT_25C  (1.43F)
#define V_REF_INT (1.2F)
#define STM32_TEMPERATURE_AVERAGES 4
/* USER CODE END PD */

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

/* USER CODE END PM */

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

/* USER CODE BEGIN PV */



uint8_t rx_buffer[256]; /*!< Buffer for received data */
uint8_t tx_data[] = "Hello RS-485 Broadcast!"; /*!< Data to send */

uint8_t adc_text[6];
uint8_t real_text[6];
uint8_t imag_text[6];
uint8_t rs485_text[6];


uint8_t newline[]={'\r','\n'};
uint8_t doubleSpace[]={'_','_','\0'};
uint8_t newline_ph[]={'_','p','H','\n','\0'};
uint8_t newline_admi[]={'_','u','S','\n','\0'};
uint8_t newline_temp[]={' ','°','C','\n','\0'};
uint8_t newline_imag[]={'_','O','h','m','\n','\0'};
uint8_t newline_485[]={'_','a','d','\n','\0'};
uint8_t minus[]={'-',' '};
uint8_t ad_t[]={'A','d',':',' ','\0'};
uint8_t ph_t[]={'p','H',':',' ','\0'};
uint8_t tm_t[]={'T','M',':',' ','\0'};

uint8_t main_state = STATE_RUNNING_OK;
uint8_t rs485_address=0;


uint16_t AD_RES[2];
uint8_t UpdateEvent = 0;
float Temperature, Temp_Sum, V_Sense, V_Ref;
float Temp_Samples[STM32_TEMPERATURE_AVERAGES]={0};

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
void intToStr(int16_t N, uint8_t *str);
void FloatToString(uint8_t * buf, double val);

/* USER CODE END PFP */

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


/* USER CODE END 0 */

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

  /* USER CODE BEGIN 1 */
	HAL_StatusTypeDef status0,status1;


	   // Variables to store previous LED states
	   uint8_t previous_green_state = 0;
	   uint8_t previous_red_state = 0;

	   uint8_t mux_connection = 0;

	   // Variables for timing
	   uint32_t previous_millis_green = 0;
	   uint32_t previous_millis_red = 0;
	   uint32_t current_millis;

	float temperature_RTD, admittance_EC, refResistance, update_value, ph7_interp, ph4_interp, ph_compensated;




	/*! Temporary variables */
	uint8_t  tempString[15] = {0};

	uint8_t i;

  /* USER CODE END 1 */

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

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

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

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_ADC1_Init();
  MX_ADC2_Init();
  MX_I2C1_Init();
  MX_I2C2_Init();
  MX_USART1_UART_Init();
  MX_TIM3_Init();
  /* USER CODE BEGIN 2 */



  Flash_Load_Page(&flash_data);


  HAL_TIM_Base_Start_IT(&htim3);

  //TempSensor_Init(&hadc1);



/*
  HAL_ADCEx_Calibration_Start(&hadc1);
  HAL_ADC_Start_DMA(&hadc1, (uint32_t*)AD_RES, 2);   // Internal Temperature conversion
*/

  digital_outputs_init();


   rs485_init();

   ADS1015_Init(); // Initializes pH Measurement


   AD5934_Init(); // Initializes CE and RTD Measurement



  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
/*
	for(i=0;i<AD5934_TEMP_AVERAGES;i++) // Loop to get stability and get averages
	{
		refResistance = AD5934_Get_Ref_Resistance();	// On Board Resistor
		temperature_RTD = AD5934_GetTemperature(refResistance);	// Liquid Temperature is stored
		admittance_EC = AD5934_GetImpedance(temperature_RTD);
		ph_compensated = ADSCalculate_ph_Compensated(temperature_RTD);
	}


	if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_RESET) // When PB3 (with internal pull up) in the Prog Header is set to GND
	{

		main_state = STATE_PH4_DRY_EC_CALIBRATION;

		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); // Red LED On

		for(i=0;i<(2*AD5934_TEMP_AVERAGES);i++) // Loop to get stability and get averages
		{
			flash_data.ph4_mV = ADSCalculate_ph_mV();
			temperature_RTD = AD5934_GetTemperature(refResistance);	// Air Temperature is not used in the function
			flash_data.ec0_mag = AD5934_GetImpedance(temperature_RTD);	// Dry Probe
			if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET)
				flash_data.EC10mS_EC5mS_switch = AD5934_CH_EC_HIGH_GAIN;	// 5mS/cm option
			else
				flash_data.EC10mS_EC5mS_switch = AD5934_CH_EC_MID_GAIN;		//10ms/cm option
			if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_SET)
				flash_data.PT100_PT1000_switch = AD5934_CH_RTD_LOW_GAIN;	//PT100 option
			else
				flash_data.PT100_PT1000_switch = AD5934_CH_RTD_MID_GAIN;	//PT1000 option

		}
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET); // Green LED On
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);


		while(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_3) == GPIO_PIN_RESET);
		HAL_Delay(500);

		main_state = STATE_PH7_WET_EC_CALIBRATION;

		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET);
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_RESET); // Red LED On



		for(i=0;i<(2*AD5934_TEMP_AVERAGES);i++) // Loop to get stability and get averages
		{
			temperature_RTD = AD5934_GetTemperature(refResistance);	// Liquid Temperature is stored
			flash_data.ec_factor = AD5934_EC_Calibrate_Temperature(temperature_RTD);
			flash_data.ph7_mV = ADSCalculate_ph_mV();
			flash_data.ec1413_mag = AD5934_GetImpedance(temperature_RTD)*flash_data.ec_factor;	// Get the magnitude of the reference liquid and compensate it to 25ºC

		}

		ph7_interp = ADSinterpolate_ph(temperature_RTD,ADS1015_PH_7_BUFFER_ROW);
		ph4_interp = ADSinterpolate_ph(temperature_RTD,ADS1015_PH_4_BUFFER_ROW);

		flash_data.ph_temperature = temperature_RTD;
		flash_data.ph_slope_mV = (flash_data.ph7_mV - flash_data.ph4_mV) / (ph7_interp - ph4_interp);
		flash_data.ph7_real = ph7_interp;

		Flash_Save_Page(&flash_data);	// Store Calibration parameters in Flash


		HAL_Delay(500);

		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET); // Green LED On
		HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET);


		main_state = STATE_RUNNING_OK;


	}
*/
	while (1)
	{


		 AD5934_Process_System();
		 ADS1015_Process_System();

		 current_millis = g_ms_counter;


		 if((current_millis % 100) == 0) // Send data each 50ms
		 {


			 if (g_ref_filter.value_valid) // Reference Resistor on Board: 100 Ohms or 1000 Ohms
			 {
				if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_SET) // OFF = PT1000, ON = PT100, PA7 has internal pull-up and switch connects to GND
					mux_connection = AD5934_CH_REF100R_LOW_GAIN; // 100 Ohms Reference Resistor
				else
					mux_connection = AD5934_CH_REF1K_MID_GAIN; // 1K Reference Resistor

				uint16_t reference_final = AD5934_REF_OUT_SCALE_MAX - AD5934_Compress_To_IntScale(g_ref_filter.filtered_value, mux_connection);
				intToStr(reference_final, tempString);
				// float ref_final = g_ref_filter.filtered_value;
				 //FloatToString(tempString, ref_final);
	 			 rs485_send_broadcast(tempString, (strlen((uint8_t*)tempString)));
	 			 rs485_send_broadcast(newline, (strlen((uint8_t*)newline)));
			 }

			 if (g_ec_filter.value_valid) // EC
			 {
				if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6) == GPIO_PIN_SET) // Hardware Setup: Gain Selection (PA6)
					mux_connection = AD5934_CH_EC_HIGH_GAIN;
				else
					mux_connection = AD5934_CH_EC_MID_GAIN;

				uint16_t ec_final = AD5934_Compress_To_IntScale(g_ec_filter.filtered_value, mux_connection);
				intToStr(ec_final, tempString);
				/* float ec_final = g_ec_filter.filtered_value;
				 FloatToString(tempString, ec_final);*/
				rs485_send_broadcast(tempString, (strlen((uint8_t*)tempString)));
				rs485_send_broadcast(newline, (strlen((uint8_t*)newline)));
			 }

			 if (g_rtd_filter.value_valid) // PT100 or PT1000 in °C
			 {
				if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7) == GPIO_PIN_SET) // OFF = PT1000, ON = PT100, PA7 has internal pull-up and switch connects to GND
					mux_connection = AD5934_CH_RTD_LOW_GAIN; //PT100
				else
					mux_connection = AD5934_CH_RTD_MID_GAIN; //PT1000

				uint16_t rtd_final = AD5934_RTD_OUT_SCALE_MAX - AD5934_Compress_To_IntScale(g_rtd_filter.filtered_value, mux_connection);
				intToStr(rtd_final, tempString);
				// float rtd_final = g_rtd_filter.filtered_value;
				// FloatToString(tempString, rtd_final);
				rs485_send_broadcast(tempString, (strlen((uint8_t*)tempString)));
				rs485_send_broadcast(newline, (strlen((uint8_t*)newline)));
			 }

			 if (g_ph_filter.value_valid) // pH
			 {
				 float ph_final = g_ph_filter.filtered_value;
				 FloatToString(tempString, ph_final);
				 rs485_send_broadcast(tempString, (strlen((uint8_t*)tempString)));
				 rs485_send_broadcast(newline, (strlen((uint8_t*)newline)));
			 }

			 rs485_send_broadcast(newline, (strlen((uint8_t*)newline)));

		 }


		 // Piscar LED verde em PB5 a cada 0,5 segundos
		 if ((current_millis - previous_millis_green) >= 500)
		 {
			 previous_millis_green = current_millis;
			 HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);

		 }

		 // Piscar LED vermelho em PB4 a cada 1 segundo
		 if ((current_millis - previous_millis_red) >= 1000)
		 {
			 previous_millis_red = current_millis;
			 HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_4);

		 }


		 /*
		 	if(UpdateEvent) // Internal Temperature
		      {

		 		 for (i = (STM32_TEMPERATURE_AVERAGES-1); i > 0; i--)
		 		 	 Temp_Samples[i] = Temp_Samples[i-1];


		 		 	// Read real and imaginary data
		 		 	 if(AD_RES[0]>0.0f)
		 		 		 V_Ref = (float)((V_REF_INT * 4095.0)/AD_RES[0]);
		 		 	 else
		 		 		V_Ref = 0.0f;
		 		 	 V_Sense = (float)(AD_RES[1] * V_Ref) / 4095.0;
		 		 	Temp_Samples[0] = (((V_AT_25C - V_Sense) * 1000.0) /AVG_SLOPE) + 25.0;

		 		 	for (i = 0, Temp_Sum=0; i < STM32_TEMPERATURE_AVERAGES; i++)
		 		 		Temp_Sum += Temp_Samples[i];

		 		 	Temperature = Temp_Sum/((float)STM32_TEMPERATURE_AVERAGES);

		 		 	UpdateEvent = 0;
		      }
		 	*/

	}


    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */




  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }

  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC;
  PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV8;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
  {
    Error_Handler();
  }
}

/* USER CODE BEGIN 4 */




/***************************************************************************//**
 * @brief Converts a float value to a character array with 3 digits of accuracy.
 *
 * @param *buf - returns the converterd value
 * @param val - value to be converted
 *
 * @return None.
*******************************************************************************/
void FloatToString(uint8_t *buf, double val)
{
    char temp[20]; // Buffer auxiliar para construção segura
    int i = 0;

    // 1. Tratar sinal negativo
    if (val < 0) {
        temp[i++] = '-';
        val = -val;
    }

    // 2. Separar parte inteira e fracionária
    long intPart = (long)val;
    // Multiplicamos por 1000 para obter 3 casas decimais fixas
    int fracPart = (int)((val - (double)intPart) * 1000.0 + 0.5);

    // 3. Converter a parte inteira para o buffer temp
    // Usamos um buffer temporário de inversão para não precisar de lógica complexa de ponteiro
    char intRev[12];
    int j = 0;

    if (intPart == 0) {
        intRev[j++] = '0';
    } else {
        while (intPart > 0) {
            intRev[j++] = (intPart % 10) + '0';
            intPart /= 10;
        }
    }

    // Inverter a parte inteira de volta para o buffer principal
    for (int k = j - 1; k >= 0; k--) {
        temp[i++] = intRev[k];
    }

    // 4. Adicionar o ponto decimal e a parte fracionária (sempre 3 casas)
    temp[i++] = '.';

    // Garantir que a parte fracionária tenha sempre 3 dígitos (ex: .005 em vez de .5)
    int fracBuffer[3];
    fracBuffer[2] = fracPart % 10;          // Unidade
    fracBuffer[1] = (fracPart / 10) % 10;   // Dezena
    fracBuffer[0] = (fracPart / 100) % 10;  // Centena

    for (int k = 2; k >= 0; k--) {
        temp[i++] = fracBuffer[k] + '0';
    }

    // 5. Finalizar a string com o caractere nulo
    temp[i] = '\0';

    // 6. Copiar para o buffer de destino final (sem risco de lixo)
    int destIdx = 0;
    while (temp[destIdx] != '\0' && destIdx < 15) { // Limite de segurança
        buf[destIdx] = temp[destIdx];
        destIdx++;
    }
    buf[destIdx] = '\0';
}



void intToStr(int16_t N, uint8_t *str)
{
    int16_t i = 0;
    int16_t sign = N;
    uint8_t max_len = 15; // Tamanho máximo do buffer
    uint8_t width = 4; // mínimo 4 caracteres

    // Trata o caso do número zero isoladamente
    if (N == 0)
    {
        // Preenche com zeros à esquerda até atingir a largura desejada
        while (width > 1 && i < (max_len - 1))
        {
            str[i++] = '0';
            width--;
        }
        str[i++] = '0';
        str[i] = '\0';
        return;
    }

    if (N < 0)
        N = -N;

    // Extração dos dígitos
    while (N > 0)
    {
        if (i >= (max_len - 1))
            break; // Proteção de estouro
        str[i++] = (N % 10) + '0';
        N /= 10;
    }

    // Adiciona o sinal de menos se necessário
    if (sign < 0)
    {
        if (i < (max_len - 1))
            str[i++] = '-';
    }

    // Preenche com zeros à esquerda (considerando o espaço ocupado pelos dígitos e sinal)
    while (i < width && i < (max_len - 1))
    {
        str[i++] = '0';
    }

    str[i] = '\0';

    // Inverte a string para colocar na ordem correta
    for (uint8_t j = 0, k = i - 1; j < k; j++, k--)
    {
        uint8_t temp = str[j];
        str[j] = str[k];
        str[k] = temp;
    }
}

/* TIM3 Timer Interrupt */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM3)
    {
    	g_ms_counter++; /* Relógio global do sistema */

    }
}



/*
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
     UpdateEvent = 1; // Internal TEmperature
}
*/

/* 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 */
