Программный модуль для датчика RSS-61S. Версия 0.19



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

Фрагмент исходного кода
/**
 *    	@file  	application.c
 *   	@brief  application.c реализует функционал устройства RSS-61S
 *  	@internal
*      Revision:  		ver 0.19
 *      Compiler:  	armcc
*
 */

/********* HEADER FILE INCLUDES *************************************************/
#include "debug_info.h"
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "nrf.h"
#include "nrf_delay.h"
#include "nrf_gpio.h"
#include "nordic_common.h"
#include "app_error.h"
#include "nrf_soc.h"
#include "app_timer.h"
#include "app_util.h"
#include "app_util_platform.h"
#include "custom_board.h"

#include "r4s_lib.h"
#include "r4s_slave.h"
#include "r4s_slave_external.h"
#include "r4s_master.h"
#include "r4s_master_external.h"

#include "com_slave.h"
#include "com_master.h"
#include "master_control_table.h"

#include "timers_config.h"
#include "control.h"
#include "indication.h"
#include "temperature.h"
#include "temperature_service.h"
#include "event_base.h"
#include "application.h"
#include "application_api.h"
#include "custom_board.h"
#include "com_slave_extention.h"
#include "cr2032.h"
#include "sfloat.h"
#include "com_slave_extention.h"
#include "cycle_buf.h"

#include "adv_data.h"
#include "adv_data_config.h"

#include "evcal.h"
#include "calendar.h"

#include "battery_service.h"
#include "log_service.h"
/********* DEFINES **************************************************************/
#define BAT_CHARGE_LIMIT		5
#define CRITICAL_TEMP		    55
#define CRITICAL_TEMP_ALARM_OFF (CRITICAL_TEMP - 5)

#define CHANNEL_MASK_TEMPERATURE    (1<<0)
#define TEMP_MASK_Pos               0
#define SMOKE_SENSOR_MASK_Pos       1

/********* MACROS ***************************************************************/
/********* VARIABLES *****************************************************/
static app_state_t 				app_state = APP_START;			   /* состояние state machine*/

static bool                     app_test_init_success = false;

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

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

static bool                     AppPairingFl = false;				/* флаг pairing*/
static bool                     AppUnPairingFl = false;		/* флаг unpairing sound */

static sense_state_t            app_sense_state = SENSE_IDLE;

static btn_data_t               btn_data[BTN_COUNT];
static sense_data_t             sense_data[SENSE_COUNT];

static app_sensors_evt_enabled_t sensors_evt_en;

static temp_trigger_t			app_temp_trigger[TEMPERATURE_TRIGGER_MAX_COUNT];
static uint8_t                  temp_triggers_count = 0;
static float                    app_temp = 0;
static bool                     app_need_update = false;

static alert_level_t            alert_level = ALERT_IDLE;
static uint8_t                  alert_count = 0;

APP_TIMER_DEF(app_timer_id);

static uint8_t					app_battery_charge = 100;

static cm_dev_support_ac_t m_required_ac = {
    .ac = {
        AC_EXTENTION_START_CMD,
        AC_EXTENTION_STOP_CMD
    }
};
/********* FUNCTION PROTOTYPES **************************************************/
uint32_t api_temp_test_proccess(void); // функция проверки температурного датчика
uint32_t api_battery_test_proccess(void); // функция проверки батареи
static void application_store_log(void);
static void api_master_evt_handler(com_master_evt_t * p_evt);
static void api_internal_action_process(mct_link_t *link);
/********* FUNCTIONS IMPLEMENTATION *********************************************/
/**@brief 	Обработчик таймера таймаута.
 *
 * @param	none
 *
 * @return 	none
 */
static void app_timeout_timer_handler( void* p_context )
{
	//APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: app_timeout_timer_handler\r\n"RTT_CTRL_RESET);
	app_timerout_flag = true;
}

/**@brief 	Обработчик таймера измерения заряда.
 *
 * @param	none
 *
 * @return 	none
 */
static void app_temp_data_handler(float *p_temp)
{
	uint32_t err_code;
    
    app_temp = *p_temp;
	
	err_code = cr2032_read_charge_val((int16_t)app_temp, &app_battery_charge );
	APP_ERROR_CHECK(err_code);
    
#ifdef DEBUG	
	char s_temp[40];
	sprintf(s_temp,"[BAT]: BAT = %d,temp = %0.2f \r\n",app_battery_charge, app_temp);
	APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_GREEN"%s"RTT_CTRL_RESET,s_temp);
#endif
	
	if(app_battery_charge <= BAT_CHARGE_LIMIT)
	{
		api_state_change(APP_BAT_LOW);
	}
	
	com_slave_send_state();
}

/**
 *  @brief  Функция вызывается при срабатывании часов (1 сек)
 *
 *  @param  none
 *  @return none
 *
 */
static void calendar_timer_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){
	if(p_data == NULL) return;
	uint8_t action_code = p_data->array[1];

	switch(action_code)
	{
		case AC_EXTENTION_START_CMD:
		{           
            exten_req_start_t *p_in_exp_pckt = (exten_req_start_t *) &p_data->array[2];
            if((p_in_exp_pckt->mask & CHANNEL_MASK_TEMPERATURE) != 0){
                api_sensor_on();
            }
			break;
		}
		case AC_EXTENTION_STOP_CMD:
		{
            exten_req_start_t *p_in_exp_pckt = (exten_req_start_t *) &p_data->array[2];
            if((p_in_exp_pckt->mask & CHANNEL_MASK_TEMPERATURE) != 0){
                api_sensor_off();
            }
			break;
		}
	}
}

static void set_monitoring_period(void) {
    if (is_connected) {
        temp_monitoring_start(CONNECTED_TEMP_PERIOD);
    } else if(temp_triggers_count) {
		temp_monitoring_start(TRIGGER_TEMP_PERIOD);
	} else {
		temp_monitoring_start(DEFAULT_TEMP_PERIOD);
	}
}

void application_init(void)
{
	uint32_t err_code;

	err_code = app_timer_create	( &app_timer_id, APP_TIMER_MODE_SINGLE_SHOT, app_timeout_timer_handler );
	APP_ERROR_CHECK(err_code);
	
	err_code = calendar_set_one_sec_callback(calendar_timer_handle); //установка callback часов
	APP_ERROR_CHECK(err_code);

	err_code = evcal_set_callback(evcal_calendar_task); //установка callback при срабатывании расписания
	APP_ERROR_CHECK(err_code);
	
	err_code = temperature_service_init(&app_temp, app_temp_data_handler);
	APP_ERROR_CHECK(err_code);
    
    api_master_set_default_config();
	
    api_need_update();
    
    com_master_set_required_ac(&m_required_ac);
    com_master_set_event_handler(api_master_evt_handler);
    com_master_set_internal_action_handler(api_internal_action_process);
    
    sensors_evt_en.temperature = 1;
}
/********************************************************************************
 *
 ********************************************************************************/


/**@brief 	Функция выполняет работу при выходе из соответствующего состояния
 *
 * @param	none
 *
 * @return 	none
 */
void application_init_prew_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_prew_state )
	{
		case APP_START:
		{
			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: APP_IDLE>>\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);
			break;
		}
		case APP_ACTIVE:
		{
			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: APP_ACTIVE>>\r\n"RTT_CTRL_RESET);
			break;
		}
		case APP_PAIRING:
		{
			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: APP_PAIRING>>\r\n"RTT_CTRL_RESET);
            APP_ERROR_CHECK( app_timer_stop(app_timer_id));
			AppPairingFl=false;
            com_master_pairing_stop();
            com_slave_pairing_stop();
            ind_state_change(IND_PAIRING_STOP, NULL);
			break;
		}
		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));
            ind_state_change(IND_FIRMWARE_UPDATE_STOP, NULL);	
			break;
		}
        case APP_SENSOR_ERROR:{
            ind_state_change(IND_HARDWARE_ERROR_IDLE, NULL);
            break;
        }
		default:
		{
			break;
		}
	}

	app_prew_state = app_new_state;
}

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

	static app_state_t    			app_prew_state = APP_START;

	if( app_prew_state == app_new_state )return;

	switch( app_new_state )
	{
		case APP_START:
		{
			break;
		}
        case APP_WAIT_TEST:
        {
            break;
        }
		case APP_TEST:
		{
			err_code = api_temp_test_proccess();
			if(err_code != NRF_SUCCESS)
			{
                ind_state_change(IND_TEST_FAILED, NULL);
                break;
			}
            
			err_code =  api_battery_test_proccess();
			if(err_code != NRF_SUCCESS)
			{
                ind_state_change(IND_TEST_FAILED, NULL);
                break;
			}
            
            ind_state_change(IND_TEST_START, NULL);
            
            err_code = app_timer_start(app_timer_id, APP_TIMER_TICKS(APP_TEST_START_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL );
            APP_ERROR_CHECK( err_code );
            
            app_test_init_success = true;
			break;
		}
		case APP_ACTIVE:
		{
            APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_ACTIVE\r\n"RTT_CTRL_RESET);
            
            err_code = app_timer_stop(app_timer_id);
			APP_ERROR_CHECK( err_code );
			
			app_timerout_flag = false;
            
			com_slave_send_state();  //отправить нотификацию на телефон о состоянии датчика
			ind_state_change(IND_ACTIVE, NULL);
			break;
		}
		case APP_PAIRING:
		{
			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_PAIRING\r\n"RTT_CTRL_RESET);

            err_code = app_timer_stop(app_timer_id);
			APP_ERROR_CHECK( err_code );
            
            app_timerout_flag = false;
            
			err_code = app_timer_start(app_timer_id, APP_TIMER_TICKS(APP_PAIRING_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL );
			APP_ERROR_CHECK( err_code );
			
            com_slave_pairing_start();
            com_master_pairing_start();
			
			ind_state_change(IND_PAIRING_START, NULL);

			break;
		}
		case APP_SLAVE_FW_UPDATE:
		{
			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_SLAVE_FW_UPDATE\r\n"RTT_CTRL_RESET);
			
            err_code = app_timer_stop(app_timer_id);
			APP_ERROR_CHECK( err_code );
            
            app_timerout_flag = false;
            
			err_code = app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_FW_UPDATE_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL );
			APP_ERROR_CHECK( err_code );
			
			ind_state_change(IND_FIRMWARE_UPDATE_START, NULL);

			break;
		}
		case APP_BAT_LOW:
		{
			ind_state_change(IND_BATTERY_LOW_START, NULL);
			break;
		}
        case APP_SENSOR_ERROR:{
            ind_state_change(IND_HARDWARE_ERROR, NULL);
            break;
        }
		default:
		{
			break;
		}
	}

	app_prew_state = app_new_state;
}

/********************************************************************************
 *
 ********************************************************************************/
void application_process()
{
	uint32_t err_code;

    for( uint8_t i0 = 0; i0< BTN_COUNT; i0++ )
    {
        err_code = button_read( i0, &btn_data[i0] );
        APP_ERROR_CHECK(err_code);
    }
    
    for( uint8_t i0 = 0; i0< SENSE_COUNT; i0++ )
    {
        err_code = sense_read( i0, &sense_data[i0] );
        APP_ERROR_CHECK(err_code);
    }

	switch(app_state)
	{
		case APP_START:
		{
            api_state_change(APP_WAIT_TEST);
			break;
		}
        case APP_WAIT_TEST:
        {
            if(btn_data[BUTTON_ID].state == BTN_PRESSED)//если какая-либо клавиша нажата за 3 секунды
            {
                if (btn_data[BUTTON_ID].pressed_time_ms > APP_TEST_STATE_ENTER_TIME_MS) {
                    err_code = app_timer_stop(app_timer_id);
                    APP_ERROR_CHECK( err_code );
                    
                    api_state_change(APP_TEST);
                }
            } else {
                ind_state_change(IND_START, NULL);
                api_state_change(APP_ACTIVE);
            }
            break;
        }
		case APP_TEST:
		{
            if(app_test_init_success)
            {
                if(app_timerout_flag)//прошло 60 секунд
                {
                    ind_state_change(IND_TEST_TIMEOUT, NULL);
                    api_state_change(APP_ACTIVE);
                    break;
                }
                
                if (sense_data[SMOKE_FAULT_SENSE_ID].event_detected == SENSE_EVENT_CAPTURED) {
                    err_code = app_timer_stop(app_timer_id);
                    APP_ERROR_CHECK( err_code );
                    
                    app_timerout_flag = false;
                    
                    err_code = app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_TEST_START_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL );
                    APP_ERROR_CHECK( err_code );
                    
                    ind_state_change(IND_TEST_SMOKE_HARD_FAULT, NULL);
                    break;
                }
                
                if (sense_data[SMOKE_FAULT_SENSE_ID].event_idle == SENSE_EVENT_CAPTURED) {
                    err_code = app_timer_stop(app_timer_id);
                    APP_ERROR_CHECK( err_code );
                    
                    app_timerout_flag = false;
                    
                    err_code = app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_TEST_START_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL );
                    APP_ERROR_CHECK( err_code );
                    
                    ind_state_change(IND_TEST_SMOKE_HARD_FAULT_IDLE, NULL);
                    break;
                }
                
                if(btn_data[BUTTON_ID].event_press == BTN_EVENT_CAPTURED)
                {
                    err_code = app_timer_stop(app_timer_id);
                    APP_ERROR_CHECK( err_code );
                    
                    app_timerout_flag = false;
                    
                    err_code = app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_TEST_START_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL );
                    APP_ERROR_CHECK( err_code );
                    ind_state_change(IND_TEST_BUTTON_PRESSED, NULL);
                    break;
                }
                
                if(btn_data[BUTTON_ID].event_release == BTN_EVENT_CAPTURED)
                {
                    err_code = app_timer_stop(app_timer_id);
                    APP_ERROR_CHECK( err_code );
                    
                    app_timerout_flag = false;
                    
                    err_code = app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_TEST_START_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL );
                    APP_ERROR_CHECK( err_code );
                    ind_state_change(IND_TEST_BUTTON_RELEASED, NULL);
                    break;
                }
                
                if(sense_data[SENSE_ID].event_detected == SENSE_EVENT_CAPTURED)
                {
                    err_code = app_timer_stop(app_timer_id);
                    APP_ERROR_CHECK( err_code );
                    
                    app_timerout_flag = false;
                    
                    err_code = app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_TEST_START_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL );
                    APP_ERROR_CHECK( err_code );
                    ind_state_change(IND_TEST_SMOKE_DETECTED, NULL);
                    break;
                }
                
                if(sense_data[SENSE_ID].event_idle == SENSE_EVENT_CAPTURED)
                {
                    err_code = app_timer_stop(app_timer_id);
                    APP_ERROR_CHECK( err_code );
                    
                    app_timerout_flag = false;
                                                   
                    err_code = app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_TEST_START_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL );
                    APP_ERROR_CHECK( err_code );
                    ind_state_change(IND_TEST_SMOKE_IDLE, NULL);
                    break;
                }
            }
			break;
		}
		case APP_PAIRING:
		{
			if( app_timerout_flag == true )
			{
                ind_state_change(IND_PAIRING_ERROR, NULL);
				api_state_change(APP_ACTIVE);
			}
			break;
		}
		case APP_SLAVE_FW_UPDATE:
		{
			if( app_timerout_flag == true )		//таймаут обновления прошивки
			{
				api_state_change(APP_ACTIVE);
			}
			break;
		}
		case APP_ACTIVE:
		{
            static bool sense_evt_released_captured = false;
            
            api_update_triggers();
/******** REED **********/
            // Если была обнаружена тревога
            if(sense_data[SENSE_ID].event_detected == SENSE_EVENT_CAPTURED)
            {                
                sense_evt_released_captured = false;
                
                if (app_sense_state != SENSE_DETECTED) {
                    app_sense_state = SENSE_DETECTED;
                    
                    com_master_prior_proccess(EVT_BINARY_SENSOR(SMOKE_EVENT_CODE, SMOKE_SENSOR_MASK_Pos, BIN_SENSOR_STATE_DETECTED)); // выполнение команд на ИУ согласно конфигуратору
                    com_slave_send_state(); //отправить нотификацию на телефон о состоянии датчика
                    application_store_log();
                    
                    app_timerout_flag = false;
                    
                    err_code = app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_SMOKE_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL );
                    APP_ERROR_CHECK( err_code );
                }
            }
            
            if (sense_data[SENSE_ID].event_idle == SENSE_EVENT_CAPTURED) {
                sense_evt_released_captured = true;
            }
            
            // Произошел таймаут 4-х минут
            if (app_timerout_flag) {
                // После четырех минут разрешено сбрасывать сигнализацию
                if (sense_evt_released_captured) {
                    err_code = app_timer_stop(app_timer_id);
                    APP_ERROR_CHECK( err_code );
                    
                    if (app_sense_state == SENSE_DETECTED) {
                        app_sense_state = SENSE_IDLE;
                        
                        com_master_prior_proccess(EVT_BINARY_SENSOR(SMOKE_EVENT_CODE, SMOKE_SENSOR_MASK_Pos, BIN_SENSOR_STATE_IDLE)); // выполнение команд на ИУ согласно конфигуратору
                        com_slave_send_state();  //отправить нотификацию на телефон о состоянии датчика
                        application_store_log();
                    }
                    
                    sense_evt_released_captured = false;
                }
            }
/******** BUTTON **********/
			if(btn_data[BUTTON_ID].event_press==BTN_EVENT_CAPTURED)
			{
				APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_RED"[APPL]: BUTTON PRESSED\r\n"RTT_CTRL_RESET);
			}
            
			if (btn_data[BUTTON_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS
					&& btn_data[BUTTON_ID].pressed_time_ms < APP_UNPAIRING_STATE_ENTER_TIME_MS)
			{
				if(AppPairingFl==false)
				{
					APP_PRINTF(0,RTT_CTRL_RESET"[APPL]: BUTTON PRESSED FOR 5s\r\n"RTT_CTRL_RESET);
					AppPairingFl=true;
					ind_state_change(IND_PAIRING_INIT, NULL); // индикация пейринга
				}
			}
			if (btn_data[BUTTON_ID].pressed_time_ms >= APP_UNPAIRING_STATE_ENTER_TIME_MS)
			{
				if(AppUnPairingFl==false)
				{
					APP_PRINTF(0,RTT_CTRL_RESET"[APPL]: BUTTON PRESSED FOR 10s\r\n"RTT_CTRL_RESET);
					AppPairingFl=false;
					AppUnPairingFl=true;
					ind_state_change(IND_UNPAIRING_INIT, NULL); // индикация анпейринга
				}
			}
			if(btn_data[BUTTON_ID].event_release==BTN_EVENT_CAPTURED)
			{
				APP_PRINTF(0,RTT_CTRL_RESET"[APPL]: BUTTON RELEASED\r\n"RTT_CTRL_RESET);

				if(AppPairingFl==true)
				{
					api_state_change(APP_PAIRING); // переход в стейт пейринга
				}
				else
				{
					if(AppUnPairingFl==true)
					{
						AppUnPairingFl=false;
                        com_master_unpairing(); // анпейринг на мастере
						com_slave_unpair_proccess(); // анпейринг на  слейве
                        api_reset_params_at_unpairing();
						ind_state_change(IND_UNPAIRING_SUCCESS, NULL);
                        api_need_update();
					}
                    api_reset_alert_state();
				}
			}
            
            if (sense_data[SMOKE_FAULT_SENSE_ID].state == SENSE_DETECTED) {
                api_state_change(APP_SENSOR_ERROR);
            }
			break;
		}
		case APP_BAT_LOW:
		{
            if(btn_data[BUTTON_ID].event_release==BTN_EVENT_CAPTURED) {
                ind_state_change(IND_BUZZ_OFF, NULL);
			}
			break;
		}
        case APP_SENSOR_ERROR:{
            if (sense_data[SMOKE_FAULT_SENSE_ID].state == SENSE_IDLE) {
                api_state_change(APP_ACTIVE);
            }
            break;
        }
		default:
		{
			APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: default\r\n"RTT_CTRL_RESET);
			api_state_change(APP_ACTIVE);
			break;
		}
	}
    
    if(app_need_update) api_param_update();
    
    // Если все в расписании выключено или удалено, то включаем триггеры
    err_code = evcal_add_transaction(EVCAL_TRANSACTION_READ);
    if (err_code == NRF_SUCCESS) {
        
        uint8_t count = 0;
        evcal_task_t task;
        bool is_any_enabled = false;
        
        evcal_get_task_number(&count);
        
        for (uint8_t i = 0; i < count; i++) {
            err_code = evcal_get_task_data(i, &task);
            APP_ERROR_CHECK(err_code);
            
            if (task.enable) {
                is_any_enabled = true;
            }
        }
        
        if (!is_any_enabled) {
            sensors_evt_en.temperature = 1;
        }
        
        evcal_dec_transaction(EVCAL_COMMIT_TRANSACTION);
    }
        
    
}

static void application_store_log(void) {
    cb_data_t record;
    calendar_utc_t utc = 0;
    calendar_shift_utc_t shift = 0;
    
    //получаем данные о текущем UTC и времени сдвига
    APP_ERROR_CHECK(calendar_get_utc_time(&utc, &shift));

    if (utc == 0) {// Время не установлено или не верное, не логировать
        return;
    }
    
    // Упаковка данных
    smoke_state_data_t pack = 
    {
        .val = {
            .smoke_flag = api_sense_state_get(),
        },
    };

    record.id = LS_ID_COMPLEX_SMOKE;
    record.utc = utc;
    record.data = pack.data_raw;

    APP_ERROR_CHECK(cb_put(&record));
}
/********************************************************************************
 *
 ********************************************************************************/

/********************************************************************************
 *
 ********************************************************************************/
/**
 * 	описание в application_api.h
*/
/***************************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 );
}


uint16_t api_get_version(void)
{
	return FIRMWARE_VER;
}

bool api_state_check(app_state_t state)
{
    return app_state == state;
}

uint8_t api_get_state(void)
{
	switch(app_state)
	{
		case APP_SLAVE_FW_UPDATE:
		{
			return APP_API_FW_UPDATE_STATE;
		}
		case APP_ACTIVE:
		{
			return APP_API_STAND_BY_STATE;
		}
        case APP_SENSOR_ERROR:
		case APP_BAT_LOW:
		{
			return APP_API_ERROR_STATE;
		}
		case APP_PAIRING:
			return APP_API_PAIRING_STATE;
		
		default:
		{
			return APP_API_ERROR_STATE;
		}
	}
}

void api_sensor_on(void) {
    sensors_evt_en.temperature = true;
}

void api_sensor_off(void) {
    sensors_evt_en.temperature = false;
}

void api_update_triggers(void) {
    temp_event_check(app_temp_trigger, temp_triggers_count);
        
    for(uint8_t i=0; i<temp_triggers_count;i++) {
        if(app_temp_trigger[i].temp_state == TRIGGER_CAPTURED)
        {
            if ((app_temp_trigger[i].perm & (1 << MCT_PERM_READ_MASK_pos)) == MCT_PERM_READ(MCT_PERM_ONLY_MASTER) ||
                (app_temp_trigger[i].perm & (1 << MCT_PERM_UPDATE_MASK_pos)) == MCT_PERM_UPDATE(MCT_PERM_ONLY_MASTER)) 
            {
                com_master_proccess(app_temp_trigger[i].com_event);
                app_temp_trigger[i].temp_state = TRIGGER_PROCESSED;
                continue;
            }
                            
            if (sensors_evt_en.temperature && (app_temp_trigger[i].perm == MCT_PERM(MCT_PERM_ANY_USER, MCT_PERM_ANY_USER))) {
                com_master_proccess(app_temp_trigger[i].com_event);
                app_temp_trigger[i].temp_state = TRIGGER_PROCESSED;
                continue;
            }
        }
    }
}

static void alert_set(alert_level_t lvl) {
    alert_level = lvl;
    
    if (lvl == ALERT_IDLE) {
        app_advertising_set_alert_state(false);
        ind_state_change(IND_ALARM_IDLE, NULL);
    } else {
        app_advertising_set_alert_state(true);
        ind_state_change(IND_ALARM, NULL);
    }
}

static void alert_evt(alert_level_t lvl) {
    if (lvl == ALERT_IDLE) {
        if (alert_count) 
            alert_count--;
        
        if (!alert_count) {
            alert_set(ALERT_IDLE);
        }
    } else {
        if (!alert_count) {
            alert_set(lvl);
        }
        if (alert_count < UINT8_MAX) 
            alert_count++;
    }
}

void api_reset_alert_state(void) {
    if (alert_count) {
        alert_count = 0;
        alert_set(ALERT_IDLE);
    }
}

void api_get_alert_state(uint8_t *p_lvl) {
    *p_lvl = alert_level;
}

void api_internal_action_process(mct_link_t *link){
    switch(link->ac){
        case AC_EXTENTION_ALARM: // Произошло событие индикации
        {
            ind_state_change(IND_BUZZ, link->data);
            break;
        }
        case AC_EXTENTION_ALERT_NOTIFY:{
            exten_req_set_alert_t *p_alert = (exten_req_set_alert_t *) link->data;
            
            alert_evt(p_alert->alert_level);
            break;
        }
        default: // неизвестный action code
            break;
    }
}

void api_need_update(void){
    app_need_update = true;
}

void api_param_update(void)
{
    uint32_t err_code;
    
    temp_triggers_count = 0;
    
	err_code = temp_param_update(app_temp_trigger, &temp_triggers_count);
    if(err_code != NRF_ERROR_BUSY)
    {
        app_need_update = false;
        set_monitoring_period();
    }
}

uint8_t api_get_battery_charge(void)
{
	return app_battery_charge;
}

/**
 * 	описание в application_api.h
*/
uint16_t api_get_temp(void)
{
	return FloatToSFloat16(app_temp);
}////////////////////////////////////////////////////////////////
/**
 * 	описание в 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);
	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));
}
/**
* 	@brief	Возвращает состояние датчика.
*
*/
uint8_t api_sense_state_get(void)
{
	return app_sense_state;
}

uint32_t api_temp_test_proccess(void)
{
	if(app_temp >= 10 && app_temp <= 60)
	{
		return NRF_SUCCESS;
	}
	return NRF_ERROR_INVALID_STATE;
}

uint32_t api_battery_test_proccess(void)
{
	uint32_t err_code;
	
	err_code = cr2032_read_charge_val((int16_t)app_temp, &app_battery_charge );
	APP_ERROR_CHECK(err_code);
	
	if(app_battery_charge > 0)
	{
		return NRF_SUCCESS;
	}
	return NRF_ERROR_NOT_FOUND;
}
void api_reset_params_at_unpairing( void )
{   
    uint32_t err_code;
    //события календаря (до этого уже должны быть очищены все транзакции)
    err_code = evcal_add_transaction(EVCAL_TRANSACTION_WRITE);
    if (err_code == NRF_SUCCESS) {
        evcal_erase_all_tasks();
        evcal_dec_transaction(EVCAL_COMMIT_TRANSACTION);
    }
    sensors_evt_en.temperature = 1;
}
/********************************************************************************/
/***************************PERIPHERAL************************************/
uint8_t api_get_error(void)
{
	switch(app_state)
	{
		case APP_BAT_LOW:
			return APP_API_BATTERY_LOW_ERROR;
        case APP_SENSOR_ERROR:
            return APP_API_INTERNAL_ERROR;
		default:
			return APP_API_NO_ERROR;
	}
}

void api_slave_set_connection_flag(void) {
    is_connected = true;
    set_monitoring_period();
    
    if(app_state == APP_PAIRING)
	{
		uint32_t err_code;
		
		err_code = app_timer_stop(app_timer_id);
		APP_ERROR_CHECK( err_code );
    }
}

void api_slave_clear_connection_flag(void) {
    is_connected = false;
    set_monitoring_period();
    
    //выйти из режимов управления камерой или перепрошивки если в них находимся
	if(app_state == APP_SLAVE_FW_UPDATE)
	{
		api_state_change(APP_ACTIVE);
	}
    
    if(app_state == APP_PAIRING) {
        ind_state_change(IND_PAIRING_ERROR, NULL);
        api_state_change(APP_ACTIVE);
    }
}

uint8_t api_enter_fw_update_state(void)
{
	if((app_state == APP_ACTIVE)
		  ||( 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 )
	{
		uint32_t err_code;
		
		err_code = app_timer_stop(app_timer_id);
		APP_ERROR_CHECK( err_code );

		err_code = app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_FW_UPDATE_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL );
		APP_ERROR_CHECK( err_code );
	}
}

uint8_t api_go_to_stand_by(void)
{
	if(( app_state != APP_ACTIVE)
		&& ( app_state != APP_PAIRING )
		&& ( app_state != APP_SLAVE_FW_UPDATE ))
	{
		return 0;
	}
	if( ( app_state == APP_PAIRING )
		|| ( app_state == APP_SLAVE_FW_UPDATE ))
	{
		api_state_change(APP_ACTIVE);
	}
	return 1;
}

void r4s_slave_pairing_success( void )
{
    if(app_state == APP_PAIRING) {
        ind_state_change(IND_PAIRING_SUCCESS, NULL);
        api_state_change(APP_ACTIVE);
    }
}

/********************************************************************************/
/***************************CENTRAL************************************/
/********************************************************************************/

void api_master_set_connection_flag(void)
{
	AppMasterConnectedFl=true;
}

void api_master_clear_connection_flag(void)
{
	AppMasterConnectedFl=false;
}

__STATIC_INLINE void set_default_config(uint8_t tok_id, cm_dev_support_ac_t * required_ac) {
    mct_transaction_t * tran = NULL;
    
    mct_link_t link;
    
    mct_searchp_context_t search;
    memset(&search, 0, sizeof(mct_searchp_context_t));
    
    uint32_t err_code;
    
	APP_PRINTF(0, RTT_CTRL_TEXT_BRIGHT_GREEN"[APPL]: api_master_pairing_complete\r\n"RTT_CTRL_RESET);
    
    if (required_ac->is_support[0] && required_ac->is_support[1]){
        // Тип транзакции 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);
             
            memset(&link, 0, sizeof(mct_link_t));
            link.tok_id = tok_id;
            link.event = EVT_BINARY_SENSOR(SMOKE_EVENT_CODE, SMOKE_SENSOR_MASK_Pos, BIN_SENSOR_STATE_DETECTED);
            link.ac = AC_EXTENTION_START_CMD;
            link.data[0]=0xFF;
            link.data[1]=0xFF;
            link.enable = 0x01;
            link.uuid = 0;

            err_code = mct_set_link(tran, &link);
            if (err_code != NRF_ERROR_NO_MEM)
                APP_ERROR_CHECK(err_code);
            
            memset(&link, 0, sizeof(mct_link_t));
            link.tok_id = tok_id;
            link.event = EVT_BINARY_SENSOR(SMOKE_EVENT_CODE, SMOKE_SENSOR_MASK_Pos, BIN_SENSOR_STATE_IDLE);
            link.ac = AC_EXTENTION_STOP_CMD;
            link.data[0]=0xFF;
            link.data[1]=0xFF;
            link.enable = 0x01;
            link.uuid = 0;

            err_code = mct_set_link(tran, &link);
            if (err_code != NRF_ERROR_NO_MEM)
                APP_ERROR_CHECK(err_code);
                

            err_code = mct_commit(&tran);
            APP_ERROR_CHECK(err_code);
        }
    }
}

static void api_master_evt_handler(com_master_evt_t * p_evt) {
    switch (p_evt->id) {
        case COM_MASTER_PAIRING_SUCCESS:{
            ind_state_change(IND_PAIRING_SUCCESS, NULL);
            api_state_change(APP_ACTIVE);
            break;
        }
        case COM_MASTER_PAIRING_ERROR: {
            ind_state_change(IND_PAIRING_ERROR, NULL);
            api_state_change(APP_ACTIVE);
            break;
        }
        case COM_MASTER_DEFAULT_CONFIG_REQUIRED:{
            set_default_config(
                p_evt->param.default_config_required.tok_id,
                p_evt->param.default_config_required.p_required_ac
            );
            break;
        }
    }
}


void api_master_set_default_config( void )
{
    uint32_t err_code;
    mct_transaction_t * tran = NULL;
    mct_link_t link;
    mct_searchp_context_t search;
    
    static const exten_req_set_alert_t alert_on = {
        .alert_level = ALERT_HIGH,
    };
    
    static const exten_req_set_alert_t alert_off = {
        .alert_level = ALERT_IDLE,
    };
    
    // Заполнение температурного события
    uint32_t temp_higher_event = EVT_REAL_SENSOR(TEMP_EVENT_CODE, TEMP_MASK_Pos, EVTB_CMP_HIGHER, FloatToSFloat16(CRITICAL_TEMP));
    uint32_t temp_lower_event = EVT_REAL_SENSOR(TEMP_EVENT_CODE, TEMP_MASK_Pos, EVTB_CMP_LOWER, FloatToSFloat16(CRITICAL_TEMP - 5));
    
    bool set_data    = false;
    
    err_code = mct_open_transaction(&tran, TRAN_READ_WRITE, TRAN_MASTER_PERM);
    if (err_code != NRF_ERROR_BUSY){
        APP_ERROR_CHECK(err_code);
        
        memset(&search, 0, sizeof(mct_searchp_context_t));
        
        search.params.filter.ac = 1;
        search.params.values.ac = AC_EXTENTION_ALERT_NOTIFY;
        
        search.params.filter.perm_mask = (1 << MCT_PERM_READ_MASK_pos) | (1 << MCT_PERM_UPDATE_MASK_pos);
        search.params.values.perm = MCT_PERM(MCT_PERM_ONLY_MASTER, MCT_PERM_ONLY_MASTER);
        
        set_data = true;
        
        // Поиск нередактируемых полей
        while (mct_find_link(tran, &search, &link) == NRF_SUCCESS){
            set_data = false; // дефолтный конфиг есть
            break;
        }
        
        if (set_data) {
            // Событие - газ замечен
            memset(&link, 0, sizeof(mct_link_t));
            link.event = EVT_BINARY_SENSOR(SMOKE_EVENT_CODE, SMOKE_SENSOR_MASK_Pos, BIN_SENSOR_STATE_DETECTED);
            link.ac = AC_EXTENTION_ALERT_NOTIFY;
            memcpy(link.data, (uint8_t *)&alert_on, MCT_DATA_SIZE);
            link.perm = MCT_PERM(MCT_PERM_ONLY_MASTER, MCT_PERM_ONLY_MASTER);
            link.enable = 0x01;

            err_code = mct_set_link(tran, &link);
            if (err_code != NRF_ERROR_NO_MEM)
                APP_ERROR_CHECK(err_code);
            
            // Событие газа нет
            memset(&link, 0, sizeof(mct_link_t));
            link.event = EVT_BINARY_SENSOR(SMOKE_EVENT_CODE, SMOKE_SENSOR_MASK_Pos, BIN_SENSOR_STATE_IDLE);
            link.ac = AC_EXTENTION_ALERT_NOTIFY;
            memcpy(link.data, (uint8_t *)&alert_off, MCT_DATA_SIZE);
            link.perm = MCT_PERM(MCT_PERM_ONLY_MASTER, MCT_PERM_ONLY_MASTER);
            link.enable = 0x01;

            err_code = mct_set_link(tran, &link);
            if (err_code != NRF_ERROR_NO_MEM)
                APP_ERROR_CHECK(err_code);
            
            // Событие - температура выше критической
            memset(&link, 0, sizeof(mct_link_t));
            link.event = temp_higher_event;
            link.ac = AC_EXTENTION_ALERT_NOTIFY;
            memcpy(link.data, (uint8_t *)&alert_on, MCT_DATA_SIZE);
            link.perm = MCT_PERM(MCT_PERM_ONLY_MASTER, MCT_PERM_ONLY_MASTER);
            link.enable = 0x01;

            err_code = mct_set_link(tran, &link);
            if (err_code != NRF_ERROR_NO_MEM)
                APP_ERROR_CHECK(err_code);
            
            // Событие - температура ниже критической
            memset(&link, 0, sizeof(mct_link_t));
            link.event = temp_lower_event;
            link.ac = AC_EXTENTION_ALERT_NOTIFY;
            memcpy(link.data, (uint8_t *)&alert_off, MCT_DATA_SIZE);
            link.perm = MCT_PERM(MCT_PERM_ONLY_MASTER, MCT_PERM_ONLY_MASTER);
            link.enable = 0x01;

            err_code = mct_set_link(tran, &link);
            if (err_code != NRF_ERROR_NO_MEM)
                APP_ERROR_CHECK(err_code);
            
            err_code = mct_commit(&tran);
            APP_ERROR_CHECK(err_code);
        } else {        
            err_code = mct_rollback(&tran);
            APP_ERROR_CHECK(err_code);
        }
    }
}
/*****************************************************************************************************/
Made on
Tilda