/* USER CODE BEGIN Header */
/* This is the bare PCB main */
/**
  ******************************************************************************
  * @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"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "rtc.h"
#include "lora.h"

#include <string.h>
#include <stdio.h>
#include <inttypes.h>
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

// set to your code
#define MY_SECRET_CODE       "alorAtMe"

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c2;

SPI_HandleTypeDef hspi2;

UART_HandleTypeDef huart5;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART5_UART_Init(void);
static void MX_I2C2_Init(void);
static void MX_SPI2_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

// for uart debugging
static void Serial_Print(const char *text)
{
  if (text == NULL)
  {
    return;
  }

  HAL_UART_Transmit(&huart5,
                    (uint8_t *)text,
                    (uint16_t)strlen(text),
                    HAL_MAX_DELAY);
}

// sx126x uses mcu interrupt
void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)
{
  if (GPIO_Pin == LORA_DIO1_Pin)
  {
    LoRa_OnDio1Interrupt();
  }
}

// latch power, high signal to nosfet BSS138 gate pulls SIP32432 ON pin low
// SIP32432 is a high side switch, pulling ON low lets power thru
// the mcu is latching its self on, keeping itself alive
static void Latch_MCU_Power(){
  HAL_GPIO_WritePin(MCU_LATCH_GPIO_Port,
                          MCU_LATCH_Pin,
                          GPIO_PIN_SET);  
}


static void Release_MCU_Power(){
  HAL_GPIO_WritePin(MCU_LATCH_GPIO_Port,
                  MCU_LATCH_Pin,
                  GPIO_PIN_RESET);
}

static void Shutdown_On_Error()
{
    HAL_Delay(20);
    Release_MCU_Power();
    while (1)
    {
      /* Power should disappear. */
    }
}

/* 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. */
  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_USART5_UART_Init();
  MX_I2C2_Init();
  MX_SPI2_Init();
  /* USER CODE BEGIN 2 */

  // this happens in MX_GPIO_Init, but it must happen, this is the mcu latching power on
  Latch_MCU_Power();

  // for debugging
  // static const uint8_t startup_message[] =
  //     "\r\nSTM32G0B1RET3 started\r\n"
  //     "Clock: HSI16, SYSCLK: 16 MHz\r\n"
  //     "UART: USART5, 115200 8N1\r\n";

  // HAL_UART_Transmit(&huart5,
  //                   (uint8_t *)startup_message,
  //                   sizeof(startup_message) - 1U,
  //                   HAL_MAX_DELAY);


  if (RTC_Init(&hi2c2) != HAL_OK)
  {
    Shutdown_On_Error();
    // Serial_Print("RV-3028 NOT found\r\n");
  }


  // // THIS MUST BE RUN ONCE, and most likely only once
  // /////////////////////////////////////////////////////////////////////////////////////////

  // sets rtx settings to use backup battery and settings are saved in rtc's EEPROM memory
  // BOTH UNIX TIME and SETTING BACKUP BATTERY always run if there is a charged battery
  // if (RTC_EnableOneTimeBackupBattery() != HAL_OK)
  // {
  //     Serial_Print("RTC backup setup failed\r\n");
  //     Shutdown_On_Error();
  // }
  /*
    I literally went to a online Unix counter, copied the value, and added 25 seconds (the time delay for flashing twice). 
    Then I compiled the code, commented out RTC_SetUnixTime (if you don't comment it out, every time the chip runs, it will reset the UTC clock to that value). 
    And then compiled the code again.
    So paste the UTC time in RTC_SetUnixTime, compile, comment out RTC_SetUnixTime, compile again, try to do it in ~25 seconds :)
    This is a very good RTC and should take a long time (months to years) to drift even a few seconds.
  */
  // RTC_SetUnixTime(1785626086 + 25); // 25 seconds becuase we have to flash twice so 25 second delay is a guess

  // HOW OFTEN DO YOU WANT TO SEND A LoRA RADIO TRANSMISSION
  // how often do you want the rtc to send a 7.8ms low pulse on its INT line
  // this rtc low pulse in combo with the SIP32432 is a high side switch and MCU_LATCH_Pin can turn the circuit on every N seconds
  // the cirucit tunrs itself off by setting MCU_LATCH_Pin LOW
  // if (RV3028_StartPeriodicTimer(60) != HAL_OK)
  // {
  //     // Serial_Print("Failed to configure RV-3028 timer\r\n");
  //     Shutdown_On_Error();
  // }

  // // end of the "THIS MUST BE RUN ONCE" block
  // /////////////////////////////////////////////////////////////////////////////////////////

  if (LoRa_Init(&hspi2) != SX126X_STATUS_OK)
  {
      // Serial_Print("SX1262 initialization failed\r\n");
      Shutdown_On_Error();
  }



  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */


  // set up tx packet
  uint32_t unix_time;
  char packet[64];
  bool tx_terminal_event = false;

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */

    // get rtc unitx time
    // even tho in a while loop, we shouldnt hammer i2c
    // everything outside of "while (LoRa_IsTxInProgress())"" should only run once
    if (RTC_GetUnixTime(&unix_time) == HAL_OK)
    {
      // snprintf(
      //     packet,
      //     sizeof(packet),
      //     "alorAtMe UTC %lu",
      //     (unsigned long)unix_time);

      snprintf(
          packet,
          sizeof(packet),
          "%s UTC %lu",
          MY_SECRET_CODE,
          (unsigned long)unix_time);


    }
    else
    {
      // snprintf(
      //     packet,
      //     sizeof(packet),
      //     "alorAtMe RTC read failed");

      snprintf(
          packet,
          sizeof(packet),
          "%s RTC read failed",
          MY_SECRET_CODE);

    }

    sx126x_status_t status = LoRa_Send(
        (const uint8_t *)packet,
        (uint8_t)strlen(packet));

    // TODO use one processing/faliure loop, maybe with some code like below
    // uint32_t started = HAL_GetTick();
    // while (!tx_terminal_event){everything else}

    if (status != SX126X_STATUS_OK)
    {
      tx_terminal_event = true;
      Shutdown_On_Error();
    }
    else
    {
      uint32_t started = HAL_GetTick();
      while (LoRa_IsTxInProgress())
      {
        lora_event_t event = LoRa_Process();

        switch (event)
        {
            case LORA_EVENT_TX_DONE:
                // Debug_Print("LoRa TX complete\r\n");
                tx_terminal_event = true;
                break;

            case LORA_EVENT_TIMEOUT:
                // Debug_Print("LoRa radio TX timeout\r\n");
                tx_terminal_event = true;
                break;

            case LORA_EVENT_IRQ_ERROR:
                // Debug_Print("Failed to process LoRa IRQ\r\n");
                tx_terminal_event = true;
                break;

            case LORA_EVENT_RX_DONE:
            case LORA_EVENT_CRC_ERROR:
            case LORA_EVENT_NONE:
            default:
                break;
        }

        if (tx_terminal_event)
        {
            break;
        }

        if ((HAL_GetTick() - started) > 3000U)
        {
            (void)LoRa_AbortTx();
            // Debug_Print("MCU TX wait timeout\r\n");
            tx_terminal_event = true;
            break;
        }

        HAL_Delay(1);
      }
  }


  if(tx_terminal_event){
      Release_MCU_Power();
      HAL_Delay(10000);
      tx_terminal_event = false; // this is for testing where the latch is not being used, to continue the loop
  }


    // HAL_Delay(1);
    
  }
  /* USER CODE END 3 */
}

/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1);

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSIDiv = RCC_HSI_DIV1;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief I2C2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_I2C2_Init(void)
{

  /* USER CODE BEGIN I2C2_Init 0 */

  /* USER CODE END I2C2_Init 0 */

  /* USER CODE BEGIN I2C2_Init 1 */

  /* USER CODE END I2C2_Init 1 */
  hi2c2.Instance = I2C2;
  hi2c2.Init.Timing = 0x00503D58;
  hi2c2.Init.OwnAddress1 = 0;
  hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c2.Init.OwnAddress2 = 0;
  hi2c2.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
  hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c2) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Analogue filter
  */
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
  {
    Error_Handler();
  }

  /** Configure Digital filter
  */
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C2_Init 2 */

  /* USER CODE END I2C2_Init 2 */

}

/**
  * @brief SPI2 Initialization Function
  * @param None
  * @retval None
  */
static void MX_SPI2_Init(void)
{

  /* USER CODE BEGIN SPI2_Init 0 */

  /* USER CODE END SPI2_Init 0 */

  /* USER CODE BEGIN SPI2_Init 1 */

  /* USER CODE END SPI2_Init 1 */
  /* SPI2 parameter configuration*/
  hspi2.Instance = SPI2;
  hspi2.Init.Mode = SPI_MODE_MASTER;
  hspi2.Init.Direction = SPI_DIRECTION_2LINES;
  hspi2.Init.DataSize = SPI_DATASIZE_8BIT;
  hspi2.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspi2.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspi2.Init.NSS = SPI_NSS_SOFT;
  hspi2.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
  hspi2.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspi2.Init.TIMode = SPI_TIMODE_DISABLE;
  hspi2.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspi2.Init.CRCPolynomial = 7;
  hspi2.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
  hspi2.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
  if (HAL_SPI_Init(&hspi2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI2_Init 2 */

  /* USER CODE END SPI2_Init 2 */

}

/**
  * @brief USART5 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART5_UART_Init(void)
{

  /* USER CODE BEGIN USART5_Init 0 */

  /* USER CODE END USART5_Init 0 */

  /* USER CODE BEGIN USART5_Init 1 */

  /* USER CODE END USART5_Init 1 */
  huart5.Instance = USART5;
  huart5.Init.BaudRate = 115200;
  huart5.Init.WordLength = UART_WORDLENGTH_8B;
  huart5.Init.StopBits = UART_STOPBITS_1;
  huart5.Init.Parity = UART_PARITY_NONE;
  huart5.Init.Mode = UART_MODE_TX_RX;
  huart5.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart5.Init.OverSampling = UART_OVERSAMPLING_16;
  huart5.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart5.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart5.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart5) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART5_Init 2 */

  /* USER CODE END USART5_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  /* USER CODE BEGIN MX_GPIO_Init_1 */

  /* USER CODE END MX_GPIO_Init_1 */

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, LORA_NSS_Pin|LORA_RST_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(MCU_LATCH_GPIO_Port, MCU_LATCH_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pins : LORA_NSS_Pin LORA_RST_Pin */
  GPIO_InitStruct.Pin = LORA_NSS_Pin|LORA_RST_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  /*Configure GPIO pin : LORA_BUSY_Pin */
  GPIO_InitStruct.Pin = LORA_BUSY_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(LORA_BUSY_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : LORA_DIO1_Pin */
  GPIO_InitStruct.Pin = LORA_DIO1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(LORA_DIO1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : MCU_LATCH_Pin */
  GPIO_InitStruct.Pin = MCU_LATCH_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(MCU_LATCH_GPIO_Port, &GPIO_InitStruct);

  /* EXTI interrupt init*/
  HAL_NVIC_SetPriority(EXTI4_15_IRQn, 2, 0);
  HAL_NVIC_EnableIRQ(EXTI4_15_IRQn);

  /* USER CODE BEGIN MX_GPIO_Init_2 */

  /* USER CODE END MX_GPIO_Init_2 */
}

/* USER CODE BEGIN 4 */

/* 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 */
