Программный модуль для датчика 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));
}
/*****************************************************************************************************/