Программный модуль для терморегулятора теплого пола RSF-171S. Версия 1.41



ПО является неотъемлемой частью терморегулятора теплого пола RSF-171S, отдельно потребителю не поставляется и эксплуатируется только в составе устройства.

Фрагмент исходного кода
////////////////////////////////////////////////////////////////

//RSF-171S

//v1.41

//application.c

/********* HEADER FILE INCLUDES *************************************************/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "debug_info.h"

#include "nrf.h"
#include "nrf_delay.h"
#include "nrf_gpio.h"
#include "nrf_soc.h"
#include "app_timer.h"
#include "app_error.h"

#include "calendar.h"
#include "evcal.h"
//#include "evcal_case.h"
#include "utc_date_convert.h"

#include "r4s_lib.h"
#include "r4s_master.h"

#include "Application.h"
#include "Application_api.h"
#include "event_base.h"
#include "timers_config.h"
#include "control.h"
#include "display.h"
#include "display_config.h"
#include "com_slave_extention.h"
//#include "config.h"
#include "pof_manager.h"

#include "evcal_config.h"
/********* TYPE **************************************************************/
#pragma pack(push, 1)

typedef enum{
	APP_ACTIVE_AIR_SENSOR = false,
	APP_ACTIVE_FLOOR_SENSOR = true,
}active_sensor_temp_t;
//биты состояния индикации температуры
typedef enum{
	TYPE_AIR_SENSOR_IND		= (uint8_t) 0x01,
	TYPE_FLOOR_SENSOR_IND = (uint8_t) 0x02,
	CURRENT_TEMP_IND			= (uint8_t) 0x04,
	SET_TEMP_IND					= (uint8_t) 0x08
} ind_state_temp_t;
//биты состояния индикации дня недели
typedef enum{
	SUNDAY_IND		= (uint8_t) 0x01,
	SATURDAY_IND	= (uint8_t) 0x02,
	FRIDAY_IND		= (uint8_t) 0x04,
	THURSDAY_IND	= (uint8_t) 0x08,
	WEDNESDAY_IND	= (uint8_t) 0x10,
	TUESDAY_IND		= (uint8_t) 0x20,
	MONDAY_IND		= (uint8_t) 0x40,
} ind_state_week_t;
//биты состояния индикации события
typedef enum{
	EVENT_SUN_IND 	= (uint8_t) 0x01,
	EVENT_OUT_IND 	= (uint8_t) 0x02,
	EVENT_IN_IND		= (uint8_t) 0x04,
	EVENT_NIGHT_IND = (uint8_t) 0x08,
}ind_state_event_t;

typedef struct{
	uint16_t app_sensor_se					:1;	//температурный датчик управления	(0/1 - AIR/FLOOR)
	uint16_t app_lock_en						:1;	//блокировка клавиатуры (0/1 - выкл/вкл)
	uint16_t app_contrast						:1;	//контраст дисплея 0/1 - 2/3)
	uint16_t app_calendar_en				:1;	//блокировка calendar (0/1 - выкл/вкл)
	uint16_t app_selfteaching_en		:1;	//самообучение (0/1 - выкл/вкл)
	uint16_t app_off_en							:1;	// 0/1-вкл/выкл
	
	uint8_t app_floor_sensor_idx;				//индекс подключенного температурного датчика пола
	float app_floor_sensor_calib;				//калибровка датчика пола -9 ... +9, шаг 0.5 (0.0)
	float app_air_sensor_calib;					//калибровка датчика воздуха -9 ... +9, шаг 0.5 (0.0)
	uint8_t app_hysteresis;							//отсрочка срабатывания реле по датчику +1 ... +9, шаг 1 (2)
	uint16_t app_selfteaching_time_s;		//
	
	uint16_t app_custom_power;
	int32_t app_shift_utc;
}app_state_device_t;

typedef struct{
	uint32_t app_timer_power_on;
	uint32_t app_count_set_relay;
}app_state_device_log_t;

typedef struct{
	float app_sensor_calendar_set;
	float app_sensor_custom_set;				//установленная температура пользователя
	float app_sensor_active_set;				//установленная температура активная
}app_state_hand_t;

typedef struct{
	uint16_t sm_clean_setting			:1; 			//сброс до заводских настроек инженерного меню
	app_service_menu_t sm_index_menu;				//выбранный пункт инженерного меню
}app_state_service_menu_t;

typedef struct{
	uint8_t app_floor_sensor_idx;				//индекс подключенного температурного датчика пола
	uint8_t app_hysteresis;							//отсрочка срабатывания реле по датчику +1 ... +9, шаг 1 (2)
	float app_floor_sensor_calib;				//калибровка датчика пола -9 ... +9, шаг 0.5 (0.0)
	float app_air_sensor_calib;					//калибровка датчика воздуха -9 ... +9, шаг 0.5 (0.0)
	uint16_t app_contrast						:1;	//контраст дисплея 0/1 - 2/3)
}app_data_service_menu_t;

typedef struct{
	uint8_t element_edit;
	uint8_t param_edit;
}app_state_clock_edit_t;

typedef struct{
	uint32_t 	timer_relay_on;
	uint32_t 	power;
	uint32_t 	count_relay_set;
}api_get_heater_info_t;

#pragma pack(pop)

/********* DEFINES **************************************************************/
#define SYMBOL_MOON					DISPLAY_S0
#define SYMBOL_LOCK					DISPLAY_S1
#define SYMBOL_AIR_TEMP			DISPLAY_S2
#define SYMBOL_WEEK_3				DISPLAY_S3
#define SYMBOL_WEEK_4				DISPLAY_S4
#define SYMBOL_SUN		 			DISPLAY_S5
#define SYMBOL_ANTI_FROST		DISPLAY_S6
#define SYMBOL_FLOOR_TEMP		DISPLAY_S7
#define SYMBOL_WEEK_2				DISPLAY_S8
#define SYMBOL_WEEK_5				DISPLAY_S9
#define SYMBOL_IN_HOUSE			DISPLAY_S10
#define SYMBOL_OUT_HOUSE		DISPLAY_S11
#define SYMBOL_HAND					DISPLAY_S12
#define SYMBOL_WEEK_1				DISPLAY_S13
#define SYMBOL_WEEK_6				DISPLAY_S14
#define SYMBOL_HEAT					DISPLAY_S17
#define SYMBOL_CLOCK_DOT		DISPLAY_S18
#define SYMBOL_WEEK_7				DISPLAY_S19
#define SYMBOL_L_CELSIUS		DISPLAY_S20
#define SYMBOL_B_CELSIUS		DISPLAY_S21
#define SYMBOL_IN_NOT				DISPLAY_S15
#define SYMBOL_OUT_NOT			DISPLAY_S16

#define BTN_ALL_RELEASE		BTN_TOUCH_KEY_NO
#define BTN_ON						BTN_TOUCH_KEY_K1
#define BTN_MENU					BTN_TOUCH_KEY_K2
#define BTN_CLOCK					BTN_TOUCH_KEY_K3
#define BTN_UP						BTN_TOUCH_KEY_K4
#define BTN_DOWN					BTN_TOUCH_KEY_K5
#define BTN_ON_CLOCK			BTN_TOUCH_KEY_K1K3
#define BTN_MENU_UP				BTN_TOUCH_KEY_K2K4
#define BTN_CLOCK_DOWN		BTN_TOUCH_KEY_K3K5


#define CALIB_SENSOR_FLOOR_MIN			-9.f
#define CALIB_SENSOR_FLOOR_MAX			9.f
#define CALIB_SENSOR_FLOOR_STEP 		0.5f
#define CALIB_SENSOR_FLOOR_DEFAULT	0.f

#define CALIB_SENSOR_AIR_MIN			-9.f
#define CALIB_SENSOR_AIR_MAX			9.f
#define CALIB_SENSOR_AIR_STEP 		0.5f
#define CALIB_SENSOR_AIR_DEFAULT	0.f

#define APP_CONTROL_IDX_MIN				0
#define APP_CONTROL_IDX_MAX				17
#define APP_CONTROL_IDX_STEP			1
#define APP_CONTROL_IDX_DEFAULT		0

#define APP_HYSTERESIS_MIN			1
#define APP_HYSTERESIS_MAX			9
#define APP_HYSTERESIS_STEP			1
#define APP_HYSTERESIS_DEFAULT	2

#define APP_FLOOR_SENSOR_IDX_MIN			0
#define APP_FLOOR_SENSOR_IDX_MAX			(APP_FLOOR_SENSOR_CONT - 1)
#define APP_FLOOR_SENSOR_IDX_STEP			1
#define APP_FLOOR_SENSOR_IDX_DEFAULT	0

#define APP_CUSTOM_SET_TEMP_DEFAULT		23	//установленная пользовательская температура по умолчанию

#define APP_TIMEOUT_ON_RELAY		6			//m задержка на включение реле после автоотключения

#define APP_TIMEOUT_SYMBOL_CALENDAR_MAX_MS		(5*1000)

//#define APP_TIMEOUT_HEAT_ERROR			(5*60*60*1000)

#define APP_TIMEOUT_SERVICE_MENU (5*1000*60)

#define APP_TIME_CALENDAR_BEFORE	(60*60)		//время вызова callback перед событием календаря
#define APP_TIMEOUT_TEST (1*60*1000)				//таймаут выхода из теста
/********* MACROS ***************************************************************/
#define DAYS_IN_WEEK	7
uint8_t SYMBOL_WEEKS[] = {SYMBOL_WEEK_1, SYMBOL_WEEK_2, SYMBOL_WEEK_3, SYMBOL_WEEK_4, SYMBOL_WEEK_5, SYMBOL_WEEK_6, SYMBOL_WEEK_7};

#define EVENT_COUNT		4
uint8_t SYMBOL_EVENTS[] = {SYMBOL_SUN, SYMBOL_OUT_HOUSE, SYMBOL_IN_HOUSE, SYMBOL_MOON};

#define TEMPS_COUNT	4
uint8_t SYMBOL_TEMPS[] = {SYMBOL_AIR_TEMP, SYMBOL_FLOOR_TEMP, SYMBOL_L_CELSIUS, SYMBOL_L_CELSIUS};

#define APP_SYMBOL_DIGIT_TEMP_ADDR				(DISPLAY_DIG1)
#define APP_SYMBOL_DIGIT_SET_TEMP_ADDR		(DISPLAY_DIG4)
#define APP_SYMBOL_DIGIT_HOURS_ADDR				(DISPLAY_DIG7)
#define APP_SYMBOL_DIGIT_MINUTES_ADDR			(DISPLAY_DIG9)

#define APP_PAIRING_TIME_MS							5000//ms		время таймаут входа в режим APP_PAIRING
#define APP_CLOCK_EDIT_TIME_MS					5000//ms		время таймаут входа в режим APP_CLOCK_EDIT
#define APP_CASE_EDIT_TIME_MS						5000//ms		время таймаут входа в режим редактирования кейсов
#define APP_TIMEOUT_PRESS_TIME_MS				500//ms 		время таймаут удержания кнопок UP/DOWN
#define APP_UNPAIRING_TIME_MS						5000//ms		время таймаут входа в режим APP_UNPAIRING
#define APP_TIMEOUT_CHANGE_STATE_TICKS	5000 //ms		время удержания типа режима(на индикаторе) и задержка на перезапись событий календаря при управлении с кнопок

#define APP_PARAM_INC(value, max, step) ((((value)+(step)) < (max))?((value)+(step)):((max)))
#define APP_PARAM_DEC(value, min, step) ((((value)-(step)) > (min))?((value)-(step)):((min)))

#define APP_TEST_TIMEOUT_STAGES_10MS		400	//timeout выполнения одного этапа теста


#define APP_OFF_DEVICE()		do{	\
															if(keyb_data.event_release == BTN_EVENT_CAPTURED && keyb_data.code_press == BTN_ON)		\
																api_state_change(APP_OFF);	\
														}while(0);

#define APP_CHANGE_LOCK_DEVICE()		do{	\
																			if(keyb_data.event_press == BTN_EVENT_CAPTURED && keyb_data.code_press == BTN_CLOCK_DOWN)	\
																				app_state_device.app_lock_en = ~app_state_device.app_lock_en;		\
																		}while(0);

#define APP_TEMP_BEFORE_MIN		20	//минут, время изменения на 1 градус

#define APP_STATE_DEVICE_DEFAULT()														\
{																															\
	.app_sensor_se						= false, 													\
	.app_lock_en							= false, 													\
	.app_contrast 						= true,														\
	.app_calendar_en					= false,													\
	.app_selfteaching_en			= false,													\
	.app_floor_sensor_idx 		= 0,															\
	.app_floor_sensor_calib 	= CALIB_SENSOR_FLOOR_DEFAULT,			\
	.app_air_sensor_calib			= CALIB_SENSOR_AIR_DEFAULT,				\
	.app_hysteresis						= APP_HYSTERESIS_DEFAULT,					\
	.app_custom_power					= 0,															\
	.app_selfteaching_time_s	= (APP_TEMP_BEFORE_MIN*60),				\
	.app_shift_utc						= 0,															\
}																			

#define APP_STATE_DEVICE_LOG_DEFAULT()			\
{																						\
	.app_timer_power_on				= 0,						\
	.app_count_set_relay			= 0,						\
}

#define APP_STATE_CLOCK_EDIT_DEFAULT()	\
{																				\
	.element_edit = 0,										\
	.param_edit = 0, 											\
}


#define APP_TIMEOUT_ADV_CUSTOM	1000 //ms, период адвертайзинга 

#define TIMERS_MINUTES(t)			((t)*60*1000)
#define TIMERS_HOURS(t)				((t)*60)
#define IN_TIMERS_HOURS(t)		((t)/60)
#define IN_TIMERS_MINUTES(t)	((t)%60)

/********* VARIABLES *****************************************************/
static uint32_t app_timeout_adv_ms = APP_TIMEOUT_ADV_CUSTOM;
static bool app_timeout_adv_en = false;

static app_error_t 				app_state_err[2] = {APP_ERROR_NO, APP_ERROR_NO}; 					// состояние ошибки

static app_state_t 				app_state = APP_START;									/* состояние state machine*/
//static app_state_t				app_prev_off_state = APP_STAND_BY;			/* состояние state machine перед отключением прибора*/

static bool app_level_err_test_fl = false;

//таймеры
static volatile uint32_t app_timeout_on_relay_ms = 0;

static volatile bool      AppTimeoutFl = false;				/* флаг timeout-a */

//static uint8_t app_case_idx = 0;
//static uint8_t app_case_event_max = EVENT_COUNT;

//static bool               is_connected = false;
//static bool               AppMasterConnectedFl = false;       /* флаг master connected*/

//static bool               AppPairingCh1Fl = false;			/* флаг pairing*/
//static bool               AppPairingCh2Fl = false;			/* флаг pairing*/
//static bool               AppUnPairingCh1Fl = false;			/* флаг unpairing*/
//static bool               AppUnPairingCh2Fl = false;			/* флаг unpairing*/

static bool flag_control_smartfone = false;	//флаг управления с телефона
static uint8_t fl_over_hi = 0;							//флаг перегрева

static volatile uint32_t 	app_timeout_ind_change_state_10ms = 0; 	//timeout отображения кейса при изменении state
static volatile uint32_t app_timeout_illumination_10s	=	0;
static uint32_t app_timeout_blink_10ms = 0;
static volatile uint32_t app_timeout_ind_pairing_10ms = 0;
static volatile uint32_t app_timeout_blink_calendar_10ms = 0;	//сигнал отсутствия задач календаря
static volatile uint8_t 	app_blink_num = 0;
static bool			app_first_press_menu = false; 
static uint8_t	app_test_btn_mask = 0;
static uint32_t app_selfteaching_time_ms = 0; //ms время выхода датчика температуры на заданную температуру
static float 		app_begin_temp_heating;				//начальная температура подогрева с учетом калибровочного коэффициента
static float 		app_end_temp_heating;					//конечная температура подогрева с учетом калибровочного коэффициента

static uint8_t app_test_stage = 0; 
static uint32_t app_timeout_test_10ms = APP_TEST_TIMEOUT_STAGES_10MS;

APP_TIMER_DEF(app_timer_id);
APP_TIMER_DEF(app_timer_10ms_id);
APP_TIMER_DEF(app_timer_before_cal_id);

static app_state_device_t app_state_device = APP_STATE_DEVICE_DEFAULT();
static app_state_device_log_t app_state_device_log = APP_STATE_DEVICE_LOG_DEFAULT();

static volatile app_state_hand_t app_state_hand = {
				.app_sensor_calendar_set	= 0,
				.app_sensor_custom_set		= APP_CUSTOM_SET_TEMP_DEFAULT,
				.app_sensor_active_set		= APP_CUSTOM_SET_TEMP_DEFAULT,
};

static app_state_service_menu_t app_state_service_menu = {.sm_index_menu = SM_FLOOR_SENSOR_IDX_01, .sm_clean_setting = false};

static app_state_clock_edit_t app_state_clock_edit = APP_STATE_CLOCK_EDIT_DEFAULT();

static app_save_info_struct_t	app_pof_info __attribute__((aligned(sizeof(uint32_t))));

static uint8_t app_symbol_calendar_on = 0; 																									//номер отображаемого символа на дисплее задачи календаря 
static uint32_t app_timeout_symbol_calendar_ms = APP_TIMEOUT_SYMBOL_CALENDAR_MAX_MS;			//ms, время смены на дисплее значка задачи календаря

static uint32_t app_timeout_update_ms = 0;
static uint16_t app_symbol_update_dysplay = 0;

static uint32_t app_timeout_service_menu = 0;

//static uint32_t app_timeout_heat_error	= 0;
static bool fl_inc_set_heat = false;		//флаг разрешения инкремента кол-ва включений реле

static app_data_service_menu_t app_data_service_menu;

static uint32_t app_timeout_test = 0;
static bool 	app_update_fw_fl = false;
/********* FUNCTION PROTOTYPES **************************************************/
void api_create_case_calendar(app_state_t state);
static void app_set_relay(void);
static void app_clear_relay(bool reach);
/********* FUNCTIONS IMPLEMENTATION *********************************************/

/********************************************************************************
 *
 ********************************************************************************/
/**@brief 	Обработчик таймера таймаута.
 *
 * @param	none
 *
 * @return 	none
 */
static void app_timeout_timer_handler( void* p_context )
{
	AppTimeoutFl = true;
	return;
}

static void app_timeout_before_calendar_handler(void* p_context)
{
	if(app_state_err[app_state_device.app_sensor_se] != APP_ERROR_NO && app_state_err[app_state_device.app_sensor_se] != APP_ERROR_AIR_LESS_MIN) return;

	app_state_hand.app_sensor_custom_set = app_state_hand.app_sensor_active_set;
	app_state_hand.app_sensor_active_set = app_state_hand.app_sensor_calendar_set;
	api_start();
	return;
}

#ifdef DEBUG_LOG_TEMP
	static uint32_t app_timeout_log_temp = 0;
	static bool app_flg_log_temp = false;
#endif

/**@brief 	Обработчик таймера 10 ms таймаута.
 *
 * @param	none
 *
 * @return 	none
 */
static void app_timeout_timer_10ms_handler( void* p_context )
{
	static uint32_t timer_log_ms = 0;
	
#ifdef DEBUG_LOG_TEMP
	if(app_timeout_log_temp) app_timeout_log_temp -= APP_TIMEOUT_CUSTOM_10MS;
	else{ app_timeout_log_temp = 10000; app_flg_log_temp = true; }
#endif	
	
	if(app_timeout_adv_ms) app_timeout_adv_ms -= APP_TIMEOUT_CUSTOM_10MS;
	else{ app_timeout_adv_ms = APP_TIMEOUT_ADV_CUSTOM; app_timeout_adv_en = true; }
	
	if(app_timeout_update_ms) app_timeout_update_ms  -= APP_TIMEOUT_CUSTOM_10MS;
	if(app_timeout_service_menu) app_timeout_service_menu -= APP_TIMEOUT_CUSTOM_10MS;
	
	app_timeout_blink_10ms += APP_TIMEOUT_CUSTOM_10MS;
	++app_timeout_ind_pairing_10ms;
	if(app_timeout_test_10ms) --app_timeout_test_10ms;
	if(app_timeout_ind_change_state_10ms) app_timeout_ind_change_state_10ms -= APP_TIMEOUT_CUSTOM_10MS;
	if(app_timeout_illumination_10s) app_timeout_illumination_10s -= APP_TIMEOUT_CUSTOM_10MS;
	else if(!app_blink_num) nrf_gpio_pin_clear(BL_PIN);
	if(app_timeout_on_relay_ms) app_timeout_on_relay_ms -= APP_TIMEOUT_CUSTOM_10MS;

	if(app_timeout_test) app_timeout_test -= APP_TIMEOUT_CUSTOM_10MS;
	app_timeout_symbol_calendar_ms -= APP_TIMEOUT_CUSTOM_10MS;
	if(!app_timeout_symbol_calendar_ms){ 
		++app_symbol_calendar_on; 
		app_symbol_calendar_on %= 4; 
		app_timeout_symbol_calendar_ms = APP_TIMEOUT_SYMBOL_CALENDAR_MAX_MS;
	}
	
	if(nrf_gpio_pin_read(RELE_PIN))
	{
		app_selfteaching_time_ms += APP_TIMEOUT_CUSTOM_10MS;	//подсчет времени нагрева до температуры установки
		
//		app_timeout_heat_error += APP_TIMEOUT_CUSTOM_10MS;		//подсчет времени нагрева до времени ошибки
		
		timer_log_ms += APP_TIMEOUT_CUSTOM_10MS;							//подсчет времени работы реле для статистики
		if(timer_log_ms >= 1000){
			++app_state_device_log.app_timer_power_on;
			timer_log_ms = 0;
		}
	}
//	else
//	{
//		if(app_timeout_heat_error < APP_TIMEOUT_HEAT_ERROR) app_timeout_heat_error = 0;
//	}
	
	if(app_timeout_blink_calendar_10ms) app_timeout_blink_calendar_10ms -= APP_TIMEOUT_CUSTOM_10MS;
}

/**
 *  @brief  Функция вызывается при срабатывании часов (1 сек)
 *
 *  @param  none
 *  @return none
 *
 */
static void calendar_one_sec_handle(void){
	uint32_t err_code;

	err_code = evcal_one_sec_handler();
	APP_ERROR_CHECK(err_code);
}
/**
 *  @brief  Функция вызывается при срабатывании событий из календаря
 *
 *  @param  none
 *  @return none
 *
 */
static void evcal_calendar_task(evcal_info_packet_t const*p_data){
//	APP_PRINTF(0, "calendar task\n");
	if(p_data == NULL || app_state_device.app_calendar_en) return;

	if(app_state_err[app_state_device.app_sensor_se] != APP_ERROR_NO && app_state_err[app_state_device.app_sensor_se] != APP_ERROR_AIR_LESS_MIN) return;

	switch(p_data->array[1])
	{
		case AC_EXTENTION_TEMP_SFLOAT_CMD:
		{
			exten_req_temp_sfloat_cmd_t* reg_temp_sfloat = (exten_req_temp_sfloat_cmd_t*)&p_data->array[2];
			
			float temp_set = SFloat16ToFloat(reg_temp_sfloat->param1);
			if(temp_set<APP_ALARM_TEMPERATURE_BOTTOM || temp_set > APP_CUSTOM_TEMPERATURE_TOP) break;
						
			app_state_hand.app_sensor_calendar_set = temp_set;
			
			app_timeout_before_calendar_handler(NULL);
			break;			
		}
		case AC_EXTENTION_STOP_CMD:
		{
			api_stop();
			break;
		}
		case AC_EXTENTION_START_CMD:
		{
			api_start();
			break;
		}

		case AC_EXTENTION_SWITCH_CMD:
		{
			api_switch();
			break;
		}
		default:
		{
			break;
		}
	}
	return;
}

static void evcal_calendar_before_task(evcal_info_packet_t const*p_data){
	if(p_data == NULL || !app_state_device.app_selfteaching_en || app_state_device.app_calendar_en) return;

	switch(p_data->array[1])
	{
		case AC_EXTENTION_TEMP_SFLOAT_CMD:
		{
			
			exten_req_temp_sfloat_cmd_t* reg_temp_sfloat = (exten_req_temp_sfloat_cmd_t*)&p_data->array[2];
			
			//текущее значение температуры
			float temp;
			api_get_temp_value_with_calibration(app_state_device.app_sensor_se, &temp);
			
			float temp_set = SFloat16ToFloat(reg_temp_sfloat->param1);
			
			if(temp_set<APP_ALARM_TEMPERATURE_BOTTOM || temp_set > APP_CUSTOM_TEMPERATURE_TOP) break;

			// проверка на большую температуру
			
			app_state_hand.app_sensor_calendar_set = temp_set;
			
			uint32_t timeout = (temp_set - temp)*app_state_device.app_selfteaching_time_s;		//
			
			if(timeout >= APP_TIME_CALENDAR_BEFORE) app_timeout_before_calendar_handler(NULL);
			else APP_ERROR_CHECK(app_timer_start(app_timer_before_cal_id,APP_TIMER_TICKS((APP_TIME_CALENDAR_BEFORE-timeout)*1000, APP_TIMER_PRESCALER), NULL ));
			
			break;			
		}
	}
	return;
}

void *application_get_adr_data(void)
{
	app_pof_info.b_sensor_se = app_state_device.app_sensor_se;
	app_pof_info.b_off_en = app_state_device.app_off_en;
	app_pof_info.b_lock_en = app_state_device.app_lock_en;
	app_pof_info.b_contrast = app_state_device.app_contrast;
	app_pof_info.b_calendar_en = app_state_device.app_calendar_en;
	app_pof_info.b_selfteaching_en	= app_state_device.app_selfteaching_en;
	app_pof_info.floor_sensor_idx = app_state_device.app_floor_sensor_idx;
	app_pof_info.floor_sensor_calib = app_state_device.app_floor_sensor_calib;
	app_pof_info.air_sensor_calib = app_state_device.app_air_sensor_calib;
	app_pof_info.sensor_hysteresis = app_state_device.app_hysteresis;
	app_pof_info.sensor_custom_set = app_state_hand.app_sensor_custom_set;
	app_pof_info.sensor_active_set = app_state_hand.app_sensor_active_set;
	app_pof_info.timer_power_on = app_state_device_log.app_timer_power_on;
	app_pof_info.count_set_relay = app_state_device_log.app_count_set_relay;
	app_pof_info.custom_power = app_state_device.app_custom_power;
	app_pof_info.selfteaching_time_s	= app_state_device.app_selfteaching_time_s;
	app_pof_info.shift_utc = app_state_device.app_shift_utc;

	app_pof_info.state = app_state;
	return &app_pof_info;
}

/********************************************************************************
* индикация 
 ********************************************************************************/
/**
 *  @brief  Вывод числа на дисплей.
 *  
 *  @param[in]  digit_index  	- начальный индекс числа на дисплее. Значения согласно DISPLAY_DIGIT_INDEX
 *  @param[in]  len  					- кол-во цифр для вывода на дисплей
 *  @param[in]  digit_int  		- число
 *  @param[in]  state 		 		- состояние (1/0 - отображать/не отображать) 
 *  
 *  @return 									- нет
*/
static void app_digit_get_symbol(uint8_t digit_index, uint8_t len, int16_t digit_int, uint8_t state)
{
	
	uint16_t del;
	bool negative = false;
	//проверка на меньше 0
	if(digit_int<0){negative = true;  digit_int = abs(digit_int);}
	
	//проверка числа на максимальное выводимое на дисплей число
	if(!negative) del = pow(10, len)-1;
	else  del = pow(10, len-1)-1;
	
	if(digit_int > del) digit_int = del;
	
	if(negative){
		if(state) display_write_digit_data(digit_index, display_symbol_get_symbol('-'));
		else display_write_digit_data(digit_index, 0);	//не выводит символ
		++digit_index;
		del = pow(10, len-2);
	}
	else 
		del = pow(10, len-1);
	
	//расклад числа на символы
	for(uint8_t i = digit_index; i < digit_index+len; ++i){
		if(state)	display_write_digit_data(i, display_digit_get_symbol((uint8_t)(digit_int/del)));
		else 	display_write_digit_data(i, 0);	//не выводит символ
		digit_int %= del;
		del /=10;
	}
}
/**
 *  @brief  Вывод символов на дисплей.
 *  
 *  @param[in]  digit_index  	- начальный индекс числа на дисплее. Значения согласно DISPLAY_DIGIT_INDEX
 *  @param[in]  len  					- кол-во цифр для вывода на дисплей
 *  @param[in]  digit_int  		- число
 *  @param[in]  state 		 		- состояние (1/0 - отображать/не отображать) 
 *  
 *  @return 									- нет
*/
static void app_symbol_get_symbol(uint8_t symbol_index, uint8_t len, uint8_t* str, uint8_t state)
{
	//проверка числа на максимальное выводимое на дисплей число
	if(len != 2) return;
	
	//расклад строки на символы
	for(uint8_t i = symbol_index; i < symbol_index+len; ++i){
		if(state)	display_write_digit_data(i, display_symbol_get_symbol(*(str+(i-symbol_index))));
		else 	display_write_digit_data(i, 0);	//не выводит символ
	}
}

/**
 *  @brief  Отображение температуры.
 *  
 *  @param[in]  state  		- маска состояния символов (1/0 - отображать/не отображать) 
 *															0x01 - тип датчика (датчик воздуха)
 *															0x02 - тип датчика (датчик пола)
 *															0x04 - текущая температура
 *															0x08 - установленная температура
 *															0xF0 - 0
 *  @param[in]  temp 			- текущая температура
 *  @param[in]  set_temp 	- установленная температура
 *  
 *  
 *  @return NRF_SUCCESS 								успешно
 *  @return NRF_ERROR_INVALID_PARAM 		недопустимые входные параметры
 */
static uint32_t app_ind_temp(float temp, float set_temp, uint8_t state)
{
	if(	state & 0xF0) return NRF_ERROR_INVALID_PARAM;
 	
	//отображение символов
	for(uint8_t i=0, mask = 0x01; i<TEMPS_COUNT; ++i, mask <<= 1)
	{
		if(!(mask&state)) display_write_symbol_data(SYMBOL_TEMPS[i], 0);
		else display_write_symbol_data(SYMBOL_TEMPS[i], 1);
	}
	if(state == 0)
		display_write_symbol_data(SYMBOL_TEMPS[0], 0);
	//отображение текущей температуры
	app_digit_get_symbol(		APP_SYMBOL_DIGIT_TEMP_ADDR, 																		\
													APP_SYMBOL_DIGIT_SET_TEMP_ADDR - APP_SYMBOL_DIGIT_TEMP_ADDR, 		\
													(uint16_t)((((uint16_t)(temp/0.5+0.5))*0.5)*10), 								\
													((state&CURRENT_TEMP_IND)?(1):(0)));
	//отображения установленной температуры 
	app_digit_get_symbol(	APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 																\
												APP_SYMBOL_DIGIT_HOURS_ADDR - APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 	\
												(uint16_t)((((uint16_t)(set_temp/0.5+0.5))*0.5)*10), 						\
												((state&SET_TEMP_IND)?(1):(0)));
	return NRF_SUCCESS;	
}

/**
 *  @brief  Отображение часов.
 *  
 *  @param[in]  hour  		- часы 
 *  @param[in]  minute	 	- минуты
 *  @param[in]  state  		- состояние (1/0 - отображать/не отображать) 
 *  
 *  
 *  @return NRF_SUCCESS 								успешно
 *  @return NRF_ERROR_INVALID_PARAM 		недопустимые входные параметры
 */

static uint32_t app_ind_clock(uint8_t hour, uint8_t minute, uint8_t state)
{
	if(	hour >= 24 ||	minute >= 60){
		hour = 0; minute = 0;
	}
	//отображение чаcа
	app_digit_get_symbol(APP_SYMBOL_DIGIT_HOURS_ADDR, 1, 0, 0);
	
	uint8_t add_adr = 0;
	if(hour<10) ++add_adr;
	app_digit_get_symbol(		APP_SYMBOL_DIGIT_HOURS_ADDR+add_adr, 																			\
													APP_SYMBOL_DIGIT_HOURS_ADDR - APP_SYMBOL_DIGIT_SET_TEMP_ADDR-1-add_adr, 	\
													hour, 																																		\
													((state&0x01)?(1):(0)));

	//отображения минут
	app_digit_get_symbol(		APP_SYMBOL_DIGIT_MINUTES_ADDR, 																\
													APP_SYMBOL_DIGIT_MINUTES_ADDR - APP_SYMBOL_DIGIT_HOURS_ADDR, 	\
													minute, 																											\
													((state&0x02)?(1):(0)));

	display_write_symbol_data(SYMBOL_CLOCK_DOT, ((state&0x04)?(1):(0)));
	return NRF_SUCCESS;	
	
}
/********************************************************************************
* индикация режимов
 ********************************************************************************/
static void disp_clear()
{
	display_write_symbol_data(SYMBOL_HAND, 0);
	display_write_symbol_data(SYMBOL_ANTI_FROST, 0);
	display_write_symbol_data(SYMBOL_HEAT, nrf_gpio_pin_read(RELE_PIN));
	display_write_symbol_data(SYMBOL_LOCK, app_state_device.app_lock_en);
	
	//отображение событий
	app_ind_event(0);
}

//****************************************
//************ режим ВЫКЛ ****************
static void app_ind_off()
{
	disp_clear();
	//отображение температур и датчика
	app_ind_temp(0, 0, 0);
	
	//отображение времени	
	app_ind_clock(0, 0, 0);
	app_ind_week(0x80);
	
}

typedef enum{
	IND_CLOCK_EDIT_HOUR 		= 0,
	IND_CLOCK_EDIT_MINUTE 	= 1,
	IND_CLOCK_EDIT_DOT 			= 2,
}ind_clock_edit_t;
//********* режим CLOCK EDIT *************
static void app_ind_clock_edit()
{
	if(app_timeout_blink_10ms >= APP_TIMEOUT_BLINK_10MS) app_timeout_blink_10ms = 0;

	disp_clear();
	
	app_ind_temp(	0, 0, 0);

	//время
	uint8_t state;
	
	if(app_timeout_blink_10ms < APP_ON_BLINK_10MS	 || app_state_clock_edit.element_edit > 1) state = (1<<IND_CLOCK_EDIT_HOUR)|(1<<IND_CLOCK_EDIT_MINUTE)|(1<<IND_CLOCK_EDIT_DOT);
	else if(app_state_clock_edit.element_edit == 0) state = (1<<IND_CLOCK_EDIT_MINUTE)|(1<<IND_CLOCK_EDIT_DOT);
	else if(app_state_clock_edit.element_edit == 1) state = (1<<IND_CLOCK_EDIT_HOUR)|(1<<IND_CLOCK_EDIT_DOT);
	
	pcf8563_date_time_t time;
	calendar_utc_t timestamp;
	calendar_shift_utc_t shift;

	calendar_get_utc_time(&timestamp, &shift);
	utc_to_date(timestamp+shift, &time);

	app_ind_clock(time.hours, time.minutes, state);
	if(app_state_clock_edit.element_edit == 2 && app_timeout_blink_10ms > APP_ON_BLINK_10MS) app_ind_week(0);
	else app_ind_week(day_of_week(&time));
}
//************ режим TEST ****************
static void app_ind_test()
{
	if(app_test_stage == 0){ 
		if(app_timeout_illumination_10s) nrf_gpio_pin_set(BL_PIN);
		//отображение режимов
		display_write_symbol_data(SYMBOL_HAND, 0);
		display_write_symbol_data(SYMBOL_HEAT, 1);
		display_write_symbol_data(SYMBOL_LOCK, 1);
		display_write_symbol_data(SYMBOL_ANTI_FROST, 1);

		//отображение температур и датчика
		app_ind_temp(	88.5, 88.5, (TYPE_FLOOR_SENSOR_IND|TYPE_AIR_SENSOR_IND|CURRENT_TEMP_IND|SET_TEMP_IND));
	
		//отображение времени	
		app_digit_get_symbol(APP_SYMBOL_DIGIT_HOURS_ADDR, 2, 28,	1);

		//отображения минут
		app_digit_get_symbol(APP_SYMBOL_DIGIT_MINUTES_ADDR, 2,88, 1);

		display_write_symbol_data(SYMBOL_CLOCK_DOT, 1);
		
		app_ind_week(MONDAY_IND|TUESDAY_IND|FRIDAY_IND|THURSDAY_IND|WEDNESDAY_IND|SUNDAY_IND|SATURDAY_IND);
		
		app_ind_event(0x0F);
	}
	else{
		//отображение режимов
		display_write_symbol_data(SYMBOL_HAND, 0);
		if(app_test_stage == 1) display_write_symbol_data(SYMBOL_HEAT, nrf_gpio_pin_read(RELE_PIN));
		else display_write_symbol_data(SYMBOL_HEAT, 0);
		display_write_symbol_data(SYMBOL_LOCK, 0);
		display_write_symbol_data(SYMBOL_ANTI_FROST, 0);

		
		//отображение температур и датчика
		if(app_test_stage == 3){ 
			float temp[2];
			for(uint8_t i=0; i<2; ++i)
				APP_ERROR_CHECK(temp_get_value(i, &temp[i]));

			uint8_t str[3][3] = {"E_", "E~", "E-"};
			uint8_t *p_str; p_str = 0;
			
			if(temp[0] >= 100.f) p_str = str[0];
			if(temp[0] < 0.f) p_str = str[1];
			if(p_str != 0){
				app_digit_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR, 3, 0, 0);									//не отображаем дробную часть
				app_symbol_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR,  2, p_str, 1);
				display_write_symbol_data(SYMBOL_B_CELSIUS, 0);															//не отображаем знак сельсия 
			}else{
				app_digit_get_symbol(	APP_SYMBOL_DIGIT_TEMP_ADDR, 																		\
															APP_SYMBOL_DIGIT_SET_TEMP_ADDR - APP_SYMBOL_DIGIT_TEMP_ADDR, 		\
															(uint16_t)((((uint16_t)(temp[0]/0.5+0.5))*0.5)*10), 								\
															(TYPE_AIR_SENSOR_IND|CURRENT_TEMP_IND|SET_TEMP_IND));
				display_write_symbol_data(SYMBOL_B_CELSIUS, 0);
			}
			
			p_str = 0;
			if(temp[1] >= 100.f) p_str = str[0];
			if(temp[1] < 0.f) p_str = str[1];
			if(app_level_err_test_fl == true) p_str = str[2];
			if(p_str != 0){
				app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, 0, 0);															//не отображаем дробную часть
				app_symbol_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR,  2, p_str, 1);
				display_write_symbol_data(SYMBOL_L_CELSIUS, 0);																							//не отображаем знак сельсия 
			}
			else{
				//отображения установленной температуры 
				app_digit_get_symbol(	APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 																\
															APP_SYMBOL_DIGIT_HOURS_ADDR - APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 	\
															(uint16_t)((((uint16_t)(temp[1]/0.5+0.5))*0.5)*10), 						\
															1);
				display_write_symbol_data(SYMBOL_L_CELSIUS, 1);
			}

//			APP_ERROR_CHECK(app_ind_temp(temp[0], temp[1], (TYPE_AIR_SENSOR_IND|CURRENT_TEMP_IND|SET_TEMP_IND)));
		}
		else app_ind_temp(0, 0, 0);
		

		//отображение времени	
		app_ind_clock(0, 0, 0);
		app_ind_week(0x80);
	
		//отображение событий
		app_ind_event(0);

		if(app_test_stage == 2){
			app_ind_week(app_test_btn_mask);
		}else 
			app_ind_week(0);
	}
}

//********************************************
//************ режим ОЖИДАНИЯ ****************
static void app_ind_stand_by()
{
	if(app_timeout_blink_10ms >= APP_TIMEOUT_BLINK_10MS) app_timeout_blink_10ms = 0;

	//отображение режимов
	//символ "РУЧНОЕ УПРАВЛЕНИЕ" не отображается
	display_write_symbol_data(SYMBOL_HAND, false);
	//символ "НАГРЕВ" отображается, если включено реле
	display_write_symbol_data(SYMBOL_HEAT, nrf_gpio_pin_read(RELE_PIN));
	//символ "БЛОКИРОВКА" отображается, если блокировка включена
	display_write_symbol_data(SYMBOL_LOCK, app_state_device.app_lock_en);
	//символ "РЕЖИМ АНТИЗАМЕРЗАНИЯ"	включен, если температура установки меньше APP_ALARM_TEMPERATURE_ANTI_FROST
	display_write_symbol_data(SYMBOL_ANTI_FROST, (app_state_hand.app_sensor_active_set <= APP_ALARM_TEMPERATURE_ANTI_FROST)&& app_state_device.app_off_en == false);
	//символы "СОБЫТИЕ" включены, если запущен календарь

	uint8_t num;
	evcal_get_task_number(&num);

	if(num && app_state_device.app_calendar_en == false) 
		app_ind_event(1<<app_symbol_calendar_on);
	else{
		if((app_timeout_blink_calendar_10ms%APP_TIMEOUT_BLINK_10MS) < APP_OFF_BLINK_10MS) app_ind_event(0);
		else app_ind_event(0x0F);
	}

	//отображение температур и датчика
	float temp[2];
	for(uint8_t i=0; i<2; ++i)
	APP_ERROR_CHECK(api_get_temp_value_with_calibration(i, &temp[i]));

	float temp_cur = temp[app_state_device.app_sensor_se];
	
		// ошибка текущей температуры: меньше минимальной допустимой
	uint8_t str[4][3] = {"LO", "HI", "E_", "E-"};
	uint8_t str0[3] = {"--"};
		
	if(fl_over_hi){
		//перегрев
		app_digit_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR, 3, 0, 0);									//не отображаем дробную часть
		app_symbol_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR,  2, str[1], ((app_timeout_blink_10ms <= APP_ON_BLINK_10MS)?(1):(0)));
		display_write_symbol_data(SYMBOL_B_CELSIUS, 0);															//не отображаем знак сельсия 
		app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, (uint16_t)((((uint16_t)(app_state_hand.app_sensor_active_set/0.5+0.5))*0.5)*10), 1);
		display_write_symbol_data(SYMBOL_L_CELSIUS, 1);
	}else{
		switch(app_state_err[app_state_device.app_sensor_se]){
			case APP_ERROR_NO:
			{
				float temp = app_state_hand.app_sensor_active_set;
				if(app_state_device.app_off_en){
					app_ind_temp(	temp_cur, temp, 				\
					(((app_state_device.app_sensor_se==APP_ACTIVE_AIR_SENSOR)?(TYPE_AIR_SENSOR_IND):(TYPE_FLOOR_SENSOR_IND))|CURRENT_TEMP_IND));
//					app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 2, app_data_service_menu.app_floor_sensor_idx, 1);
					app_symbol_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR,  2, str0, 1);
				}
				else
					app_ind_temp(	temp_cur, temp, 				\
					(((app_state_device.app_sensor_se==APP_ACTIVE_AIR_SENSOR)?(TYPE_AIR_SENSOR_IND):(TYPE_FLOOR_SENSOR_IND))|CURRENT_TEMP_IND|SET_TEMP_IND));
				break;
			}
			case APP_ERROR_FLOOR_LESS_MIN:
			case APP_ERROR_AIR_LESS_MIN:
			{
				app_digit_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR, 3, 0, 0);									//не отображаем дробную часть
				app_symbol_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR,  2, str[0], ((app_timeout_blink_10ms <= APP_ON_BLINK_10MS)?(1):(0)));
				display_write_symbol_data(SYMBOL_B_CELSIUS, 0);															//не отображаем знак сельсия 
				app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, (uint16_t)((((uint16_t)(app_state_hand.app_sensor_active_set/0.5+0.5))*0.5)*10), 1);
				display_write_symbol_data(SYMBOL_L_CELSIUS, 1);
				display_write_symbol_data(SYMBOL_TEMPS[0], 0);
				display_write_symbol_data(SYMBOL_TEMPS[1], 0);
				display_write_symbol_data(SYMBOL_TEMPS[(app_state_device.app_sensor_se==APP_ACTIVE_AIR_SENSOR)?(0):(1)], 1);
				break;
			}
			case APP_ERROR_FLOOR_OVER_MAX:
			case APP_ERROR_AIR_OVER_MAX:
			{
				app_digit_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR, 3, 0, 0);									//не отображаем дробную часть
				app_symbol_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR,  2, str[1], ((app_timeout_blink_10ms <= APP_ON_BLINK_10MS)?(1):(0)));
				display_write_symbol_data(SYMBOL_B_CELSIUS, 0);															//не отображаем знак сельсия 
				app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, (uint16_t)((((uint16_t)(app_state_hand.app_sensor_active_set/0.5+0.5))*0.5)*10), 1);
				display_write_symbol_data(SYMBOL_L_CELSIUS, 1);
				break;
			}
			case APP_ERROR_FLOOR_SENSOR_DROP:
			case APP_ERROR_AIR_SENSOR_DROP:
			{
				app_digit_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR, 3, 0, 0);									//не отображаем дробную часть
				app_symbol_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR,  2, str[2], 1);
				display_write_symbol_data(SYMBOL_B_CELSIUS, 0);															//не отображаем знак сельсия
				display_write_symbol_data(((app_state_device.app_sensor_se==APP_ACTIVE_AIR_SENSOR)?(SYMBOL_AIR_TEMP):(SYMBOL_FLOOR_TEMP)), ((app_timeout_blink_10ms <= APP_ON_BLINK_10MS)?(1):(0)));		
				display_write_symbol_data(((app_state_device.app_sensor_se!=APP_ACTIVE_AIR_SENSOR)?(SYMBOL_AIR_TEMP):(SYMBOL_FLOOR_TEMP)), 0);
				app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, (uint16_t)((((uint16_t)(app_state_hand.app_sensor_active_set/0.5+0.5))*0.5)*10), 1);
				display_write_symbol_data(SYMBOL_L_CELSIUS, 1);				
				break;
			}
			case APP_ERROR_FLOOR_SENSOR_LOWER:
			case APP_ERROR_AIR_SENSOR_LOWER:
			{
				app_digit_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR, 3, 0, 0);									//не отображаем дробную часть
				app_symbol_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR,  2, str[3], 1);
				display_write_symbol_data(SYMBOL_B_CELSIUS, 0);															//не отображаем знак сельсия
				display_write_symbol_data(((app_state_device.app_sensor_se==APP_ACTIVE_AIR_SENSOR)?(SYMBOL_AIR_TEMP):(SYMBOL_FLOOR_TEMP)), ((app_timeout_blink_10ms <= APP_ON_BLINK_10MS)?(1):(0)));		
				display_write_symbol_data(((app_state_device.app_sensor_se!=APP_ACTIVE_AIR_SENSOR)?(SYMBOL_AIR_TEMP):(SYMBOL_FLOOR_TEMP)), 0);
				display_write_symbol_data(SYMBOL_B_CELSIUS, 0);															//не отображаем знак сельсия 
				app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, (uint16_t)((((uint16_t)(app_state_hand.app_sensor_active_set/0.5+0.5))*0.5)*10), 1);
				display_write_symbol_data(SYMBOL_L_CELSIUS, 1);
 				break;
			}
		}
	}	
	//отображение времени	
	pcf8563_date_time_t time;
	calendar_utc_t timestamp;
	calendar_shift_utc_t shift;

	calendar_get_utc_time(&timestamp, &shift);
	utc_to_date(timestamp+shift, &time);

	app_ind_clock(time.hours, time.minutes, (1<<IND_CLOCK_EDIT_HOUR)|(1<<IND_CLOCK_EDIT_MINUTE)|(1<<IND_CLOCK_EDIT_DOT));

	app_ind_week(day_of_week(&time));
}
//******************************************
static void app_ind_service_menu()
{
	disp_clear();
	
	app_digit_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR,  2, app_state_service_menu.sm_index_menu, 1);
	//отображение температур и датчика
	switch(app_state_service_menu.sm_index_menu)
	{
		case SM_FLOOR_SENSOR_IDX_01:
		{
			app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, 0, 0);															//не отображаем дробную часть
			display_write_symbol_data(SYMBOL_L_CELSIUS, 0);																							//не отображаем знак сельсия 
			app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 2, app_data_service_menu.app_floor_sensor_idx, 1);
			break;
		}
		case SM_HYSTERESIS_02:
		{
			app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, app_data_service_menu.app_hysteresis*10, 1);
			display_write_symbol_data(SYMBOL_L_CELSIUS, 1);
			break;
		}
		case SM_CALIB_AIR_03:
		{
			app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, app_data_service_menu.app_air_sensor_calib*10, 1);
			display_write_symbol_data(SYMBOL_L_CELSIUS, 1);
			break;
		}
		case SM_CALIB_FLOOR_04:
		{
			app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, app_data_service_menu.app_floor_sensor_calib*10, 1);
			display_write_symbol_data(SYMBOL_L_CELSIUS, 1);
			break;
		}
		case SM_CONTRAST_05:
		{
			app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, 0, 0);															//не отображаем дробную часть
			display_write_symbol_data(SYMBOL_L_CELSIUS, 0);																							//не отображаем знак сельсия 
			app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 2, app_data_service_menu.app_contrast, 1);
			break;
		}

		case SM_CLEAN_SETTING_06: 
		{
			app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, 0, 0);															//не отображаем дробную часть
			display_write_symbol_data(SYMBOL_L_CELSIUS, 0);																							//не отображаем знак сельсия 
			app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 2, (!app_state_service_menu.sm_clean_setting)?(0):(3), 1);
			break;
		}
	}
	
	//отображение времени	
	app_ind_clock(0, 0, 0);
	app_ind_week(0);
	
}

void app_ind_pairing()
{
	if(app_timeout_ind_pairing_10ms >= 70) app_timeout_ind_pairing_10ms = 0;
	
	disp_clear();
	
	//отображение температур и тип датчика
	APP_ERROR_CHECK(app_ind_temp(0, 0, 0));
	if(AppTimeoutFl == false){
		display_write_digit_data(APP_SYMBOL_DIGIT_TEMP_ADDR, display_symbol_get_symbol(((app_timeout_ind_pairing_10ms>50)?(1):(2))));
		display_write_digit_data(APP_SYMBOL_DIGIT_TEMP_ADDR+1, display_symbol_get_symbol(((app_timeout_ind_pairing_10ms>50)?(2):(1))));
	}else{
			uint8_t str[] = "OO";
			app_symbol_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR,  2, str, 1);
			
			app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, 0, 0);	//не отображаем дробную часть
			display_write_symbol_data(SYMBOL_L_CELSIUS, 0);									//не отображаем знак сельсия 
			app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 2, 0, 0);	//отображаем целую часть
	}
	
	//отображение времени
	app_ind_clock(0, 0, 0);
	app_ind_week(0);
}
//********************************************
//************                ****************
void application_init(void)
{
    
	APP_ERROR_CHECK(app_timer_create( &app_timer_id, APP_TIMER_MODE_SINGLE_SHOT, app_timeout_timer_handler ));
	
	APP_ERROR_CHECK(app_timer_create( &app_timer_10ms_id, APP_TIMER_MODE_REPEATED, app_timeout_timer_10ms_handler ));
	APP_ERROR_CHECK(app_timer_start(app_timer_10ms_id, APP_TIMER_TICKS(APP_TIMEOUT_CUSTOM_10MS, APP_TIMER_PRESCALER), NULL ));
		
	APP_ERROR_CHECK(app_timer_create(&app_timer_before_cal_id, APP_TIMER_MODE_SINGLE_SHOT, app_timeout_before_calendar_handler));
	
	//инициализация реле
	NRF_GPIO->OUTCLR = (1UL << RELE_PIN);
	NRF_GPIO->PIN_CNF[RELE_PIN] =	(GPIO_PIN_CNF_DRIVE_S0H1 << GPIO_PIN_CNF_DRIVE_Pos)		\
									|	(GPIO_PIN_CNF_PULL_Pulldown << GPIO_PIN_CNF_PULL_Pos)							\
									|	(GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos)					\
									|	(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);

	//инициализация подсветки 
	NRF_GPIO->OUTCLR = (1UL << BL_PIN);
	NRF_GPIO->PIN_CNF[BL_PIN] =	(GPIO_PIN_CNF_DRIVE_S0H1 << GPIO_PIN_CNF_DRIVE_Pos)		\
									|	(GPIO_PIN_CNF_PULL_Pulldown << GPIO_PIN_CNF_PULL_Pos)							\
									|	(GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos)					\
									|	(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);

//	NRF_GPIO->OUTCLR = (1UL << UNUSED10_PIN);
//	NRF_GPIO->PIN_CNF[UNUSED10_PIN] =	(GPIO_PIN_CNF_DRIVE_S0H1 << GPIO_PIN_CNF_DRIVE_Pos)		\
//									|	(GPIO_PIN_CNF_PULL_Pulldown << GPIO_PIN_CNF_PULL_Pos)							\
//									|	(GPIO_PIN_CNF_INPUT_Disconnect << GPIO_PIN_CNF_INPUT_Pos)					\
//									|	(GPIO_PIN_CNF_DIR_Output << GPIO_PIN_CNF_DIR_Pos);
	
		//инициализация календаря
	APP_ERROR_CHECK(calendar_set_one_sec_callback(calendar_one_sec_handle)); //установка callback часов
	APP_ERROR_CHECK(evcal_set_callback(evcal_calendar_task)); //установка callback при срабатывании расписания
	APP_ERROR_CHECK(evcal_set_before_callback(evcal_calendar_before_task)); //установка callback при срабатывании расписания
	APP_ERROR_CHECK(evcal_set_offset_task(APP_TIME_CALENDAR_BEFORE, 0));
	
	api_param_update();
	
//	calendar_utc_t timestamp;

//	calendar_shift_utc_t shift;
//	calendar_get_utc_time(&timestamp, &shift);

	return;
}

}

/**@brief 	Функция выполняет работу при входе в новое состояние
 *
 * @param	none
 *
 * @return 	none
 */
static void application_init_new_state( app_state_t app_new_state )
{
	static app_state_t    			app_prew_state = APP_START;

	if( app_prew_state == app_new_state )return;

	switch( app_new_state )
	{
		case APP_OFF:
		{
//			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_OFF\r\n"RTT_CTRL_RESET);
//			app_timeout_heat_error = 0;
			APP_ERROR_CHECK(r4s_slave_disconnect());
			APP_ERROR_CHECK(r4s_slave_adv_disable());
			//выключаем подсветку
			nrf_gpio_pin_clear(BL_PIN);
		
			app_ind_temp(	88.5, 88.5, (TYPE_FLOOR_SENSOR_IND|TYPE_AIR_SENSOR_IND|CURRENT_TEMP_IND|SET_TEMP_IND));
			
//			app_prev_off_state = app_prew_state;
			break;
		}
		case APP_START:
		{
//			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_START\r\n"RTT_CTRL_RESET);
			break;
		}
		case APP_TEST:
		{
//			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_TEST\r\n"RTT_CTRL_RESET);
			app_test_stage = 0; 
//			app_timeout_test_10ms = APP_TEST_TIMEOUT_STAGES_10MS;
			app_test_btn_mask = MONDAY_IND|TUESDAY_IND|FRIDAY_IND|THURSDAY_IND|WEDNESDAY_IND;
			app_timeout_test = APP_TIMEOUT_TEST;
			break;
		}
		case APP_STAND_BY:
		{
//			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_STAND_BY\r\n"RTT_CTRL_RESET);
			break;
		}
		case APP_PREPAIRING:
		{
//			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_PREPAIRING\r\n"RTT_CTRL_RESET);
			app_timeout_ind_pairing_10ms = 0;
			app_blink_num = 1;
			nrf_gpio_pin_set(BL_PIN);
			break;
		}
		case APP_PREUNPAIRING:
		{
//			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_PREUNPAIRING\r\n"RTT_CTRL_RESET);
			app_timeout_ind_pairing_10ms = 0;
			app_blink_num = 1;
			nrf_gpio_pin_set(BL_PIN);
			break;
		}
		case APP_PAIRING:
		{
//			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_PAIRING\r\n"RTT_CTRL_RESET);
			
			app_timeout_ind_change_state_10ms = 3000;
      r4s_enable_bonding();
//      app_pairing_set();
			
			APP_ERROR_CHECK(app_timer_stop(app_timer_id));
			
			AppTimeoutFl = false;

			APP_ERROR_CHECK(app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_PAIRING_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL ));
			
//			com_master_pairing_start(&m_required_ac);
            
      app_advertising_set_paring_flag();
			break;
		}
		case APP_UNPAIRING:
		{
//			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_UNPAIRING\r\n"RTT_CTRL_RESET);
//			com_master_unpairing();
			com_slave_unpair_proccess();
			api_param_update();
			app_blink_num = 3;
			app_timeout_ind_pairing_10ms = 0;
			break;
		}
#ifdef NRF51
		case APP_SLAVE_FW_UPDATE:
		{
//			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_SLAVE_FW_UPDATE\r\n"RTT_CTRL_RESET);

			APP_ERROR_CHECK(app_timer_stop(app_timer_id));
			
			AppTimeoutFl = false;

			APP_ERROR_CHECK(app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_FW_UPDATE_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL ));
			
			app_clear_relay(false);
			app_ind_off();
			
			app_update_fw_fl = false;
			break;
		}
#endif
		case APP_CLOCK_EDIT:
		{
//			APP_PRINTF(0, ">>APP_CLOCK_EDIT\n");
			app_state_clock_edit_t state_case_edit = APP_STATE_CLOCK_EDIT_DEFAULT();
			memcpy(&app_state_clock_edit, &state_case_edit, sizeof(app_state_clock_edit_t));
			app_first_press_menu = false; 
			break;
		}
		case APP_SERVICE_MENU:
		{
//			APP_PRINTF(0, ">>APP_SERVICE_MENU\n");
			app_state_service_menu.sm_index_menu = SM_FLOOR_SENSOR_IDX_01;
			app_state_service_menu.sm_clean_setting = false;
			app_timeout_service_menu = APP_TIMEOUT_SERVICE_MENU;
			
			//копируем данные во временную структуру
			app_data_service_menu.app_air_sensor_calib = app_state_device.app_air_sensor_calib;
			app_data_service_menu.app_floor_sensor_calib = app_state_device.app_floor_sensor_calib;
			app_data_service_menu.app_contrast = app_state_device.app_contrast;
			app_data_service_menu.app_floor_sensor_idx = app_state_device.app_floor_sensor_idx;
			app_data_service_menu.app_hysteresis = app_state_device.app_hysteresis;

			break;
		}
		default:
		{
			break;
		}
	}

	app_prew_state = app_new_state;

	return;
}

static void app_service_menu_handler(const btn_data_t* p_keyb_data)
{
	switch(app_state_service_menu.sm_index_menu)
	{
		case SM_HYSTERESIS_02:
		{
			//изменение гистирезиса для порога срабатывания напольного температурного датчика
			if((p_keyb_data->code_press == BTN_UP)&&(app_data_service_menu.app_hysteresis < APP_HYSTERESIS_MAX)) 
				app_data_service_menu.app_hysteresis += APP_HYSTERESIS_STEP;
			if((p_keyb_data->code_press == BTN_DOWN)&&(app_data_service_menu.app_hysteresis > APP_HYSTERESIS_MIN))
				app_data_service_menu.app_hysteresis -= APP_HYSTERESIS_STEP;
			break;
		}
		case SM_CALIB_AIR_03:
		{
			//изменение значения калибровки температурного датчика воздуха
			if((p_keyb_data->code_press == BTN_UP)&&(app_data_service_menu.app_air_sensor_calib < CALIB_SENSOR_AIR_MAX)) 
				app_data_service_menu.app_air_sensor_calib += CALIB_SENSOR_AIR_STEP;
			if((p_keyb_data->code_press == BTN_DOWN)&&(app_data_service_menu.app_air_sensor_calib > CALIB_SENSOR_AIR_MIN))
				app_data_service_menu.app_air_sensor_calib -= CALIB_SENSOR_AIR_STEP;
			break;
		}
		case SM_CALIB_FLOOR_04:
		{
			//изменение значения калибровки напольного температурного датчика
			if((p_keyb_data->code_press == BTN_UP)&&(app_data_service_menu.app_floor_sensor_calib < CALIB_SENSOR_FLOOR_MAX)) 
				app_data_service_menu.app_floor_sensor_calib += CALIB_SENSOR_FLOOR_STEP;
			if((p_keyb_data->code_press == BTN_DOWN)&&(app_data_service_menu.app_floor_sensor_calib > CALIB_SENSOR_FLOOR_MIN))
				app_data_service_menu.app_floor_sensor_calib -= CALIB_SENSOR_FLOOR_STEP;
			break;
		}
		case SM_CONTRAST_05:
		{
			//вкл./выкл. управления устройствами в помещениии
			if(p_keyb_data->code_press == BTN_UP) app_data_service_menu.app_contrast = 1;
			if(p_keyb_data->code_press == BTN_DOWN) app_data_service_menu.app_contrast = 0;
			display_set_bias(app_data_service_menu.app_contrast);
			break;
		}
		case SM_FLOOR_SENSOR_IDX_01:
		{
			//изменение подключенного напольного датчика
			if((p_keyb_data->code_press == BTN_UP)&&(app_data_service_menu.app_floor_sensor_idx < APP_FLOOR_SENSOR_IDX_MAX)) 
				app_data_service_menu.app_floor_sensor_idx += APP_FLOOR_SENSOR_IDX_STEP;
			if((p_keyb_data->code_press == BTN_DOWN)&&(app_data_service_menu.app_floor_sensor_idx > APP_FLOOR_SENSOR_IDX_MIN))
				app_data_service_menu.app_floor_sensor_idx -= APP_FLOOR_SENSOR_IDX_STEP;
			temp_set_idx_floor(app_data_service_menu.app_floor_sensor_idx);
			break;
		}
		case SM_CLEAN_SETTING_06:
		{
			//изменение сброса до заводских настроек
			if(p_keyb_data->code_press == BTN_UP) app_state_service_menu.sm_clean_setting = 1;
			if(p_keyb_data->code_press == BTN_DOWN) app_state_service_menu.sm_clean_setting = 0;
			break;
		}
		default:
		{
			break;
		}
	}
}
/********************************************************************************
 *
 ********************************************************************************/
void application_process()
{
	static uint32_t pre_time_ms = 0;
	
	calendar_utc_t timestamp;
	calendar_shift_utc_t shift;
	pcf8563_date_time_t time;

	//чтение состояния кнопок
	btn_data_t keyb_data;
	APP_ERROR_CHECK(button_read(&keyb_data));
	
	//проверка во всех режимах факта нажатия кнопок (кроме режима ВЫКЛ - в этом режиме обрабатывается индивидуально)   
	if(!app_state_device.app_lock_en && app_state != APP_OFF  && keyb_data.code_release != BTN_ALL_RELEASE){

		app_timeout_illumination_10s = APP_TIMEOUT_ILLUMINATION_MS;	//запуск timeout подсветки экрана
		if(!app_blink_num) nrf_gpio_pin_set(BL_PIN);	//вкл. подсветку экрана, если не в состоянии мигания

		if(app_timeout_service_menu) app_timeout_service_menu = APP_TIMEOUT_SERVICE_MENU;
		
		fl_over_hi = 0;
	}
	
	//получаем значения с температурных датчиков с калибровкой
	float temp[2];
	for(uint8_t i=0; i<2; ++i)
		APP_ERROR_CHECK(api_get_temp_value_with_calibration(i, &temp[i]));
	
#ifdef DEBUG_LOG_TEMP
	if(app_flg_log_temp == true){
		app_flg_log_temp = false;
		APP_PRINTF(0, "%d,%02d, %d,%02d\n", (uint16_t) temp[0], (uint16_t)(temp[0]*100)%100, (uint16_t) temp[1], (uint16_t)(temp[1]*100)%100);
	}	
#endif	

	
	//обработка ошибок
	if(temp[0] >=100.f) app_state_err[0] = APP_ERROR_AIR_SENSOR_LOWER;	// КЗ
	else if(temp[0] < -5.f) app_state_err[0] = APP_ERROR_AIR_SENSOR_DROP;			// ОБРЫВ
	else if(temp[0] >= -5.f && temp[0] < APP_ALARM_TEMPERATURE_BOTTOM) app_state_err[0] = APP_ERROR_AIR_LESS_MIN;
	else if(temp[0] > APP_ALARM_TEMPERATURE_TOP && temp[0] < 100.f){ app_state_err[0] = APP_ERROR_AIR_OVER_MAX; fl_over_hi = 1;}
	else app_state_err[0] = APP_ERROR_NO;
	
	if(temp[1] >=100.f) app_state_err[1] = APP_ERROR_FLOOR_SENSOR_LOWER;	// КЗ
	else if(temp[1]< -5.f) 	app_state_err[1] = APP_ERROR_FLOOR_SENSOR_DROP;		// ОБРЫВ
	else if(temp[1] < APP_ALARM_TEMPERATURE_BOTTOM && temp[1] >= -5.f) app_state_err[1] = APP_ERROR_FLOOR_LESS_MIN;
	else if(temp[1] > APP_ALARM_TEMPERATURE_TOP && temp[1] < 100.f){ app_state_err[1] = APP_ERROR_FLOOR_OVER_MAX; fl_over_hi = 2;}
	else app_state_err[1] = APP_ERROR_NO;

	if(fl_over_hi && temp[0]<=25.f && temp[1]<=25.f) fl_over_hi = 0;
	
	if(app_timeout_adv_en){
		app_timeout_adv_en = false;
		app_advertising_set_temp(temp[app_state_device.app_sensor_se]);
	}
	
	if(app_state != APP_START){
		//если значения не дефолтные и режим не START
		APP_ERROR_CHECK(pof_manager_record_enable());
	}else{
		APP_ERROR_CHECK(pof_manager_record_disable());
	} 

	switch(app_state)
	{
		case APP_START:
		{
			//выключение реле
			app_clear_relay(false);

			uint8_t b_pof_have_data = 0;
			//восстановление сохраненных конфигураций
			APP_ERROR_CHECK(pof_manager_get_state(&b_pof_have_data));	
//			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: pof_manager %d\r\n"RTT_CTRL_RESET, b_pof_have_data);

			pcf8563_date_time_t time;
								
			time.seconds = 0;
			time.minutes = 7;
			time.hours = 12;
			time.days = 18;
			time.weekdays = pcf8563_wednesday;
			time.months = april;
			time.years = 2018;

			if( b_pof_have_data){ 
				//если есть данные, которые надо восстановить
				APP_ERROR_CHECK(pof_manager_copy_data()); 		//чтение из ПЗУ сохраненных параметров
				APP_ERROR_CHECK(pof_manager_clear_memory());	//затираем данные для ускорения записи при пропадании питания
//				APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: pof_manager %d\r\n"RTT_CTRL_RESET, app_pof_info.state);

				app_state_device.app_sensor_se = app_pof_info.b_sensor_se;
				app_state_device.app_lock_en = app_pof_info.b_lock_en;
				app_state_device.app_contrast = app_pof_info.b_contrast;
				app_state_device.app_calendar_en = app_pof_info.b_calendar_en;
				app_state_device.app_floor_sensor_idx = app_pof_info.floor_sensor_idx;
				app_state_device.app_floor_sensor_calib = app_pof_info.floor_sensor_calib;
				app_state_device.app_air_sensor_calib = app_pof_info.air_sensor_calib;
				app_state_device.app_hysteresis = app_pof_info.sensor_hysteresis;
				app_state_device_log.app_timer_power_on = app_pof_info.timer_power_on;
				app_state_device_log.app_count_set_relay = app_pof_info.count_set_relay;
				app_state_device.app_custom_power = app_pof_info.custom_power;
				app_state_device.app_selfteaching_en = app_pof_info.b_selfteaching_en;
				app_state_device.app_selfteaching_time_s = app_pof_info.selfteaching_time_s;
				app_state_device.app_off_en = app_pof_info.b_off_en;

				app_state_hand.app_sensor_custom_set = app_pof_info.sensor_custom_set;
				app_state_hand.app_sensor_active_set = app_pof_info.sensor_active_set;
				
				temp_set_idx_floor(app_state_device.app_floor_sensor_idx);
				display_set_bias(app_state_device.app_contrast);
				
				app_state_device.app_shift_utc = app_pof_info.shift_utc;

				do{
					uint32_t utc;
					uint32_t timeout = 10;
					
					uint16_t err_code = pcf8563_get_time_utc(&utc);
					
					if (err_code == NRF_SUCCESS) {
						calendar_set_time(utc, app_state_device.app_shift_utc);
						break;
					}else
						if(err_code == NRF_ERROR_INTERNAL) {
							APP_PRINTF(0, "Cannot access\r\n");
							nrf_delay_ms(100);
							if(timeout-- == 0) {
								APP_ERROR_CHECK(err_code);
							}
							continue;
						}else 
							if(err_code != NRF_ERROR_INVALID_DATA) {
								APP_ERROR_CHECK(err_code);
							}else
							{
								pcf8563_set_time(&time);
							}
				}while(1);
							
				
				switch(app_pof_info.state){
					case APP_STAND_BY:
					case APP_SLAVE_FW_UPDATE:
					{
//						if(app_pof_info.state == APP_SLAVE_FW_UPDATE) APP_PRINTF(0,"UPDATE\r\n");
						api_state_change(APP_STAND_BY);
						return;
					}
					default:
					{
//						APP_PRINTF(0,"DFAULT\r\n");
						api_state_change(APP_OFF);
						return;
					}
				}
			}
			pcf8563_set_time(&time);
//			APP_PRINTF(0,"OFF\r\n");
			temp_set_idx_floor(app_state_device.app_floor_sensor_idx);
			api_state_change(APP_OFF);
			return;
		}
		case APP_TEST:
		{
			if(app_timeout_test_10ms == 0 && app_test_stage == 1){ app_test_stage = 2; app_clear_relay(false); }
	
			if(app_test_stage == 3){
				float temp[4];
				for(uint8_t i=0; i<=3; ++i){
					NRF_GPIO->OUTSET = (1UL << TEMP_LEVEL_1);
					NRF_GPIO->OUTSET = (1UL << TEMP_LEVEL_2);
					NRF_GPIO->OUTSET = (1UL << TEMP_LEVEL_3);

					if(i == 1) NRF_GPIO->OUTCLR = (1UL << TEMP_LEVEL_1);
					if(i == 2) NRF_GPIO->OUTCLR = (1UL << TEMP_LEVEL_2);
					if(i == 3) NRF_GPIO->OUTCLR = (1UL << TEMP_LEVEL_3);

					nrf_delay_ms(100);
					temp[i] = temp_v2_meas();
				}
				for(uint8_t i=1; i<=3; ++i){
					if((temp[0] < temp[i]+5) && (temp[0] > temp[i]-5)) app_level_err_test_fl = true;
				}
			}
			//управление дисплеем
			app_ind_test();

			if(!app_timeout_test)
			{				
				api_state_change(APP_OFF);
			}
			
			//обработка кнопок
			APP_CHANGE_LOCK_DEVICE();		//блокировка/разблокировка кнопок

			if(app_state_device.app_lock_en || evcal_get_cur_transaction()) break;

			if(app_test_stage != 2) APP_OFF_DEVICE();						//transition in mode APP_OFF, if press button "ON/OFF"

			if(keyb_data.event_press == BTN_EVENT_CAPTURED)
			{
				app_timeout_test = APP_TIMEOUT_TEST;
				nrf_gpio_pin_set(BL_PIN);
			}
			if(keyb_data.event_release == BTN_EVENT_CAPTURED){
				nrf_gpio_pin_clear(BL_PIN);
				if(keyb_data.code_press == BTN_MENU && app_test_stage == 0) {app_test_stage = 1; app_timeout_test_10ms = APP_TEST_TIMEOUT_STAGES_10MS; app_set_relay();}
				if(keyb_data.code_press == BTN_ON && app_test_stage == 2) app_test_btn_mask &= ~MONDAY_IND;
				if(keyb_data.code_press == BTN_MENU && app_test_stage == 2)	app_test_btn_mask &= ~TUESDAY_IND;
				if(keyb_data.code_press == BTN_CLOCK && app_test_stage == 2) app_test_btn_mask &= ~WEDNESDAY_IND;
				if(keyb_data.code_press == BTN_UP && app_test_stage == 2) app_test_btn_mask &= ~THURSDAY_IND;
				if(keyb_data.code_press == BTN_DOWN && app_test_stage == 2) app_test_btn_mask &= ~FRIDAY_IND;
				
			}
			if(app_test_stage == 2 && !app_test_btn_mask)	app_test_stage = 3;						


			break;
		}
		case APP_PREPAIRING:
		{
			//обработка подсветки дисплея
			if(app_blink_num && app_timeout_ind_pairing_10ms >= 70){ app_timeout_ind_pairing_10ms = 0; --app_blink_num; }

			if(app_blink_num && app_timeout_ind_pairing_10ms > 30) nrf_gpio_pin_clear(BL_PIN);
			if(app_blink_num && app_timeout_ind_pairing_10ms <= 30) nrf_gpio_pin_set(BL_PIN);

			
			if(keyb_data.event_release == BTN_EVENT_CAPTURED){
					api_state_change(APP_PAIRING); 
			}
//			if(	(keyb_data.code_press == BTN_MENU_UP) && (!(keyb_data.pressed_time_ms%APP_UNPAIRING_TIME_MS)&& keyb_data.pressed_time_ms))
//			{
//				api_state_change(APP_PREUNPAIRING);
//			}
			
			break;
		}
		case APP_PAIRING:
		{
			app_ind_pairing();
			
			app_clear_relay(false);
			
			if(AppTimeoutFl == false)	app_timeout_ind_change_state_10ms = 3000;
			if(AppTimeoutFl == true && !app_timeout_ind_change_state_10ms)
			{		//таймаут пейринга
				api_state_change(APP_OFF);
			}
			
			break;
		}
		case APP_SLAVE_FW_UPDATE:
		{

			//выключение реле
			app_clear_relay(false);

			uint8_t symbol[8][2]={{0x01, 0x00}, {0x00, 0x01}, {0x00, 0x02}, {0x00, 0x04}, {0x00, 0x08}, {0x08, 0x00}, {0x10, 0x00}, {0x20, 0x00}};
			
			//update prosses
			if(AppTimeoutFl == false && app_update_fw_fl == false){
				app_blink_num = 3;
				app_timeout_ind_pairing_10ms = 0;
				app_timeout_ind_change_state_10ms = 3000;

				if(!app_timeout_update_ms){ app_timeout_update_ms = 1000; app_symbol_update_dysplay = (app_symbol_update_dysplay+1)%8;}
			
				display_write_digit_data(APP_SYMBOL_DIGIT_TEMP_ADDR, symbol[app_symbol_update_dysplay][0]);
				display_write_digit_data(APP_SYMBOL_DIGIT_TEMP_ADDR+1, symbol[app_symbol_update_dysplay][1]);
			}
			//update success
			if(app_update_fw_fl == true)
			{
				//обработка подсветки дисплея
				if(app_blink_num && app_timeout_ind_pairing_10ms >= 70){ app_timeout_ind_pairing_10ms = 0; --app_blink_num; }

				if(app_blink_num && app_timeout_ind_pairing_10ms <= 30) nrf_gpio_pin_set(BL_PIN);	
				if(app_blink_num && app_timeout_ind_pairing_10ms > 30) nrf_gpio_pin_clear(BL_PIN);

				if(app_blink_num == 0){
					app_timeout_ind_change_state_10ms = 0;
          APP_ERROR_CHECK(r4s_slave_disconnect());
					r4s_bsl_start();
				}
			}
			//update error
			if(AppTimeoutFl == true && app_update_fw_fl == false){
				uint8_t str[] = "OO";
				app_symbol_get_symbol(APP_SYMBOL_DIGIT_TEMP_ADDR,  2, str, 1);
			
				app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 3, 0, 0);	//не отображаем дробную часть
				display_write_symbol_data(SYMBOL_L_CELSIUS, 0);									//не отображаем знак сельсия 
				app_digit_get_symbol(APP_SYMBOL_DIGIT_SET_TEMP_ADDR, 2, 0, 0);	//отображаем целую часть
				
			}			
			
			if((AppTimeoutFl == true || app_update_fw_fl == true) && !app_timeout_ind_change_state_10ms)
			{	
				app_blink_num = 0;
				api_state_change(APP_STAND_BY);
			}
			
					//проверка кнопки "ON/OFF"
			APP_OFF_DEVICE();						//transition in mode APP_OFF, if press button "ON/OFF"
			
			break;
		}
		case APP_OFF:
		{
			bool flg_press_btn = false;
			//управление дисплеем
			app_ind_off();

			//выключение реле
			app_clear_relay(false);

			//обработка кнопок
//			APP_CHANGE_LOCK_DEVICE();		//блокировка/разблокировка кнопок
			
//			if(app_state_device.app_lock_en) break;

			if(keyb_data.code_press == BTN_UP && keyb_data.pressed_time_ms == APP_PAIRING_TIME_MS){
				//переход в режим pairing
				api_state_change(APP_PREPAIRING);
			}
			if(keyb_data.code_press == BTN_DOWN && keyb_data.pressed_time_ms == APP_UNPAIRING_TIME_MS){
				//переход в режим unpairing
				api_state_change(APP_UNPAIRING);
			}

			if(keyb_data.event_release == BTN_EVENT_CAPTURED)
			{
				flg_press_btn = true;
				switch(keyb_data.code_press){
					case BTN_ON:
					{
						api_state_change(APP_STAND_BY);
						break;
					}
					case BTN_ON_CLOCK:		//переход в сервисный режим
					{
						api_state_change(APP_SERVICE_MENU);
						break;
					}
					case BTN_MENU_UP:	//переход в тестовый режим
					{
						api_state_change(APP_TEST);
						break;
					}
					default:
						flg_press_btn = false;
				}
			}
			if(flg_press_btn == true){
				app_timeout_illumination_10s = APP_TIMEOUT_ILLUMINATION_MS;
				if(!app_blink_num)  nrf_gpio_pin_set(BL_PIN);
			}
			break;
		}
		case APP_STAND_BY:
		{
			//управление дисплеем
			app_ind_stand_by();

			//управление реле
			
			if((temp[app_state_device.app_sensor_se] >= app_state_hand.app_sensor_active_set+app_state_device.app_hysteresis)/*температура активного датчика превышает установку*/ \
				|| (app_state_device.app_sensor_se == APP_ACTIVE_AIR_SENSOR 											\
						&& app_state_err[app_state_device.app_sensor_se] != APP_ERROR_NO 							\
						&& app_state_err[app_state_device.app_sensor_se] != APP_ERROR_AIR_LESS_MIN)		\
				|| (app_state_device.app_sensor_se == APP_ACTIVE_FLOOR_SENSOR 										\
						&& app_state_err[app_state_device.app_sensor_se] != APP_ERROR_NO 							\
						&& app_state_err[app_state_device.app_sensor_se] != APP_ERROR_FLOOR_LESS_MIN)	\
				|| (app_state_device.app_off_en == true) /*отключено реле*/		\
				|| (fl_over_hi))	/*превышение максимального порога на любом из датчиков*/
			{
				if(nrf_gpio_pin_read(RELE_PIN) && !app_timeout_illumination_10s && !flag_control_smartfone){ 
				//реле включено и нет пользовательского управления => считаем selfteachin
					app_end_temp_heating = temp[app_state_device.app_sensor_se];	//запоминаем конечную температуру нагрева
					app_clear_relay(true);	//выключаем реле и расчитываем значения selfteachin
				}
				if(flag_control_smartfone == true) flag_control_smartfone = false;
					//запускаем задержку на повторное включение реле
				else app_timeout_on_relay_ms = TIMERS_MINUTES(APP_TIMEOUT_ON_RELAY);
				//выключаем реле без расчета selfteaching
				app_clear_relay(false);
			}
			else if(temp[app_state_device.app_sensor_se] <= app_state_hand.app_sensor_active_set-app_state_device.app_hysteresis)
			{ 
				flag_control_smartfone = false;
				if(!nrf_gpio_pin_read(RELE_PIN)){
					//запоминаем начальную температуру нагрева
					app_begin_temp_heating = temp[app_state_device.app_sensor_se];
				}
				//включаем реле
				app_set_relay();
			}
			//обработка подсветки дисплея
			if(app_blink_num && app_timeout_ind_pairing_10ms >= 70){ app_timeout_ind_pairing_10ms = 0; --app_blink_num; }

			if(app_blink_num && app_timeout_ind_pairing_10ms <= 30) nrf_gpio_pin_set(BL_PIN);
			if(app_blink_num && app_timeout_ind_pairing_10ms > 30) nrf_gpio_pin_clear(BL_PIN);
			
			
			//обработка кнопок
			APP_CHANGE_LOCK_DEVICE();		//блокировка/разблокировка кнопок
			
			if(app_state_device.app_lock_en) break;
						
			//проверка кнопки "ON/OFF"
			APP_OFF_DEVICE();						//transition in mode APP_OFF, if press button "ON/OFF"

			if(	(keyb_data.code_press == BTN_UP) && (keyb_data.event_press == BTN_EVENT_CAPTURED || 				\
					(!(keyb_data.pressed_time_ms%APP_TIMEOUT_PRESS_TIME_MS)&&(pre_time_ms != keyb_data.pressed_time_ms))))
			{
				//инкремент/декримент порога выставленной температуры
				pre_time_ms = keyb_data.pressed_time_ms;
				//увеличиваем/уменьшаем значение порога температурных датчиков на step
				
				if(app_state_device.app_off_en == true) app_state_device.app_off_en = false;
				else{
					app_state_hand.app_sensor_active_set = (APP_PARAM_INC(app_state_hand.app_sensor_active_set, APP_CUSTOM_TEMPERATURE_TOP, 0.5f));
					app_state_hand.app_sensor_custom_set = app_state_hand.app_sensor_active_set;
				}
			}
			
			if(	(keyb_data.code_press == BTN_DOWN) && (keyb_data.event_press == BTN_EVENT_CAPTURED || 				\
					(!(keyb_data.pressed_time_ms%APP_TIMEOUT_PRESS_TIME_MS)&&(pre_time_ms != keyb_data.pressed_time_ms))))
			{
				//инкремент/декримент порога выставленной температуры
				pre_time_ms = keyb_data.pressed_time_ms;
				//увеличиваем/уменьшаем значение порога температурных датчиков на step
				
				if(app_state_hand.app_sensor_active_set == APP_ALARM_TEMPERATURE_BOTTOM) app_state_device.app_off_en = true;
				else{
					app_state_hand.app_sensor_active_set = (APP_PARAM_DEC(app_state_hand.app_sensor_active_set, APP_ALARM_TEMPERATURE_BOTTOM, 0.5f));
					app_state_hand.app_sensor_custom_set = app_state_hand.app_sensor_active_set;
				}
			}

				//переключение температурного датчика (AIR/FLOOR) для управления прибором
			if(keyb_data.code_press == BTN_ON_CLOCK && keyb_data.event_press == BTN_EVENT_CAPTURED)
			{
				app_state_device.app_sensor_se = ~app_state_device.app_sensor_se;	
			}
				
			if(keyb_data.event_release == BTN_EVENT_CAPTURED){
				//включение/выключение выполнения задач календаря
				if(keyb_data.code_press == BTN_MENU){
					app_state_device.app_calendar_en = ~app_state_device.app_calendar_en;
					
					uint8_t num;
					evcal_get_task_number(&num);
					
					if(app_state_device.app_calendar_en == false && num){
						//установка пользовательского температурного порога
						app_state_hand.app_sensor_active_set = app_state_hand.app_sensor_custom_set;
					}else{
						app_timeout_blink_calendar_10ms = APP_TIMEOUT_BLINK_10MS*3;	
					}
				}
			}
			if(	keyb_data.code_press == BTN_CLOCK && (keyb_data.pressed_time_ms >= APP_CLOCK_EDIT_TIME_MS))
			{
				//переход в режим настройки часов
				api_state_change(APP_CLOCK_EDIT);
			}
//			if(	(keyb_data.code_press == BTN_MENU_UP) && (!(keyb_data.pressed_time_ms%APP_PAIRING_TIME_MS) && keyb_data.pressed_time_ms))
//			{
//				api_state_change(APP_PREPAIRING);
//			}
			break;
		}
		case APP_PREUNPAIRING:
		{
			if(app_blink_num && app_timeout_ind_pairing_10ms >= 70){ app_timeout_ind_pairing_10ms = 0; --app_blink_num; }

			if(app_blink_num && app_timeout_ind_pairing_10ms > 30) nrf_gpio_pin_clear(BL_PIN);
			if(app_blink_num && app_timeout_ind_pairing_10ms <= 30) nrf_gpio_pin_set(BL_PIN);

			if(keyb_data.event_release == BTN_EVENT_CAPTURED){
					api_state_change(APP_UNPAIRING); 
			}
			break;
		}
		case APP_SERVICE_MENU:
		{
			//управление дисплеем
			app_ind_service_menu();

			//выключение реле
			app_clear_relay(false);

			//обработка кнопок
			APP_CHANGE_LOCK_DEVICE();		//блокировка/разблокировка кнопок
			
			if(app_state_device.app_lock_en) break;

			APP_OFF_DEVICE();						//transition in mode APP_OFF, if press button "ON/OFF"
			
			//удержание кнопки UP/DOWN
			if(	(keyb_data.code_press == BTN_UP || keyb_data.code_press == BTN_DOWN)	\
					&& (keyb_data.event_press == BTN_EVENT_CAPTURED || (!(keyb_data.pressed_time_ms%APP_TIMEOUT_PRESS_TIME_MS)&&(pre_time_ms != keyb_data.pressed_time_ms))))
			{
				pre_time_ms = keyb_data.pressed_time_ms;
				//обработка сервисного меню 
				app_service_menu_handler(&keyb_data);
			}
			if(keyb_data.event_release == BTN_EVENT_CAPTURED){
				if(keyb_data.code_press == BTN_MENU){
					if(++app_state_service_menu.sm_index_menu > SM_CLEAN_SETTING_06){
						
						
						if(app_state_service_menu.sm_clean_setting == true){
							//сброс к заводским настройкам
							app_state_device.app_air_sensor_calib = CALIB_SENSOR_AIR_DEFAULT;
							app_state_device.app_floor_sensor_calib = CALIB_SENSOR_FLOOR_DEFAULT;
							app_state_device.app_contrast = true;
							app_state_device.app_floor_sensor_idx = 0;
							app_state_device.app_hysteresis = APP_HYSTERESIS_DEFAULT;
							app_state_device.app_custom_power	= 0;
				
							display_set_bias(app_state_device.app_contrast);
							temp_set_idx_floor(app_state_device.app_floor_sensor_idx);
						}else{
							//копируем данные из временной структуры
							app_state_device.app_air_sensor_calib = app_data_service_menu.app_air_sensor_calib;
							app_state_device.app_floor_sensor_calib = app_data_service_menu.app_floor_sensor_calib;
							app_state_device.app_contrast = app_data_service_menu.app_contrast;
							app_state_device.app_floor_sensor_idx = app_data_service_menu.app_floor_sensor_idx;
							app_state_device.app_hysteresis = app_data_service_menu.app_hysteresis;
						}
						api_state_change(APP_STAND_BY);
					}
				}
			}
			
			if(!app_timeout_service_menu) api_state_change(APP_STAND_BY);
			break;
		}
		case APP_UNPAIRING:
		{
			if(app_blink_num && app_timeout_ind_pairing_10ms >= 70)
			{ 
				app_timeout_ind_pairing_10ms = 0; 
				--app_blink_num; 
			}

			if(app_blink_num && app_timeout_ind_pairing_10ms <= 30) 
				nrf_gpio_pin_set(BL_PIN);
			else nrf_gpio_pin_clear(BL_PIN);

			if(!app_blink_num && keyb_data.code_release == BTN_ALL_RELEASE)
			{ 
				api_state_change(APP_OFF);
			}
			break;
		}
		case APP_CLOCK_EDIT:
		{
			//display shows
			app_ind_clock_edit();

			//off relay
			app_clear_relay(false);

			//controls buttons
			APP_CHANGE_LOCK_DEVICE();		//block/unblock buttons
			
			if(app_state_device.app_lock_en) break;
			
			APP_OFF_DEVICE();						//transition in mode APP_OFF, if press button "ON/OFF"

			if(keyb_data.code_press == BTN_UP 
				&& (keyb_data.event_press == BTN_EVENT_CAPTURED || (!(keyb_data.pressed_time_ms%APP_TIMEOUT_PRESS_TIME_MS)&&(pre_time_ms != keyb_data.pressed_time_ms))))
			{
								//инкремент/декримент порога выставленной температуры
				pre_time_ms = keyb_data.pressed_time_ms;

				calendar_get_utc_time(&timestamp, &shift);
				utc_to_date(timestamp, &time);
					
				if(app_state_clock_edit.element_edit == 0){ if(time.hours <= 23) if(++time.hours >=24) time.hours = 0; }
				else if(app_state_clock_edit.element_edit == 1){	if(time.minutes <= 59) if(++time.minutes >= 60) time.minutes = 0; }
				else if(app_state_clock_edit.element_edit == 2){
					++time.days;
				}
				else break;
					
				calendar_set_time(date_to_utc(&time), shift);

			}
			if(keyb_data.code_press == BTN_DOWN && (keyb_data.event_press == BTN_EVENT_CAPTURED || 					\
				(!(keyb_data.pressed_time_ms%APP_TIMEOUT_PRESS_TIME_MS)&&(pre_time_ms != keyb_data.pressed_time_ms))))
			{
								//инкремент/декримент порога выставленной температуры
				pre_time_ms = keyb_data.pressed_time_ms;

				calendar_get_utc_time(&timestamp, &shift);
				utc_to_date(timestamp, &time);
					
				if(app_state_clock_edit.element_edit == 0){ if(time.hours != 0) --time.hours; else time.hours = 23;}
				else if(app_state_clock_edit.element_edit == 1){	if(time.minutes != 0) --time.minutes; else time.minutes = 59;}
				else if(app_state_clock_edit.element_edit == 2){
					--time.days; 
				}
				else break;
					
				calendar_set_time(date_to_utc(&time), shift);
			}
		
			if(keyb_data.event_release == BTN_EVENT_CAPTURED && !app_state_device.app_lock_en && !evcal_get_cur_transaction()){
				if(keyb_data.code_press == BTN_CLOCK){
					if(app_first_press_menu == false) app_first_press_menu = true; 
					else if(++app_state_clock_edit.element_edit >= 3){
						calendar_get_utc_time(&timestamp, &shift);
						pcf8563_set_time_utc(&timestamp);
						api_state_change(APP_STAND_BY);
					}
				}
			}
			break;
		}
		default:
		{
//			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: default\r\n"RTT_CTRL_RESET);
			api_state_change(APP_STAND_BY);
			break;
		}
	}
	return;
}
/********************************************************************************
 *
 ********************************************************************************/
static void app_clear_relay(bool reach)
{
	if(reach == true && ((int8_t)(app_end_temp_heating - app_begin_temp_heating))> 0)
	{
		app_state_device.app_selfteaching_time_s = app_selfteaching_time_ms/1000/(app_end_temp_heating - app_begin_temp_heating);
		APP_PRINTF(0,"[SELFTEACHING]: timer_ms=%d, temp=%d.02%d, time_s=%d%\n", app_selfteaching_time_ms, 
											(uint16_t)(app_end_temp_heating - app_begin_temp_heating),  (uint16_t)((app_end_temp_heating - app_begin_temp_heating)*100)%100,
											app_state_device.app_selfteaching_time_s);
	}
	nrf_gpio_pin_clear(RELE_PIN);
	app_selfteaching_time_ms = 0;
	fl_inc_set_heat = false;
}

static void app_set_relay(void)
{
	nrf_gpio_pin_set(RELE_PIN);
	if(!fl_inc_set_heat){
		fl_inc_set_heat = true;
		++app_state_device_log.app_count_set_relay;
	}
}

/********************************************************************************
 *
 ********************************************************************************/
/**
 * 	описание в application_api.h
*/

uint16_t api_get_version(void)
{
	return FIRMWARE_VER;
}
/***************************COMMON************************************/
void api_state_change(app_state_t new_state)
{
	app_state = new_state;
	application_init_prew_state( app_state );
	application_init_new_state( app_state );
}

bool api_state_check(app_state_t state)
{
	if(app_state == state)
	{
		return true;
	}
	else
	{
		return false;
	}
}
uint8_t api_get_state(void)
{
	switch(app_state)
	{
#ifdef NRF51
		case APP_SLAVE_FW_UPDATE:
		{
			return APP_API_FW_UPDATE_STATE;
		}
#endif
		case APP_STAND_BY:
		{
			if( (app_state_err[app_state_device.app_sensor_se] == APP_ERROR_AIR_OVER_MAX)				\
				||(app_state_err[app_state_device.app_sensor_se] == APP_ERROR_AIR_SENSOR_LOWER)		\
				||(app_state_err[app_state_device.app_sensor_se] == APP_ERROR_AIR_SENSOR_DROP)		\
				||(app_state_err[app_state_device.app_sensor_se] == APP_ERROR_FLOOR_OVER_MAX) 		\
				||(app_state_err[app_state_device.app_sensor_se] == APP_ERROR_FLOOR_SENSOR_LOWER)	\
				||(app_state_err[app_state_device.app_sensor_se] == APP_ERROR_FLOOR_SENSOR_DROP)	\
				|| fl_over_hi)
					return APP_API_ERROR_STATE;
			
			if(app_state_device.app_off_en == true) return APP_API_STAND_BY_STATE;
			else return APP_API_RUN_STATE;
		}
		case APP_PAIRING:
			return APP_API_PAIRING_STATE;
		default:
		{
			return APP_API_ERROR_STATE;
		}
	}
}
/********************************************************************************/
/***************************PERIPHERAL************************************/
/**
 * 	описание в application_api.h
*/
float api_get_temp(void)
{
	float temp;
	temp_get_value(app_state_device.app_sensor_se, &temp);
	temp += ((app_state_device.app_sensor_se==APP_ACTIVE_AIR_SENSOR)?(app_state_device.app_air_sensor_calib):(app_state_device.app_floor_sensor_calib));
	return temp;
}

uint8_t api_get_error(void)
{
	if((app_state_err[APP_ACTIVE_AIR_SENSOR] == APP_ERROR_AIR_OVER_MAX 				\
			|| app_state_err[APP_ACTIVE_AIR_SENSOR] == APP_ERROR_AIR_SENSOR_LOWER	\
			|| app_state_err[APP_ACTIVE_AIR_SENSOR] == APP_ERROR_AIR_SENSOR_DROP))\
					return app_state_err[APP_ACTIVE_AIR_SENSOR];
			
	if((app_state_err[APP_ACTIVE_FLOOR_SENSOR] == APP_ERROR_FLOOR_OVER_MAX 				\
			|| app_state_err[APP_ACTIVE_FLOOR_SENSOR] == APP_ERROR_FLOOR_SENSOR_LOWER	\
			|| app_state_err[APP_ACTIVE_FLOOR_SENSOR] == APP_ERROR_FLOOR_SENSOR_DROP))\
					return app_state_err[APP_ACTIVE_FLOOR_SENSOR];
	
	if(fl_over_hi == 1) return APP_ERROR_AIR_OVER_MAX;
	if(fl_over_hi == 2) return APP_ERROR_FLOOR_OVER_MAX;
	
	return APP_ERROR_NO;
}

/**
 * 	описание в application_api.h
*/
/*
void api_master_event_proccess(uint8_t index)
{
//	com_master_proccess(EVT_BTN_PRESSED(index, BTN_CLICK_SINGLE));
}
*/
#ifdef NRF51
uint8_t api_enter_fw_update_state(void)
{
	if((app_state == APP_STAND_BY)	
		  ||( app_state == APP_SLAVE_FW_UPDATE))
	{
		api_state_change(APP_SLAVE_FW_UPDATE);
		return 1;
	}
	return 0;
}

void api_fw_update_proc(void)
{
	if( app_state == APP_SLAVE_FW_UPDATE )
	{
		APP_ERROR_CHECK(app_timer_stop(app_timer_id));

		APP_ERROR_CHECK(app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_FW_UPDATE_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL ));
	}
}
#endif

void api_slave_pairing_complete( void )
{
}

void api_slave_pairing_error(void)
{
}

/********************************************************************************/
/***************************CENTRAL************************************/
/********************************************************************************/
//формирование событий календаря по case
//void api_create_case_calendar(app_state_t state)
//{
//	calendar_time_t time;
//	calendar_get_calendar_time(&time);
//	
//	evcal_task_t data = {0};
//	data.enable = true;
//	data.repeat_type = 2;
//	
//	*(data.cmd.array+1) = AC_EXTENTION_TEMP_SFLOAT_CMD;

//	exten_req_temp_sfloat_cmd_t* param_ac = (exten_req_temp_sfloat_cmd_t*)(data.cmd.array+2);
//	param_ac->mode = 0x02;	//задаем режим подогрева до температуры param1
//	
//	api_add_trans_in_calendar(EVCAL_TRANSACTION_WRITE);
//	
//	switch(state)
//	{
//		case APP_CASE_HOLIDAY:
//		{
//			//удаляем предыдущие задачи календаря
//			evcal_erase_all_tasks();
//			//создаем новые задачи по кейсу
//			for(uint8_t i=0; i<4; ++i){
//				//задаем время 
//				time.hours = IN_TIMERS_HOURS(app_state_case_holiday.holiday.state_event[i].time_m);
//				time.minutes = IN_TIMERS_MINUTES(app_state_case_holiday.holiday.state_event[i].time_m);
//				data.timestart = date_to_utc(&time);
//				//задаем дни недели
//				data.repeat_rules = 0x7F;
//				//задаем температуру
//				param_ac->param1 = FloatToSFloat16(app_state_case_holiday.holiday.state_event[i].temp_set);
//				//сохраняем задачу
//				APP_ERROR_CHECK(evcal_save_task(&data));
//			}			
//			break;
//		}
//		case APP_CASE_COTTAGE:
//		{
//			//удаляем предыдущие задачи календаря
//			evcal_erase_all_tasks();
//			//создаем новые задачи по кейсу
//			for(uint8_t i=0; i<4; ++i){
//				time.hours = IN_TIMERS_HOURS(app_state_case_cottage.cottage.state_event[i].time_m);
//				time.minutes = IN_TIMERS_MINUTES(app_state_case_cottage.cottage.state_event[i].time_m);
//				data.timestart = date_to_utc(&time);
//				data.repeat_rules = 0x60;
//				param_ac->param1 = FloatToSFloat16(app_state_case_cottage.cottage.state_event[i].temp_set);
//				APP_ERROR_CHECK(evcal_save_task(&data));
//			}			
//			break;
//		}
//		default:
//		{
//			break;
//		}
//	}
//	api_dec_trans_in_calendar(EVCAL_COMMIT_TRANSACTION);
//}

//static uint32_t app_parse_case_calendar(uint8_t* p_index, app_state_case_t* p_case, evcal_task_t* p_data, const uint8_t max_count)
//{
//	calendar_time_t time;
//	
//	//задач в календаре больше допустимого
//	if(*p_index > max_count) return NRF_ERROR_INVALID_PARAM;

//	//определяем время
//	utc_to_date(p_data->timestart, &time);
//	p_case->state_event[*p_index].time_m = TIMERS_HOURS(time.hours)+time.minutes;
//	if((p_case->state_event[*p_index].time_m)>TIMERS_HOURS(24)) return NRF_ERROR_INVALID_PARAM;
//		
//	//определяем температурный порог
//	exten_req_temp_sfloat_cmd_t* param_ac = (exten_req_temp_sfloat_cmd_t*)(p_data->cmd.array+2);
//	p_case->state_event[*p_index].temp_set = SFloat16ToFloat(param_ac->param1);
//	
//	if((param_ac->mode&0x0F) != 2) return NRF_ERROR_INVALID_PARAM;
//	if((param_ac->mode&0x0F) == 2 && p_case->state_event[*p_index].temp_set > APP_ALARM_TEMPERATURE_TOP) p_case->state_event[*p_index].temp_set = APP_ALARM_TEMPERATURE_TOP;
//	if((param_ac->mode&0x0F) == 2 && p_case->state_event[*p_index].temp_set > APP_ALARM_TEMPERATURE_BOTTOM) p_case->state_event[*p_index].temp_set = APP_ALARM_TEMPERATURE_BOTTOM;
//	
//	//увеличиваем кол-во задач в календаре
//	++(*p_index);
//	
//	return NRF_SUCCESS;
//}

//сортировка (выбором) полученных из календаря задач
//static void app_choices_sort_case_calendar(app_state_case_t* p_case, const uint8_t max_count)
//{
//	uint32_t tmp_time_m;
//	float tmp_temp_set;

//	for(int repeat_counter=0; repeat_counter<max_count; ++repeat_counter){
//		for(int element_counter=repeat_counter+1; element_counter<max_count; ++element_counter){
//			if (p_case->state_event[repeat_counter].time_m > p_case->state_event[element_counter].time_m){
//				tmp_time_m = p_case->state_event[repeat_counter].time_m;
//				tmp_temp_set = p_case->state_event[repeat_counter].temp_set;
//						
//				p_case->state_event[repeat_counter].time_m = p_case->state_event[element_counter].time_m;
//				p_case->state_event[repeat_counter].temp_set = p_case->state_event[element_counter].temp_set;
//					
//				p_case->state_event[element_counter].time_m = tmp_time_m;
//				p_case->state_event[element_counter].temp_set = tmp_temp_set;
//			}
//		}
//	}
//}

//uint32_t api_get_case_calendar(app_state_t state)
//{
//	evcal_task_t data;
//	uint8_t num_task;
//	APP_ERROR_CHECK(evcal_get_task_number(&num_task));
//
//	return NRF_SUCCESS;
//}
/*
void api_master_pairing_success( uint8_t tok_id, cm_dev_support_ac_t * required_ac )
{
//    mct_transaction_t * tran = NULL;
    
    mct_link_t link;
    memset(&link, 0, sizeof(mct_link_t));
    
    mct_searchp_context_t search;
    memset(&search, 0, sizeof(mct_searchp_context_t));
    
    
    bool start_support = false;
    bool stop_support = false;
    
 //   uint32_t err_code;
    
	APP_PRINTF(0, RTT_CTRL_TEXT_BRIGHT_GREEN"[APPL]: api_master_pairing_complete\r\n"RTT_CTRL_RESET);
    
    // Тип транзации READ_WRITE не рекомендуется использовать, если не будет гарантированной записи
    // иначе пока транзакция не будет закрыта остальные транзакции не возможны
//    err_code = mct_open_transaction(&tran, TRAN_READ_WRITE, TRAN_COMMON_PERM);
//    if (err_code != NRF_ERROR_BUSY){
//        APP_ERROR_CHECK(err_code);
        
        bool defaultAllowFl = true;
        
//  Чтобы пэйринг по дефолту был доступен 5 раз нужно раскоментировать код ниже
//        search.params.filter.event_mask = EVENT_CODE_MASK; // Также будем искать по коду события
//        search.params.values.event = BTN_EVENT_CODE<<EVENT_CODE_Pos; // Только события нажатия кнопки
//        
//        uint8_t evt_cnt = 0;
//        while (mct_find_link(tran, &search, &link) == NRF_SUCCESS){ // События найдено
//            if (link.event & ((1ul<<PairButtonId)<<(BTN_EVT_MASK_Pos))) {
//                evt_cnt++;
//                if (evt_cnt > 5) { // 5 - количество событий, позволенных для кнопки PairButtonId
//                   defaultAllowFl = false; // Тогда события не нужны
//                   break;
//                }
//            }
//        }
        
        if (required_ac->is_support[0]){
            start_support = true;
        }
        if (required_ac->is_support[1]){
            stop_support = true;
        }
        
        if(defaultAllowFl && (stop_support || start_support))
        {
            link.tok_id = tok_id;
            
            if (stop_support) {            
//                link.event = EVT_BTN_PRESSED(PairButtonId, BTN_CLICK_SINGLE);
                link.ac = AC_EXTENTION_STOP_CMD;
                link.data[0]=0xFF;
                link.data[1]=0xFF;
                link.enable = 0x01;
                link.perm = MCT_PERM(MCT_PERM_ANY_USER, MCT_PERM_ANY_USER); // полный доступ
                // Рекомендуется создавать события с ограничениями НЕ ПРИ ПРЯМОМ ПЭЙРИНГЕ
                // поскольку при анпейринге такие события не будут удалены.
                //
                // Событие, которое может быть прочитано только устройством
                //link.perm = MCT_PERM(MCT_PERM_ONLY_MASTER, MCT_PERM_ANY_USER);
                // Событие, которое может быть изменено только устройством
                //link.perm = MCT_PERM(MCT_PERM_ANY_USER, MCT_PERM_ONLY_MASTER);
                // Событие, которое не может быть изменено и не может быть прочитано через приложение
                //link.perm = MCT_PERM(MCT_PERM_ONLY_MASTER, MCT_PERM_ONLY_MASTER);
                link.uuid = 0;

//                err_code = mct_set_link(tran, &link);
//								if (err_code != NRF_ERROR_NO_MEM)
//									APP_ERROR_CHECK(err_code);
            }
            
            if (start_support){
//                link.event = EVT_BTN_PRESSED(PairButtonId-1, BTN_CLICK_SINGLE);
                link.ac = AC_EXTENTION_START_CMD;
                link.data[0]=0xFF;
                link.data[1]=0xFF;
                link.enable = 0x01;
                link.perm = MCT_PERM(MCT_PERM_ANY_USER, MCT_PERM_ANY_USER); // полный доступ
                link.uuid = 0;

//                err_code = mct_set_link(tran, &link);
//                if (err_code != NRF_ERROR_NO_MEM)
//                    APP_ERROR_CHECK(err_code);
            }
        }
        
//        APP_ERROR_CHECK(mct_commit(&tran));
        
        // Уведомляем модули о обновлении mct
        com_slave_mct_notify();
        api_param_update();
//    }
	
	api_state_change(APP_STAND_BY);
}

void api_master_pairing_error( void )
{
	APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_GREEN"[APPL]: api_master_pairing_error\r\n"RTT_CTRL_RESET);
	api_state_change(APP_STAND_BY);
}
*/
uint8_t api_go_to_stand_by(void)
{
	if(( app_state != APP_STAND_BY )
		&& ( app_state != APP_PAIRING )
#ifdef NRF51
		&& ( app_state != APP_SLAVE_FW_UPDATE )
#endif
    )
	{
		return 0;
	}
	if( app_state == APP_PAIRING)
	{
		app_blink_num = 3;
		app_timeout_ind_pairing_10ms = 0;
		api_state_change(APP_STAND_BY);
		return 1;
	}
#ifdef NRF51
	if(app_state == APP_SLAVE_FW_UPDATE){
		AppTimeoutFl = true;
	}
#endif
	return 1;
}

void api_process_event(mct_link_t *link){
    switch(link->ac){
//        case AC_EXTANSION_START_CMD:{
//                if (api_state_check(APP_PASSIVE)) {
//                    api_state_change(APP_STAND_BY);
//                }
//            }
//            break;
//        case AC_EXTANSION_STOP_CMD: {
//            if (!api_state_check(APP_PASSIVE))
//                 api_state_change(APP_PASSIVE);
//            }
//            break;
        default: // неизвестный action code
            break;
    }
}

void api_param_update(void)
{
}

app_state_t  api_get_state_prev_off(app_state_t state)
{
	switch(state)
	{
		case APP_START:
#ifdef NRF51
		case APP_SLAVE_FW_UPDATE:	//обновление прошивки
#endif
		{
			return state;
		}
		case APP_CLOCK_EDIT:
		case APP_OFF:
		case APP_SERVICE_MENU:
		case APP_STAND_BY:
		case APP_PAIRING:							//pairing
		case APP_UNPAIRING:						//pairing
		case APP_PREPAIRING:					//prepairing
		case APP_PREUNPAIRING:				//prepairing
		case APP_ERROR:								//ошибка
		case APP_TEST:
		{
			return APP_STAND_BY;
		}
	}
	return APP_STAND_BY;
}

/**
 * 	описание в application_api.h
*/
uint8_t api_set_calendar_data(void *timestamp, void *shift)
{
	calendar_utc_t m_time_stamp = 0;
	calendar_shift_utc_t m_shift = 0;
	
	m_time_stamp = uint32_decode(timestamp); 	//преобразуем данные массива в uint32_t
	m_shift = uint32_decode(shift); 					//преобразуем данные массива в uint32_t

	uint32_t err_code = calendar_set_time(m_time_stamp, m_shift);
	pcf8563_set_time_utc(&m_time_stamp);
	
	app_state_device.app_shift_utc = m_shift;

	switch (err_code)
	{
		case NRF_SUCCESS:
			return ERSP_SUCCESS;
		case NRF_ERROR_INVALID_DATA:
			return ERSP_ERROR_INVALID_DATA;
		case DRIVER_MODULE_NOT_INITIALZED:
			return ERSP_ERROR_INTERNAL;
	}
	return ERSP_ERROR_UNKNOWN;
}
/**
 * 	описание в application_api.h
*/
void api_get_calendar_data(void *timestamp, void *shift)
{
	calendar_utc_t m_time_stamp = 0;
	calendar_shift_utc_t m_shift = 0;

	//получаем данные о текущем UTC и времени сдвига
	uint32_t err_code = calendar_get_utc_time(&m_time_stamp, &m_shift);
	APP_ERROR_CHECK(err_code);
	
	memcpy(timestamp, &m_time_stamp, sizeof(calendar_utc_t));
	memcpy(shift, &m_shift, sizeof(calendar_shift_utc_t));
}
/**
 * 	описание в application_api.h
*/
void api_get_evcal_task(uint8_t task_num, void *p_buf)
{
	uint32_t err_code = evcal_get_task_data(task_num, p_buf);
	if(err_code != NRF_SUCCESS){
		memset(p_buf, 0, sizeof(evcal_task_t));
	}
}
/**
 * 	описание в application_api.h
*/
uint8_t api_add_evcal_task(void *p_input, uint8_t *uid)
{
	//записываем задачу в календарь
//	uint8_t id = ((evcal_task_t*)p_input)->uid;
	uint32_t err_code = evcal_save_task(p_input);
	*uid = ((evcal_task_t *)p_input)->uid; //записываем UID присвоенной задачи
	switch (err_code)
	{
		case NRF_SUCCESS:
		{
//			if(id == 0) app_state_device.app_calendar_en = true;
			return ERSP_SUCCESS;
		}
		case NRF_ERROR_NO_MEM:
		{
			return ERSP_ERROR_NO_MEM;
		}
		case DRIVER_MODULE_NOT_INITIALZED:
		case NRF_ERROR_NULL:
		{
			return ERSP_ERROR_INTERNAL;
		}
	}
	return ERSP_ERROR_UNKNOWN;
}
/**
 * 	описание в application_api.h
*/
uint8_t api_del_evcal_task(uint8_t uid_task)
{
	//записываем новую задачу в календарь
	uint32_t err_code = evcal_delete_task(uid_task);

	uint8_t num;
	evcal_get_task_number(&num);

//	if(!num) app_state_device.app_calendar_en = false;
	
	switch (err_code)
	{
		case NRF_SUCCESS:
		{
			return ERSP_SUCCESS;
		}
		case NRF_ERROR_NOT_FOUND:
		{
			return ERSP_ERROR_NOT_FOUND;
		}
		case NRF_ERROR_INVALID_PARAM:
		{
			return ERSP_ERROR_INVALID_PARAM;
		}
		case DRIVER_MODULE_NOT_INITIALZED:
		{
			return ERSP_ERROR_INTERNAL;
		}
	}
	return ERSP_ERROR_UNKNOWN;
}
/**
 * 	описание в application_api.h
*/
uint8_t api_erase_evcal(void)
{
	if(evcal_erase_all_tasks() == NRF_SUCCESS){
//		app_state_device.app_calendar_en = false;
		return ERSP_SUCCESS;
	} else {
		return ERSP_ERROR_INTERNAL;
	}
}
/**
 * 	описание в application_api.h
*/
uint8_t api_get_ver_evcal(void)
{
	return EVCAL_DRIVER_VERSION;
}
/**
 * 	описание в application_api.h
*/
uint8_t api_get_max_task_evcal(void)
{
	return EVCAL_MAX_TASKS;
}
/**
 * 	описание в application_api.h
*/
uint8_t api_get_cur_task_evcal(void)
{
	uint8_t m_num_task = 0;
	APP_ERROR_CHECK(evcal_get_task_number(&m_num_task));
	return m_num_task;
}
/**
* 	@brief	Закрытие сессии в календаре
*
*	@param	task commit/rollback
*
*	@return возвращает код ошибки
*
*/
uint8_t api_dec_trans_in_calendar(uint8_t task)
{
	if(task!=0 && task!=1) return ERSP_ERROR_INVALID_PARAM;
		
	uint32_t err_code = evcal_dec_transaction((!task)?(EVCAL_COMMIT_TRANSACTION):(EVCAL_ROLLBACK_TRANSACTION));
	switch (err_code)
	{
		case NRF_SUCCESS:
		{
			return ERSP_SUCCESS;
		}
		case NRF_ERROR_INVALID_PARAM:
		{
			return ERSP_ERROR_INVALID_PARAM;
		}
		case DRIVER_MODULE_NOT_INITIALZED:
		{
			return ERSP_ERROR_INTERNAL;
		}
		case NRF_ERROR_FORBIDDEN:
			return  ERSP_ERROR_FORBIDDEN;
	}
	return ERSP_ERROR_UNKNOWN;

}

/*****************************************************************************************************/
/**
 * 	описание в application_api.h
*/
uint8_t api_start(void)
{
	switch(app_state)
	{
		case APP_STAND_BY:
		{
			app_state_device.app_off_en = false;
			return 1;
		}
		default:
		{
			return 0;
		}
	}
}
/**
 * 	описание в application_api.h
*/
uint8_t api_stop(void)
{
	switch(app_state)
	{
		case APP_STAND_BY:
		{
			app_state_device.app_off_en = true;
			return 1;
		}
		default:
		{
			return 0;
		}
	}
}
/**
 * 	описание в application_api.h
*/
uint8_t api_switch(void)
{
	switch(app_state)
	{
		case APP_STAND_BY:
		{
			app_state_device.app_off_en = ~app_state_device.app_off_en;
			return 1;
		}
		default:
		{
			return 0;
		}
	}
}

Made on
Tilda