Программный модуль для датчика RSC-51S. v.3.14



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

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

/********* 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_soc.h"
#include "nrf_gpio.h"
#include "app_timer.h"
#include "app_error.h"

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

#include "Application.h"
#include "Application_api.h"

#include "timers_config.h"
#include "timers.h"
#include "master_control_table.h"
#include "event_base.h"
#include "evcal.h"
#include "calendar.h"
#include "indication.h"
#include "cycle_buf.h"
#include "measurements.h"
#include "pcf8563.h"
#include "sfloat.h"
#include "nrf_gpio.h"
#include "at25sf041.h"
#include "pof_manager.h"

#include "log_service.h"

#include "adv_data.h"

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

#include "com_slave_extention.h"
#include "peer_manager.h"
/********* DEFINES **************************************************************/
//#define APP_DEBUG

#define TAG "[APPL]: "
#define PAIRING_BUTTON 0

#define BTN_WAIT_PAIRING 1
#define BTN_WAIT_UNPAIRING 2
#define BTN_NO_WAIT 0

#define CHANNEL_NUM_TEMPERATURE     0
#define CHANNEL_NUM_PRESSURE        1
#define CHANNEL_NUM_HUMIDITY        2
#define CHANNEL_NUM_TVOC            3

#define CHANNEL_MASK_TEMPERATURE    (1<<0)
#define CHANNEL_MASK_PRESSURE       (1<<1)
#define CHANNEL_MASK_HUMIDITY       (1<<2)
#define CHANNEL_MASK_TVOC           (1<<3)

#define CHANNEL_MASK_ALL            (CHANNEL_MASK_TEMPERATURE | CHANNEL_MASK_PRESSURE | CHANNEL_MASK_HUMIDITY | CHANNEL_MASK_TVOC)

#define MAX_COOLING_TIME_AFFECT_S     (30*60)

/********* MACROS ***************************************************************/
#ifdef APP_DEBUG
    #define APP_LOG(...) APP_PRINTF(0, TAG);APP_PRINTF(0, __VA_ARGS__);
#else
    #define APP_LOG(...)
#endif

#ifndef DEBUG
    #define DEFAULT_CONFIG
#endif
/********* VARIABLES *****************************************************/
static app_state_t app_state = APP_STARTUP; /* состояние state machine*/

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

// Триггеры и данные
static btn_data_t   app_keyboard_data[BTN_COUNT];
static uint8_t      button_wait_release = 0;

APP_TIMER_DEF(app_timer_id);

static app_sensors_evt_enabled_t    sensors_evt_en;
static app_save_info_struct_t       pof_info;
static uint32_t                     off_time_correction;
static uint32_t                     off_time;

static sensor_evt_trigger_t humidity_triggers[MAX_EVENT_TYPE_COUNT];
static sensor_evt_trigger_t temp_triggers[MAX_EVENT_TYPE_COUNT];
static sensor_evt_trigger_t tvoc_triggers[MAX_EVENT_TYPE_COUNT];
static sensor_evt_trigger_t pressure_triggers[MAX_EVENT_TYPE_COUNT];

static uint8_t humidity_triggers_count = 0;
static uint8_t temp_triggers_count = 0;
static uint8_t tvoc_triggers_count = 0;
static uint8_t pressure_triggers_count = 0;

static bool param_update_required = true;
static bool default_conf_is_set = false;

static uint8_t self_test_error_code = 0;
/********* FUNCTION PROTOTYPES **************************************************/
static void app_keyb_proccess(void);
static void application_store_log(void);
static void app_sensors_update(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 *********************************************/
static void reset_default_config() {
    default_conf_is_set = false;
}

static void check_default_config() {
#ifdef DEFAULT_CONFIG
    uint32_t err_code;
    uint16_t links_cnt;
    uint16_t tokens_cnt;
    mct_link_t link;
    static mct_transaction_t * tran;
    
    if (default_conf_is_set) return;
    
    if (!pm_peer_count()) {
        err_code = mct_open_transaction(&tran, TRAN_WRITE_ONLY, TRAN_COMMON_PERM);
        if (err_code == NRF_ERROR_BUSY) return;
        
        APP_ERROR_CHECK(err_code);
    
        links_cnt = mct_get_links_packet_count(tran);
        tokens_cnt = mct_get_tokens_packet_count(tran);
        
        if (links_cnt || tokens_cnt) {
            err_code = mct_rollback(&tran);
            APP_ERROR_CHECK(err_code);
            
            default_conf_is_set = true;
            return;
        }
        
        exten_req_alarm_t* alarm_param = (exten_req_alarm_t*) link.data;
        
        memset(&link, 0, sizeof(mct_link_t));
        link.tok_id = 0;
        link.event = EVT_REAL_SENSOR(TVOC_EVENT_CODE, CHANNEL_NUM_TVOC, EVTB_CMP_HIGHER, FloatToSFloat16(220));
        link.ac = AC_EXTENTION_ALARM_CMD;
        
        alarm_param->alarm_type = ALARM_ON;
        alarm_param->alarm_param.alarm_param_on.interval = 60 * 1000;
        alarm_param->alarm_param.alarm_param_on.duration = BUZZ_SHORT_SIGNAL_MS;
        
        link.perm = MCT_PERM(MCT_PERM_ANY_USER, MCT_PERM_ANY_USER);
        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 = 0;
        link.event = EVT_REAL_SENSOR(TVOC_EVENT_CODE, CHANNEL_NUM_TVOC, EVTB_CMP_LOWER, FloatToSFloat16(220));
        link.ac = AC_EXTENTION_ALARM_CMD;
        
        alarm_param->alarm_type = ALARM_OFF;
        
        link.perm = MCT_PERM(MCT_PERM_ANY_USER, MCT_PERM_ANY_USER);
        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 = 0;
        link.event = EVT_REAL_SENSOR(HUMIDITY_EVENT_CODE, CHANNEL_NUM_HUMIDITY, EVTB_CMP_LOWER, FloatToSFloat16(30));
        link.ac = AC_EXTENTION_ALARM_CMD;
        
        alarm_param->alarm_type = ALARM_ON;
        alarm_param->alarm_param.alarm_param_on.interval = 60 * 1000;
        alarm_param->alarm_param.alarm_param_on.duration = BUZZ_SHORT_SIGNAL_MS;
        
        link.perm = MCT_PERM(MCT_PERM_ANY_USER, MCT_PERM_ANY_USER);
        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 = 0;
        link.event = EVT_REAL_SENSOR(HUMIDITY_EVENT_CODE, CHANNEL_NUM_HUMIDITY, EVTB_CMP_HIGHER, FloatToSFloat16(32));
        link.ac = AC_EXTENTION_ALARM_CMD;
        
        alarm_param->alarm_type = ALARM_OFF;
        
        link.perm = MCT_PERM(MCT_PERM_ANY_USER, MCT_PERM_ANY_USER);
        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);
        
        default_conf_is_set = true;
    }
#endif
}

/**@brief 	Обработчик таймера таймаута.
 *
 * @param	none
 *
 * @return 	none
 */
static void app_timeout_timer_handler(void* p_context) {
    APP_LOG("[APPL]: app_timeout_timer_handler\r\n");
    app_timeout_flag = true;
}

void* application_get_adr_data(void) {
    uint32_t utc;
    int32_t unused;
    
    calendar_get_utc_time( &utc, &unused );
    
    pof_info.off_time = utc;
    return &pof_info;
}

void application_read_pof_data(void) {
    off_time = pof_info.off_time;
}

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

    err_code = evcal_one_sec_handler();
    APP_ERROR_CHECK(err_code);
    
    // Синхронизация с часами реального времени
    if ((++time_sync) % APP_RTC_SYNC_TIME_S == 0) {
        uint32_t utc;
        err_code = pcf8563_get_time_utc(&utc);
        if (err_code == NRF_SUCCESS) {
            APP_LOG("RTC sync success\r\n");
            calendar_set_time(utc, 0);
        } else {
            APP_LOG("RTC sync failed\r\n");
        }
    }
}

/**
 *  @brief  Функция вызывается при срабатывании событий из календаря
 *
 *  @param  none
 *  @return none
 *
 */
static void evcal_calendar_task(evcal_info_packet_t const*p_data) {
    if (p_data == NULL) return;
    com_extension_parse_ac_without_resp((ac_input_t*)&p_data->array[1]);
}

void api_internal_action_process(mct_link_t *link) {
    if (link == NULL) APP_ERROR_CHECK(NRF_ERROR_NULL);
    ac_output_t ac_output;
    com_extension_parse_ac(link->ac, link->data, &ac_output);
}

void application_start(uint16_t mask) {
    uint32_t err_code;
    // Включаем датчики
    if ((mask & CHANNEL_MASK_TEMPERATURE) != 0) {
        sensors_evt_en.temperature = true;
    }
    if ((mask & CHANNEL_MASK_PRESSURE) != 0) {
        sensors_evt_en.pressure = true;
    }
    if ((mask & CHANNEL_MASK_HUMIDITY) != 0) {
        sensors_evt_en.humidity = true;
    }
    if ((mask & CHANNEL_MASK_TVOC) != 0) {
        sensors_evt_en.tvoc = true;
    }
    
    if (mask & CHANNEL_MASK_ALL) {
        // Сохраним настройки в RTC, если хоть что-то поменялось
        err_code = pcf8563_write_alarm_data(
                (uint8_t*) &sensors_evt_en, sizeof (sensors_evt_en));
        APP_ERROR_CHECK(err_code);
    }
}

void application_stop(uint16_t mask) {
    uint32_t err_code;
    
    // Если устройство не выключено, команда отключения событий датчиков
    if ((mask & CHANNEL_MASK_TEMPERATURE) != 0) {
        sensors_evt_en.temperature = false;
    }
    if ((mask & CHANNEL_MASK_PRESSURE) != 0) {
        sensors_evt_en.pressure = false;
    }
    if ((mask & CHANNEL_MASK_HUMIDITY) != 0) {
        sensors_evt_en.humidity = false;
    }
    if ((mask & CHANNEL_MASK_TVOC) != 0) {
        sensors_evt_en.tvoc = false;
    }
    
    if (mask & CHANNEL_MASK_ALL) {
        // Сохраним настройки в RTC, если хоть что-то поменялось
        err_code = pcf8563_write_alarm_data(
                (uint8_t*) &sensors_evt_en, sizeof (sensors_evt_en));
        APP_ERROR_CHECK(err_code);
    }
}

static void sensor_evt_reset() {
    uint32_t err_code;
    
    sensors_evt_en.tvoc = false;
    sensors_evt_en.humidity = false;
    sensors_evt_en.pressure = false;
    sensors_evt_en.temperature = false;
    sensors_evt_en.is_set = true;
    
    // Сохраним настройки в RTC
    err_code = pcf8563_write_alarm_data(
            (uint8_t*) &sensors_evt_en, sizeof (sensors_evt_en));
    APP_ERROR_CHECK(err_code);
}

void app_init_rtc() {
    uint32_t err_code;
    uint32_t utc;
    uint32_t timeout = 10;
    bool utc_is_set = false;
    
    err_code = pcf8563_init();
    APP_ERROR_CHECK(err_code);

    do {
        err_code = pcf8563_get_time_utc(&utc);
        if (err_code == NRF_SUCCESS) {
            APP_LOG("Restore time utc: %d\r\n", utc);
            calendar_set_time(utc, 0);
            utc_is_set = true;
            break;
        } else if (err_code == NRF_ERROR_INTERNAL) {
            APP_LOG("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 {
            APP_LOG("UTC is unset\r\n");
            break;
        }
    } while(1);
    
    off_time_correction = UINT32_MAX;
    
    // Получим время последнего выключения
    if (utc_is_set) {
        uint8_t b_pof_have_data = 0;
        //восстановление сохраненных конфигураций
        APP_ERROR_CHECK(pof_manager_get_state(&b_pof_have_data));	
        if(b_pof_have_data){ 
            //если есть данные, которые надо восстановить
            APP_ERROR_CHECK(pof_manager_copy_data()); 		//чтение из ПЗУ сохраненных параметров

            application_read_pof_data();
            APP_LOG("POF data: time=%u\r\n", off_time);
        }
        
        if (utc - off_time < MAX_COOLING_TIME_AFFECT_S) {
            // Выставим время, которое прошло после выключения
            // Время нужно для поправки на остывание платы
            off_time_correction = utc - off_time;
            
            APP_LOG("Time correction: %u\r\n", off_time_correction);
        }
        
        //запускаем таймер задержанного разрешения записи страницы
        pof_manager_delay_enable_record();
    }
    
    // Загрузим настройки из RTC
    err_code = pcf8563_read_alarm_data((uint8_t*) & sensors_evt_en, sizeof (sensors_evt_en));
    APP_ERROR_CHECK(err_code);
    
    // В pcf8563 по умолчанию регистр может быть забит нулями и некоторые поля 1.
    // Поэтому с помощью поля is_set определяется регистр был ли очищен из-за
    // потери питания rtc или нет
    if (!sensors_evt_en.is_set || !utc_is_set) {
        APP_LOG("Reset events sense config\r\n");
        sensor_evt_reset();
    }
}

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);

    // Загрузка основных триггеров
    api_param_update();

    check_default_config();
    
    com_master_set_event_handler(api_master_evt_handler);
    com_master_set_internal_action_handler(api_internal_action_process);
    
    //чтение состояния кнопки
    if (!nrf_gpio_pin_read(BUTTON_PIN)) {
        api_state_change(APP_SELFTEST);
    } else {
        api_state_change(APP_INIT);
    }
}
/********************************************************************************
 *
 ********************************************************************************/

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

    if (app_prew_state == app_new_state)return;

    switch (app_prew_state) {
        case APP_STAND_BY:
        {
            APP_LOG("APP_STAND_BY>>\r\n");
            break;
        }
        case APP_PAIRING:
        {
            APP_LOG("APP_PAIRING>>\r\n");
            com_master_pairing_stop();
            r4s_disable_bonding();
            break;
        }
        default:
        {
            break;
        }
    }

    app_prew_state = app_new_state;
}

__STATIC_INLINE void api_reset_params_at_unpairing( void )
{
    //события календаря (до этого уже должны быть очищены все транзакции)
    if (evcal_add_transaction(EVCAL_TRANSACTION_WRITE) == NRF_SUCCESS) {    
        evcal_erase_all_tasks();
        evcal_dec_transaction(EVCAL_COMMIT_TRANSACTION);
    }
    
    reset_default_config();
    check_default_config();
    sensor_evt_reset();
}

/**@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_STARTUP;

    if (app_prew_state == app_new_state)return;

    switch (app_new_state) {
        case APP_STARTUP:
            break;
        case APP_SELFTEST:
        {
            APP_LOG(" >>APP_SELFTEST\r\n");
            ind_state_change(IND_START_SELFTEST);
            
            err_code = at25sf041_init();
            APP_ERROR_CHECK(err_code);
            
            // Инициализация модуля логирования
            err_code = cb_init(false);
            APP_ERROR_CHECK(err_code);
            ind_state_change(IND_TEST_PASSED);
            // Сигнал о том, что циклический буфер в порядке
            
            app_init_rtc();
            ind_state_change(IND_TEST_PASSED);
            // Сигнал о том, что rtc исправен
            
            meas_ret_t ret = meas_init(app_sensors_update, off_time_correction);
            switch (ret) {
                case MEAS_SUCCESS: // 3 сигнала - все датчики работают
                    ind_state_change(IND_TEST_PASSED);
                case MEAS_SHT20_ERROR: // 2 сигнала - sht20 не работает
                    ind_state_change(IND_TEST_PASSED);
                case MEAS_BMP280_ERROR: // 1 сигнал - bmp280 не работает
                    ind_state_change(IND_TEST_PASSED);
                case MEAS_GAS_SENSOR_ERROR: // Нет сигнала - датчик со2 не работает
                    break;
            }
            
            if (ret != MEAS_SUCCESS) APP_ERROR_CHECK(NRF_ERROR_INTERNAL);
            
            meas_start(APP_MEASUREMENT_MS, true); // Начать мониторинг
            
            app_timeout_flag = false;

            err_code = app_timer_start(app_timer_id, APP_TIMER_TICKS(APP_SELFTEST_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL);
            APP_ERROR_CHECK(err_code);
            break;
        }
        case APP_INIT:
        {
            APP_LOG(">>APP_INIT\r\n");
            ind_state_change(IND_STARTUP);
            
            err_code = at25sf041_init();
            APP_ERROR_CHECK(err_code);
            
            err_code = cb_init(false);
            APP_ERROR_CHECK(err_code);

            app_init_rtc();

            meas_ret_t ret = meas_init(app_sensors_update, off_time_correction);
            
            if (ret != MEAS_SUCCESS) APP_ERROR_CHECK(NRF_ERROR_INTERNAL);
            
            app_timeout_flag = false;

            err_code = app_timer_start(app_timer_id, APP_TIMER_TICKS(APP_INIT_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL);
            APP_ERROR_CHECK(err_code);
            break;
        }
        case APP_STAND_BY:
        {
            APP_LOG(">>APP_STAND_BY\r\n");
            ind_state_change(IND_ACTIVE);
            break;
        }
        case APP_UNPAIRING:
        {
            APP_LOG(">>APP_UNPAIRING\r\n");
            ind_state_change(IND_UNPAIRING);
            r4s_slave_disconnect();
            APP_ERROR_CHECK(r4s_slave_unpairing());
            com_master_unpairing();
            api_reset_params_at_unpairing();
            break;
        }
        case APP_PAIRING:
        {
            APP_LOG(">>APP_PAIRING\r\n");
            err_code = app_timer_stop(app_timer_id);
            APP_ERROR_CHECK(err_code);

            app_timeout_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);

            ind_state_change(IND_PAIRING);
            
            r4s_enable_bonding();
            com_master_pairing_start();
            break;
        }
        default:
        {
            break;
        }
    }

    app_prew_state = app_new_state;
}

/* ===============[ Работа с триггерами ]===================== */

/** @brief Функция для обработки триггера и выполнения событий триггера
 */
void app_trigger_event_process(sensor_evt_trigger_t * trigger, uint8_t len) {
    for (uint8_t i = 0; i < len; i++) {
        if (trigger[i].state == TRIGGER_CAPTURED) {
            com_master_proccess(trigger[i].com_evt);
            trigger[i].state = TRIGGER_PROCESSED;
        }
    }
}

void app_update_triggers() {
    // Обновление включенных триггеров
    if (sensors_evt_en.temperature) {
        event_sensor_check(temp_triggers, temp_triggers_count, api_get_temperature());
        app_trigger_event_process(temp_triggers, temp_triggers_count);
    }
    if (sensors_evt_en.pressure) {
        event_sensor_check(pressure_triggers, pressure_triggers_count, api_get_pressure());
        app_trigger_event_process(pressure_triggers, pressure_triggers_count);
    }
    if (sensors_evt_en.tvoc) {
        event_sensor_check(tvoc_triggers, tvoc_triggers_count, api_get_tvoc());
        app_trigger_event_process(tvoc_triggers, tvoc_triggers_count);
    }
    if (sensors_evt_en.humidity) {
        event_sensor_check(humidity_triggers, humidity_triggers_count, api_get_humidity());
        app_trigger_event_process(humidity_triggers, humidity_triggers_count);
    }
}

/********************************************************************************
 *
 ********************************************************************************/
void application_process() {

    app_keyb_proccess();

    switch (app_state) {
        case APP_STARTUP:{
            break;
        }
        case APP_SELFTEST:{
            if (app_timeout_flag == true) {
                ret_code_t ret = meas_get_err_device();
                if (ret == MEAS_SUCCESS) {
                    api_state_change(APP_STAND_BY);
                } else {
                    self_test_error_code = ret;
                    api_state_change(APP_SELFTEST_ERROR);
                }
            }
            break;
        }
        case APP_SELFTEST_ERROR: {
            if (app_timeout_flag == true) {
                // Мигаем то количество раз, которое укажет на ошибку в датчике
                for (uint8_t i = 0; i < self_test_error_code; i++){
                    ind_state_change(IND_SELFTEST_FAULT_STATE);
                }
                
                app_timeout_flag = false;
                APP_ERROR_CHECK(app_timer_start(app_timer_id, 
                    APP_TIMER_TICKS(APP_SELFTEST_ERROR_TIMEOUT_MS, APP_TIMER_PRESCALER), 
                    NULL));
            }
            break;
        }
        case APP_INIT: {
            if (app_timeout_flag == true){
                meas_start(APP_MEASUREMENT_MS, false); // Начать мониторинг
                api_state_change(APP_STAND_BY);
            }
            break;
        }
        case APP_UNPAIRING:
        {
            api_state_change(APP_STAND_BY);
            break;
        }
        case APP_PAIRING:
        {
            if (app_timeout_flag == true) {
                ind_state_change(IND_PAIRING_ERROR); 
                api_state_change(APP_STAND_BY);
            }
            break;
        }
        case APP_STAND_BY:
        {
            if (param_update_required) {
                api_param_update();
            }
            // Обновление значений триггеров для датчиков
            app_update_triggers();
            break;
        }
        default:
        {
            break;
        }
    }
    
    check_default_config();
    
    meas_update();
    
    // Жирненький костыль, включающий сценарии конфигуратора, если в календаре удалили таски на включение
    uint32_t err_code = evcal_add_transaction(EVCAL_TRANSACTION_READ);
    if (err_code == NRF_SUCCESS) {
        // Маска каналов, для которых не было найдено включающих событий календаря
        uint16_t enable_not_found_mask = UINT16_MAX;
        
        uint8_t count = 0;
        evcal_task_t task;
        
        evcal_get_task_number(&count);
        
        for (uint8_t i = 0; i < count; i++) {
            evcal_get_task_data(i, &task);
            
            if (task.enable) { // Если таска включена
                ac_input_t * p_input = (ac_input_t*) &task.cmd.array[1];
                exten_req_start_t * p_in_exp_pckt = (exten_req_start_t*) p_input->data;
                
                if (p_input->action_code == AC_EXTENTION_START_CMD) { // это таска на старт
                    // "Выколем" биты в маске каналов, для которых не найдено включающих тасок, так как таска найдена
                    enable_not_found_mask &= ~(p_in_exp_pckt->mask);
                }
            }
        }
        
        // Включаем каналы, которые остались без тасок на включение
        application_start(enable_not_found_mask);
        
        evcal_dec_transaction(EVCAL_COMMIT_TRANSACTION);
    }
}

static void app_keyb_proccess(void) {
    for (uint8_t i = 0; i < BTN_COUNT; i++) //чтение состояния кнопок
    {
        APP_ERROR_CHECK(button_read(i, &app_keyboard_data[i]));
    }

    if (app_keyboard_data[PAIRING_BUTTON].pressed_time_ms >= APP_KEY_UNPARING_TIME_MS) {
        switch (app_state) {
            case APP_STAND_BY:
            {
                if (button_wait_release == BTN_WAIT_PAIRING
                        || button_wait_release == BTN_NO_WAIT) {
                    ind_state_change(IND_UNPAIRING_START);
                }
                button_wait_release = BTN_WAIT_UNPAIRING;
                return;
            }
            default:
                button_wait_release = BTN_NO_WAIT;
                break;
        }
    } else if (app_keyboard_data[PAIRING_BUTTON].pressed_time_ms >= APP_KEY_PARING_TIME) {
        switch (app_state) {
            case APP_STAND_BY:
            {
                if (button_wait_release == BTN_NO_WAIT) {
                    ind_state_change(IND_PAIRING_START);
                }
                button_wait_release = BTN_WAIT_PAIRING;
                return;
            }
            default:
                button_wait_release = BTN_NO_WAIT;
                break;
        }
    }

    if (app_keyboard_data[PAIRING_BUTTON].event_release == BTN_EVENT_CAPTURED) {
        switch (button_wait_release) {
            case BTN_WAIT_PAIRING:
                if (app_state == APP_STAND_BY) {
                    api_state_change(APP_PAIRING);
                }
                break;
            case BTN_WAIT_UNPAIRING:
                if (app_state == APP_STAND_BY) {
                    api_state_change(APP_UNPAIRING);
                }
                break;
            default:
            {
                break;
            }
        }
        
        button_wait_release = BTN_NO_WAIT;
        
        ind_state_change(IND_BUZZ_STOP);
    }
}
/********************************************************************************
 *
 ********************************************************************************/

/********************************************************************************
 *
 ********************************************************************************/

/**
 * 	описание в 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) {
        case APP_STAND_BY:
        {
            return APP_API_STAND_BY_STATE;
        }
        case APP_PAIRING:
            return APP_API_PAIRING_STATE;

        default:
        {
            return APP_API_ERROR_STATE;
        }
    }
}

void api_param_update(void) {
    uint32_t err_code;

    // Загрузка триггеров
    humidity_triggers_count = 0;
    err_code = event_load_mct(humidity_triggers, &humidity_triggers_count,
            MAX_EVENT_TYPE_COUNT, HUMIDITY_EVENT_CODE);
    if (err_code == NRF_ERROR_BUSY) {
        param_update_required = true;
        return;
    }
    APP_ERROR_CHECK(err_code);

    tvoc_triggers_count = 0;
    err_code = event_load_mct(tvoc_triggers, &tvoc_triggers_count,
            MAX_EVENT_TYPE_COUNT, TVOC_EVENT_CODE);
    if (err_code == NRF_ERROR_BUSY) {
        param_update_required = true;
        return;
    }
    APP_ERROR_CHECK(err_code);

    pressure_triggers_count = 0;
    err_code = event_load_mct(pressure_triggers, &pressure_triggers_count,
            MAX_EVENT_TYPE_COUNT, PRESSURE_EVENT_CODE);
    if (err_code == NRF_ERROR_BUSY) {
        param_update_required = true;
        return;
    }
    APP_ERROR_CHECK(err_code);

    temp_triggers_count = 0;
    err_code = event_load_mct(temp_triggers, &temp_triggers_count,
            MAX_EVENT_TYPE_COUNT, TEMP_EVENT_CODE);
    if (err_code == NRF_ERROR_BUSY) {
        param_update_required = true;
        return;
    }
    APP_ERROR_CHECK(err_code);

    ind_alarm_off();
    
    param_update_required = false;
}

/**
 * 	описание в 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:
        {
            APP_ERROR_CHECK(pcf8563_set_time_utc(&m_time_stamp));
            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));
}

/********************************************************************************/

/***************************PERIPHERAL************************************/
uint8_t api_get_error(void) {
    switch (app_state) {
        default:
            return APP_API_NO_ERROR;
    }
}

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

void r4s_slave_pairing_success(void) {
    api_state_change(APP_STAND_BY);
}

void api_slave_on_connected() {
    uint32_t err_code;
    
    if (app_state == APP_PAIRING) {
        err_code = app_timer_stop(app_timer_id);
        APP_ERROR_CHECK(err_code);
    }
}

void api_slave_on_disconnected() {
    if (app_state == APP_PAIRING) {
        ind_state_change(IND_PAIRING_ERROR); 
        api_state_change(APP_STAND_BY);
    }
}
/********************************************************************************/
/***************************CENTRAL************************************/

/********************************************************************************/

static void api_master_evt_handler(com_master_evt_t * p_evt) {
    switch (p_evt->id) {
        case COM_MASTER_PAIRING_SUCCESS:{
            APP_LOG("api_master_pairing_success\r\n");
            api_state_change(APP_STAND_BY);
            break;
        }
        case COM_MASTER_PAIRING_ERROR: {
            APP_LOG("api_master_pairing_error\r\n");
            api_state_change(APP_STAND_BY);
            break;
        }
        case COM_MASTER_DEFAULT_CONFIG_REQUIRED:{
            // do nothing
            break;
        }
    }
}

/* ===============[ Обработка датчиков, мониторинг и логирование ]===================== */

/** @brief Хандлер показаний датчиков
 * 
 * @param p_values - полученные показания датчиков
 */
static void app_sensors_update() {
    static uint32_t ticks = 1;
    uint32_t period_ms = meas_get_period_ms();

    com_slave_send_state();
    
    // Проверяем, пришло ли время записывать
    if (ticks % (APP_LOG_STORE_PERIOD_MS / period_ms) == 0) {
        APP_LOG("Store log\r\n");
        application_store_log();
    }

    ticks++;
}

static void application_store_log() {
    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) {// Время не установлено или не верное, не логировать
        APP_LOG("Setup time to store\r\n");
        return;
    }
    
    // Упаковка данных
    sensor_values_pack_sfloat_t pack = 
    {
        .val = {
            .temperature_deg    = api_get_temperature_encoded(),
            .pressure_Pa        = api_get_pressure_encoded(),
            .humidity_rh        = api_get_humidity_encoded(),
            .tvoc_ppb           = api_get_tvoc_encoded(),
        },
    };

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

    APP_ERROR_CHECK(cb_put(&record));
}

/*****************************************************************************************************/

Made on
Tilda