/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : app_freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2021 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 "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "tim.h"
#include "st7793_8bit.h"
#include "max6675.h"
#include "stdio.h"
#include "pid.h"
#include "bitmaps.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef struct btnAttr{
	uint8_t btn_type;
	uint16_t color, textcolor, x, y, x2, y2;		//Color, Text color, X, Y, X2, Y2
	uint8_t fontsize;
	const char *data;
} btnAttr;

typedef struct {							// a preset profile consists of 4 turning points and a name.
	uint16_t  temperature[5];		// new temperature for a point
	uint16_t time[5];						// time mark for a point
	char name[20];							// Preset name
} presets_t;

typedef struct {
	uint8_t temp_offset;
	uint8_t color_scheme;
	uint8_t btn_clicks;
	uint8_t brightness;
	uint8_t language;
	uint8_t boot_screen;
} settings_t;
/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define GRAPH_X			200				// Graph window Left coord
#define GRAPH_X1		70				// Wide Graph window Left coord
#define GRAPH_Y			35				// Graph window Top coord
#define GRAPH_W			195				// Graph window Width
#define GRAPH_W1		325				// Wide Graph window Width
#define GRAPH_H			165				// Graph window Height

//Sound types
#define S_LONG_BEEP		0
#define S_SHORT_BEEP	1
#define S_CLICK				2

//Button IDs
#define BTN_PRE1			0
#define BTN_PRE2			1
#define BTN_PRE3			2
#define BTN_PRE4			3
#define BTN_START			4
#define BTN_COG				5
#define BTN_STOP			6
#define BTN_OK				7
#define BTN_YES				8
#define BTN_NO				9
#define BTN_0					10
#define BTN_1					11
#define BTN_2					12
#define BTN_3					13
#define BTN_4					14
#define BTN_5					15
#define BTN_6					16
#define BTN_7					17
#define BTN_8					18
#define BTN_9					19

#define BTN_COUNT			20

//Screens
#define SCR_MAIN			0
#define SCR_RUNNING		1
#define SCR_SETTINGS	2
#define SCR_DIALOG		3
#define SCR_BOOT			99

//Pulses
#define P_WIDTH				40        // Triac gate Pulse width
#define P_DELAY        5				// Minimum delay before gate pulse

/* USER CODE END PD */

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

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */
static const char Version[] = "v1.0";

static const btnAttr buttons[] = {
	{BTN_TEXT, Darkcyan,   White,  10,  35,  95,  85, 2, "LEAD"},
	{BTN_TEXT, Darkcyan,   White, 105,  35, 190,  85, 2, "LFRE"},
	{BTN_TEXT, Darkcyan,   White,  10,  90,  95, 140, 2, "LOWR"},
	{BTN_TEXT, Darkcyan,   White, 105,  90, 190, 140, 2, "CSTM"},
	{BTN_TEXT, Darkgreen,  White,  70, 150, 190, 200, 2, "START>"},
	{BTN_ICON, Darkviolet, White,  10, 150,  60, 200, 2, cog_icon_28x28},
	{BTN_ICON, Darkred,    White,  10,  35,  60, 135, 2, stop_icon_32x50},
	{BTN_TEXT, Darkgreen,  White, 140, 180, 190, 220, 2, "OK"},
	{BTN_TEXT, Darkgreen,  White, 140, 140, 190, 175, 2, "OK"},
	{BTN_TEXT, Darkred,    White, 250, 140, 350, 175, 1, "Defaults"},
	{BTN_TEXT, Darkcyan,   White, 285, 145, 335, 185, 2, "0"},
	{BTN_TEXT, Darkcyan,   White, 230, 100, 280, 140, 2, "1"},
	{BTN_TEXT, Darkcyan,   White, 285, 100, 335, 140, 2, "2"},
	{BTN_TEXT, Darkcyan,   White, 340, 100, 390, 140, 2, "3"},
	{BTN_TEXT, Darkcyan,   White, 230,  55, 280,  95, 2, "4"},
	{BTN_TEXT, Darkcyan,   White, 285,  55, 335,  95, 2, "5"},
	{BTN_TEXT, Darkcyan,   White, 340,  55, 390,  95, 2, "6"},
	{BTN_TEXT, Darkcyan,   White, 230,  10, 280,  50, 2, "7"},
	{BTN_TEXT, Darkcyan,   White, 285,  10, 335,  50, 2, "8"},
	{BTN_TEXT, Darkcyan,   White, 340,  10, 390,  50, 2, "9"}
};

static const uint32_t scr_buttons[] = {
		0b00000000000000111111,
		0b00000000000001000000,
		0b11111111110010000000,
		0b00000000001100000000
};

static const char stageNames[5][8] = {
		"Preheat",
		"Soak",
		"Reflow",
		"Reflow",
		"Cooling"
};

static uint8_t	Preset = 0;									//selected preset
static uint8_t	Screen = SCR_BOOT;					//current screen active
static float		Temperature = 0.0f;					//current temperature
static float		aimTemperature = 0.0f;			//current temperature from a profile
static uint8_t	Stage = 0;									//current stage of a profile
static float		spp;												//Seconds per pixel (horizontal)
static float		dpp;												//Degrees per pixel (vertical)
uint16_t				Timer = 0;									//Time counter
uint16_t				Timer10 = 0;								//Time x10 counter
static uint8_t  fanActive = 0;							//Fans are active flag
static uint8_t  Running = 0;								//Running program flag
settings_t 			settings = {								//settings structure
									20,							//	uint8_t temp_offset
									0,							//	uint8_t color_scheme
									1,							//	uint8_t btn_clicks
									100,						//	uint8_t brightness
									0,							//	uint8_t language
									1								//	uint8_t boot_screen
};







const presets_t presets[4] = {
	{{150, 180, 215, 215, 95}, {90, 180, 210, 230, 260}, "Lead (Sn63 Pb37)"},
	{{150, 180, 240, 240, 120}, {90, 180, 225, 255, 285}, "Lead-free (SAC305)"},
	{{150, 165, 200, 200, 80}, {80, 180, 210, 220, 250}, "Lead Lower"},
	{{90, 130, 165, 165, 45}, {90, 180, 240, 250, 280}, "Custom profile"}
};

PIDController pid = {
		.Kp  = 8.0f,
		.Ki  = 0.2f,
		.Kd  = 0.5f,
		.T   = 0.25f,			//2 times per second
		.tau = 0.2f,			//Low-pass filter (0 - no filter)
		.limMin = -100.0f,
		.limMax =  100.0f
};

/* USER CODE END Variables */
/* Definitions for display_svc */
osThreadId_t display_svcHandle;
const osThreadAttr_t display_svc_attributes = {
  .name = "display_svc",
  .priority = (osPriority_t) osPriorityNormal,
  .stack_size = 384 * 4
};
/* Definitions for sensor_svc */
osThreadId_t sensor_svcHandle;
const osThreadAttr_t sensor_svc_attributes = {
  .name = "sensor_svc",
  .priority = (osPriority_t) osPriorityAboveNormal,
  .stack_size = 256 * 4
};
/* Definitions for touch_svc */
osThreadId_t touch_svcHandle;
const osThreadAttr_t touch_svc_attributes = {
  .name = "touch_svc",
  .priority = (osPriority_t) osPriorityNormal1,
  .stack_size = 128 * 4
};
/* Definitions for tempUpdate */
osMessageQueueId_t tempUpdateHandle;
const osMessageQueueAttr_t tempUpdate_attributes = {
  .name = "tempUpdate"
};
/* Definitions for newTouch */
osMessageQueueId_t newTouchHandle;
const osMessageQueueAttr_t newTouch_attributes = {
  .name = "newTouch"
};
/* Definitions for secTimer */
osTimerId_t secTimerHandle;
const osTimerAttr_t secTimer_attributes = {
  .name = "secTimer"
};
/* Definitions for mutScreen */
osMutexId_t mutScreenHandle;
const osMutexAttr_t mutScreen_attributes = {
  .name = "mutScreen"
};

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */
void btnPressed(uint8_t id);									//buttons actions
void updateTInfo(uint16_t lvl);
void drawBootScreen(void);
void drawMainScreen(void);
void drawRunningScreen(void);
void drawSettingsScreen(void);
void drawPlan(uint8_t use_mutex, uint8_t numbers, uint8_t wide);
void playSound(uint8_t sound, uint8_t note);	//Play one of the beeps
void playTestSound();													//Play a test sound
void setSoundPitch(uint8_t note);							//Set sound pitch (note) 0-6 - A2-A8
void drawButton(uint8_t id, uint8_t func);		//Draw a button by ID
void setFansSpeed(uint8_t speed);							//Set fans speed
void stopHeater(void);												//Stop the heater and reset the PID

/* USER CODE END FunctionPrototypes */

void svc_display(void *argument);
void svc_sensor(void *argument);
void svc_touch(void *argument);
void clockTick(void *argument);

void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */
  /* Create the mutex(es) */
  /* creation of mutScreen */
  mutScreenHandle = osMutexNew(&mutScreen_attributes);

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* Create the timer(s) */
  /* creation of secTimer */
  secTimerHandle = osTimerNew(clockTick, osTimerPeriodic, NULL, &secTimer_attributes);

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* Create the queue(s) */
  /* creation of tempUpdate */
  tempUpdateHandle = osMessageQueueNew (8, sizeof(float), &tempUpdate_attributes);

  /* creation of newTouch */
  newTouchHandle = osMessageQueueNew (8, sizeof(TouchPoint), &newTouch_attributes);

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* creation of display_svc */
  display_svcHandle = osThreadNew(svc_display, NULL, &display_svc_attributes);

  /* creation of sensor_svc */
  sensor_svcHandle = osThreadNew(svc_sensor, NULL, &sensor_svc_attributes);

  /* creation of touch_svc */
  touch_svcHandle = osThreadNew(svc_touch, NULL, &touch_svc_attributes);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

  /* USER CODE BEGIN RTOS_EVENTS */
  /* add events, ... */
  /* USER CODE END RTOS_EVENTS */

}

/* USER CODE BEGIN Header_svc_display */
/**
  * @brief  Function implementing the display_svc thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_svc_display */
void svc_display(void *argument)
{
  /* USER CODE BEGIN svc_display */
  /* Infinite loop */
	TouchPoint t;
	float temp;
	int in;
	static char str[12] = {0};
	static uint8_t id, lpcnt = 0, lastPressed = 0xFF;

	if (settings.boot_screen) {
		drawBootScreen();
		osDelay(4000);
	}

	drawMainScreen();

	for(;;)
	{
		//iterations counter to "unpress" buttons and free double press protection
		if (lastPressed != 0xFF) {
			if (lpcnt > 2) {
				if (Screen == SCR_SETTINGS && lastPressed != BTN_COG) {
					osMutexAcquire(mutScreenHandle, osWaitForever);
					drawButton(lastPressed, 0);
					osMutexRelease(mutScreenHandle);
				}
				lpcnt = 0;
				lastPressed = 0xFF;
			} else lpcnt++;
		}
		//Check if there are messages in the Touch queue
		if (osMessageQueueGetCount(newTouchHandle) > 0) {
			osMessageQueueGet(newTouchHandle, &t, NULL, 100);
/*
			ST7793_SetFontSize(1);
			osMutexAcquire(mutScreenHandle, osWaitForever);
			snprintf(str, 8, "X:%d", t.x);
			ST7793_FillRect(Lightgray, 205, 60, 270, 90);
			ST7793_DrawString(Black, Lightgray, str, 205, 60);
			snprintf(str, 8, "Y:%d", t.y);
			ST7793_DrawString(Black, Lightgray, str, 205, 75);
			osMutexRelease(mutScreenHandle);
*/
			//Each screen has a set of buttons
			//Check if user has touched a button
			for (id = 0; id < BTN_COUNT; id++) {
				if (id != lastPressed &&
						(scr_buttons[Screen] >> id) & 1 &&
						ST7793_TouchInRect(&t, buttons[id].x, buttons[id].y, buttons[id].x2, buttons[id].y2)
						) {

					lastPressed = id;
					playSound(S_CLICK, 2);

					osMutexAcquire(mutScreenHandle, osWaitForever);
					if (id < 4) {
						ST7793_SetFontSize(buttons[Preset].fontsize);
						drawButton(Preset, 0);
						//ST7793_DrawButton(buttons[Preset].btn_type, buttons[Preset].color, buttons[Preset].textcolor, buttons[Preset].x, buttons[Preset].y, buttons[Preset].x2, buttons[Preset].y2, buttons[Preset].data, 0);
						Preset = id;
						ST7793_SetFontSize(1);
						ST7793_FillRect(Darkergray, 10, 18, 190, 34);
						ST7793_DrawStringC(White, Darkergray, presets[Preset].name, 10, 18);
					}
					ST7793_SetFontSize(buttons[id].fontsize);
					drawButton(id, 1);
					//ST7793_DrawButton(buttons[id].btn_type,buttons[id].color, buttons[id].textcolor, buttons[id].x, buttons[id].y, buttons[id].x2, buttons[id].y2, buttons[id].data, 1);
					if (id < 4) drawPlan(0, 1, 0);
					osMutexRelease(mutScreenHandle);

					btnPressed(id);

				} // check if the button is pressed (if)
			} // button iterator (for)
		} // Touch handler (if)

		//Check temperature queue °
		if (osMessageQueueGetCount(tempUpdateHandle) > 0) {
			osMessageQueueGet(tempUpdateHandle, &temp, NULL, 100);
			if (Screen == SCR_MAIN || Screen == SCR_RUNNING) {
				if (temp >= 0.0f) {
					in = (int)temp;
					//in = 215;
					//fr = (int)((temp-in)*100);
					snprintf(str, 9, "%3d\xB0\x43", in);
				} else {
					if (temp == -2.0f) snprintf(str, 3, "NC");
					if (temp == -1.0f) snprintf(str, 4, "SPI");
				}

				osMutexAcquire(mutScreenHandle, osWaitForever);
				ST7793_SetFontSize(2);
				//ST7793_FillRect(Darkergray, 374, 208, 390, 235);
				ST7793_DrawString(Red, Darkergray, str, 310, 208);
				osMutexRelease(mutScreenHandle);
			}
		}


		osDelay(50);
	}
  /* USER CODE END svc_display */
}

/* USER CODE BEGIN Header_svc_sensor */
/**
* @brief Function implementing the sensor_svc thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_svc_sensor */
void svc_sensor(void *argument)
{
  /* USER CODE BEGIN svc_sensor */
  /* Infinite loop */
  float temp, res, res_old;
  uint8_t i;

  PID_Init(&pid);

  for(;;)
  {
	res = 0;
	for (i=0; i<3; i++) {				//Taking 3 measures
		temp = MAX6675_GetTemp();
		if (temp < 0.0f) break;			//if there is an error - break out
		res += temp;
	}
	if (temp >= 0.0f) {
		res = res / 3;
	} else {
		res = temp;
		stopHeater();
	}
	Temperature = res;
	if (res != res_old) {
		res_old = res;
		osMessageQueuePut(tempUpdateHandle, &res, 0, 50);
	}

	//If no program is running and T is more than 45C - turn on Fans; else - turn them off
	if (!Running && Temperature > 50.0f) setFansSpeed(100);
	if (!Running && Temperature <= 44.0f && fanActive) setFansSpeed(0);

	osDelay(250);
  }
  /* USER CODE END svc_sensor */
}

/* USER CODE BEGIN Header_svc_touch */
/**
* @brief Function implementing the touch_svc thread.
* @param argument: Not used
* @retval None
*/
/* USER CODE END Header_svc_touch */
void svc_touch(void *argument)
{
  /* USER CODE BEGIN svc_touch */
  /* Infinite loop */
	TouchPoint t;
	uint16_t x = 0, y = 0;
	for(;;)
	{
		osMutexAcquire(mutScreenHandle, osWaitForever);
		//Protected Screen access code
		t = ST7793_CheckTouch();
		osMutexRelease(mutScreenHandle);

		if (t.z > ST7793_Z_THRESHOLD) {
			if ((t.x != x) && (t.y != y)) {		//Check if it is a new point
				x = t.x;
				y = t.y;
				osMessageQueuePut(newTouchHandle, &t, 0, 50);
			}
		}

		osDelay(80);
	}
  /* USER CODE END svc_touch */
}

/* clockTick function */
void clockTick(void *argument)
{
  /* USER CODE BEGIN clockTick */
	char str[8];
	uint16_t x, y;
	static uint16_t stime, stemp; 	//starting time and temperature for a stage
	static float dps;								//degrees per second
  static uint16_t pulse;          //to calculate pulse for TIM1

	if (Temperature < 0.0f) {
		stopHeater();
		return;
	}


	//PID
	PID_Update(&pid, aimTemperature, Temperature);
	if (pid.out >= 0.0f) {
		pulse = (uint16_t)pid.out;
		if (fanActive) setFansSpeed(0);
	}
	else {
		pulse = 0;
		setFansSpeed((uint8_t)(pid.out * -1));
	}
	TIM1->CCR2 = pulse;

	//Draw PID value
	if (Screen == SCR_RUNNING) {
		snprintf(str, 7, "%3.2f", pid.out);
		//snprintf(str, 7, "%3d.%02d", (int)pid.out, (int)((pid.out-(int)pid.out)*100));
		osMutexAcquire(mutScreenHandle, osWaitForever);
		ST7793_SetFontSize(1);
		ST7793_FillRect(Darkergray, 11, 180, 27, 192);
		ST7793_DrawString(Black, Darkergray, str, 11, 180);

		snprintf(str, 7, "P:%4d", pulse);
		ST7793_FillRect(Lightgray, 100, 50, 116, 62);
		ST7793_DrawString(Black, Lightgray, str, 100, 50);
		osMutexRelease(mutScreenHandle);
	}

	if (Timer == 0 && Timer10 == 0) {
		stime = 0;
		stemp = 20;
		dps = (presets[Preset].temperature[Stage] - stemp) / (float)(presets[Preset].time[Stage]);
		Running = 1;
	}

	Timer10++;
	if (Timer10%4 != 0) return;

	Timer++;
	osMutexAcquire(mutScreenHandle, osWaitForever);

	if (Timer >= presets[Preset].time[4]) {
		ST7793_FillRect(Darkergray, 290, 5, 322, 30);
		ST7793_SetFontSize(2);
		ST7793_DrawStringC(Yellow, Darkergray, "Done!", 210, 5);
		osMutexRelease(mutScreenHandle);
		stopHeater();
		setFansSpeed(100);
		return;
	}

	snprintf(str, 6, "%02d:%02d", Timer/60, Timer%60);
	ST7793_SetFontSize(2);
	ST7793_DrawString(Cyan, Darkergray, str, 106, 208);
	//Draw a dot only if temperature is higher than the bottom of the graph
	if (Temperature >= 20.0f) {
		x = GRAPH_X1+Timer/spp;
		y = GRAPH_Y+GRAPH_H-(Temperature-20)/dpp;
		ST7793_FillRect(Red, x, y, x+1, y-1);						//4-pixel dot
	}

	//Check if we have a new stage started
	if (Timer == presets[Preset].time[Stage]) {
		Stage++;
		ST7793_FillRect(Darkergray, 274, 5, 322, 30);
		ST7793_SetFontSize(2);
		ST7793_DrawStringC(Yellow, Darkergray, stageNames[Stage], 210, 5);
		stime = presets[Preset].time[Stage-1];
		stemp = presets[Preset].temperature[Stage-1];
		dps = (presets[Preset].temperature[Stage] - stemp) / (float)(presets[Preset].time[Stage] - stime);
	}

	//Calculate new aim temperature
	aimTemperature = (Timer - stime) * dps + stemp;
	snprintf(str, 8, "%3.2f", aimTemperature);
	//snprintf(str, 8, "%3d.%02d", (int)aimTemperature, (int)((aimTemperature - (int)aimTemperature)*100));
	ST7793_SetFontSize(1);
	ST7793_DrawString(Black, Darkergray, str, 10, 140);

	osMutexRelease(mutScreenHandle);



  /* USER CODE END clockTick */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */
char str[10] = {0};
//uint16_t lvl = 989;
void btnPressed(uint8_t id) {

	switch (id) {
	case BTN_PRE1:								// PRE1

		break;
	case BTN_PRE2:								// PRE2

		break;
	case BTN_PRE3:								// PRE3

		break;
	case BTN_PRE4:								// PRE4

		break;
	case BTN_START:								// START
		Timer = 0;
		Timer10 = 0;
		Stage = 0;
		drawRunningScreen();
		osTimerStart(secTimerHandle, 250);
		break;
	case BTN_COG:								// COG (Settings)
		drawSettingsScreen();
		break;
	case BTN_STOP:
		stopHeater();
		drawMainScreen();
		break;
	case BTN_OK:
		drawMainScreen();
		break;
	}
}

void updateTInfo(uint16_t lvl) {
	osMutexAcquire(mutScreenHandle, osWaitForever);
	ST7793_SetFontSize(1);
	snprintf(str, 8, "T:%d", lvl);
	ST7793_FillRect(Lightgray, 205, 40, 270, 55);
	ST7793_DrawString(Black, Lightgray, str, 205, 40);
	osMutexRelease(mutScreenHandle);
}

//Draw boot screen
void drawBootScreen(void) {
	Screen = SCR_BOOT;
	osMutexAcquire(mutScreenHandle, osWaitForever);
	ST7793_FillScreen(Black);
	ST7793_Draw1bBitmap(White, Black, 152, 91, logo_96x58);
	ST7793_SetFontSize(2);
	ST7793_DrawString(White, Black, "PCB Heater", 120, 60);
	ST7793_DrawString(White, Black, "Reflow station", 88, 155);
	ST7793_SetFontSize(1);
	ST7793_DrawString(Green1, Black, "Firmware", 10, 220);
	ST7793_DrawStringC(Green1, Black, Version, 82, 220);
	ST7793_DrawString(Green1, Black, "\xA9 Anton Mukhin", 280, 220);
	osMutexRelease(mutScreenHandle);
}

//Draw main screen
void drawMainScreen(void) {
	static uint8_t in;

	Screen = SCR_MAIN;
	osMutexAcquire(mutScreenHandle, osWaitForever);
	ST7793_FillScreen(Darkergray);
	for (in=0; in<=5; in++) {
		ST7793_SetFontSize(buttons[in].fontsize);
		drawButton(in, (in == Preset));
	}
	ST7793_SetFontSize(2);
	ST7793_DrawStringC(Cyan, Darkergray, "Time: 00:00", 10, 208);

	ST7793_DrawStringC(Yellow, Darkergray, "Temperature", 210, 5);
	ST7793_SetFontSize(1);
	ST7793_DrawStringC(White, Darkergray, "Profile name:", 10, 2);
	ST7793_DrawStringC(White, Darkergray, presets[Preset].name, 10, 18);
	ST7793_DrawStringC(Navy, Darkergray, "Planned", 225, 205);
	ST7793_DrawStringC(Red, Darkergray, "Realtime", 225, 220);
	ST7793_DrawLine(Navy, 200, 211, 220, 211);
	ST7793_DrawLine(Navy, 200, 212, 220, 212);
	ST7793_DrawLine(Red, 200, 226, 220, 226);
	ST7793_DrawLine(Red, 200, 227, 220, 227);

	drawPlan(0, 1, 0);
	osMutexRelease(mutScreenHandle);
}

//Draw running screen
void drawRunningScreen(void) {
	Screen = SCR_RUNNING;
	osMutexAcquire(mutScreenHandle, osWaitForever);
	drawPlan(0, 0, 1);
	ST7793_FillRect(Darkergray, buttons[0].x, buttons[0].y, GRAPH_X1-2, GRAPH_Y+GRAPH_H+1);
	drawButton(BTN_STOP, 0);
	ST7793_FillRect(Darkergray, 210, 5, 386, 30);
	ST7793_DrawStringC(Yellow, Darkergray, stageNames[Stage], 210, 5);
	ST7793_SetFontSize(1);
	ST7793_DrawString(Black, Darkergray, "PID", 24, 160);

	osMutexRelease(mutScreenHandle);
}

// Draw settings screen
void drawSettingsScreen(void) {
	uint8_t i;

	Screen = SCR_SETTINGS;
	osMutexAcquire(mutScreenHandle, osWaitForever);
	ST7793_FillScreen(Darkergray);

	//Draw buttins
	for (i=0; i<BTN_COUNT; i++) {
		if ((scr_buttons[Screen] >> i) & 1) {
			drawButton(i, 0);
		}
	}

	//Draw check boxes
	ST7793_SetFontSize(1);
	ST7793_DrawRect(Black, 10, 20, 27, 37);
	ST7793_Draw1bBitmap(Black, Lightgray, 11, 21, check_16x16);
	ST7793_DrawString(Black, Darkergray, "Boot screen", 36, 23);

	ST7793_DrawRect(Black, 10, 50, 27, 67);
	ST7793_FillRect(Lightgray, 11, 51, 26, 66);
	ST7793_DrawString(Black, Darkergray, "Button sounds", 36, 53);


	osMutexRelease(mutScreenHandle);
}

// Draw planned graph
void drawPlan(uint8_t use_mutex, uint8_t numbers, uint8_t wide) {
	uint16_t max = 0;
	static uint8_t i, d;
	static uint16_t x, y, x1, y1, g_x, g_w, x5;
	char str[5];

	if (wide) {
		g_x = GRAPH_X1;
		g_w = GRAPH_W1;
	} else {
		g_x = GRAPH_X;
		g_w = GRAPH_W;
	}

	spp = (presets[Preset].time[3] + 30) / (float)g_w;
	for (i=0; i<4; i++) if (presets[Preset].temperature[i] > max) max = presets[Preset].temperature[i];		// Max temperature in the profile
	dpp = (max - 20) / (float)(GRAPH_H - 32);		//We start the graph from 20C / 32 pixels on top for numbers

	if (use_mutex) osMutexAcquire(mutScreenHandle, osWaitForever);
	//Clear the graph area
	ST7793_DrawRect(Black, g_x-1, GRAPH_Y-1, g_x+g_w+1, GRAPH_Y+GRAPH_H+1);
	ST7793_FillRect(Lightgray, g_x, GRAPH_Y, g_x+g_w, GRAPH_Y+GRAPH_H);
	// draw time axis marks
	y = GRAPH_Y+GRAPH_H;
	for (i=1; i*30/spp<g_w; i++) {
		x = g_x + i*30/spp;
		if (i % 2) d = 3; else d = 5;
		y1 = GRAPH_Y+GRAPH_H - d;
		ST7793_DrawLine(Black, x, y, x, y1);
	}
	// draw temperature axis marks
	x  = g_x;
	for (i=1; (i*25 - 20)/dpp<GRAPH_H; i++) {
		y = GRAPH_Y+GRAPH_H - (i*25 - 20) / dpp;
		if (i % 2) d = 3; else d = 5;
		x1 = g_x + d;
		ST7793_DrawLine(Darkred, x, y, x1, y);
	}

	// draw stage 1
	x  = g_x;
	y  = GRAPH_Y+GRAPH_H;
	x1 = g_x + presets[Preset].time[0] / spp;
	y1 = GRAPH_Y+GRAPH_H - (presets[Preset].temperature[0] - 20 ) / dpp;
	ST7793_DrawLine(Navy, x, y, x1, y1);
	ST7793_DrawLine(Navy, x+1, y, x1+1, y1);

	// draw stages 2, 3, 4, 5
	for (i=1; i<5; i++) {
		//Calculate graph line coords
		x  = g_x + presets[Preset].time[i-1] / spp;
		y  = GRAPH_Y+GRAPH_H - (presets[Preset].temperature[i-1] - 20) / dpp;
		x1 = g_x + presets[Preset].time[i] / spp;
		y1 = GRAPH_Y+GRAPH_H - (presets[Preset].temperature[i] - 20) / dpp;
		//Draw numbers
		if (numbers) {
			x5 = x - ((i < 4) ? 24 : 5);
			//Print temperature
			snprintf(str, 5, "%3d\xB0", presets[Preset].temperature[i-1]);
			ST7793_DrawTNString(Darkred, Lightgray, str, x5, y - 16);
			//Print time
			snprintf(str, 5, "%3ds", presets[Preset].time[i-1]);
			ST7793_DrawTNString(Black, Lightgray, str, x5, y - 8);
		}
		//Draw graph line (two for a bolder line)
		ST7793_DrawLine(Navy, x, y, x1, y1);
		if (i < 4) ST7793_DrawLine(Navy, x, y+1, x1, y1+1);
		else ST7793_DrawLine(Navy, x-1, y, x1-1, y1);
	}

	if (use_mutex) osMutexRelease(mutScreenHandle);
}

//Play one of the beeps
void playSound(uint8_t sound, uint8_t note) {
	uint16_t time;
	uint8_t enabled = 1;

	switch (sound) {
		case S_LONG_BEEP:
			time = 300;
			break;
		case S_SHORT_BEEP:
			time = 40;
			break;
		case S_CLICK:
			if (!settings.btn_clicks) enabled = 0;
			else time = 5;
			break;
	}
	if (enabled) {
		setSoundPitch(note);
		TIM16->ARR = time;
		//HAL_GPIO_WritePin(BUZZER_GPIO_Port, BUZZER_Pin, GPIO_PIN_SET);
		HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1);
		HAL_TIM_Base_Start_IT(&htim16);
	}
}

//Play a test sound
void playTestSound() {
	static uint8_t i;

	for (i=0; i<6; i++) {
		setSoundPitch(i);
		HAL_TIM_PWM_Start(&htim17, TIM_CHANNEL_1);
		osDelay(500);
	}
	HAL_TIM_PWM_Stop(&htim17, TIM_CHANNEL_1);

}

//Set sound pitch
void setSoundPitch(uint8_t note) {
	static const uint16_t notes[] = {
			58182, //A2
			29091, //A3
			14545, //A4
			7273,  //A5
			3636,  //A6
			1818,  //A7
			909    //A8
	};

	if (note > 6) note = 0;
	htim17.Init.Period = notes[note];
	HAL_TIM_Base_Init(&htim17);
	TIM_OC_InitTypeDef conf = {0};
	conf.OCMode = TIM_OCMODE_PWM1;
	conf.Pulse = notes[note] >> 1;
	conf.OCPolarity = TIM_OCPOLARITY_HIGH;
	conf.OCFastMode = TIM_OCFAST_DISABLE;
	HAL_TIM_PWM_ConfigChannel(&htim17, &conf, TIM_CHANNEL_1);
}

//Draw a button by ID
void drawButton(uint8_t id, uint8_t func) {
	ST7793_SetFontSize(buttons[id].fontsize);
	ST7793_DrawButton(
		buttons[id].btn_type,
		buttons[id].color,
		buttons[id].textcolor,
		buttons[id].x,
		buttons[id].y,
		buttons[id].x2,
		buttons[id].y2,
		buttons[id].data,
		func
	);
}

//Set fans speed
void setFansSpeed(uint8_t speed) {
	if (speed == 0) {
		if (fanActive) {
			HAL_TIM_PWM_Stop(&htim14, TIM_CHANNEL_1);
			fanActive = 0;
		}
	} else {
		if (speed > 100) speed = 100;
		TIM14->CCR1 = speed;
		if (!fanActive) {
			fanActive = 1;
			HAL_TIM_PWM_Start(&htim14, TIM_CHANNEL_1);
		}
	}
}

//Stop the heater and reset the PID
void stopHeater(void) {
	if (osTimerIsRunning(secTimerHandle))	osTimerStop(secTimerHandle);
	aimTemperature = 0.0f;
	PID_Init(&pid);
	TIM1->CCR2 = 0;
	Running = 0;
}
/* USER CODE END Application */

