/* 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"
#include "math.h"
#include "stdbool.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 ---------------------------------------------------------*/
RTC_HandleTypeDef hrtc;
UART_HandleTypeDef huart2;
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
RTC_AlarmTypeDef sAlarm;

//Nuernberg coordinates
int latitude_nbg = 49;
int longitude_nbg = 11;

//German UTC time,summer (+2) and winter (+1)
int UTC_DER_sum = 2;
int UTC_DER_win = 1;
bool winterTime = true;

/* USER CODE BEGIN PV */

/* USER CODE END PV */

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_RTC_Init(void);
/* USER CODE BEGIN PFP */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/*******************************************************************************
* Function Name  : deg_to_rad
* Description    : converts degrees to radians
* Return         : angle in radians
*******************************************************************************/
double deg_to_rad(double deg)
{
	double rad = deg*(M_PI/180);
	return rad;
}

/*******************************************************************************
* Function Name  : rad_to_deg
* Description    : converts radians to degrees
* Return         : angle in degrees
*******************************************************************************/
double rad_to_deg(double rad)
{
	double deg = rad*(180/M_PI);
	return deg;
}

/*******************************************************************************
* Function Name  : leap_year_check
* Description    : checks if year is a leap year
* Return         : false: no leap year, true: leap year
*******************************************************************************/
int leap_year_check(int year)
{
	if((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
	{
		return true;
	}
	return false;
}

/*******************************************************************************
* Function Name  : calc_day_of_year
* Description    : calculates the day of year
* Return         : day of year (1.1.. = 1, 2.1.. = 2,...)
* Source		 : https://overiq.com/c-examples/c-program-to-calculate-the-day-of-year-from-the-date/
*******************************************************************************/
int calc_day_of_year(int day, int mon, int year)
{
	int days_in_feb = 28;
	int doy = day;   //day of year

	// check for leap year
	bool leap_year = leap_year_check(year);
	if(leap_year == true)
	{
	    days_in_feb = 29;
	}

	switch(mon)
	{
		case 2:
			doy += 31;
			break;
		case 3:
			doy += 31+days_in_feb;
			break;
		case 4:
			doy += days_in_feb+62;
			break;
		case 5:
			doy += days_in_feb+92;
			break;
		case 6:
			doy += days_in_feb+123;
			break;
		case 7:
			doy += days_in_feb+153;
			break;
		case 8:
			doy += days_in_feb+184;
			break;
		case 9:
			doy += days_in_feb+215;
			break;
		case 10:
			doy += days_in_feb+245;
			break;
		case 11:
			doy += days_in_feb+276;
			break;
		case 12:
			doy += days_in_feb+306;
			break;
	}
	return doy;
}

/*******************************************************************************
* Function Name  : calc_sunrise_sunset
* Description    : calculates the sunrise and sunset time of a specific date
* Source		 : General Solar Position Calculations, NOAA Global Monitoring Division
*******************************************************************************/
void calc_sunrise_sunset(int date, int month, int year, int sunrise_time[2], int sunset_time[2])
{
	double gamma = 0;
	bool leap_year;
	double eqtime = 0;
	double decl = 0;
	double decl_deg = 0;
	double zenith_sun = 0;
	double lat_nbg_rad = 0;
	double ha = 0;
	double sunrise = 0;
	double sunset = 0;
	double ha_deg = 0;
	int sunrise_h = 0;
	int sunset_h = 0;
	double sunrise_min = 0;
	double sunset_min = 0;
	int int_sunrise_min = 0;
	int int_sunset_min = 0;

	//day of year calculation
	int day_of_year = calc_day_of_year(date, month, year);

	// fractional year (γ) in radians
    // check for leap year
	leap_year = leap_year_check(year);
	if(leap_year == false)
	{
		//The back part of the formula was omitted, because there is no difference in the result
		gamma = ((2 * M_PI)/365)*(day_of_year - 1);
	} else {
		//The back part of the formula was omitted, because there is no difference in the result
		gamma = ((2 * M_PI)/366)*(day_of_year - 1);
	}

	//Equation of time in minutes
	eqtime = 229.18*(0.000075 + 0.001868*cos(gamma) - 0.032077*sin(gamma) - 0.014615*cos(2*gamma) - 0.040849*sin(2*gamma));

	//Solar declination angle in radians
	decl = 0.006918 - 0.399912*cos(gamma) + 0.070257*sin(gamma) - 0.006758*cos(2*gamma) + 0.000907*sin(2*gamma) - 0.002697*cos(3*gamma) + 0.00148*sin(3*gamma);

	//Solar declination angle in degrees
	decl_deg = rad_to_deg(decl);

	//Hour angle in degrees, positive number corresponds to sunrise, negative to sunset
	//special case of sunrise or sunset, the zenith is set to 90.833Deg
	zenith_sun = deg_to_rad(90.833);
	//Latitude of Nuernberg in rad
	lat_nbg_rad = deg_to_rad(latitude_nbg);
	ha = acos((cos(zenith_sun)/(cos(lat_nbg_rad)*cos(decl)))-(tan(lat_nbg_rad)*tan(decl)));
	ha_deg = rad_to_deg(ha);

	//UTC time of sunrise (or sunset) in minutes
	sunrise = (720-4*(longitude_nbg+ha_deg)-eqtime);
	sunset = 720-4*(longitude_nbg-ha_deg)-eqtime;

	//Convert sunrise (or sunset) UTC time in hours
	sunrise = sunrise/60;
	sunset = sunset/60;

	//Seperate hours and minutes
	sunrise_h = floor(sunrise);
	sunrise_min = sunrise - sunrise_h;
	//Cut off after two decimal places
	int_sunrise_min = floor(sunrise_min * 100.0);
	if (int_sunrise_min >= 60)
	{
		sunrise_h = sunrise_h + 1;
		int_sunrise_min = int_sunrise_min - 60;
	}
	sunset_h = floor(sunset);
	sunset_min = sunset - sunset_h;
	//Cut off after two decimal places
	int_sunset_min = floor(sunset_min * 100.0);
	if (int_sunset_min >= 60)
	{
		sunset_h = sunset_h + 1;
		int_sunset_min = int_sunset_min - 60;
	}

	//Add time difference from German time to UTC Time
	//Private variable winterTime must be initialized accordingly
	if (winterTime)
	{
		sunrise_h = sunrise_h + UTC_DER_win;
		sunset_h = sunset_h + UTC_DER_win;
	} else {
		sunrise_h = sunrise_h + UTC_DER_sum;
	    sunset_h = sunset_h + UTC_DER_sum;
	}

	sunrise_time[0] = sunrise_h;
	sunrise_time[1] = int_sunrise_min;
	sunset_time[0] = sunset_h;
	sunset_time[1] = int_sunset_min;
}

/*******************************************************************************
* Function Name  : calc_tomorrows_date
* Description    : calculates tomorrow's date
* Source		 : https://github.com/vyacht/stm32/blob/master/vynmea/rtc.c
*******************************************************************************/
void calc_tomorrows_date(int day, int wday, int month, int year, int DaysInMonth[12], int tomorrows_date[4])
{
	bool leap_year;

	day++;							// next day
	wday++;							// next weekday
	if(wday == 8)
	{
		wday = 1;					// Monday
	}
	if(day > DaysInMonth[month-1])
	{								// next month
		day = 1;
		month++;
	}
	if(day > 31 && month == 12)     // next year
	{
		day = 1;
		month = 1;
		year++;
	}

	tomorrows_date[0] = day;
	tomorrows_date[1] = wday;
	tomorrows_date[2] = month;
	tomorrows_date[3] = year;

}

/*******************************************************************************
* Function Name  : set_Alarm
* Description    : sets the wake up Alarm
*******************************************************************************/
void set_Alarm(int h, int min, int weekDay)
{
	/** Enable the Alarm A*/
	sAlarm.AlarmTime.Hours = h;
	sAlarm.AlarmTime.Minutes = min;
	sAlarm.AlarmTime.Seconds = 0;
	sAlarm.AlarmTime.SubSeconds = 0;
	sAlarm.AlarmTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
	sAlarm.AlarmTime.StoreOperation = RTC_STOREOPERATION_RESET;
	sAlarm.AlarmMask = RTC_ALARMMASK_NONE;						//only by specific time
	sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
	sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY;
	sAlarm.AlarmDateWeekDay = weekDay;
	sAlarm.Alarm = RTC_ALARM_A;
	if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK)
	{
		Error_Handler();
	}
}

// sending to UART
void transmit_uart(char *string){
	uint8_t len = strlen(string);
	HAL_UART_Transmit(&huart2, (uint8_t*) string, len, 200);
}


/* 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_USART2_UART_Init();
  MX_RTC_Init();
  /* USER CODE BEGIN 2 */
  int hours = 0;
  int minutes = 0;
  int seconds = 0;
  int weekDay = 0;
  int month = 0;
  int date = 0;
  int year = 0;

  int sunrise_h = 0;
  int sunset_h = 0;
  int int_sunrise_min = 0;
  int int_sunset_min = 0;
  int sunrise_time[2] = {0};
  int sunset_time[2] = {0};
  int tomorrows_date[4] = {0};
  int DaysInMonth[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  int DaysInMonthLeapYear[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
  bool leap_year = false;



  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

	//Get Time and Date
	if (HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK)
	{
		hours = sTime.Hours;
		minutes = sTime.Minutes;
		seconds = sTime.Seconds;
	}
	if (HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN) == HAL_OK)
	{
		weekDay = sDate.WeekDay;
		month = sDate.Month;
		date = sDate.Date;
		year = 2000 + sDate.Year;
	}

	// check for leap year
	leap_year = leap_year_check(year);
	if (leap_year)
	{
		//Calculate tomorrow's date
		calc_tomorrows_date(date, weekDay, month, year, DaysInMonthLeapYear, tomorrows_date);
	} else {
		//Calculate tomorrow's date
		calc_tomorrows_date(date, weekDay, month, year, DaysInMonth, tomorrows_date);
	}

	//Calculate sunrise and sunset time for tomorrow
	calc_sunrise_sunset(tomorrows_date[0], tomorrows_date[2], tomorrows_date[3], sunrise_time, sunset_time);

	set_Alarm(16, 22, 1);

	HAL_Delay(5000);

	transmit_uart("Ich gehe schlafen!\r\n");

	// Suspend Tick increment to prevent wake up by Systick interrupt
	HAL_SuspendTick();

	HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);  //Interrupt for wake up

	HAL_ResumeTick();

	transmit_uart("Bin wieder wach!\r\n");
  }
  /* 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 PeriphClkInitStruct = {0};

  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_LSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.LSIState = RCC_LSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 16;
  RCC_OscInitStruct.PLL.PLLN = 336;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
  RCC_OscInitStruct.PLL.PLLQ = 7;
  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_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
  PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
  PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
}

/**
  * @brief RTC Initialization Function
  * @param None
  * @retval None
  */
static void MX_RTC_Init(void)
{

  /* USER CODE BEGIN RTC_Init 0 */

  /* USER CODE END RTC_Init 0 */

  /* USER CODE BEGIN RTC_Init 1 */

  /* USER CODE END RTC_Init 1 */
  /** Initialize RTC Only
  */
  hrtc.Instance = RTC;
  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  hrtc.Init.AsynchPrediv = 127;
  hrtc.Init.SynchPrediv = 255;
  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  if (HAL_RTC_Init(&hrtc) != HAL_OK)
  {
    Error_Handler();
  }

  /* USER CODE BEGIN Check_RTC_BKUP */

  /* USER CODE END Check_RTC_BKUP */

  /** Initialize RTC and set the Time and Date
  */
  sTime.Hours = 16;
  sTime.Minutes = 20;
  sTime.Seconds = 30;
  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sTime.StoreOperation = RTC_STOREOPERATION_RESET;
  if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }
  sDate.WeekDay = RTC_WEEKDAY_MONDAY;
  sDate.Month = RTC_MONTH_JANUARY;
  sDate.Date = 11;
  sDate.Year = 21;

  if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK)
  {
    Error_Handler();
  }

  /* USER CODE BEGIN RTC_Init 2 */

  /* USER CODE END RTC_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 */

  /* USER CODE BEGIN USART2_Init 1 */

  /* USER CODE END USART2_Init 1 */
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART2_Init 2 */

  /* USER CODE END USART2_Init 2 */

}

/**
  * @brief GPIO Initialization Function
  * @param None
  * @retval None
  */
static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);

  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);

  /*Configure GPIO pin : LD2_Pin */
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);

}

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

/**
  * @brief  Alarm callback
  * @param  hrtc: RTC handle
  * @retval None
  */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
  /* Alarm generation */
  transmit_uart("Alarm!!!!\r\n");
}


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