Программный модуль для терморегулятора батареи RST-191S. v.1.6
ПО является неотъемлемой частью терморегулятора батареи RST-191S, отдельно потребителю не поставляется и эксплуатируется только в составе устройства.
////////////////////////////////////////////////////////////////
//RST-191S
//v1.6
//application.c
////////////////////////////////////////////////////////////////
/*==============================================================
include
==============================================================*/
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "debug_info.h"
#include "nrf.h"
#include "nrf_delay.h"
#include "nrf_gpio.h"
#include "nrf_soc.h"
#include "app_timer.h"
#include "app_error.h"
#include "app_scheduler.h"
#include "crc16.h"
#include "com_slave.h"
#include "com_master.h"
#include "r4s_lib.h"
#include "r4s_master.h"
#include "param.h"
#include "Application_api.h"
#include "event_base.h"
#include "timers_config.h"
#include "buz_ac.h"
#include "button.h"
#include "button_services.h"
#include "indication.h"
#include "batt_nAA.h"
#include "com_slave_extention.h"
#include "master_control_table.h"
#include "temperature.h"
#include "temperature_service.h"
#include "adv_data_config.h"
#include "sfloat.h"
#include "calendar.h"
#include "soft_ind.h"
#include "pcf8563.h"
#include "display.h"
#include "motor.h"
#include "adv_data_config.h"
#include "adv_data.h"
#include "evcal.h"
/********* DEFINES **************************************************************/
/********* MACROS ***************************************************************/
/********* GLOBAL VARIABLES *****************************************************/
bool BoolRtcOneSec = false;
uint32_t AppCurrUtcTime = 0, AppCurrUtcShiftTime = 0;
bool AppBoolLowBattery = false;
uint32_t AppCountdownSeconds;//счетчик обратного отсчета секунд в BOOST
uint32_t AppCountdownSecForNoCalendarTasks;//счетчик обратного отсчета секунд в режиме NO_CALENDAR_TASK
//uint16_t AppCountdownVentilationMinutes;//счетчик обратного отсчета минут в режиме вентиляции
uint8_t AppLockCalendarState = LOCK_CALENDAR_STATE;//это значит, что по умолчанию РУЧНОЙ режим без календаря
float AppTargetTemperature;
bool AppFlagBlinkingNoCalendarTasks = false;//флаг мигания значком календаря AUTO
//Значения по умолчанию, для параметров в долговременной памяти, необходимы при сбросе к заводским настройкам при
// инициализации и при анперинге
param_t AppParam0 __attribute__((aligned(4))) =
{
PARAM_VER, //uint16_t
UNLOCK_CALENDAR_STATE,//uint8_t
0,//powerOnCnt //uint8_t
DEFAULT_PARAM_T_SHIFT,//float
DEFAULT_PARAM_T_VENTILATION,//float
0,//reserved uint16_t ctrLifeTestCnt;
DEFAULT_PARAM_BOOST,//uint16_t
DEFAULT_PARAM_T_HYSTERESIS,//float
APP_DEFAULT_MANUAL_MODE_TARGET_TEMPERATURE,//float
MOSCOW_UTC_SHIFT_TIME,//uint32_t
0,//global_motor_cnt //uint32_t
0,//flash_page_erase_cnt //uint16_t
0,//bool=false VentilationState
0,0,0, //reserved[3]
0xFFFF //CRC16
};
uint8_t AppSubstate = SUBSTATE_0; //подсостояние state machine
float AppCurrTemp = 0.;//значение текущей температуры (уже с коррекцией)
param_t AppParam __attribute__((aligned(4)));
bool AppLockState = false; //состояние блокировки кнопок
app_state_t AppState = APP_START; //состояние state machine
app_state_t appPrevStateAtForceCalibration;//состояние перед калибровкой, необходимой при неисправности мотора (раннее заклинивание, мотор не доходит до нужного места)
bool appFlagForceCalibration = false;//флаг проведения доп. калибровки при неправильной работе мотора
/*********LOCAL VARIABLES *****************************************************/
static app_state_t AppStatePrevious = APP_START; //предыдущее состояние state machine
static app_state_t app_nextstate; //следующее состояние сразу после завершения ожидания полного останова мотора
static uint8_t app_lock_substate = SUBSTATE_0; //подсостояния нажатий кнопки для блокировки
static bool app_in_program_transition = false;//приложение переходит из одной программы в другую
static bool app_wait_key_release = false; //флаг ожидания отжатия кнопки (нужен когда идет переход в другое состояние по времени по долгому нажатию)
static bool app_btns_enabled = true; //кнопки доступны для анализа (полностью вышли из блокировки кнопок)
static uint8_t app_button_inactivity_cnt = 0; //счетчик бездействия кнопок каждые 5с
static btn_data_t keyb_data;
static btn_services_data_t keyb_serv_data; /* данные состояния сервиса клавиатуры*/
static uint8_t app_param_reset_val = DEFAULT_PARAM_RESET;
static uint8_t app_param_calibration_val = APP_CALIBRATION_OFF;
static float app_curr_temp; //текущая измеренная температура (еще без сдвига, без коррекции)
static bool app_bValveMustMoving = false; //состояние движения/покоя штока клапана
static bool app_bValveStopped = true; //мотор точно остановился после движения
static int app_full_motor_cnt = 0; //количество импульсов с мотора при полном проходе в одном направлении
static int app_curr_motor_cnt = 0; //текущий счетчик импульсов с мотора, отсчет начинаем с полностью открытого штока
static int app_target_motor_cnt = 0; //целевое значение счетчика мотора
static uint8_t app_motor_direction; //направление движения штока в т.ч. при торможении (остаточное движение)
static bool app_timeout_flag = false; /* флаг timeout-a для app_timer_id*/
static bool app_ble_connected = false;
APP_TIMER_DEF(app_timer_id);
APP_TIMER_DEF(led_interval_id);
static uint8_t app_battery_charge = 100;
//Это список AC, которые данный мастер запрашивает у ИУ при прямом коннекте с ним
static cm_dev_support_ac_t m_required_ac = {
.ac = {
AC_EXTENTION_START_CMD,
AC_EXTENTION_STOP_CMD
//а это пока только для календаря - AC_EXTENTION_TEMP_SFLOAT_CMD, поэтому здесь он не нужен
}
};
/********* FUNCTION PROTOTYPES **************************************************/
uint32_t api_temp_test_proccess(void);
uint32_t api_battery_test_proccess(void); // фукция проверки батареи
void app_start_timer_for_next_motor_regulation(void);
void app_start_motor_regulation(void);
static void api_master_evt_handler(com_master_evt_t * p_evt);
static void master_process_event(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_timeout_flag = true;
return;
}
static void led_timer_sched_handler(void *p_event_data, uint16_t event_size)
{
if(app_button_inactivity_cnt == SUBSTATE_0)//5 секунд
{
ind_led_light(false);//выключение подсветки после 5 секунд бездействия кнопок
if(AppState == APP_CLOCK_SET_MIN || AppState == APP_CLOCK_SET_HOUR || AppState == APP_CLOCK_SET_DAY ||
AppState == APP_CLOCK_BLINKING || AppState == APP_DISPLAY_AdA || AppState == APP_DISPLAY_AdA_1_2)//последние два - при управлении из вне при старте
{
//сохраняем время через 5с бездействия
api_set_calendar_data(&AppCurrUtcTime, &AppCurrUtcShiftTime);
}
if(AppState == APP_ADVANCED_MENU || AppState == APP_SELECT_PROGRAM || AppState == APP_STAND_BY)
{
//через 5 сек бездействия ЕСЛИ изменились AppParam сохраняем в долговременной памяти все программы и параметры
if(memcmp(¶m, &AppParam, sizeof(param_t) - sizeof(param.CRC)))
{
AppParam.flash_page_erase_cnt++;
memcpy(¶m, &AppParam, sizeof(param_t) - sizeof(param.CRC));
Param_Save();
APP_PRINTF(0,"after 5s we saved param: LockCalendarState = %d\n", param.LockCalendarState);
}
}
if(AppState == APP_SELECT_PROGRAM)
{
api_state_change(APP_STAND_BY);
}
if(AppState == APP_SELF_TEST)
{
//если мотор работает
if(app_bValveMustMoving || !app_bValveStopped)
{
app_timer_stop(led_interval_id);//перестаем контролировать время до остановки мотора
return;
}
}
}
else if(app_button_inactivity_cnt == SUBSTATE_1)//10 секунд
{
if(AppState == APP_SELF_TEST)
{
//выход по таймауту из self-test
ht9b92_display_on();
soft_ind_UI(IND_SET);
app_timer_start( app_timer_id, APP_TIMER_TICKS(DISPLAY_SYMBOLS_UI_AT_LCD_TICK_MS, APP_TIMER_PRESCALER), NULL );
api_state_change(APP_TEST);
}
}
else if(app_button_inactivity_cnt == SUBSTATE_2)
{
//через 15с бездействия выходим из режима
if(AppState == APP_CLOCK_SET_MIN || AppState == APP_CLOCK_SET_HOUR || AppState == APP_CLOCK_SET_DAY)
{
if(app_full_motor_cnt == 0)//если до калибровки
{
api_state_change(APP_CLOCK_BLINKING);
}
else
{
api_state_change(APP_STAND_BY);
}
}
if(AppState == APP_ADVANCED_MENU)
{
api_state_change(APP_STAND_BY);
}
}
else
{
if(app_button_inactivity_cnt >= TICKS_FOR_LOCK_AFTER_INACTIVITY_10_MIN - 1) //-1 так как app_button_inactivity_cnt++ у нас стоит после - в конце
{
app_timer_stop(led_interval_id);
AppLockState = true;
display_write_symbol_data(SYMB_T14_CHILD_LOCK, AppLockState);
}
}
app_button_inactivity_cnt++;
}
static void led_timer_handler(void* p_context)
{
app_sched_event_put(NULL, 0, led_timer_sched_handler);
}
////////////////////////////////////////////////////////////////
/**
* @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){
APP_PRINTF(0,"evcal callback -> evcal_calendar_task()\n");
//добавлена защита от срабатывания задач календаря при заблокированном календаре и в случае состояния Ошибки
if(p_data == NULL || AppLockCalendarState == LOCK_CALENDAR_STATE || AppState == APP_ERROR ||
AppState == APP_BOOST || AppState == APP_DISPLAY_AdA || AppState == APP_DISPLAY_AdA_1_2)
{
APP_PRINTF(0,"return as p_data == NULL || AppLockCalendarState == LOCK_CALENDAR_STATE || AppState == APP_ERROR || AppState == APP_BOOST etc \n");
return;
}
uint8_t action_code = p_data->array[1];
switch(action_code)
{
case AC_EXTENTION_START_CMD:
{
exten_req_start_t *p_in_ext_pckt = (exten_req_start_t *) &p_data->array[2];
if((p_in_ext_pckt->mask & 0x1) != 0){
app_state_change_after_stop(APP_OP_STATE);
}
break;
}
case AC_EXTENTION_STOP_CMD:
{
exten_req_stop_t *p_in_ext_pckt = (exten_req_stop_t *) &p_data->array[2];
if((p_in_ext_pckt->mask & 0x1) != 0){
app_state_change_after_stop(APP_OFF_STATE);
}
break;
}
case AC_EXTENTION_TEMP_SFLOAT_CMD:
{
exten_req_temp_sfloat_cmd_t* reg_temp_sfloat = (exten_req_temp_sfloat_cmd_t*)&p_data->array[2];
float temp_set = SFloat16ToFloat(reg_temp_sfloat->param1);
#ifdef DEBUG
char s_temp[6];
sprintf(s_temp,"%0.1f", temp_set);
#endif
if(temp_set < MIN_REG_TEMPERATURE || temp_set > MAX_REG_TEMPERATURE)
{
#ifdef DEBUG
APP_PRINTF(0,"return as temp_set = %s -> it is out of range\n", s_temp);
#endif
return;
}
#ifdef DEBUG
APP_PRINTF(0,"AppCurrUtcTime=%d, AC temp_set = %s\n", AppCurrUtcTime, s_temp);
#endif
AppParam.TargetTemperature = AppTargetTemperature = temp_set;
app_state_change_after_stop(APP_STAND_BY);//в APP_STAND_BY можно входить и повторно с полной инициализацией
break;
}
default:
{
break;
}
}
return;
}
/**@brief Обработчик таймера измерения температуры и заряда.
*
* @param none
*
* @return none
*/
static void app_temp_data_handler( float *p_temp )
{
AppCurrTemp = *p_temp + AppParam.A1_T_shift;
if(AppState == APP_STAND_BY || AppState == APP_VENTILATION || AppState == APP_BOOST ||
AppState == APP_OP_STATE || AppState == APP_OFF_STATE)
{
soft_ind_current_temp(AppCurrTemp, IND_SET);
}
if(api_temp_test_proccess() != NRF_SUCCESS)
{
api_state_change_to_error(APP_API_TEMPERATURE_SENSOR_ERROR);
}
app_battery_charge = batt_nAA_get_charge();
#ifdef DEBUG
char s_temp[40];
sprintf(s_temp,"[BAT]: BAT = %d,temp = %0.1f \r\n",app_battery_charge, AppCurrTemp);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_GREEN"%s"RTT_CTRL_RESET,s_temp);
#endif
if(app_battery_charge <= MIN_BATTERY_CHARGE_LEVEL && !AppBoolLowBattery)
{
ind_state_change(IND_LOW_BATTERY_START);
}
if(app_battery_charge > MIN_BATTERY_CHARGE_LEVEL && AppBoolLowBattery)
{
ind_state_change(IND_LOW_BATTERY_STOP);
}
return;
}
////////////////////////////////////////////////////////////////
void app_init_rtc() {
uint32_t err_code;
uint32_t utc;// = 0x5A785640;
err_code = pcf8563_init();
APP_ERROR_CHECK(err_code);
//pcf8563_set_time_utc(&utc);
err_code = pcf8563_get_time_utc(&utc);
if (err_code == NRF_SUCCESS)
{
// calendar_set_time(utc, MOSCOW_UTC_SHIFT_TIME);//+3 часа по умолчанию для Москвы
calendar_set_time(utc, AppParam.utcShift);//восстанавливаем тайм зону
APP_PRINTF(0,"Restore time utc: %X, shift: %X\r\n", utc, AppParam.utcShift);
}
else if(err_code == NRF_ERROR_INVALID_DATA)
{
utc = 0;
//err_code =
pcf8563_set_time_utc(&utc);
// if(err_code != NRF_SUCCESS)
// api_state_change_to_error(APP_API_RTC_ERROR);
}
// else
// {
// api_state_change_to_error(APP_API_RTC_ERROR);
// }
}
uint32_t app_rtc_update(void)
{
#define MIN_TIME_DIFFERENCE 1 //если время nordic отличается от RTC более чем на 1 секунду, то синхронизируем
uint32_t curutc, curshift, utc;
static uint32_t secCnt = 0;//счетчик секунд
uint32_t err_code = NRF_SUCCESS;
if(BoolRtcOneSec)
{
BoolRtcOneSec = false;
if(++secCnt > TIME_CHECK_POINT_IN_SEC)
{
secCnt = 0;
api_get_calendar_data(&curutc, &curshift);
err_code = pcf8563_get_time_utc(&utc);
if (err_code == NRF_SUCCESS)
{
//Проверка ухода времени nordic от RTC
if(abs((int32_t)utc - (int32_t)curutc) >= MIN_TIME_DIFFERENCE)
{
api_set_calendar_data(&utc, &curshift);
APP_PRINTF(0,"Restored and synchronized utc time=%X (old curutc=%X)\r\n", utc, curutc);
}
#ifdef DEBUG
else
{
APP_PRINTF(0,"No difference between nordic and RTC utc time: %X, shift=%X\r\n", utc, curshift);
}
#endif
}
else
{
APP_ERROR_CHECK(err_code);
}
}
}
return err_code;
}
void application_init(void)
{
uint32_t err_code;
//задаем адрес от куда считывать данные о клавиатуре
err_code = button_services_set_address(&keyb_data);
APP_ERROR_CHECK(err_code);
APP_ERROR_CHECK(calendar_set_one_sec_callback(calendar_timer_handle)); //установка callback часов
APP_ERROR_CHECK(evcal_set_callback(evcal_calendar_task)); //установка callback при срабатывании расписания
err_code = app_timer_create( &app_timer_id, APP_TIMER_MODE_SINGLE_SHOT, app_timeout_timer_handler );
APP_ERROR_CHECK( err_code );
//по первому прерыванию 5с выключаем подсветку и т.д.
APP_ERROR_CHECK(app_timer_create( &led_interval_id, APP_TIMER_MODE_REPEATED, led_timer_handler ));
err_code = temperature_service_init(&app_curr_temp, app_temp_data_handler);
APP_ERROR_CHECK(err_code);
if(Param_Init() == true)
{
//восстанавливаем необходимые параметры из долговременной памяти
memcpy(&AppParam, ¶m, sizeof(param_t));
}
else
{
//устанавливаем начальные значения по умолчанию
memcpy(&AppParam, &AppParam0, sizeof(param_t));
memcpy(¶m, &AppParam0, sizeof(param_t));
Param_Save();
}
AppLockCalendarState = AppParam.LockCalendarState;
APP_PRINTF(0, "application_init(): AppLockCalendarState = %d, AppParam.powerOnCnt=%d, AppParam.ctrLifeTestCnt = %d\n", AppLockCalendarState,
AppParam.powerOnCnt, AppParam.ctrLifeTestCnt);
memcpy(&AppTargetTemperature, &AppParam.TargetTemperature, sizeof(float));
AppCurrTemp = temp_read_val() + AppParam.A1_T_shift;//коррекция температуры, первый раз надо здесь делать
if(AppParam.powerOnCnt <= MAX_POWER_ON_CNT_FOR_MASS_FW_UPDATE)//чтобы досчитал до MAX + 1 = 4
{
AppParam.powerOnCnt++;//счетчик включения питания/перепрошивки
}
soft_ind_set_all(IND_SET);
app_timer_start( app_timer_id, APP_TIMER_TICKS(DISPLAY_ALL_LCD_SEG_TICK_MS, APP_TIMER_PRESCALER), NULL );
com_master_set_required_ac(&m_required_ac);
com_master_set_event_handler(api_master_evt_handler);
com_master_set_internal_action_handler(master_process_event);
}
/**
* @brief Открытие сессии/транзакции в календаре
*
* @param type read/write
*
* @return возвращает код ошибки
*
*/
uint8_t api_add_trans_in_calendar(uint8_t type)
{
if(type!=0 && type!=1) return ERSP_ERROR_INVALID_PARAM;
uint32_t err_code = evcal_add_transaction((!type)?(EVCAL_TRANSACTION_READ):(EVCAL_TRANSACTION_WRITE));
switch (err_code)
{
case NRF_SUCCESS:
{
return ERSP_SUCCESS;
}
case NRF_ERROR_INVALID_PARAM:
{
return ERSP_ERROR_INVALID_PARAM;
}
case DRIVER_MODULE_NOT_INITIALZED:
{
return ERSP_ERROR_INTERNAL;
}
case NRF_ERROR_FORBIDDEN:
return ERSP_ERROR_FORBIDDEN;
}
return ERSP_ERROR_UNKNOWN;
}
/**
* @brief Закрытие сессии в календаре
*
* @param task commit/rollback
*
* @return возвращает код ошибки
*
*/
uint8_t api_dec_trans_in_calendar(uint8_t task)
{
if(task!=0 && task!=1) return ERSP_ERROR_INVALID_PARAM;
uint32_t err_code = evcal_dec_transaction((!task)?(EVCAL_COMMIT_TRANSACTION):(EVCAL_ROLLBACK_TRANSACTION));
switch (err_code)
{
case NRF_SUCCESS:
{
return ERSP_SUCCESS;
}
case NRF_ERROR_INVALID_PARAM:
{
return ERSP_ERROR_INVALID_PARAM;
}
case DRIVER_MODULE_NOT_INITIALZED:
{
return ERSP_ERROR_INTERNAL;
}
case NRF_ERROR_FORBIDDEN:
return ERSP_ERROR_FORBIDDEN;
}
return ERSP_ERROR_UNKNOWN;
}
uint8_t api_erase_evcal(void)
{
if(evcal_erase_all_tasks() == NRF_SUCCESS){
return ERSP_SUCCESS;
} else {
return ERSP_ERROR_INTERNAL;
}
}
/********************************************************************************
*
********************************************************************************/
/**@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_TEST:
{
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: APP_TEST >>\r\n"RTT_CTRL_RESET);
break;
}
case APP_STAND_BY:
{
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: APP_STAND_BY>>\r\n"RTT_CTRL_RESET);
break;
}
case APP_CLOCK_SET_MIN:
case APP_CLOCK_SET_HOUR:
case APP_CLOCK_SET_DAY:
soft_ind_set_all(IND_CLR);
break;
case APP_PAIRING:
{
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: APP_PAIRING>>\r\n"RTT_CTRL_RESET);
app_advertising_unset_paring_flag();
com_master_pairing_stop();
break;
}
case APP_PAIRING_ERROR:
soft_ind_current_temp(0, IND_CLR);
break;
default:
{
break;
}
}
app_prew_state = app_new_state;
return;
}
/**@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;
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_RED"[APPL]: >>APP SET NEW STATE - %d\r\n"RTT_CTRL_RESET, api_get_state()); // Отладка стейта выдаваемого для приложения
if( app_prew_state == app_new_state && app_new_state != APP_STAND_BY)return;
switch( app_new_state )
{
case APP_START:
{
break;
}
case APP_MASS_FW_UPDATE:
{
AppParam.powerOnCnt = 0;//сбрасываем счетчик при входе в режим массовой перепрошивки
soft_ind_set_all(IND_CLR);
soft_ind_FU(IND_SET);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_MASS_FW_UPDATE\n"RTT_CTRL_RESET);
AppParam.flash_page_erase_cnt++;
memcpy(¶m, &AppParam, sizeof(param_t) - sizeof(param.CRC));
Param_Save();
break;
}
case APP_SELF_TEST:
{
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_SELF_TEST\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_CLOCK_BLINKING:
ind_state_change(IND_CLOCK_BLINKING);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_CLOCK_BLINKING\r\n"RTT_CTRL_RESET);
break;
case APP_CLOCK_SET_MIN:
soft_ind_set_all(IND_CLR);
ind_state_change(IND_CLOCK_SET_MIN);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_CLOCK_SET_MIN\r\n"RTT_CTRL_RESET);
break;
case APP_CLOCK_SET_HOUR:
ind_state_change(IND_CLOCK_SET_HOUR);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_CLOCK_SET_HOUR\r\n"RTT_CTRL_RESET);
break;
case APP_CLOCK_SET_DAY:
ind_state_change(IND_CLOCK_SET_DAY);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_CLOCK_SET_DAY\r\n"RTT_CTRL_RESET);
break;
case APP_DISPLAY_AdA:
ind_state_change(IND_DISPLAY_AdA);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_DISPLAY_AdA\r\n"RTT_CTRL_RESET);
break;
case APP_DISPLAY_AdA_1_2:
app_timer_stop(app_timer_id);
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
app_full_motor_cnt = 0;
api_motor_move(CLOSE_VALVE);
AppSubstate = SUBSTATE_0;
ind_state_change(IND_DISPLAY_AdA_1);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_DISPLAY_AdA_1_2\r\n"RTT_CTRL_RESET);
break;
case APP_STAND_BY:
{
app_timer_stop(app_timer_id);
soft_ind_set_all(IND_CLR);
display_write_symbol_data(SYMB_T6_INDOOR_T, IND_SET);
display_write_symbol_data(SYMB_T7_SET_T_SET, IND_SET);
display_write_symbol_data(SYMB_T24_VALVE_TOP, IND_SET);
display_write_symbol_data(SYMB_T16_VALVE_BOTTOM, IND_SET);
soft_ind_current_temp(AppCurrTemp, IND_SET);//чтобы не ждать 10(или 60) секунд
ind_state_change(IND_APP_STANDBY);
if(AppLockCalendarState == UNLOCK_CALENDAR_STATE)
{
display_write_symbol_data(SYMB_T12_AUTOMATIC_MODE, IND_SET);
}
soft_ind_target_temp(AppTargetTemperature, IND_SET);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_STAND_BY\n"RTT_CTRL_RESET);
AppSubstate = SUBSTATE_1;
app_start_timer_for_next_motor_regulation();
break;
}
// case APP_NO_CALENDAR_TASKS:
// AppCountdownSeconds = APP_BLINKING_AUTO;
// APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_NO_CALENDAR_TASKS\n"RTT_CTRL_RESET);
// ind_state_change(IND_NO_CALENDAR_TASKS);
// break;
case APP_SELECT_PROGRAM:
app_timer_stop(app_timer_id);
AppSubstate = SUBSTATE_0;
ind_state_change(IND_PROGRAM_ADVANCED);//IND_PROGRAM_P0);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_SELECT_PROGRAM\n"RTT_CTRL_RESET);
break;
case APP_ADVANCED_MENU:
AppSubstate = SUBSTATE_0;
app_param_reset_val = DEFAULT_PARAM_RESET;
ind_state_change(IND_ADVANCED_MENU);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_ADVANCED_MENU\n"RTT_CTRL_RESET);
break;
case APP_PARAMS_RESET:
memcpy(&AppParam.A1_T_shift, &AppParam0.A1_T_shift, ((uint8_t*)&AppParam0.TargetTemperature/*Programms*/ - (uint8_t*)&AppParam0.A1_T_shift));
//быть внимательнее если будет меняться структура параметров по умолчанию
AppParam.VentilationState = AppParam0.VentilationState;//исправление бага
soft_ind_set_all(IND_SET);
app_timer_start( app_timer_id, APP_TIMER_TICKS(DISPLAY_ALL_LCD_SEG_TICK_MS, APP_TIMER_PRESCALER), NULL );
break;
case APP_VENTILATION:
app_timer_stop(app_timer_id);
AppSubstate = SUBSTATE_1;
// AppCountdownVentilationMinutes = AppParam.A2_time_ventilation;
soft_ind_set_all(IND_CLR);
display_write_symbol_data(SYMB_T23_VENTIALTION_MODE, IND_SET);
soft_ind_current_temp(AppCurrTemp, IND_SET);//чтобы не ждать 10(или 60) секунд
soft_ind_target_temp(AppParam.TargetTemperature, IND_SET);
display_write_symbol_data(SYMB_T24_VALVE_TOP, IND_SET);
display_write_symbol_data(SYMB_T16_VALVE_BOTTOM, IND_SET);
ind_state_change(IND_VENTILATION);
if(AppLockCalendarState == UNLOCK_CALENDAR_STATE)
{
display_write_symbol_data(SYMB_T12_AUTOMATIC_MODE, IND_SET);
}
app_set_valve_closed_percent(APP_DEFAULT_VENTILATION_VALVE_POSITION_PERCENT);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_VENTILATION\n"RTT_CTRL_RESET);
break;
case APP_OP_STATE:
case APP_OFF_STATE:
case APP_BOOST:
{
app_timer_stop(app_timer_id);
soft_ind_set_all(IND_CLR);
display_write_symbol_data(SYMB_T24_VALVE_TOP, IND_SET);
display_write_symbol_data(SYMB_T16_VALVE_BOTTOM, IND_SET);
soft_ind_current_temp(AppCurrTemp, IND_SET);//чтобы не ждать 10(или 60) секунд
switch(app_new_state)
{
case APP_OP_STATE:
api_motor_move(OPEN_VALVE);
ind_state_change(IND_MOTOR_OP);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_OP_STATE\r\n"RTT_CTRL_RESET);
break;
case APP_OFF_STATE:
api_motor_move(CLOSE_VALVE);
ind_state_change(IND_MOTOR_OFF);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_OFF_STATE\r\n"RTT_CTRL_RESET);
break;
case APP_BOOST:
api_motor_move(OPEN_VALVE);
app_in_program_transition = true;//переходное состояние из одной программы в другую (пока только в Boost)
AppCountdownSeconds = AppParam.A4_boost;
ind_state_change(IND_MOTOR_BOOST);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_BOOST\r\n"RTT_CTRL_RESET);
break;
default:
break;
}
if(AppLockCalendarState == UNLOCK_CALENDAR_STATE)
{
display_write_symbol_data(SYMB_T12_AUTOMATIC_MODE, IND_SET);
}
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
AppSubstate = SUBSTATE_0;//главное что не SUBSTATE_2 - переход к следующему состоянию после останова
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_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);
com_master_pairing_start();
app_advertising_set_paring_flag();
break;
}
case APP_PAIRING_SUCCESS:
err_code = app_timer_stop(app_timer_id);//чтобы остановить контроль ошибки перинга по таймауту
APP_ERROR_CHECK( err_code );
ind_state_change(IND_PAIRING_SUCCESS);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_PAIRING_SUCCESS\r\n"RTT_CTRL_RESET);
break;
case APP_PAIRING_ERROR:
err_code = app_timer_start(app_timer_id,APP_TIMER_TICKS(APP_PAIRING_ERROR_MS, APP_TIMER_PRESCALER), NULL );
APP_ERROR_CHECK( err_code );
ind_state_change(IND_PAIRING_ERROR);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_PAIRING_ERROR\r\n"RTT_CTRL_RESET);
break;
case APP_UNPAIRING:
{
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_UNPAIRING_MS, APP_TIMER_PRESCALER), NULL );
APP_ERROR_CHECK( err_code );
ind_state_change(IND_UNPAIRING);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_UNPAIRING\r\n"RTT_CTRL_RESET);
//события очистки при анперинге
evcal_clear_transactions();//сброс событий календаря
//события календаря (до этого уже должны быть очищены все транзакции)
api_add_trans_in_calendar(EVCAL_TRANSACTION_WRITE);
api_erase_evcal();
api_dec_trans_in_calendar(EVCAL_COMMIT_TRANSACTION);
//эти счетчики не надо сбрасывать
AppParam0.global_motor_cnt = AppParam.global_motor_cnt;
AppParam0.flash_page_erase_cnt = AppParam.flash_page_erase_cnt;
AppParam0.powerOnCnt = AppParam.powerOnCnt;
memcpy(&AppParam, &AppParam0, sizeof(param_t));//возвращение к зав.настройкам, сохранение во flash будет через 5 с
AppLockCalendarState = AppParam.LockCalendarState;
memcpy(&AppTargetTemperature, &AppParam.TargetTemperature, sizeof(float));
AppCurrTemp = app_curr_temp + AppParam.A1_T_shift;//коррекция температуры, первый раз надо здесь делать тоже
com_master_unpairing();
com_slave_unpair_proccess();
//api_param_update();
break;
}
#ifdef NRF51
case APP_SLAVE_FW_UPDATE:
{
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_SLAVE_FW_UPDATE\r\n"RTT_CTRL_RESET);
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_FW_UPDATE_TIMEOUT_MS, APP_TIMER_PRESCALER), NULL );
APP_ERROR_CHECK( err_code );
break;
}
#endif
case APP_ERROR:
{
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_ERROR err=%d\r\n"RTT_CTRL_RESET, AppSubstate);
switch(AppSubstate)
{
case APP_API_MOTOR_ERROR:
ind_state_change(IND_ERROR_MOTOR);
break;
case APP_API_TEMPERATURE_SENSOR_ERROR:
ind_state_change(IND_ERROR_TEMPERATURE_SENSOR);
break;
// case APP_API_RTC_ERROR:
// ind_state_change(IND_ERROR_RTC);
// break;
}
break;
}
default:
{
break;
}
}
//чтобы не возвращаться из состояний PAIRING_SUCCESS или PAIRING_ERROR снова в APP_PAIRING, а возвращаться
// в состояние перед началом перинга
if(app_prew_state != APP_PAIRING)
{
AppStatePrevious = app_prew_state;//запоминаем состояние перед блокировкой, чтобы потом в него вернуться
APP_PRINTF(0, "AppStatePrevious = %d\n", AppStatePrevious);
}
app_prew_state = app_new_state;
{
uint8_t adv_app_state;
uint16_t adv_app_state_param;// = ADV_STAND_BY_PRM_NO;
//конвертер - настраивается в каждом старом проекте индивидуально
//adv_app_state = api_get_state();
// if(AppState == APP_FW_UPDATE_STATE)
// {
// adv_app_state = ADV_ST_STAND_BY;
// adv_app_state_param = ADV_STAND_BY_PRM_FW_UPDATE;
// }
if(AppState == APP_PAIRING)
{
adv_app_state = ADV_ST_STAND_BY;
adv_app_state_param = ADV_STAND_BY_PRM_PAIRING;
}
else if(AppState == APP_STAND_BY)
{
adv_app_state = ADV_ST_WORK;
adv_app_state_param = ADV_WORK_PRM_EXECUTION;
}
else
{
adv_app_state = ADV_ST_STAND_BY;
adv_app_state_param = ADV_STAND_BY_PRM_PROG_SELECT;
}
app_advertising_set_app_state(adv_app_state, adv_app_state_param);
}
return;
}
void appStartForceCalibration(void)
{
//задача для мотора не была выполнена и видимо по причине либо неисправности мотора, либо заметного снижения
// напряжения питания батарей => надо откалиброваться
appFlagForceCalibration = true;
appPrevStateAtForceCalibration = AppState;
api_state_change(APP_DISPLAY_AdA_1_2);
}
/********************************************************************************
*
********************************************************************************/
void application_process()
{
uint32_t err_code;
uint16_t motor_cnt;
static int sum_motor_cnt = 0, motor_cnt_AdA1, motor_cnt_AdA2;
err_code = button_read( &keyb_data );
APP_ERROR_CHECK(err_code);
err_code = button_services_read( &keyb_serv_data );
APP_ERROR_CHECK(err_code);
// if(AppIterateTimerFl == true) {
// if(keyb_data.btn_state_mask == 0){
// AppIterateTimerFl = false;
// APP_ERROR_CHECK(app_timer_stop(app_iterate_timer_id));
// }
// }
{
static uint32_t old_mask = 0xFFFFFFFF;//для того чтобы при старте этот код уже начал работать
uint32_t new_mask = ALL_BUTTONS_MASK & keyb_data.btn_state_mask;
if( new_mask != old_mask)
{
//произошло любое действие с любыми кнопками
old_mask = new_mask;
ind_led_light(true);//включаем подсветку
app_button_inactivity_cnt = SUBSTATE_0;
app_timer_stop(led_interval_id);
app_timer_start( led_interval_id, APP_TIMER_TICKS(LED_TICK_MS, APP_TIMER_PRESCALER), NULL );
}
}
//во всех режимах можно по нажатию btn2 включать/выключать блокировку кнопок
if(app_lock_substate == SUBSTATE_0)
{
if( keyb_data.btn_state[BUTTON_CHAN_2_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_2_ID].pressed_time_ms >= APP_LOCK_STATE_ENTER_TIME_MS)
{
AppLockState = !AppLockState;
display_write_symbol_data(SYMB_T14_CHILD_LOCK, AppLockState);
app_lock_substate = SUBSTATE_1;//чтобы не было сразу повторных переключений блокировки
}
}
else
{
if( keyb_data.btn_state[BUTTON_CHAN_2_ID].event_release == BTN_EVENT_CAPTURED)
{
app_lock_substate = SUBSTATE_0;
if(!AppLockState)
{
return;//чтобы исключить обработку в приложении только данного конкретного отжатия кнопки при разблокировке
}
}
}
if(!AppLockState && app_lock_substate == SUBSTATE_0)
{
app_btns_enabled = true;
}
else
{
app_btns_enabled = false;
}
//если ожидаем отжатия любой из клавиш
if(app_wait_key_release)
{
if( keyb_data.btn_state[BUTTON_CHAN_1_ID].event_release + keyb_data.btn_state[BUTTON_CHAN_2_ID].event_release
+ keyb_data.btn_state[BUTTON_CHAN_3_ID].event_release + keyb_data.btn_state[BUTTON_CHAN_4_ID].event_release
+ keyb_data.btn_state[BUTTON_CHAN_5_ID].event_release >= BTN_EVENT_CAPTURED)
{
app_wait_key_release = false;
return;//пропускаем обработку только ожидаемых отжатий, которые надо игнорировать
}
}
{ //Код контроля мотором во всех состояниях
static int tmp_cnt = 0, tmp_cnt2 = 0;
static bool bMotorJustStopped = false;
if(app_bValveMustMoving || !app_bValveStopped)//если шток клапана должен двигаться или еще не успел остановиться после команды Стоп
{
if(app_timeout_flag)//срабатывает (1000 / MOTOR_CONTROL_TICK_MS) раз в секунду при движении штока
{
static uint16_t max_motor_cnt = 0, last_motor_cnt = 0;
motor_cnt = getMotorFreqCnt();
APP_PRINTF(0,"motor_cnt = %d\n", motor_cnt);
#ifdef MY_DEBUG
if(app_curr_motor_cnt > 800) motor_cnt = 0;//для ускорения тестирования
#endif
//поиск максимального числа шагов на периоде измерения
if(max_motor_cnt < motor_cnt) max_motor_cnt = motor_cnt;
//проверка торможения мотора и при нормальной скорости,
if((max_motor_cnt > MIN_MOTOR_CNT && motor_cnt < max_motor_cnt * MOTOR_DECELERATION_PERCENT / 100) ||
(motor_cnt <= MIN_MOTOR_CNT && last_motor_cnt >= motor_cnt) //и при пониженной скорости (это надо тестировать)
)
{
api_motor_move(STOP_VALVE);
APP_PRINTF(0,"motorStop(): max_motor_cnt = %d, motor_cnt = %d\n", max_motor_cnt, motor_cnt);
}
last_motor_cnt = motor_cnt;
if(motor_cnt == 0)
{
app_bValveStopped = true;
bMotorJustStopped = true;
max_motor_cnt = 0;//каждый раз сбрасываем, так как при понижении напряжения на батарейках это значение может тоже меняться
if(++tmp_cnt >= MAX_CNT_MOTOR_ERROR_DETECTION_S)
{
tmp_cnt = 0;
app_timeout_flag = false;
api_motor_move(STOP_VALVE);
APP_PRINTF(0,"motorStop(): cnt for error\n");
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
api_state_change_to_error(APP_API_MOTOR_ERROR);
}
}
else
{
tmp_cnt = tmp_cnt2 = 0;//сбрасываем эти счетчики при движении
app_bValveStopped = false;
//подсчет номера текущего шага
if( app_motor_direction == OPEN_VALVE)
{
app_curr_motor_cnt -= (int)motor_cnt;
}
else
{
app_curr_motor_cnt += (int)motor_cnt;
}
AppParam.global_motor_cnt += (uint32_t)motor_cnt;
}
}
}//if(app_bValveMustMoving || !app_bValveStopped)
else
{
//здесь точно известно, что стоим
tmp_cnt = 0;
if(bMotorJustStopped)
{
#define MOTOR_TO_STANDBY_CNT_IN_MAIN_CYCLE_TICKS 20 //это примерно пара секунд
if(++tmp_cnt2 > MOTOR_TO_STANDBY_CNT_IN_MAIN_CYCLE_TICKS)
{
tmp_cnt2 = 0;
bMotorJustStopped = false;
motorStandby();//снимаем питание с микросхемы драйвера мотора (~ через пару секунд после команды Стоп)
}
}
}
}
switch(AppState)
{
case APP_SELF_TEST:
if(app_timeout_flag)
{
app_timeout_flag = false;
if(!app_bValveMustMoving && app_bValveStopped)
{
app_button_inactivity_cnt = SUBSTATE_0;
app_timer_stop(led_interval_id);
app_timer_start( led_interval_id, APP_TIMER_TICKS(LED_TICK_MS, APP_TIMER_PRESCALER), NULL );
}
else
{
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
}
}
if( keyb_data.btn_state[BUTTON_CHAN_4_ID].event_release == BTN_EVENT_CAPTURED)
{
buz_add_beep(150,150,BUZ_BEEP_SINGLE);
soft_ind_prg_stage(4, IND_SET);
api_motor_move(CLOSE_VALVE);
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
}
if( keyb_data.btn_state[BUTTON_CHAN_5_ID].event_release == BTN_EVENT_CAPTURED)
{
buz_add_beep(150,150,BUZ_BEEP_SINGLE);
soft_ind_prg_stage(5, IND_SET);
api_motor_move(OPEN_VALVE);
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
}
if( keyb_data.btn_state[BUTTON_CHAN_1_ID].event_release == BTN_EVENT_CAPTURED)
{
buz_add_beep(150,150,BUZ_BEEP_SINGLE);
soft_ind_prg_stage(1, IND_SET);
api_motor_move(STOP_VALVE);
}
if( keyb_data.btn_state[BUTTON_CHAN_2_ID].event_release == BTN_EVENT_CAPTURED)
{
buz_add_beep(150,150,BUZ_BEEP_SINGLE);
soft_ind_prg_stage(2, IND_SET);
ht9b92_display_on();
}
if( keyb_data.btn_state[BUTTON_CHAN_3_ID].event_release == BTN_EVENT_CAPTURED)
{
buz_add_beep(150,150,BUZ_BEEP_SINGLE);
soft_ind_prg_stage(3, IND_SET);
motorStandby();
app_timer_stop( app_timer_id);
ht9b92_display_off();
}
break;
case APP_MASS_FW_UPDATE:
break;
case APP_START:
{
if(app_timeout_flag)
{
app_timeout_flag = false;
if(keyb_data.btn_state[BUTTON_CHAN_4_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].state == BTN_PRESSED &&
AppParam.powerOnCnt <= MAX_POWER_ON_CNT_FOR_MASS_FW_UPDATE)
{
api_state_change(APP_MASS_FW_UPDATE);
break;
}
//делаем здесь, так как к этому моменту конденсатор 100 мкФ на питании микросхемы RTC уже точно зарядится
app_init_rtc();
//очистка всего дисплея
soft_ind_set_all(IND_CLR);
err_code = api_temp_test_proccess();
if(err_code != NRF_SUCCESS)
{
api_state_change_to_error(APP_API_TEMPERATURE_SENSOR_ERROR);
break;
}
err_code = api_battery_test_proccess();
if(err_code != NRF_SUCCESS)
{
ind_state_change(IND_LOW_BATTERY_START);
}
if(keyb_data.btn_state[BUTTON_CHAN_2_ID].state == BTN_PRESSED)
{
api_state_change(APP_SELF_TEST);
}
else
{
soft_ind_UI(IND_SET);
app_timer_start( app_timer_id, APP_TIMER_TICKS(DISPLAY_SYMBOLS_UI_AT_LCD_TICK_MS, APP_TIMER_PRESCALER), NULL );
api_state_change(APP_TEST);
}
}
break;
}
case APP_TEST:
{
if(app_timeout_flag)
{
app_timeout_flag = false;
// api_param_update();//старт мониторинга температуры, напряжения питания (м.б. можно упростить)
#ifdef MY_DEBUG
api_state_change(APP_STAND_BY);//для пропуска калибровки
app_full_motor_cnt = 800;
app_curr_motor_cnt = 0;
APP_PRINTF(0, "app_full_motor_cnt = %d\n", app_full_motor_cnt);
#else
api_state_change(APP_DISPLAY_AdA);
//api_state_change(APP_CLOCK_BLINKING);
if(AppParam.powerOnCnt <= MAX_POWER_ON_CNT_FOR_MASS_FW_UPDATE)
{
AppParam.flash_page_erase_cnt++;
memcpy(¶m, &AppParam, sizeof(param_t) - sizeof(param.CRC));
Param_Save();
}
else
{
//чтобы в дальнейшем при сохранении других параметров этот счетчик оставался максимальным
AppParam.powerOnCnt = MAX_POWER_ON_CNT_FOR_MASS_FW_UPDATE;
}
#endif
}
break;
}
case APP_CLOCK_BLINKING:
if( app_btns_enabled && keyb_data.btn_state[BUTTON_CHAN_2_ID].event_release == BTN_EVENT_CAPTURED)
{
api_state_change(APP_CLOCK_SET_MIN);
break;
}
if( app_btns_enabled && keyb_data.btn_state[BUTTON_CHAN_4_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_4_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS)
{
app_wait_key_release = true;
api_state_change(APP_PAIRING);
break;
}
break;
case APP_CLOCK_SET_MIN:
if(app_btns_enabled)
{
if( keyb_data.btn_state[BUTTON_CHAN_2_ID].event_release == BTN_EVENT_CAPTURED)
{
api_state_change(APP_CLOCK_SET_HOUR);
}
if( keyb_data.btn_state[BUTTON_CHAN_4_ID].event_release == BTN_EVENT_CAPTURED)
{
AppCurrUtcTime -= 60;//уменьшаем на 60 секунд= 1 минуту
}
if( keyb_data.btn_state[BUTTON_CHAN_5_ID].event_release == BTN_EVENT_CAPTURED)
{
AppCurrUtcTime += 60;//добавляем 60 секунд= 1 минуту
}
if( app_btns_enabled && keyb_data.btn_state[BUTTON_CHAN_4_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_4_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS)
{
app_wait_key_release = true;
api_state_change(APP_PAIRING);
break;
}
}
else
{
if(app_full_motor_cnt == 0)//до калибровки
{
api_state_change(APP_CLOCK_BLINKING);
}
else //при редактировании времени из меню программ
{
api_state_change(APP_STAND_BY);
}
}
break;
case APP_CLOCK_SET_HOUR:
if(app_btns_enabled)
{
if( keyb_data.btn_state[BUTTON_CHAN_2_ID].event_release == BTN_EVENT_CAPTURED)
{
api_state_change(APP_CLOCK_SET_DAY);
}
if( keyb_data.btn_state[BUTTON_CHAN_4_ID].event_release == BTN_EVENT_CAPTURED)
{
AppCurrUtcTime -= 3600;//уменьшаем на 1 час
}
if( keyb_data.btn_state[BUTTON_CHAN_5_ID].event_release == BTN_EVENT_CAPTURED)
{
AppCurrUtcTime += 3600;//добавляем 1 час
}
if( app_btns_enabled && keyb_data.btn_state[BUTTON_CHAN_4_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_4_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS)
{
app_wait_key_release = true;
api_state_change(APP_PAIRING);
break;
}
}
else
{
if(app_full_motor_cnt == 0)//до калибровки
{
api_state_change(APP_CLOCK_BLINKING);
}
else //при редактировании времени из меню программ
{
api_state_change(APP_STAND_BY);
}
}
break;
case APP_CLOCK_SET_DAY:
if(app_btns_enabled)
{
if( keyb_data.btn_state[BUTTON_CHAN_2_ID].event_release == BTN_EVENT_CAPTURED)
{
//здесь полностью закончили редактирование => сохраняем время
api_set_calendar_data(&AppCurrUtcTime, &AppCurrUtcShiftTime);
if(app_full_motor_cnt == 0)//до калибровки
{
api_state_change(APP_DISPLAY_AdA);
}
else //при редактировании времени из меню программ
{
api_state_change(APP_SELECT_PROGRAM);
}
}
if( keyb_data.btn_state[BUTTON_CHAN_4_ID].event_release == BTN_EVENT_CAPTURED)
{
AppCurrUtcTime -= (3600*24);//уменьшаем на 1 день
}
if( keyb_data.btn_state[BUTTON_CHAN_5_ID].event_release == BTN_EVENT_CAPTURED)
{
AppCurrUtcTime += (3600*24);//добавляем 1 день
}
if( app_btns_enabled && keyb_data.btn_state[BUTTON_CHAN_4_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_4_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS)
{
app_wait_key_release = true;
api_state_change(APP_PAIRING);
break;
}
}
else
{
if(app_full_motor_cnt == 0)//до калибровки
{
api_state_change(APP_CLOCK_BLINKING);
}
else //при редактировании времени из меню программ
{
api_state_change(APP_STAND_BY);
}
}
break;
case APP_DISPLAY_AdA:
if(app_btns_enabled && keyb_data.btn_state[BUTTON_CHAN_2_ID].event_release == BTN_EVENT_CAPTURED)
{
api_state_change(APP_DISPLAY_AdA_1_2);
}
if( app_btns_enabled && keyb_data.btn_state[BUTTON_CHAN_4_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_4_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS)
{
app_wait_key_release = true;
api_state_change(APP_PAIRING);
break;
}
break;
case APP_DISPLAY_AdA_1_2:
if(app_timeout_flag)
{
// static volatile int sum_motor_cnt = 0, motor_cnt_AdA1, motor_cnt_AdA2;
app_timeout_flag = false;
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
switch(AppSubstate)
{
case SUBSTATE_0://начальное небольшое призакрытие клапана AdA 1
{
static uint8_t sub0_cnt = 0;//счетчик периодов контроля мотора MOTOR_CONTROL_TICK_MS
if(motor_cnt == 0 || (++sub0_cnt > MAX_SUB0_ADA1_CNT))
{
//клапан немного призакрылся, это чтобы отошел от жесткого края если был полностью открыт (чтобы не ломать шестеренки)
sub0_cnt = 0;
api_motor_move(OPEN_VALVE);
app_curr_motor_cnt = 0;//для тестирования (хотя можно и оставить, это не будет мешать)
sum_motor_cnt = 0;
AppSubstate++;
}
}
break;
case SUBSTATE_1://начальное небольшое призакрытие клапана AdA 1
if(motor_cnt == 0)
{
//клапан полностью открылся
api_motor_move(CLOSE_VALVE);
app_curr_motor_cnt = 0;//для тестирования (хотя можно и оставить, это не будет мешать)
sum_motor_cnt = 0;
AppSubstate++;
}
break;
case SUBSTATE_2://полное закрытие клапана AdA 1
if(motor_cnt == 0)
{
//клапан полностью закрылся
motor_cnt_AdA1 = sum_motor_cnt;
sum_motor_cnt = 0;
if(motor_cnt_AdA1 < MOTOR_CNT_FOR_ERROR)
{
api_motor_move(STOP_VALVE);
api_state_change_to_error(APP_API_MOTOR_ERROR);
}
else
{
ind_state_change(IND_DISPLAY_AdA_2);
api_motor_move(OPEN_VALVE);
AppSubstate++;
}
}
else
{
sum_motor_cnt += motor_cnt;
if(sum_motor_cnt > MAX_THRESHOLD_FOR_FULL_MOTOR_CNT)
{
api_state_change_to_error(APP_API_MOTOR_ERROR);
}
}
break;
case SUBSTATE_3://полное открытие клапана AdA 2
if(motor_cnt == 0)
{
//клапан полностью открылся
api_motor_move(STOP_VALVE);
app_timer_stop(app_timer_id);
motor_cnt_AdA2 = sum_motor_cnt;
APP_PRINTF(0, "motor_cnt_AdA1 = %d, motor_cnt_AdA2 = %d\n", motor_cnt_AdA1, motor_cnt_AdA2);
//если разница счета в прямом и обратном направлении сильно отличается,
// то это тоже какая-то неисправность и ее надо индицировать
if(abs(motor_cnt_AdA2 - motor_cnt_AdA1) > MOTOR_CNT_FOR_ERROR)
{
api_state_change_to_error(APP_API_MOTOR_ERROR);
}
else
{
//берем меньшее для более надежной работы (чтобы точно мотор доходил до выбранного положения закрыто)
if(motor_cnt_AdA1 < motor_cnt_AdA2) app_full_motor_cnt = motor_cnt_AdA1;
else app_full_motor_cnt = motor_cnt_AdA2;
//app_full_motor_cnt = (motor_cnt_AdA1 + motor_cnt_AdA2) / 2;
app_curr_motor_cnt = 0;//обязательное обнуление счетчика (происходит в конце калибровки)
APP_PRINTF(0, "app_full_motor_cnt = %d\n", app_full_motor_cnt);
if( appFlagForceCalibration )
{
appFlagForceCalibration = false;
api_state_change(appPrevStateAtForceCalibration);//успешное завершение доп.калибровки при сбоях мотора
}
else
{
#if 0
//для зацикливания калибровки - для теста
app_timer_stop(app_timer_id);
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
api_motor_move(CLOSE_VALVE);
AppSubstate = SUBSTATE_0;
ind_state_change(IND_DISPLAY_AdA_1);
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: >>APP_DISPLAY_AdA_1_2\r\n"RTT_CTRL_RESET);
#endif
api_state_change(APP_STAND_BY);//успешное завершение обычной калибровки
}
}
}
else
{
sum_motor_cnt += motor_cnt;
if(sum_motor_cnt > MAX_THRESHOLD_FOR_FULL_MOTOR_CNT)
{
api_motor_move(STOP_VALVE);
app_timer_stop(app_timer_id);
api_state_change_to_error(APP_API_MOTOR_ERROR);
}
}
break;
}
}
break;
case APP_STAND_BY:// state machine
{
if(app_timeout_flag)
{
app_timeout_flag = false;
//если должны двигаться или еще не остановились до полного останова после команды Стоп
if(app_bValveMustMoving || !app_bValveStopped)
{
//Алгоритм регулировки температуры
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
APP_PRINTF(0, "app_target_motor_cnt = %d, app_curr_motor_cnt = %d\n", app_target_motor_cnt, app_curr_motor_cnt);
//если достигли пределов или целевого счетчика (шага)
if(
((app_curr_motor_cnt >= app_full_motor_cnt - MOTOR_CNT_EDGE_SHIFT ||
app_curr_motor_cnt >= app_target_motor_cnt) && app_motor_direction == CLOSE_VALVE) ||
((app_curr_motor_cnt <= MOTOR_CNT_EDGE_SHIFT || app_curr_motor_cnt <= app_target_motor_cnt)
&& app_motor_direction == OPEN_VALVE) ||
(app_bValveMustMoving == false && AppSubstate != SUBSTATE_2)//или останавливаемся по событию торможения мотора (но не в SUBSTATE_2) - это нужно, чтобы присвоить AppSubstate = SUBSTATE_0
)
{
api_motor_move(STOP_VALVE);
APP_PRINTF(0,"motorStop(): at APP_STAND_BY\n");
AppSubstate = SUBSTATE_0;//состояние: команда остановиться
}
}
else
{ //полностью остановились
if(AppSubstate == SUBSTATE_0)
{
if(
((app_curr_motor_cnt >= app_full_motor_cnt - MOTOR_CNT_EDGE_SHIFT ||
app_curr_motor_cnt >= app_target_motor_cnt) && app_motor_direction == CLOSE_VALVE) ||
((app_curr_motor_cnt <= MOTOR_CNT_EDGE_SHIFT || app_curr_motor_cnt <= app_target_motor_cnt)
&& app_motor_direction == OPEN_VALVE)
)
{
AppSubstate = SUBSTATE_1;//ждем когда снова можно управлять мотором
app_start_timer_for_next_motor_regulation();
}
else
{
appStartForceCalibration();
break;
}
}
else if(AppSubstate == SUBSTATE_1)
{
app_start_motor_regulation();
}
else if(AppSubstate == SUBSTATE_2)
{
APP_PRINTF(0, "last app_curr_motor_cnt after stop and before next state = %d\n", app_curr_motor_cnt);
api_state_change(app_nextstate);
}
}
}
// api_temp_process();
if(app_btns_enabled && !app_wait_key_release)
{
if( keyb_data.btn_state[BUTTON_CHAN_1_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_3_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_1_ID].pressed_time_ms >= APP_UNPAIRING_STATE_ENTER_TIME_MS &&
keyb_data.btn_state[BUTTON_CHAN_3_ID].pressed_time_ms >= APP_UNPAIRING_STATE_ENTER_TIME_MS)
{
app_wait_key_release = true;
app_state_change_after_stop(APP_UNPAIRING);
break;
}
if( keyb_data.btn_state[BUTTON_CHAN_1_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_3_ID].state != BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_1_ID].pressed_time_ms >= APP_ENTER_PROGRAMMING_MENU_TIME_MS
)
{
app_wait_key_release = true;
app_state_change_after_stop(APP_SELECT_PROGRAM);
break;
}
if( keyb_data.btn_state[BUTTON_CHAN_2_ID].event_release == BTN_EVENT_CAPTURED)//Ok, Boost
{
app_state_change_after_stop(APP_BOOST);
}
if( keyb_data.btn_state[BUTTON_CHAN_3_ID].event_release == BTN_EVENT_CAPTURED)
{
api_lock_calendar(!AppLockCalendarState);
// app_state_change_after_stop(APP_STAND_BY);
}
if( keyb_data.btn_state[BUTTON_CHAN_4_ID].event_release == BTN_EVENT_CAPTURED)
{
if(AppTargetTemperature > MIN_REG_TEMPERATURE)
{
AppTargetTemperature -= TEMPERATURE_REG_STEP;
}
soft_ind_target_temp(AppTargetTemperature, IND_SET);
//com_master_prior_proccess(EVT_BTN_PRESSED(BUTTON_CHAN_2_ID, BTN_CLICK_SINGLE));
AppParam.TargetTemperature = AppTargetTemperature;
}
if( keyb_data.btn_state[BUTTON_CHAN_5_ID].event_release == BTN_EVENT_CAPTURED)
{
if(AppTargetTemperature < MAX_REG_TEMPERATURE)
{
AppTargetTemperature += TEMPERATURE_REG_STEP;
}
soft_ind_target_temp(AppTargetTemperature, IND_SET);
//com_master_prior_proccess(EVT_BTN_PRESSED(BUTTON_CHAN_2_ID, BTN_CLICK_SINGLE));
AppParam.TargetTemperature = AppTargetTemperature;
}
if( keyb_data.btn_state[BUTTON_CHAN_4_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_4_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].pressed_time_ms >= APP_PAIRING_STATE_ENTER_TIME_MS)
{
app_wait_key_release = true;
app_state_change_after_stop(APP_PAIRING);
break;
}
if( keyb_data.btn_state[BUTTON_CHAN_4_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].state != BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_4_ID].pressed_time_ms >= APP_OP_OFF_STATE_ENTER_TIME_MS)
{
app_wait_key_release = true;
app_state_change_after_stop(APP_OFF_STATE);
}
if( keyb_data.btn_state[BUTTON_CHAN_5_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_4_ID].state != BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].pressed_time_ms >= APP_OP_OFF_STATE_ENTER_TIME_MS)
{
app_wait_key_release = true;
app_state_change_after_stop(APP_OP_STATE);
}
}//if(app_btns_enabled && !app_wait_key_release)
app_check_tasks_availability();
//если температура стала <= критической Т вентиляции и режим вентиляции разрешен
if(AppCurrTemp <= AppParam.A2_T_ventilation && AppParam.VentilationState)
{
app_state_change_after_stop(APP_VENTILATION);
}
break;
}//case APP_STAND_BY:
// case APP_NO_CALENDAR_TASKS:
// //3 секунды моргаем знаком auto
// if(AppCountdownSeconds == 0)
// {
// AppParam.LockCalendarState = AppLockCalendarState = LOCK_CALENDAR_STATE;
// api_state_change(AppStatePrevious);
// //api_state_change(APP_STAND_BY);
// api_save_params_with_delay();//emulation of 5s noactions for saving of params
// }
// break;
case APP_VENTILATION:
{
if(app_timeout_flag)
{
app_timeout_flag = false;
//если должны двигаться или еще не остановились до полного останова после команды Стоп
if(app_bValveMustMoving || !app_bValveStopped)
{
//Алгоритм регулировки температуры
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
APP_PRINTF(0, "app_target_motor_cnt = %d, app_curr_motor_cnt = %d\n", app_target_motor_cnt, app_curr_motor_cnt);
//если достигли пределов или целевого счетчика (шага)
if(
((app_curr_motor_cnt >= app_full_motor_cnt - MOTOR_CNT_EDGE_SHIFT ||
app_curr_motor_cnt >= app_target_motor_cnt) && app_motor_direction == CLOSE_VALVE) ||
((app_curr_motor_cnt <= MOTOR_CNT_EDGE_SHIFT || app_curr_motor_cnt <= app_target_motor_cnt)
&& app_motor_direction == OPEN_VALVE) ||
(app_bValveMustMoving == false && AppSubstate != SUBSTATE_2)//или останавливаемся по событию торможения мотора - - это нужно, чтобы присвоить AppSubstate = SUBSTATE_0
)
{
api_motor_move(STOP_VALVE);
APP_PRINTF(0,"motorStop(): at APP_VENTILATION\n");
AppSubstate = SUBSTATE_0;//состояние: команда остановиться
}
}
else
{ //полностью остановились
if(AppSubstate == SUBSTATE_2)
{
APP_PRINTF(0, "last app_curr_motor_cnt after stop and before next state = %d\n", app_curr_motor_cnt);
api_state_change(app_nextstate);
}
}
}
if(AppCurrTemp >= AppParam.A2_T_ventilation + AppParam.A5_T_hysteresis)
{
app_state_change_after_stop(APP_STAND_BY);
}
if(app_btns_enabled && !app_wait_key_release)
{
if( keyb_data.btn_state[BUTTON_CHAN_1_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_1_ID].pressed_time_ms >= APP_ENTER_PROGRAMMING_MENU_TIME_MS)
{
app_wait_key_release = true;
app_state_change_after_stop(APP_SELECT_PROGRAM);
}
if( keyb_data.btn_state[BUTTON_CHAN_3_ID].event_release == BTN_EVENT_CAPTURED)
{
api_lock_calendar(!AppLockCalendarState);
}
}
app_check_tasks_availability();
break;
}
case APP_SELECT_PROGRAM:
{
if(app_btns_enabled && !app_wait_key_release)
{
if( keyb_data.btn_state[BUTTON_CHAN_1_ID].event_release == BTN_EVENT_CAPTURED)
{
if(++AppSubstate > SUBSTATE_1) AppSubstate = SUBSTATE_0;
ind_state_change((ind_state_t)(IND_PROGRAM_ADVANCED/*IND_PROGRAM_P0*/ + AppSubstate));
}
if( keyb_data.btn_state[BUTTON_CHAN_2_ID].event_release == BTN_EVENT_CAPTURED)
{
switch(AppSubstate)
{
case SUBSTATE_0://advanced
api_state_change(APP_ADVANCED_MENU);
break;
case SUBSTATE_1://clock
api_state_change(APP_CLOCK_SET_MIN);
break;
}
}
}
break;
}
case APP_PARAMS_RESET:
if(app_timeout_flag)
{
app_timeout_flag = false;
api_state_change(APP_STAND_BY);
}
break;
case APP_ADVANCED_MENU:
{
if(app_btns_enabled && !app_wait_key_release)
{
if( keyb_data.btn_state[BUTTON_CHAN_2_ID].event_release == BTN_EVENT_CAPTURED)
{
AppSubstate++;
if(AppSubstate > SUBSTATE_5)
{
if(app_param_reset_val == DEFAULT_PARAM_RESET_DO)
{
//advanced params reset
api_state_change(APP_PARAMS_RESET);
}
else
{
if(app_param_calibration_val == APP_CALIBRATION_ON) //ВКЛ калибровки
{
api_state_change(APP_DISPLAY_AdA_1_2);
app_param_calibration_val = APP_CALIBRATION_OFF;//ВЫКЛ калибровки (по умолчанию)
}
else
{
api_state_change(APP_STAND_BY);
}
}
}
else
{
ind_state_change(IND_ADVANCED_MENU);
}
}
if( keyb_data.btn_state[BUTTON_CHAN_4_ID].event_release == BTN_EVENT_CAPTURED)
{
switch(AppSubstate)
{
case SUBSTATE_0:
if(AppParam.A1_T_shift > MIN_PARAM_T_SHIFT)
{
AppParam.A1_T_shift -= TEMPERATURE_REG_STEP;
AppCurrTemp -= TEMPERATURE_REG_STEP;
soft_ind_target_temp(AppParam.A1_T_shift, IND_SET);//коррекция текущей температуры
}
break;
case SUBSTATE_1:
if(AppParam.A2_T_ventilation > MIN_PARAM_T_VENTILATION)
{
AppParam.A2_T_ventilation -= REG_STEP_PARAM_T_VENTILATION;
}
else
{
AppParam.VentilationState = false;
}
break;
// case SUBSTATE_2:
// if(AppParam.A2_time_ventilation > MIN_PARAM_TIME_VENTILATION)
// {
// AppParam.A2_time_ventilation -= REG_STEP_PARAM_TIME_VENTILATION;
// }
// break;
case SUBSTATE_2:
app_param_calibration_val ^= 1;
soft_ind_target_temp(app_param_calibration_val, IND_SET);
display_write_symbol_data(SYMB_T27_SET_GRAD_C, IND_CLR);
break;
case SUBSTATE_3:
if(AppParam.A4_boost > MIN_PARAM_BOOST)
{
AppParam.A4_boost -= REG_STEP_PARAM_BOOST;
}
soft_ind_countdown(AppParam.A4_boost, IND_SET);
break;
case SUBSTATE_4:
if(AppParam.A5_T_hysteresis > MIN_PARAM_T_HYSTERESIS)
{
AppParam.A5_T_hysteresis -= REG_STEP_PARAM_T_HYSTERESIS;
}
soft_ind_target_temp(AppParam.A5_T_hysteresis, IND_SET);
break;
case SUBSTATE_5:
app_param_reset_val = DEFAULT_PARAM_RESET_DO;
soft_ind_target_temp(DEFAULT_PARAM_RESET_DO, IND_SET);
display_write_symbol_data(SYMB_T27_SET_GRAD_C, IND_CLR);
break;
}
}
if( keyb_data.btn_state[BUTTON_CHAN_5_ID].event_release == BTN_EVENT_CAPTURED)
{
switch(AppSubstate)
{
case SUBSTATE_0:
if(AppParam.A1_T_shift < MAX_PARAM_T_SHIFT)
{
AppParam.A1_T_shift += TEMPERATURE_REG_STEP;
AppCurrTemp += TEMPERATURE_REG_STEP;
soft_ind_target_temp(AppParam.A1_T_shift, IND_SET);//коррекция текущей температуры
}
break;
case SUBSTATE_1:
if(AppParam.A2_T_ventilation < MAX_PARAM_T_VENTILATION)
{
if(AppParam.VentilationState)
{
AppParam.A2_T_ventilation += REG_STEP_PARAM_T_VENTILATION;
}
else
{
AppParam.VentilationState = true;
}
}
break;
// case SUBSTATE_2:
// if(AppParam.A2_time_ventilation < MAX_PARAM_TIME_VENTILATION)
// {
// AppParam.A2_time_ventilation += REG_STEP_PARAM_TIME_VENTILATION;
// }
// break;
case SUBSTATE_2:
app_param_calibration_val ^= 1;
soft_ind_target_temp(app_param_calibration_val, IND_SET);
display_write_symbol_data(SYMB_T27_SET_GRAD_C, IND_CLR);
break;
case SUBSTATE_3:
if(AppParam.A4_boost < MAX_PARAM_BOOST)
{
AppParam.A4_boost += REG_STEP_PARAM_BOOST;
}
soft_ind_countdown(AppParam.A4_boost, IND_SET);
break;
case SUBSTATE_4:
if(AppParam.A5_T_hysteresis < MAX_PARAM_T_HYSTERESIS)
{
AppParam.A5_T_hysteresis += REG_STEP_PARAM_T_HYSTERESIS;
}
soft_ind_target_temp(AppParam.A5_T_hysteresis, IND_SET);
break;
case SUBSTATE_5:
app_param_reset_val = DEFAULT_PARAM_RESET;
soft_ind_target_temp(DEFAULT_PARAM_RESET, IND_SET);
display_write_symbol_data(SYMB_T27_SET_GRAD_C, IND_CLR);
break;
}
}
}
break;
}
case APP_OP_STATE:
case APP_OFF_STATE:
{
if(app_timeout_flag)
{
app_timeout_flag = false;
//если должны двигаться или еще не остановились до полного останова после команды Стоп
if(app_bValveMustMoving || !app_bValveStopped)
{
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
APP_PRINTF(0, "app_curr_motor_cnt = %d\n", app_curr_motor_cnt);
//если достигли пределов или целевого счетчика (шага)
if(
(((app_curr_motor_cnt >= app_full_motor_cnt - MOTOR_CNT_EDGE_SHIFT ) && app_motor_direction == CLOSE_VALVE) ||
((app_curr_motor_cnt <= MOTOR_CNT_EDGE_SHIFT ) && app_motor_direction == OPEN_VALVE)) && (AppSubstate != SUBSTATE_2)
)
{ //а по событию торможения мотора эта же функция уже была вызвана ранее, поэтому еще раз ее не надо вызывать
api_motor_move(STOP_VALVE);
APP_PRINTF(0,"motorStop(): at APP_OP_STATE or APP_OFF_STATE\n");
}
}
else
{ //полностью остановились
if(AppSubstate == SUBSTATE_2)
{
APP_PRINTF(0, "last app_curr_motor_cnt after stop and before next state = %d\n", app_curr_motor_cnt);
api_state_change(app_nextstate);
}
else
{
//Если команда не выполнилась
if(
!(((app_curr_motor_cnt >= app_full_motor_cnt - MOTOR_CNT_EDGE_SHIFT ) && app_motor_direction == CLOSE_VALVE) ||
((app_curr_motor_cnt <= MOTOR_CNT_EDGE_SHIFT ) && app_motor_direction == OPEN_VALVE))
)
{
appStartForceCalibration();
break;
}
}
#ifdef CTR_LIFE_TEST
if(AppState == APP_OP_STATE)
{
api_state_change(APP_OFF_STATE);
AppParam.ctrLifeTestCnt++;
APP_PRINTF(0, "AppParam.ctrLifeTestCnt = %d\n", AppParam.ctrLifeTestCnt);
memcpy(¶m, &AppParam, sizeof(param_t) - sizeof(param.CRC));
Param_Save();
}
else
{
api_state_change(APP_OP_STATE);
}
soft_ind_current_temp(AppParam.ctrLifeTestCnt / 100, IND_SET);
soft_ind_target_temp (AppParam.ctrLifeTestCnt % 100, IND_SET);
#endif
}
}
if(app_btns_enabled && !app_wait_key_release)
{
if(AppState == APP_OP_STATE)
{
if( keyb_data.btn_state[BUTTON_CHAN_5_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_5_ID].pressed_time_ms >= APP_OP_OFF_STATE_ENTER_TIME_MS)
{
app_wait_key_release = true;
app_state_change_after_stop(APP_STAND_BY);//возвращаемся всегда в нормальный режим
}
}
else
{
if( keyb_data.btn_state[BUTTON_CHAN_4_ID].state == BTN_PRESSED &&
keyb_data.btn_state[BUTTON_CHAN_4_ID].pressed_time_ms >= APP_OP_OFF_STATE_ENTER_TIME_MS)
{
app_wait_key_release = true;
app_state_change_after_stop(APP_STAND_BY);//возвращаемся всегда в нормальный режим
}
}
if( keyb_data.btn_state[BUTTON_CHAN_3_ID].event_release == BTN_EVENT_CAPTURED)
{
api_lock_calendar(!AppLockCalendarState);
}
}
app_check_tasks_availability();
break;
}
case APP_BOOST:// state machine
{
if(app_timeout_flag)
{
app_timeout_flag = false;
//если должны двигаться или еще не остановились до полного останова после команды Стоп
if(app_bValveMustMoving || !app_bValveStopped)
{
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
APP_PRINTF(0, "app_curr_motor_cnt = %d\n", app_curr_motor_cnt);
//если достигли предела
if( app_curr_motor_cnt <= MOTOR_CNT_EDGE_SHIFT && AppSubstate != SUBSTATE_2)
{ //а по событию торможения мотора эта же функция уже была вызвана ранее, поэтому еще раз ее не надо вызывать
api_motor_move(STOP_VALVE);
APP_PRINTF(0,"motorStop(): at APP_BOOST\n");
}
}
else
{ //полностью остановились
app_in_program_transition = false;//переходное состояние завершилось
if(AppSubstate == SUBSTATE_2)
{
APP_PRINTF(0, "last app_curr_motor_cnt after stop and before next state = %d\n", app_curr_motor_cnt);
api_state_change(app_nextstate);
}
else
{
//если правильно дошли
if( app_curr_motor_cnt <= MOTOR_CNT_EDGE_SHIFT )
{
//начинаем обратный отсчет (сюда попадем даже если при старте уже стояли в полностью открытом состоянии, т.к. таймер всегда стартует)
ind_state_change(IND_MOTOR_BOOST_COUNTDOWN);
}
else
{
appStartForceCalibration();
break;
}
}
}
}
if(app_bValveStopped && !app_bValveMustMoving)
{
if(AppCountdownSeconds == 0)
{
//api_state_change(APP_STAND_BY);
api_state_change(AppStatePrevious);// теперь возвращаемся в предыдущее до BOOST состояние
break;
}
}
if(app_btns_enabled)
{
//выход из boost по отжатию любой кнопки
if( keyb_data.btn_state[BUTTON_CHAN_1_ID].event_release + keyb_data.btn_state[BUTTON_CHAN_2_ID].event_release
+ keyb_data.btn_state[BUTTON_CHAN_3_ID].event_release + keyb_data.btn_state[BUTTON_CHAN_4_ID].event_release
+ keyb_data.btn_state[BUTTON_CHAN_5_ID].event_release >= BTN_EVENT_CAPTURED)
{
app_state_change_after_stop(APP_STAND_BY);
}
}
app_check_tasks_availability();
break;
}
case APP_ERROR:// state machine
{
if(app_btns_enabled && (AppSubstate == APP_API_TEMPERATURE_SENSOR_ERROR))
{
//выход из ошибки Е5 по долгому (3s) нажатию b1
if( keyb_data.btn_state[BUTTON_CHAN_1_ID].pressed_time_ms >= APP_ERROR_TEMPERATURE_RESET_TIME_MS)
{
app_wait_key_release = true;
app_state_change_after_stop(APP_STAND_BY);
}
}
break;
}
case APP_PAIRING:
{
if( app_timeout_flag == true )//таймаут перинга
{
app_timeout_flag = false;
api_state_change(APP_PAIRING_ERROR);
}
break;
}
case APP_PAIRING_SUCCESS:
// api_state_change(APP_STAND_BY);
api_state_change(AppStatePrevious);
break;
case APP_PAIRING_ERROR:
if( app_timeout_flag == true )//время вывода 00
{
app_timeout_flag = false;
api_state_change(AppStatePrevious);
//api_state_change(APP_STAND_BY);
}
break;
#ifdef NRF51
case APP_SLAVE_FW_UPDATE:
{
if( app_timeout_flag == true ) //таймаут обновления прошивки
{
api_state_change(APP_STAND_BY);
}
break;
}
#endif
case APP_UNPAIRING:
{
if( app_timeout_flag == true )//время вывода []
{
app_timeout_flag = false;
api_state_change(APP_STAND_BY);
}
break;
}
default:
{
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_YELLOW"[APPL]: default\r\n"RTT_CTRL_RESET);
api_state_change(APP_STAND_BY);
break;
}
}
return;
}
/********************************************************************************
*
********************************************************************************/
/********************************************************************************
*
********************************************************************************/
/**
* описание в application_api.h
*/
uint16_t api_get_version(void)
{
return FIRMWARE_VER;
}
/***************************COMMON************************************/
void api_state_change(app_state_t new_state)
{
AppState = new_state;
application_init_prew_state( AppState );
application_init_new_state( AppState );
api_ind_connected(app_ble_connected);//всегда выводим состояние коннекта по BLE
}
bool api_state_check(app_state_t state)
{
if(AppState == state)
{
return true;
}
else
{
return false;
}
}
uint8_t api_get_state(void)
{
if(app_in_program_transition) return APP_API_IN_PROGRAM_TRANSITION;
switch(AppState)
{
#ifdef NRF51
case APP_SLAVE_FW_UPDATE:
{
return APP_API_FW_UPDATE_STATE;
}
#endif
case APP_MASS_FW_UPDATE:
return APP_API_MASS_FW_UPDATE;
case APP_DISPLAY_AdA:
return APP_API_NO_CALIBRATION;
case APP_STAND_BY:
case APP_BOOST: //открытие на полную на определенное время
case APP_OP_STATE: //открытие на полную
case APP_OFF_STATE: //полное закрытие
return APP_API_NORMAL_STATE;
case APP_CLOCK_BLINKING:
case APP_CLOCK_SET_MIN:
case APP_CLOCK_SET_HOUR:
case APP_CLOCK_SET_DAY:
case APP_SELECT_PROGRAM://выбор программы
case APP_ADVANCED_MENU://инженерное меню
return APP_API_MAINTENANCE_STATE;
case APP_DISPLAY_AdA_1_2:
return APP_API_ADA_CALIBRATION_STATE;
case APP_VENTILATION:
return APP_API_VENTILATION_STATE;
case APP_PAIRING:
return APP_API_PAIRING_STATE;
case APP_ERROR:
return APP_API_ERROR_STATE;
/* Стейт подменяется на APP_API_NORMAL_STATE */
//case APP_NO_CALENDAR_TASKS:
case APP_PAIRING_SUCCESS:
//case APP_PAIRING_ERROR: Приложение не может видеть эти состояния
//case APP_UNPAIRING:
case APP_PARAMS_RESET:
return APP_API_NORMAL_STATE;
default:
return APP_API_OTHER_STATE;
}
}
uint32_t api_temp_test_proccess(void)
{
if(AppCurrTemp>= MIN_TEMPERATURE && AppCurrTemp <= MAX_TEMPERATURE)
{
return NRF_SUCCESS;
}
return NRF_ERROR_INVALID_STATE;
}
uint32_t api_battery_test_proccess(void)
{
uint32_t err_code;
err_code = batt_nAA_read_charge_val(&app_battery_charge );
APP_ERROR_CHECK(err_code);
if(app_battery_charge > MIN_BATTERY_CHARGE_LEVEL)
{
return NRF_SUCCESS;
}
return NRF_ERROR_INVALID_STATE;
}
/***************************PERIPHERAL************************************/
/**
* описание в application_api.h
*/
uint8_t api_get_battery_charge(void)
{
return app_battery_charge;
}
/**
* описание в application_api.h
*/
int16_t api_get_temp(void)
{
return FloatToSFloat16(AppCurrTemp);
}
uint8_t api_get_error(void)
{
uint8_t err;
switch(AppState)
{
case APP_ERROR:
err = AppSubstate;
break;
default:
err = APP_API_NO_ERROR;
break;
}
return (err | (AppBoolLowBattery ? APP_API_LOW_BATTERY_WARNING : 0));
}
/**
* описание в application_api.h
*/
//void api_master_event_proccess(uint8_t index)
//{
// com_master_proccess(EVT_BTN_PRESSED(index, BTN_CLICK_SINGLE));
//}
#ifdef NRF51
uint8_t api_enter_fw_update_state(void)
{
if((AppState == APP_STAND_BY)
||( AppState == APP_SLAVE_FW_UPDATE))
{
api_state_change(APP_SLAVE_FW_UPDATE);
return 1;
}
return 0;
}
void api_fw_update_proc(void)
{
uint32_t err_code;
if( AppState == APP_SLAVE_FW_UPDATE )
{
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 );
}
}
#endif
/********************************************************************************/
/***************************CENTRAL************************************/
/********************************************************************************/
static void api_master_set_default( uint8_t tok_id, cm_dev_support_ac_t * required_ac )
{
mct_transaction_t * tran = NULL;
mct_link_t link;
memset(&link, 0, sizeof(mct_link_t));
mct_searchp_context_t search;
memset(&search, 0, sizeof(mct_searchp_context_t));
bool start_support = false;
bool stop_support = false;
uint32_t err_code;
APP_PRINTF(0, RTT_CTRL_TEXT_BRIGHT_GREEN"[APPL]: api_master_pairing_complete\r\n"RTT_CTRL_RESET);
// Тип транзакции READ_WRITE не рекомендуется использовать, если не будет гарантированной записи
// иначе пока транзакция не будет закрыта остальные транзакции не возможны
err_code = mct_open_transaction(&tran, TRAN_READ_WRITE, TRAN_COMMON_PERM);
if (err_code != NRF_ERROR_BUSY){
APP_ERROR_CHECK(err_code);
bool defaultAllowFl = true;
// Чтобы пэйринг по дефолту был доступен 5 раз нужно раскоментировать код ниже
// search.params.filter.event_mask = EVENT_CODE_MASK; // Также будем искать по коду события
// search.params.values.event = BTN_EVENT_CODE<<EVENT_CODE_Pos; // Только события нажатия кнопки
//
// uint8_t evt_cnt = 0;
// while (mct_find_link(tran, &search, &link) == NRF_SUCCESS){ // События найдено
// if (link.event & ((1ul<<PairButtonId)<<(BTN_EVT_MASK_Pos))) {
// evt_cnt++;
// if (evt_cnt > 5) { // 5 - количество событий, позволенных для кнопки PairButtonId
// defaultAllowFl = false; // Тогда события не нужны
// break;
// }
// }
// }
if (required_ac->is_support[0]){
start_support = true;
}
if (required_ac->is_support[1]){
stop_support = true;
}
if(defaultAllowFl && (stop_support || start_support))
{
link.tok_id = tok_id;
if (stop_support) {
link.event = EVT_REGULATOR(EVT_REG_TEMPERATURE, EVT_REG_OFF);
link.ac = AC_EXTENTION_STOP_CMD;
link.data[0]=0xFF;
link.data[1]=0xFF;
link.enable = 0x01;
link.perm = MCT_PERM(MCT_PERM_ANY_USER, MCT_PERM_ANY_USER); // полный доступ
// Рекомендуется создавать события с ограничениями НЕ ПРИ ПРЯМОМ ПЭЙРИНГЕ
// поскольку при анпейринге такие события не будут удалены.
//
// Событие, которое может быть прочитано только устройством
//link.perm = MCT_PERM(MCT_PERM_ONLY_MASTER, MCT_PERM_ANY_USER);
// Событие, которое может быть изменено только устройством
//link.perm = MCT_PERM(MCT_PERM_ANY_USER, MCT_PERM_ONLY_MASTER);
// Событие, которое не может быть изменено и не может быть прочитано через приложение
//link.perm = MCT_PERM(MCT_PERM_ONLY_MASTER, MCT_PERM_ONLY_MASTER);
link.uuid = 0;
err_code = mct_set_link(tran, &link);
if (err_code != NRF_ERROR_NO_MEM)
APP_ERROR_CHECK(err_code);
}
if (start_support){
link.event = EVT_REGULATOR(EVT_REG_TEMPERATURE, EVT_REG_MUCH_MORE);
link.ac = AC_EXTENTION_START_CMD;
link.data[0]=0xFF;
link.data[1]=0xFF;
link.enable = 0x01;
link.perm = MCT_PERM(MCT_PERM_ANY_USER, MCT_PERM_ANY_USER); // полный доступ
link.uuid = 0;
err_code = mct_set_link(tran, &link);
if (err_code != NRF_ERROR_NO_MEM)
APP_ERROR_CHECK(err_code);
}
}
err_code = mct_commit(&tran);
APP_ERROR_CHECK(err_code);
// Уведомляем модули о обновлении mct
com_slave_mct_notify();
// api_param_update();
}
}
static void api_master_evt_handler(com_master_evt_t * p_evt) {
switch (p_evt->id) {
case COM_MASTER_PAIRING_SUCCESS:{
api_state_change(APP_STAND_BY);
break;
}
case COM_MASTER_PAIRING_ERROR: {
APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_GREEN"[APPL]: api_master_pairing_error\r\n"RTT_CTRL_RESET);
api_state_change(APP_STAND_BY);
break;
}
case COM_MASTER_DEFAULT_CONFIG_REQUIRED:{
api_master_set_default(
p_evt->param.default_config_required.tok_id,
p_evt->param.default_config_required.p_required_ac
);
break;
}
}
}
#ifdef NRF51
uint8_t api_go_to_stand_by(void)
{
if(( AppState != APP_STAND_BY )
&& ( AppState != APP_PAIRING )
&& ( AppState != APP_SLAVE_FW_UPDATE )
)
{
return 0;
}
if( AppState == APP_PAIRING
|| AppState == APP_SLAVE_FW_UPDATE
)
{
api_state_change(APP_STAND_BY);
}
return 1;
}
#endif
//void api_temp_process(void)
//{
// for(uint8_t i=0; i<TEMPERATURE_TRIGGER_MAX_COUNT;i++)
// {
// temp_event_state_read(&app_temp_trigger[i]);
//
// if(app_temp_trigger[i].temp_event == TEMP_EVENT_CAPTURED)
// {
// com_master_proccess(app_temp_trigger[i].com_event);
// app_temp_trigger[i].temp_event = TEMP_NO_EVENT;
// APP_PRINTF(0,RTT_CTRL_TEXT_BRIGHT_GREEN"[TEMP]: TEMP TRIGGER %d\r\n",app_temp_trigger[i].trigger_temp_value);
// }
// }
//}
static void master_process_event(mct_link_t *link) {
switch(link->ac){
// case AC_EXTANSION_START_CMD:{
// if (api_state_check(APP_PASSIVE)) {
// api_state_change(APP_STAND_BY);
// }
// }
// break;
// case AC_EXTANSION_STOP_CMD: {
// if (!api_state_check(APP_PASSIVE))
// api_state_change(APP_PASSIVE);
// }
// break;
default: // неизвестный action code
break;
}
}
void api_param_update(void)
{
//temp_param_update(app_temp_trigger);
return;
}
/*****************************************************************************************************/
///////////Календарь ////////////////////
uint8_t api_set_calendar_data(void *timestamp, void *shift) {
calendar_utc_t m_time_stamp = 0;
calendar_shift_utc_t m_shift = 0;
AppCurrUtcTime = m_time_stamp = uint32_decode(timestamp); //преобразуем данные массива в uint32_t
AppCurrUtcShiftTime = 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));
}
void api_motor_move(uint8_t direction)
{
#ifdef MY_DEBUG
return;
#endif
switch(direction)
{
case STOP_VALVE:
motorStop();
app_bValveMustMoving = false;
break;
case OPEN_VALVE:
if(app_curr_motor_cnt > MOTOR_CNT_EDGE_SHIFT || app_full_motor_cnt == 0)//или в калибровке
{
motorMoveForward();//открыть клапан
app_bValveMustMoving = true;
app_bValveStopped = false;//так как период измерений 0.5с
}
app_motor_direction = direction;
display_write_symbol_data(SYMB_T17_VALVE_CLOSED, IND_CLR);
break;
case CLOSE_VALVE:
if(app_curr_motor_cnt < app_full_motor_cnt - MOTOR_CNT_EDGE_SHIFT || app_full_motor_cnt == 0)//или в калибровке
{
motorMoveBackward();//закрыть клапан
app_bValveMustMoving = true;
app_bValveStopped = false;//так как период измерений 0.5с
}
app_motor_direction = direction;
display_write_symbol_data(SYMB_T17_VALVE_CLOSED, IND_SET);
break;
}
}
void api_state_change_to_error(uint8_t err)
{
AppSubstate = err;
api_state_change(APP_ERROR);
}
void app_start_timer_for_next_motor_regulation(void)
{
int temper_diff = (int)(AppTargetTemperature - AppCurrTemp);
int app_motor_next_move_time_minutes = MAX_TIME_OF_NEXT_MOTOR_REGULATION_MINUTES - abs(temper_diff);
if(app_motor_next_move_time_minutes < MIN_TIME_OF_NEXT_MOTOR_REGULATION_MINUTES)
app_motor_next_move_time_minutes = MIN_TIME_OF_NEXT_MOTOR_REGULATION_MINUTES;
if(app_motor_next_move_time_minutes > MAX_TIME_OF_NEXT_MOTOR_REGULATION_MINUTES)
app_motor_next_move_time_minutes = MAX_TIME_OF_NEXT_MOTOR_REGULATION_MINUTES;
//при входе в standby (это основные режимы работы) стартуем таймер следующей регулировки штока
// и это время зависит от разности температур (текущей и целевой)
app_timer_stop(app_timer_id);
app_timer_start( app_timer_id, APP_TIMER_TICKS(app_motor_next_move_time_minutes * 60000, APP_TIMER_PRESCALER), NULL );
}
void app_start_motor_regulation(void)
{
float temper_diff = AppTargetTemperature - AppCurrTemp;
//если температура не вышла из гистерезиса или уже стоим на предельных значениях штока и поэтому продолжать дальше нельзя
if(fabs(temper_diff) < AppParam.A5_T_hysteresis ||
(temper_diff > 0 && app_curr_motor_cnt < MOTOR_CNT_EDGE_SHIFT) ||
(temper_diff < 0 && app_curr_motor_cnt > app_full_motor_cnt - MOTOR_CNT_EDGE_SHIFT))
{
app_start_timer_for_next_motor_regulation();
}
else
{
app_target_motor_cnt = (int)((float)app_curr_motor_cnt - temper_diff * (float)MOTOR_CNTS_FOR_ONE_GRAD_RELATION * app_full_motor_cnt);
APP_PRINTF(0, "app_target_motor_cnt = %d\n", app_target_motor_cnt);
if(temper_diff > 0)
{
api_motor_move(OPEN_VALVE);//c контролем шага
com_master_proccess(EVT_REGULATOR(EVT_REG_TEMPERATURE, EVT_REG_MUCH_MORE));
}
else
{
api_motor_move(CLOSE_VALVE);//c контролем шага
com_master_proccess(EVT_REGULATOR(EVT_REG_TEMPERATURE, EVT_REG_OFF));
}
app_timer_stop(app_timer_id);
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
}
}
bool app_param_check_init()
{
uint16_t crc = crc16_compute((uint8_t*)¶m, sizeof(param)-sizeof(param.CRC),NULL);
if(param.Ver != PARAM_VER || crc != param.CRC)
{
APP_PRINTF(0,"Param_Init false, param.Ver=%d, crc=%X, param.CRC=%X \r\n", param.Ver, crc, param.CRC);
memset(¶m,0xFF,sizeof(param_t));
return false;
}
APP_PRINTF(0,"Param_Init true\r\n", param.Ver, crc, param.CRC);
return true;//successive initialization/reading of parameters
}
uint8_t api_get_current_week_day(void)
{
uint32_t curutc, curshift;
api_get_calendar_data(&curutc, &curshift);
return (uint8_t)(((curutc + curshift)/86400 + 4) % 7);//+4 это четверг в начале отсчета UTC
}
void app_state_change_after_stop(app_state_t state)
{
if(app_bValveStopped && app_bValveMustMoving == false)//чтобы счетчик шагов не сбился
{
APP_PRINTF(0, "last app_curr_motor_cnt when stopped = %d\n", app_curr_motor_cnt);
api_state_change(state);
}
else
{
//подсостояние состояния APP_STAND_BY, APP_VENTILATION, APP_BOOST, APP_OP_STATE, APP_OFF_STATE:
// ожидание полного останова мотора
AppSubstate = SUBSTATE_2;
app_nextstate = state;//после останова надо будет перейти в это состояние
// app_in_program_transition = true;//переходное состояние из одной программы в другую
api_motor_move(STOP_VALVE);
APP_PRINTF(0,"motorStop(): app_state_change_after_stop()\n");
}
}
void app_set_valve_closed_percent(uint8_t closed_percent)
{
app_target_motor_cnt = (int)(((float)closed_percent * app_full_motor_cnt) / (float)100.);
if(app_curr_motor_cnt < app_target_motor_cnt - MOTOR_CNT_EDGE_SHIFT / 2)
{
api_motor_move(CLOSE_VALVE);//c контролем шага
}
else if(app_curr_motor_cnt > app_target_motor_cnt + MOTOR_CNT_EDGE_SHIFT / 2)
{
api_motor_move(OPEN_VALVE);//c контролем шага
}
app_timer_start( app_timer_id, APP_TIMER_TICKS(MOTOR_CONTROL_TICK_MS, APP_TIMER_PRESCALER), NULL );
}
//индикация коннекта по BLE
void api_ind_connected(bool ConnectedFl)
{
app_ble_connected = ConnectedFl;
display_write_symbol_data(SYMB_T19_BLE_APP_CONNECTED, ConnectedFl);
}
//сохранение параметров после 5 секунд отсутствия новых команд, эмуляция бездействия нажатий кнопок
void api_save_params_with_delay(void)
{
app_button_inactivity_cnt = SUBSTATE_0;
app_timer_stop(led_interval_id);
app_timer_start( led_interval_id, APP_TIMER_TICKS(LED_TICK_MS, APP_TIMER_PRESCALER), NULL );
}
//установка нового режима из мобильного приложения
bool api_set_program(uint8_t prg, bool lockCal, float targetTemperature)
{
app_state_t state;
if(prg > APP_PRG_CLOSE) return false;
switch(prg)
{
case APP_PRG_MANUAL:
if(targetTemperature < MIN_REG_TEMPERATURE || targetTemperature > MAX_REG_TEMPERATURE)
{
return false;
}
state = APP_STAND_BY;
AppParam.TargetTemperature = AppTargetTemperature = targetTemperature;
soft_ind_target_temp(AppTargetTemperature, IND_SET);
break;
case APP_PRG_BOOST:
state = APP_BOOST;
break;
case APP_PRG_OPEN:
state = APP_OP_STATE;
break;
case APP_PRG_CLOSE:
state = APP_OFF_STATE;
break;
}
api_lock_calendar(lockCal);
if(state != AppState)//защита от повторной установки той же программы (состояния)
{
app_state_change_after_stop(state);
}
api_save_params_with_delay();
return true;
}
//возвращает текущую программу
uint8_t api_get_program(void)
{
static uint8_t prg = APP_PRG_MANUAL;
// if(app_in_program_transition) return prg;//то есть при переходе возвращаем предыдущую установленную программу
switch(AppState)
{
case APP_STAND_BY:
prg = APP_PRG_MANUAL;
break;
case APP_BOOST:
prg = APP_PRG_BOOST;
break;
case APP_OP_STATE:
prg = APP_PRG_OPEN;
break;
case APP_OFF_STATE:
prg = APP_PRG_CLOSE;
break;
default:
//программа не меняется
break;
}
return prg;
}
void app_check_tasks_availability(void)
{
if(AppLockCalendarState == UNLOCK_CALENDAR_STATE)
{
//постоянно проверяем наличие хотя бы одной незавершенной задачи в календаре
// bool bTaskExist;
// uint32_t error = evcal_at_least_one_task_is_not_over(&bTaskExist);
// if(error != NRF_SUCCESS || !bTaskExist)
//постоянно проверяем наличие хотя бы одной задачи в календаре
uint8_t taskNum;
uint32_t error = evcal_get_task_number(&taskNum);
if(error != NRF_SUCCESS || taskNum == 0)
{
AppCountdownSecForNoCalendarTasks = APP_BLINKING_AUTO;
AppFlagBlinkingNoCalendarTasks = true;
AppLockCalendarState = LOCK_CALENDAR_STATE;
//app_state_change_after_stop(APP_NO_CALENDAR_TASKS);
}
}
}
void api_lock_calendar(bool lockCal)
{
AppLockCalendarState = lockCal;
if(AppState == APP_STAND_BY || AppState == APP_VENTILATION || AppState == APP_BOOST ||
AppState == APP_OP_STATE || AppState == APP_OFF_STATE)
{
display_write_symbol_data(SYMB_T12_AUTOMATIC_MODE, (AppLockCalendarState==UNLOCK_CALENDAR_STATE ? IND_SET : IND_CLR));
}
app_check_tasks_availability();
AppParam.LockCalendarState = AppLockCalendarState;
api_save_params_with_delay();
}