Программный модуль для розетки 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*)&param, sizeof(param)-sizeof(param.CRC),NULL);

	if(param.Ver != PARAM_VER || crc != param.CRC)
	{
        memset(&param,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;//сброс таймеров при анперинге
}
Made on
Tilda