/* 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 <string.h>
/* 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 */


/* 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);
void Convert2Hex(uint32_t value, char *str);

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

	uint8_t tempString[15] = {0};

	uint8_t averages = 0;
	uint8_t newline = '\n';

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

	float reference_resistor_100R = 0, reference_resistor_1K = 0, temperature_RTD = 0, thermal_compensaded_EC = 0;


	float mag_rtd_reference = 0, mag_ec_reference = 0, mag_rtd = 0, mag_ec = 0;

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

	HAL_TIM_Base_Start_IT(&htim3);


	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_SET); // LED Green Off
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, GPIO_PIN_SET); // LED Red Off

	digital_outputs_init();
	rs485_init();

	ADG715_ResetChannels();
	ADS1015_Init(); // Initializes pH Measurement
	AD5934_Init(); // Initializes CE and RTD Measurement

  /* USER CODE END 2 */

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

	while(averages<50)
	{
		AD5934_Process_System();
		ADS1015_Process_System();

		current_millis = g_ms_counter;

		if((current_millis % 20) == 0) // Send data each 40ms
		{

			averages++;
		}
	}

	digital_outputs_toggle(0);



	while (1)
	{

		AD5934_Process_System();
		ADS1015_Process_System();


		current_millis = g_ms_counter;

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


			if (g_rtd_filter.value_valid) // PT100 or PT1000 in °C
			{
				temperature_RTD = AD5934_Calculate_Temperature(g_ref_100R_LowGain_filter.filtered_value, g_ref_1K_MidGain_filter.filtered_value, g_rtd_filter.filtered_value);// AD5934_GetImpedance(mag_reference,mag_rtd);//
				//uint16_t rtd_final = AD5934_RTD_OUT_SCALE_MAX - AD5934_Compress_To_IntScale(g_rtd_filter.filtered_value, current_rtd_mux);
				//uint16_t rtd_final = AD5934_Compress_To_IntScale(temperature_RTD, current_rtd_mux);
				//intToStr(rtd_final, tempString);
				FloatToString(tempString, temperature_RTD);
				rs485_send_broadcast(tempString, (strlen((uint8_t*)tempString)));
				rs485_send_broadcast(&newline, 1);

			}
			if (g_ec_filter.value_valid) // EC
			{
				mag_ec = AD5934_GetAdmittance(g_ref_100R_MidGain_filter.filtered_value, g_ref_1K_MidGain_filter.filtered_value,  g_ref_1K_HighGain_filter.filtered_value, g_ref_10K_HighGain_filter.filtered_value, g_ec_filter.filtered_value);
				thermal_compensaded_EC = AD5934_EC_Compensate_Magnitude_To_25C(mag_ec, temperature_RTD);
				//FloatToString(tempString, thermal_compensaded_EC);
				//rs485_send_broadcast(tempString, (strlen((uint8_t*)tempString)));
				//rs485_send_broadcast(&newline, 1);
				uint16_t ec_final = AD5934_Compress_To_IntScale(thermal_compensaded_EC, current_ec_mux);
				intToStr(ec_final, tempString);
				rs485_send_broadcast(tempString, (strlen((uint8_t*)tempString)));
				rs485_send_broadcast(&newline, 1);
			}

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

			rs485_send_broadcast(&newline, 1);

		}


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

		}

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

		 }*/



	}


    /* 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 = 0; k <= 2; 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;
    }
}


/**
 * Converte um valor uint32_t para string hexadecimal,
 * escrevendo o resultado no buffer apontado por 'str'.
 *
 * @param value  Valor a ser convertido (ex: 4276803469)
 * @param str    Ponteiro para buffer de destino (mínimo 9 bytes:
 *               8 dígitos hex + terminador nulo)
 */
void Convert2Hex(uint32_t value, char *str)
{
    const char hexTable[] = "0123456789ABCDEF";

    for (int8_t i = 7; i >= 0; i--)
    {
        str[i] = hexTable[value & 0xF];
        value >>= 4;
    }
    //str[8] = '\0';
}


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

    }
}


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