Программный модуль для розетки RSP-103S. Версия 5.12
ПО является неотъемлемой частью розетки RSP-103S, отдельно потребителю не поставляется и эксплуатируется только в составе устройства.
////////////////////////////////////////////////////////////////
//RSP-103S
//v5.12
//application.c
////////////////////////////////////////////////////////////////
/*==============================================================
include
==============================================================*/
#include <string.h>
#include "debug_info.h"
#include "common.h"
#include "crc16.h"
#include "application.h"
#include "api.h"
#include "com_slave_extention.h"
#include "stderrnum.h"
#include "app_timer.h"
#include "timers.h"
#include <timers_config.h>
#include "keyb.h"
#include "keyb_services.h"
#include "keyb_services_config.h"
#include "led.h"
#include "led_services.h"
#include "led_services_config.h"
#include "app_error.h"
#include "com.h"
#include "param.h"
#include "pof_manager.h"
#include "time.h"
#include "storage.h"
#include "evcal.h"
#include "calendar.h"
#include "adv_data_config.h"
#include "adv_data.h"
#include "hal.h"
////////////////////////////////////////////////////////////////
//состояния индикации
typedef enum
{
IND_BEGIN_STATE,
IND_ON_STATE,
IND_OFF_STATE,
IND_SAVE_MODE_RED_ON_STATE,
// IND_SAVE_MODE_RED_OFF_STATE,
IND_PRE_PAIRING_STATE,
IND_PAIRING_RED_STATE,
IND_PAIRING_GREEN_STATE,
IND_PRE_UNPAIRING_STATE,
IND_FW_UPDATE_RED_ON_STATE,
IND_FW_UPDATE_RED_OFF_STATE,
IND_VIRTUAL_PROGRAM_ON_STATE,
IND_VIRTUAL_PROGRAM_GREEN_OFF_STATE,
} IND_STATE;
/////////// LOCALS /////////////////////////////////////////////////////
static void SetAppState(APP_STATE NewState);
static void SetIndState(IND_STATE NewState);
static void clk_10ms(void* p_context);
static void clk_1s(void* p_context);
static void appModeWithPwmProcess(uint32_t totalTime_S, uint32_t powerDown_ms, uint32_t powerUp_ms);
static void resetCalTimStor(void);
////////////////////////////////////////////////////////////////
static app_timer_id_t application_timer_id; // 1sec application таймер
static app_timer_id_t app_virt_prg_timer_id; // таймер выполнения виртуальных программ
static bool bVirtPrgTickWas = false;
static uint32_t durationVirtPrg_ms = 0;//счетчик длительности этапов виртуальных программ
static uint32_t curPrgTime_ms = 0;//текущий счетчик времени от начала старта программы
static uint32_t curCountDown_s = 0;//обратный отсчет
static APP_STATE AppState = APP_BEGIN_STATE;
static IND_STATE IndState = IND_BEGIN_STATE;
static B8 bUnpairing;
static B8 bConnection;
static keyb_data_t KeybData[KEYB_BUTTON_COUNT];
static B8 bKeybLongPressUsed[KEYB_BUTTON_COUNT];
static U8 KeybPressCnt[KEYB_BUTTON_COUNT];
static U32 Timer;
static U16 IndTimer;
U32 OnTimer, OffTimer;
volatile B8 bOnTimerFl, bOffTimerFl;
uint32_t time_device_on_s = 0; //время работы включенного реле
uint32_t relay_switch_on_cnt = 0; //количество включений реле
static app_save_info_struct_t app_pof_info __attribute__((aligned(sizeof(uint32_t))));
static uint8_t m_virtProgNum = VIRT_PROG_NUM_ZERO;
static uint8_t m_virtProgStageNum = VIRT_PRG_STAGE_WITHOUT_PWM;//номер этапа программы (Ex: готовка-автоподогрев; старт разогрева на 100% - разогрев; старт разморозки на 100% - разморозка
static bool m_pwmPowerState = false;
static uint32_t m_progTime_s = 0;
static bool m_autoheating = false;
bool bSendNotification = false;//флаг необходимости отправки нотификации
bool bNeedToSendDelayedNotification = false;//флаг необ.отправки задержанной нотификации при старте виртуальной программы
////////////////////////////////////////////////////////////////
#define TIMER_MS(n) (TIMERS_MILLISECOND(n))
////////////////////////////////////////////////////////////////
void *application_get_adr_data(void)
{
app_pof_info.time_device_on_s = time_device_on_s;
app_pof_info.relay_switch_on_cnt = relay_switch_on_cnt;
//app_pof_info.state = AppState;
return &app_pof_info;
}
void application_read_pof_data(void)
{
time_device_on_s = app_pof_info.time_device_on_s;
relay_switch_on_cnt = app_pof_info.relay_switch_on_cnt;
}
//индикация
static inline void GreenOn(void)
{
APP_ERROR_CHECK(led_write_data(LED_GREEN, LED_ON));
}
static inline void GreenOff(void)
{
APP_ERROR_CHECK(led_write_data(LED_GREEN, LED_OFF));
}
static inline void RedOn(void)
{
APP_ERROR_CHECK(led_write_data(LED_RED, LED_ON));
}
static inline void RedOff(void)
{
APP_ERROR_CHECK(led_write_data(LED_RED, LED_OFF));
}
static inline void ClrInd(void)
{
GreenOff();
RedOff();
}
////////////////////////////////////////////////////////////////
//нагрузка
static inline void LoadOn(void)
{
APP_ERROR_CHECK(hal_relay_on());
}
static inline void LoadOff(void)
{
APP_ERROR_CHECK(hal_relay_off());
}
////////////////////////////////////////////////////////////////
//таймеры
#define IS_RELAY_ON() ( (NRF_GPIO->OUT & (1UL << REL_PIN)) != 0 )
static void clk_10ms(void* p_context)
{
if(Timer) Timer--;
if(IndTimer) IndTimer--;
}
static void clk_1s(void* p_context)
{
if(IS_RELAY_ON())
{
time_device_on_s++;
}
//устаревшие таймеры включения/выключения (пока это надо только для виртуального устройства - мультипекаря)
if(OnTimer && !--OnTimer) bOnTimerFl = TRUE;
if(OffTimer && !--OffTimer) bOffTimerFl = TRUE;
}
////////////////////////////////////////////////////////////////
/**
* @brief Функция вызывается при срабатывании часов (1 сек)
*
* @param none
* @return none
*
*/
static void calendar_timer_handle(void){
uint32_t err_code;
err_code = evcal_one_sec_handler();
APP_ERROR_CHECK(err_code);
}
/**
* @brief Функция вызывается при срабатывании событий из календаря
*
* @param none
* @return none
*
*/
static void evcal_calendar_task(evcal_info_packet_t const*p_data){
//добавлена защита от срабатывания задач календаря при включенных таймерах ВКЛ или ВЫКЛ
if((p_data == NULL) || OnTimer || OffTimer) 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){
API_On();
}
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){
if(AppState != APP_FW_UPDATE_STATE)
{
API_Off();
}
}
break;
}
case AC_EXTENTION_SWITCH_CMD:
{
exten_req_switch_t *p_in_ext_pckt = (exten_req_switch_t *) &p_data->array[2];
if((p_in_ext_pckt->mask & 0x1) != 0){
if(API_GetState() == API_OFF_STATE)
{
API_On();
}
else if(API_GetState() == API_ON_STATE)
{
API_Off();
}
}
break;
}
}
return;
}
/**
* @brief Обработчик таймера virt prg.
*
* @param none
*
* @return none
*/
static void virt_prg_clock_handler(void* p_context)
{
bVirtPrgTickWas = true;
}
////////////////////////////////////////////////////////////////
void application_init(void)
{
uint32_t err_code;
uint8_t b_pof_have_data = 0;
APP_ERROR_CHECK(hal_hw_init());
APP_ERROR_CHECK(keyb_services_set_address(KeybData));
APP_ERROR_CHECK(calendar_set_one_sec_callback(calendar_timer_handle)); //установка callback часов
APP_ERROR_CHECK(evcal_set_callback(evcal_calendar_task)); //установка callback при срабатывании расписания
//создаем app таймер для выполнения виртуальных программ
err_code = app_timer_create(&app_virt_prg_timer_id, APP_TIMER_MODE_REPEATED, virt_prg_clock_handler);
APP_ERROR_CHECK(err_code);
//создаем app таймер для application секундного таймера, так как нельзя делать контроль ДЛИТЕЛЬНЫХ промежутков на времени
// на основе timers 10MS, так как они дают ошибку порядка 5 секунд за 1 час времени
err_code = app_timer_create(&application_timer_id, APP_TIMER_MODE_REPEATED, clk_1s);
APP_ERROR_CHECK(err_code);
err_code = app_timer_start(application_timer_id, APP_TIMER_TICKS(APPLICATION_TIME_MS, APP_TIMER_PRESCALER), NULL);
APP_ERROR_CHECK(err_code);
APP_ERROR_CHECK(timer_create(APP_TIMER_MS, TIMERS_MILLISECOND(10), (callback_t)clk_10ms));
APP_ERROR_CHECK(timer_start(APP_TIMER_MS));
//восстановление сохраненных конфигураций
err_code = pof_manager_get_state(&b_pof_have_data);
APP_ERROR_CHECK(err_code);
if(b_pof_have_data)
{
err_code = pof_manager_copy_data();
APP_ERROR_CHECK(err_code);
//запускаем таймер задержанного разрешения записи страницы
pof_manager_delay_enable_record();
application_read_pof_data();
APP_PRINTF(0,"POF data: time=%d, relay_cnt=%d\r\n", time_device_on_s, relay_switch_on_cnt);
}
else
{
//запускаем таймер задержанного разрешения записи страницы
pof_manager_delay_enable_record();
APP_PRINTF(0,"no POF data: time=%d, relay_cnt=%d\r\n", time_device_on_s, relay_switch_on_cnt);
}
// if(api_get_virtual_device_id() == SKYGRILL_VIRTUAL_DEVICE_ID ||
// api_get_virtual_device_id() == SKYBAKER_VIRTUAL_DEVICE_ID)
// {
// param.bSafeMode = API_SAVE_MODE_OFF;
// }
}
////////////////////////////////////////////////////////////////
void application_process( void )
{
//uint32_t err_code;
static uint8_t state_before_pair_unpair;
//устаревшие таймеры
if(bOnTimerFl)
{
#ifdef LIFE_TEST
LoadOn();
SetIndState(IND_ON_STATE);
#else
API_On();
#endif
bOnTimerFl = FALSE;
}
if(bOffTimerFl)
{
#ifdef LIFE_TEST
LoadOff();
SetIndState(IND_OFF_STATE);
#else
if(AppState != APP_FW_UPDATE_STATE)
{
API_Off();
}
#endif
bOffTimerFl = FALSE;
}
//чтение данных клавиатуры
for(int i=0; i<KEYB_BUTTON_COUNT; i++)
{
APP_ERROR_CHECK( keyb_read_data(i, &KeybData[i]) );
if(KeybData[i].state == KEYB_RELEASED) bKeybLongPressUsed[i] = false;
}
//число нажатий
for(int i=0; i<KEYB_BUTTON_COUNT; i++)
{
APP_ERROR_CHECK( keyb_services_read_pres(i, &KeybPressCnt[i]) );
if(KeybData[i].state == KEYB_PRESSED) KeybPressCnt[i] = 0; //убрать, если по отжатию
}
switch(AppState)
{
case APP_BEGIN_STATE:
if(param.bSafeMode) SetAppState(APP_SAFE_MODE_STATE);
else SetAppState(APP_OFF_STATE);
break;
#ifdef LIFE_TEST
case APP_LIFE_TEST:
//исходно при попадании в это состояние устройство выключено
if(API_GetOffTimer() == 0)
{
API_SetOnTimer(TIME_3S);//включаем через 3с
API_SetOffTimer(TIME_3S + TIME_5S);
}
break;
#endif
case APP_VIRTUAL_PROGRAM_ON_STATE:
case APP_VIRTUAL_PROGRAM_AUTOHEATING_ON_STATE:
{
if(KeybPressCnt[BUTTON] == LOAD_OFF_BUTTON_CNT)
{
SetAppState(APP_OFF_STATE);
break;
}
// if(api_get_virtual_device_id() != SKYGRILL_VIRTUAL_DEVICE_ID &&
// api_get_virtual_device_id() != SKYBAKER_VIRTUAL_DEVICE_ID)
// {
if(KeybPressCnt[BUTTON] == SAFE_MODE_ON_BUTTON_CNT)
{
API_SetSafeMode(API_SAVE_MODE_ON);
break;
}
// }
if(bVirtPrgTickWas)
{
bVirtPrgTickWas = false;
if(bNeedToSendDelayedNotification)
{
bNeedToSendDelayedNotification = false;
bSendNotification = true;
}
durationVirtPrg_ms += VIRT_PROGRAM_TICK_MS;
curPrgTime_ms += VIRT_PROGRAM_TICK_MS;
if(m_virtProgStageNum == VIRT_PRG_STAGE_WITHOUT_PWM)
{
curCountDown_s = m_progTime_s - curPrgTime_ms / 1000;
switch(m_virtProgNum)
{
case VIRT_PROG_NUM_MAIN_HEAT://готовка
//если программа завершена по заданной длительности
if(durationVirtPrg_ms >= m_progTime_s * 1000)
{
//если есть опция автоподогрева
if(m_autoheating)
{
SetAppState(APP_VIRTUAL_PROGRAM_AUTOHEATING_ON_STATE);
LoadOff();
m_pwmPowerState = false;
durationVirtPrg_ms = 0;//обнуление счетчика длительности этапов виртуальных программ
}
else
{//сюда попадаем, когда готовка БЕЗ подогрева
SetAppState(APP_OFF_STATE);
}
}
break;
case VIRT_PROG_NUM_WARMING://разогрев
case VIRT_PROG_NUM_DEFROSTING://разморозка
if(durationVirtPrg_ms >= VIRT_FAST_WARMING_TIME_AT_START_S * 1000)
{
m_virtProgStageNum = VIRT_PRG_STAGE_WITH_PWM;
LoadOff();
m_pwmPowerState = false;
durationVirtPrg_ms = 0;//обнуление счетчика длительности этапов виртуальных программ
}
break;
case VIRT_PROG_NUM_ZERO:
default:
SetAppState(APP_OFF_STATE);//защита от случайностей
break;
}//switch(m_virtProgNum)
}//if(m_virtProgStageNum == VIRT_PRG_STAGE_WITHOUT_PWM)
else
{//а здесь with PWM
if(AppState == APP_VIRTUAL_PROGRAM_AUTOHEATING_ON_STATE)
{//здесь идет автоподогрев в конце готовки
curCountDown_s = VIRT_MAX_AUTOHEATING_TIME_S - curPrgTime_ms / 1000;
appModeWithPwmProcess(VIRT_MAX_AUTOHEATING_TIME_S, VIRT_AUTOHEATING_POWER_DOWN_TIME_MS,
VIRT_AUTOHEATING_POWER_UP_TIME_MS);
}
else
{//AppState == APP_VIRTUAL_PROGRAM_ON_STATE at stage with PWM
curCountDown_s = m_progTime_s - curPrgTime_ms / 1000;
switch(m_virtProgNum)
{
case VIRT_PROG_NUM_WARMING://разогрев
appModeWithPwmProcess(m_progTime_s, VIRT_WARMING_POWER_DOWN_TIME_MS,
VIRT_WARMING_POWER_UP_TIME_MS);
break;
case VIRT_PROG_NUM_DEFROSTING://разморозка
appModeWithPwmProcess(m_progTime_s, VIRT_DEFROSTING_POWER_DOWN_TIME_MS,
VIRT_DEFROSTING_POWER_UP_TIME_MS);
break;
case VIRT_PROG_NUM_ZERO:
default:
SetAppState(APP_OFF_STATE);//защита от случайностей
break;
}//switch(m_virtProgNum)
}//AppState == APP_VIRTUAL_PROGRAM_ON_STATE
}//with PWM
}//if(bVirtPrgTickWas)
break;
}//case APP_VIRTUAL_PROGRAM_ON_STATE
case APP_ON_STATE:
if(KeybPressCnt[BUTTON] == LOAD_OFF_BUTTON_CNT)
{
#ifdef LIFE_TEST
SetAppState(APP_LIFE_TEST);
#else
SetAppState(APP_OFF_STATE);
#endif
break;
}
// if(api_get_virtual_device_id() != SKYGRILL_VIRTUAL_DEVICE_ID &&
// api_get_virtual_device_id() != SKYBAKER_VIRTUAL_DEVICE_ID)
// {
if(KeybPressCnt[BUTTON] == SAFE_MODE_ON_BUTTON_CNT)
{
API_SetSafeMode(API_SAVE_MODE_ON);
break;
}
// }
//по новому ТЗ от сентября 2017 - пэринг должен запускаться из состояния ВКЛ или ВЫКЛ
if(KeybData[BUTTON].pressed_time_ms >= PAIRING_BUTTON_PRESS_TIME_MS && !bKeybLongPressUsed[BUTTON])
{
state_before_pair_unpair = APP_ON_STATE;
SetAppState(APP_PRE_PAIRING_STATE);
break;
}
break;
case APP_OFF_STATE:
if(KeybPressCnt[BUTTON] == LOAD_ON_BUTTON_CNT)
{
m_virtProgNum = VIRT_PROG_NUM_ZERO;//по договоренности от 19.07.18 со всеми андроидами, ios...
API_On();
break;
}
// if(api_get_virtual_device_id() != SKYGRILL_VIRTUAL_DEVICE_ID &&
// api_get_virtual_device_id() != SKYBAKER_VIRTUAL_DEVICE_ID)
// {
if(KeybPressCnt[BUTTON] == SAFE_MODE_ON_BUTTON_CNT)
{
API_SetSafeMode(API_SAVE_MODE_ON);
break;
}
// }
//по новому ТЗ от сентября 2017 - пэринг должен запускаться из состояния ВКЛ или ВЫКЛ
if(KeybData[BUTTON].pressed_time_ms >= PAIRING_BUTTON_PRESS_TIME_MS && !bKeybLongPressUsed[BUTTON])
{
state_before_pair_unpair = APP_OFF_STATE;
SetAppState(APP_PRE_PAIRING_STATE);
break;
}
break;
case APP_SAFE_MODE_STATE:
if(KeybPressCnt[BUTTON] == SAFE_MODE_OFF_BUTTON_CNT)
{
API_SetSafeMode(API_SAVE_MODE_OFF);
break;
}
break;
case APP_PRE_PAIRING_STATE:
if(KeybData[BUTTON].state == KEYB_RELEASED)
{
if(!bConnection)
{
//adv_data_set_paring();//установка флага перинга в advertizing
//adv_data_set_pairing_param(true);//установка флага перинга в advertizing
app_advertising_set_paring_flag();
SetAppState(APP_PAIRING_STATE);
}
else SetAppState((APP_STATE)state_before_pair_unpair);
break;
}
if(KeybData[BUTTON].pressed_time_ms >= UNPAIRING_BUTTON_PRESS_TIME_MS && !bKeybLongPressUsed[BUTTON])
{
SetAppState(APP_PRE_UNPAIRING_STATE);
break;
}
break;
case APP_PAIRING_STATE:
if(bConnection || !Timer)//если соединились или таймаут перинга
{
//adv_data_unset_paring();//сброс флага перинга в advertizing
//adv_data_set_pairing_param(false);//сброс флага перинга в advertizing
app_advertising_unset_paring_flag();
SetAppState((APP_STATE)state_before_pair_unpair);
}
break;
case APP_PRE_UNPAIRING_STATE:
if(KeybData[BUTTON].state == KEYB_RELEASED)
{
SetAppState(APP_UNPAIRING_STATE);
}
break;
case APP_UNPAIRING_STATE:
if(KeybData[BUTTON].state == KEYB_RELEASED) SetAppState((APP_STATE)state_before_pair_unpair);
break;
case APP_FW_UPDATE_STATE:
if(!bConnection || !Timer) SetAppState(APP_OFF_STATE);
break;
default:
break;
}
switch(IndState)
{
// case IND_SAVE_MODE_RED_ON_STATE:
// if(!IndTimer) SetIndState(IND_SAVE_MODE_RED_OFF_STATE);
// break;
// case IND_SAVE_MODE_RED_OFF_STATE:
// if(!IndTimer) SetIndState(IND_SAVE_MODE_RED_ON_STATE);
// break;
case IND_VIRTUAL_PROGRAM_ON_STATE:
if(!IndTimer) SetIndState(IND_VIRTUAL_PROGRAM_GREEN_OFF_STATE);
break;
case IND_VIRTUAL_PROGRAM_GREEN_OFF_STATE:
if(!IndTimer) SetIndState(IND_VIRTUAL_PROGRAM_ON_STATE);
break;
case IND_PRE_PAIRING_STATE:
case IND_PRE_UNPAIRING_STATE:
if(!IndTimer) SetIndState(IND_OFF_STATE);
break;
case IND_PAIRING_RED_STATE:
if(!IndTimer) SetIndState(IND_PAIRING_GREEN_STATE);
break;
case IND_PAIRING_GREEN_STATE:
if(!IndTimer) SetIndState(IND_PAIRING_RED_STATE);
break;
case IND_FW_UPDATE_RED_ON_STATE:
if(!IndTimer) SetIndState(IND_FW_UPDATE_RED_OFF_STATE);
break;
case IND_FW_UPDATE_RED_OFF_STATE:
if(!IndTimer) SetIndState(IND_FW_UPDATE_RED_ON_STATE);
break;
default:
break;
}
}
////////////////////////////////////////////////////////////////
//установка состояний
static void SetAppState(APP_STATE NewState)
{
uint32_t err_code;
APP_PRINTF(0,"Set new state = %d\r\n", NewState);
switch(NewState)
{
case APP_VIRTUAL_PROGRAM_AUTOHEATING_ON_STATE:
curPrgTime_ms = 0;//обнуление счетчика длительности всей программы -> для общей длительности подогрева
m_virtProgStageNum = VIRT_PRG_STAGE_WITH_PWM;
break;
case APP_VIRTUAL_PROGRAM_ON_STATE:
SetIndState(IND_VIRTUAL_PROGRAM_ON_STATE);
curPrgTime_ms = durationVirtPrg_ms = 0;//обнуление счетчиков длительностей всей программы и этапов виртуальных программ
m_virtProgStageNum = VIRT_PRG_STAGE_WITHOUT_PWM;//номер этапа программы обнуляем
//и запускаем таймер 100ms тиков
err_code = app_timer_start(app_virt_prg_timer_id, APP_TIMER_TICKS(VIRT_PROGRAM_TICK_MS, APP_TIMER_PRESCALER), NULL);
APP_ERROR_CHECK(err_code);
LoadOn();
break;
case APP_ON_STATE:
LoadOn();
SetIndState(IND_ON_STATE);
break;
#ifdef LIFE_TEST
case APP_LIFE_TEST:
#endif
case APP_OFF_STATE:
LoadOff();
SetIndState(IND_OFF_STATE);
err_code = app_timer_stop(app_virt_prg_timer_id);
APP_ERROR_CHECK(err_code);
m_progTime_s = 0;
curCountDown_s = 0;
break;
case APP_SAFE_MODE_STATE:
LoadOff();
SetIndState(IND_SAVE_MODE_RED_ON_STATE);
break;
case APP_PRE_PAIRING_STATE:
SetIndState(IND_PRE_PAIRING_STATE);
break;
case APP_PAIRING_STATE:
SetIndState(IND_PAIRING_RED_STATE);
Timer = TIMER_MS(PAIRING_TIME_MS);
break;
case APP_PRE_UNPAIRING_STATE:
SetIndState(IND_PRE_UNPAIRING_STATE);
break;
case APP_UNPAIRING_STATE:
bUnpairing = true;
//события при анперинге
api_set_virtual_device_id(0);//сброс ID виртуального устройства, там же сбрасываются и календарь и таймеры и storage
break;
case APP_FW_UPDATE_STATE:
SetIndState(IND_FW_UPDATE_RED_ON_STATE);
Timer = TIMER_MS(FW_UPDATE_TIME_MS);
timer_stop(TIMER_POF_MANAGER);//останавливаем таймер задержанного стирания pof flash
//запись данных в флеш перед обновлением прошивки
pof_write_data_in_flash();
//а уж если будет сбой при обновлении прошивки, то либо пользователь заново будет обновлять либо дождемся reset
break;
default:
return;
}
AppState = NewState;
{
uint8_t adv_app_state;
uint16_t adv_app_state_param = ADV_STAND_BY_PRM_NO;
//конвертер - настраивается в каждом старом проекте индивидуально
adv_app_state = API_GetState();
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_STATE)
{
adv_app_state = ADV_ST_STAND_BY;
adv_app_state_param = ADV_STAND_BY_PRM_PAIRING;
}
app_advertising_set_app_state(adv_app_state, adv_app_state_param);
}
{
static uint8_t lastApiState = API_ERROR_STATE, newApiState;
newApiState = API_GetState();
if(newApiState != lastApiState)
{
lastApiState = newApiState;
if(AppState == APP_VIRTUAL_PROGRAM_ON_STATE)
{
bNeedToSendDelayedNotification = true;
}
else
{
bSendNotification = true;
}
}
}
}
static void SetIndState(IND_STATE NewState)
{
switch(NewState)
{
case IND_VIRTUAL_PROGRAM_GREEN_OFF_STATE:
GreenOff();
IndTimer = TIMER_MS(VIRTUAL_PROGRAM_GREEN_IND_OFF_TIME_MS);
break;
case IND_VIRTUAL_PROGRAM_ON_STATE:
RedOn();
IndTimer = TIMER_MS(VIRTUAL_PROGRAM_GREEN_IND_ON_TIME_MS);
GreenOn();
break;
case IND_ON_STATE:
RedOff();
GreenOn();
break;
case IND_OFF_STATE:
ClrInd();
break;
case IND_SAVE_MODE_RED_ON_STATE:
GreenOff();
RedOn();
//IndTimer = TIMER_MS(SAVE_MODE_RED_IND_ON_TIME_MS);
break;
// case IND_SAVE_MODE_RED_OFF_STATE:
// GreenOff();
// RedOff();
// IndTimer = TIMER_MS(SAVE_MODE_RED_IND_OFF_TIME_MS);
// break;
case IND_PRE_PAIRING_STATE:
RedOff();
GreenOn();
IndTimer = TIMER_MS(PRE_PAIRING_IND_OFF_TIME_MS);
break;
case IND_PAIRING_RED_STATE:
GreenOff();
RedOn();
IndTimer = TIMER_MS(PAIRING_RED_IND_TIME_MS);
break;
case IND_PAIRING_GREEN_STATE:
RedOff();
GreenOn();
IndTimer = TIMER_MS(PAIRING_GREEN_IND_TIME_MS);
break;
case IND_PRE_UNPAIRING_STATE:
GreenOff();
RedOn();
IndTimer = TIMER_MS(PRE_UNPAIRING_IND_OFF_TIME_MS);
break;
case IND_FW_UPDATE_RED_ON_STATE:
GreenOff();
RedOn();
IndTimer = TIMER_MS(FW_UPDATE_IND_ON_TIME_MS);
break;
case IND_FW_UPDATE_RED_OFF_STATE:
GreenOff();
RedOff();
IndTimer = TIMER_MS(FW_UPDATE_IND_OFF_TIME_MS);
break;
default:
return;
}
IndState = NewState;
}
////////////////////////////////////////////////////////////////
uint16_t API_GetVersion(void)
{
return FIRMWARE_VER;
}
////////////////////////////////////////////////////////////////
uint8_t API_GetState(void)
{
switch(AppState)
{
case APP_BEGIN_STATE:
case APP_OFF_STATE:
return API_OFF_STATE;
case APP_SAFE_MODE_STATE:
return API_SAFE_MODE_STATE;
case APP_FW_UPDATE_STATE:
return API_FW_UPDATE_STATE;
case APP_ON_STATE:
case APP_VIRTUAL_PROGRAM_ON_STATE:
return API_ON_STATE;
case APP_VIRTUAL_PROGRAM_AUTOHEATING_ON_STATE:
return API_AUTOHEATING_STATE;
case APP_PAIRING_STATE:
return API_PAIRING_STATE;
default:
return API_ERROR_STATE;
}
}
////////////////////////////////////////////////////////////////
//bool API_FW_AllowState(void)
//{
// switch(AppState)
// {
// case APP_BEGIN_STATE:
// case APP_OFF_STATE:
// case APP_FW_UPDATE_STATE:
// return true;
// default:
// return false;
// }
//}
////////////////////////////////////////////////////////////////
void API_FW_UPDATE_PROC(void)
{
if( AppState == APP_FW_UPDATE_STATE )
{
Timer = TIMER_MS(FW_UPDATE_TIME_MS);
}
}
////////////////////////////////////////////////////////////////
bool API_ENTER_FW_UPDATE(void)
{
switch(AppState)
{
case APP_BEGIN_STATE:
case APP_OFF_STATE:
//case APP_SAFE_MODE_STATE:
case APP_FW_UPDATE_STATE:
SetAppState(APP_FW_UPDATE_STATE);
return true;
default:
return false;
}
}
////////////////////////////////////////////////////////////////
bool API_SetOnTimer(uint32_t time)
{
OnTimer = time;
return true;
}
uint32_t API_GetOnTimer(void)
{
return OnTimer;
}
bool API_SetOffTimer(uint32_t time)
{
OffTimer = time;
return true;
}
uint32_t API_GetOffTimer(void)
{
return OffTimer;
}
////////////////////////////////////////////////////////////////
bool API_On(void)
{
switch(AppState)
{
case APP_OFF_STATE:
if(m_virtProgNum >= VIRT_PROG_MIN_NUM && m_virtProgNum <= VIRT_PROG_MAX_NUM)
{
SetAppState(APP_VIRTUAL_PROGRAM_ON_STATE);
}
else
{
SetAppState(APP_ON_STATE);
}
relay_switch_on_cnt++;//теперь это количество пользовательских включений
return true;
case APP_SAFE_MODE_STATE:
case APP_FW_UPDATE_STATE:
return false;
default:
return true;
}
}
////////////////////////////////////////////////////////////////
bool API_Off(void)
{
switch(AppState)
{
case APP_OFF_STATE:
case APP_SAFE_MODE_STATE:
return true;
case APP_FW_UPDATE_STATE:
default:
SetAppState(APP_OFF_STATE);
return true;
}
}
bool app_check_prog_time(uint32_t* progTime)
{
uint32_t maxTime;
switch(m_virtProgNum)
{
case VIRT_PROG_NUM_MAIN_HEAT:
maxTime = MAX_MAIN_HEAT_TIME_S;
break;
case VIRT_PROG_NUM_WARMING:
case VIRT_PROG_NUM_DEFROSTING:
maxTime = MAX_WARMING_OR_DEFROSTING_TIME_S;
break;
default:
return true;
}
if(*progTime > maxTime) return false;
//проверка времени во время идущей виртуальной программы (изменение времени в этом случае - это функция Master Chief Light)
if(AppState == APP_VIRTUAL_PROGRAM_ON_STATE)
{
//если задаваемое время уже прошло
if(*progTime < curPrgTime_ms / 1000) return false;
}
if(m_virtProgNum >= VIRT_PROG_MIN_NUM && m_virtProgNum <= VIRT_PROG_MAX_NUM)
{
bNeedToSendDelayedNotification = true;
}
return true;
}
bool API_SetCookingTime(uint32_t* progTime)
{
if(app_check_prog_time(progTime) == true)
{
m_progTime_s = *progTime;
return true;
}
return false;
}
uint32_t API_GetCookingTime(void)
{
return m_progTime_s;
}
bool API_SetVirtualProgram(uint8_t virtProgNum, uint32_t* progTime, bool autoheating)
{
m_virtProgNum = virtProgNum;
m_autoheating = autoheating;
bSendNotification = true;
if(app_check_prog_time(progTime) == true)
{
m_progTime_s = *progTime;
return true;
}
return false;
}
uint8_t API_GetVirtualProgram(uint32_t* progTime, uint32_t* countDownTime, bool* autoheating)
{
*progTime = m_progTime_s;
*countDownTime = curCountDown_s;
*autoheating = m_autoheating;
return m_virtProgNum;
}
////////////////////////////////////////////////////////////////
//bool API_SetProgram(uint8_t prog)
//{
// if(param.Program == prog) return true;
//
// param.Program = prog;
// pof_man_param_save();
// return true;
//}
////////////////////////////////////////////////////////////////
//uint8_t API_GetProgram(void)
//{
// return param.Program;
//}
////////////////////////////////////////////////////////////////
bool API_SetSafeMode(uint8_t mode)
{
bSendNotification = true;
switch(mode)
{
case API_SAVE_MODE_ON:
// flagNeedSetSafeMode = false;
if(param.bSafeMode == API_SAVE_MODE_ON) return true;
param.bSafeMode = API_SAVE_MODE_ON;
pof_man_param_save();
SetAppState(APP_SAFE_MODE_STATE);
return true;
case API_SAVE_MODE_OFF:
if(param.bSafeMode == API_SAVE_MODE_OFF) return true;
param.bSafeMode = API_SAVE_MODE_OFF;
pof_man_param_save();
SetAppState(APP_OFF_STATE);
return true;
default:
return false;
}
}
////////////////////////////////////////////////////////////////
uint8_t API_GetSafeMode(void)
{
return param.bSafeMode;
}
/////////////////////////////////////////////////////
bool API_SetAutoheating(bool reheat)
{
bSendNotification = true;
m_autoheating = reheat;
return true;
}
////////////////////////////////////////////////////////////////
bool API_GetAutoheating(void)
{
return m_autoheating;
}
////////////////////////////////////////////////////////////////
bool API_IsPairing(void)
{
if(AppState == APP_PAIRING_STATE) return true;
return false;
}
////////////////////////////////////////////////////////////////
bool API_IsUnpairing(void)
{
return bUnpairing;
}
void API_ClrUnpairing(void)
{
bUnpairing = false;
}
////////////////////////////////////////////////////////////////
void API_SetConnection(void)
{
bConnection = true;
// switch (param.Program)
// {
// case API_PROG_AT_HOME:
// {
// if(AppState != APP_SAFE_MODE_STATE)
// {
// uint32_t err_code = app_timer_stop(not_at_home_timer_id); //останавливаем app таймер
// APP_ERROR_CHECK(err_code);
// }
// break;
// }
// default:
// break;
// }
}
void API_ClrConnection(void)
{
bConnection = false;
evcal_clear_transactions();
storage_clear_all_transactions();
}
////////////////////////////////////////////////////////////////
/**
* описание в application_api.h
*/
uint8_t api_set_calendar_data(void *timestamp, void *shift)
{
calendar_utc_t m_time_stamp = 0;
calendar_shift_utc_t m_shift = 0;
m_time_stamp = uint32_decode(timestamp); //преобразуем данные массива в uint32_t
m_shift = uint32_decode(shift); //преобразуем данные массива в uint32_t
uint32_t err_code = calendar_set_time(m_time_stamp, m_shift);
switch (err_code)
{
case NRF_SUCCESS:
return ERSP_SUCCESS;
case NRF_ERROR_INVALID_DATA:
return ERSP_ERROR_INVALID_DATA;
case DRIVER_MODULE_NOT_INITIALZED:
return ERSP_ERROR_INTERNAL;
}
return ERSP_ERROR_UNKNOWN;
}
/**
* описание в application_api.h
*/
void api_get_calendar_data(void *timestamp, void *shift)
{
calendar_utc_t m_time_stamp = 0;
calendar_shift_utc_t m_shift = 0;
//получаем данные о текущем UTC и времени сдвига
uint32_t err_code = calendar_get_utc_time(&m_time_stamp, &m_shift);
APP_ERROR_CHECK(err_code);
memcpy(timestamp, &m_time_stamp, sizeof(calendar_utc_t));
memcpy(shift, &m_shift, sizeof(calendar_shift_utc_t));
}
/**
* описание в application_api.h
*/
void api_get_evcal_task(uint8_t task_num, void *p_buf)
{
uint32_t err_code = evcal_get_task_data(task_num, p_buf);
if(err_code != NRF_SUCCESS){
memset(p_buf, 0, sizeof(evcal_task_t));
}
}
/**
* описание в application_api.h
*/
uint8_t api_add_evcal_task(void *p_input, uint8_t *uid)
{
//записываем новую задачу в календарь
uint32_t err_code = evcal_save_task(p_input);
*uid = ((evcal_task_t *)p_input)->uid; //записываем UID присвоенной задачи
switch (err_code)
{
case NRF_SUCCESS:
{
return ERSP_SUCCESS;
}
case NRF_ERROR_NO_MEM:
{
return ERSP_ERROR_NO_MEM;
}
case DRIVER_MODULE_NOT_INITIALZED:
case NRF_ERROR_NULL:
{
return ERSP_ERROR_INTERNAL;
}
}
return ERSP_ERROR_UNKNOWN;
}
/**
* описание в application_api.h
*/
uint8_t api_del_evcal_task(uint8_t uid_task)
{
//записываем новую задачу в календарь
uint32_t err_code = evcal_delete_task(uid_task);
switch (err_code)
{
case NRF_SUCCESS:
{
return ERSP_SUCCESS;
}
case NRF_ERROR_NOT_FOUND:
{
return ERSP_ERROR_NOT_FOUND;
}
case NRF_ERROR_INVALID_PARAM:
{
return ERSP_ERROR_INVALID_PARAM;
}
case DRIVER_MODULE_NOT_INITIALZED:
{
return ERSP_ERROR_INTERNAL;
}
}
return ERSP_ERROR_UNKNOWN;
}
/**
* описание в application_api.h
*/
uint8_t api_erase_evcal(void)
{
if(evcal_erase_all_tasks() == NRF_SUCCESS){
return ERSP_SUCCESS;
} else {
return ERSP_ERROR_INTERNAL;
}
}
/**
* описание в application_api.h
*/
uint8_t api_get_ver_evcal(void)
{
return EVCAL_DRIVER_VERSION;
}
/**
* описание в application_api.h
*/
uint8_t api_get_max_task_evcal(void)
{
return EVCAL_MAX_TASKS;
}
/**
* описание в application_api.h
*/
uint8_t api_get_cur_task_num_evcal(void)
{
uint8_t m_num_task = 0;
APP_ERROR_CHECK(evcal_get_task_number(&m_num_task));
return m_num_task;
}
/**
* @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_get_statistic_info(uint8_t idx, app_statistic_info_t *p_out_str)
{
if(idx > 0) return ERSP_ERROR_INVALID_PARAM;//пока только одно реле с индексом 0.
if(p_out_str == NULL) return ERSP_ERROR_NULL;
double result = 0;
p_out_str->time_device_on_s = time_device_on_s;
p_out_str->switch_on_cnt = relay_switch_on_cnt;
result = ((double)p_out_str->time_device_on_s / 3600.0) * 0;//APP_HEATER_WATS;
p_out_str->spent_energy_w_hour = result;
return ERSP_SUCCESS;
}
uint32_t api_get_relay_switch_on_cnt(void)
{
return relay_switch_on_cnt;
}
uint16_t api_get_flash_page_erase_cnt(void)
{
return param.flash_page_erase_cnt;
}
uint32_t api_get_virtual_device_id(void)
{
return param.VirtualDeviceID;
}
void api_set_virtual_device_id(uint32_t virtDevId)
{
if(param.VirtualDeviceID != virtDevId) m_virtProgNum = VIRT_PROG_NUM_ZERO;
param.VirtualDeviceID = virtDevId;
API_Off();
resetCalTimStor();
pof_man_param_save();
}
static void appModeWithPwmProcess(uint32_t totalTime_S, uint32_t powerDown_ms, uint32_t powerUp_ms)
{
if(curPrgTime_ms >= totalTime_S * 1000)
{
//максимальное время - выходим
SetAppState(APP_OFF_STATE);
return;
}
if(m_pwmPowerState == false)
{
if(durationVirtPrg_ms >= powerDown_ms)
{
LoadOn();
m_pwmPowerState = true;
durationVirtPrg_ms = 0;
}
}
else
{
if(durationVirtPrg_ms >= powerUp_ms)
{
LoadOff();
m_pwmPowerState = false;
durationVirtPrg_ms = 0;
}
}
}
bool app_param_check_init()
{
#ifdef DEBUG
uint32_t active_addr_param = Param_Get_Active_Addr();
APP_PRINTF(0,"Param_Init: active_addr_param=%X param.Ver=%d param.bSafeMode=%d param.CRC=%X\r\n", active_addr_param, param.Ver, param.bSafeMode, param.CRC);
for (int i=0; i< 12+12*15; i++)
{
APP_PRINTF(0,"%X ", *(uint8_t*)(active_addr_param + i));
}
APP_PRINTF(0,"\r\n");
#endif
uint16_t crc = crc16_compute((uint8_t*)¶m, sizeof(param)-sizeof(param.CRC),NULL);
if(param.Ver != PARAM_VER || crc != param.CRC)
{
memset(¶m,0xFF,sizeof(param_t));
param.Ver = PARAM_VER;
param.bSafeMode = FALSE;
param.VirtualDeviceID = 0;
APP_PRINTF(0,"Param_Init false, param.Ver=%d, crc=%X, param.CRC=%X \r\n", param.Ver, crc, param.CRC);
return false;
}
APP_PRINTF(0,"Param_Init true\r\n", param.Ver, crc, param.CRC);
return true;//successive initialization/reading of parameters
}
bool app_is_pof_data_were_changed(void)
{
static app_save_info_struct_t last_pof_data = {0, 0};
if(last_pof_data.relay_switch_on_cnt != relay_switch_on_cnt ||
last_pof_data.time_device_on_s != time_device_on_s)
{
last_pof_data.relay_switch_on_cnt = relay_switch_on_cnt;
last_pof_data.time_device_on_s = time_device_on_s;
return true;
}
return false;
}
static void resetCalTimStor(void)
{
evcal_clear_transactions();//сброс событий календаря
//события календаря (до этого уже должны быть очищены все транзакции)
api_add_trans_in_calendar(EVCAL_TRANSACTION_WRITE);
api_erase_evcal();
api_dec_trans_in_calendar(EVCAL_COMMIT_TRANSACTION);
storage_clear_all_transactions();//? наверно и здесь надо очистить тоже всё помимо транзакций
OnTimer = OffTimer = 0;//сброс таймеров при анперинге
}