blob: 44eaf5c94cb5e0f722a407cefbff73093957cae7 [file] [log] [blame]
/********************************************************************************************************
* @file app_fmr.c
*
* @brief This is the source file for b85m
*
* @author ROW GROUP
* @date 06,2020
*
* @par Copyright (c) 2020, Telink Semiconductor (Shanghai) Co., Ltd. ("TELINK")
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*******************************************************************************************************/
#include "tl_common.h"
#include "drivers.h"
#include "stack/ble/ble.h"
#include "../app_config.h"
#include "../app_att.h"
#include "../battery_check.h"
#include "app_fmr.h"
#include "app_buzzer.h"
/**********************************************************************
* LOCAL MARCO
*/
#define GOOGLE_FMR_SERVICE_UUID 0x64,0xB6,0x17,0xF6,0x01,0xAF,0x7D,0xBC,0x05,0x4F,0x21,0x5A,0x01,0x00,0x03,0x18
#define GOOGL_FMR_CHAR_UUID 0x64,0xB6,0x17,0xF6,0x01,0xAF,0x7D,0xBC,0x05,0x4F,0x21,0x5A,0x02,0x00,0x03,0x18
#define GOOGL_FMR_MAX_DURATION (0x12C) // 30 sec
#define FMR_DEBUG_LOG(...)
#define fmr_buzzer_play(duty) app_buzzer_play(APP_BUZZER_PLAY_INDEX_RINGTONE_0, APP_BUZZER_STATUS_PLAY_LOOP, duty);
/**********************************************************************
* LOCAL TYPES
*/
typedef enum{
GOOGLE_FMR_EVENT_REQUEST = 0,
GOOGLE_FMR_EVENT_DURATION_ELAPSED,
GOOGLE_FMR_EVENT_RCU_FOUND,
GOOGLE_FMR_EVENT_DURATION_TOO_LONG,
GOOGLE_FMR_EVENT_LOW_BATTERY,
GOOGLE_FMR_EVENT_OTHER = 0x7F,
GOOGLE_FMR_EVENT_INVALID_REQUEST = 0xFF,
}fmr_event_t;
typedef struct {
u8 en_fmr;
u8 en_periodic_wakeup;
u16 periodic_wakeup_interval;
}app_fmr_ctrl_t;
/**********************************************************************
* ATT SERVICE
*/
#define WRAPPING_BRACES(__DATAS__) { __DATAS__ }
const u8 app_fmr_service_uuid[16] = WRAPPING_BRACES(GOOGLE_FMR_SERVICE_UUID);
const u8 app_fmr_char_uuid[16] = WRAPPING_BRACES(GOOGL_FMR_CHAR_UUID);
const u8 app_fmr_char_val[19] = {
CHAR_PROP_READ | CHAR_PROP_NOTIFY | CHAR_PROP_WRITE_WITHOUT_RSP,
U16_LO(APP_FMR_CMD_OUT_DP_H), U16_HI(APP_FMR_CMD_OUT_DP_H),
GOOGL_FMR_CHAR_UUID,
};
_attribute_data_retention_ volatile u8 app_fmr_data[3] = {0x00};
_attribute_data_retention_ u16 app_fmr_ccc = 0;
/**********************************************************************
* GLOBAL VARIABLES
*/
/**********************************************************************
* LOCAL VARIABLES
*/
_attribute_data_retention_ u32 s_fmr_counter_tick = 0;
_attribute_data_retention_ app_fmr_ctrl_t s_fmr_ctrl = {0};
/**********************************************************************
* LOCAL FUNCTIONS
*/
fmr_event_t app_fmr_get_event(void){
return app_fmr_data[0];
}
void app_fmr_set_event(fmr_event_t event){
fmr_event_t my_fmr_event = event;
switch (event) {
case GOOGLE_FMR_EVENT_REQUEST:
case GOOGLE_FMR_EVENT_DURATION_ELAPSED:
case GOOGLE_FMR_EVENT_RCU_FOUND:
case GOOGLE_FMR_EVENT_DURATION_TOO_LONG:
case GOOGLE_FMR_EVENT_LOW_BATTERY:
case GOOGLE_FMR_EVENT_OTHER:
case GOOGLE_FMR_EVENT_INVALID_REQUEST:
break;
default:
my_fmr_event = GOOGLE_FMR_EVENT_OTHER;
break;
}
app_fmr_data[0] = my_fmr_event;
printf("Set FMR event to %d\n", app_fmr_data[0]);
}
u16 app_fmr_get_duration(void){
u16 my_duration = MAKE_U16(app_fmr_data[1], app_fmr_data[2]);
return my_duration;
}
void app_fmr_set_duration(u16 duration){
app_fmr_data[1] = U16_HI(duration);
app_fmr_data[2] = U16_LO(duration);
FMR_DEBUG_LOG("Set FMR duration to DEC:[%d] HEX:[0x%02X%02X]" , duration, app_fmr_data[1], app_fmr_data[2]);
}
void app_fmr_decrease_duration(void){
u16 my_duration = app_fmr_get_duration();
if(my_duration){
my_duration--;
app_fmr_set_duration(my_duration);
}
}
void app_fmr_set_fmr_para(fmr_event_t event, u16 duration){
/* Is set in the response to write operation when the provided remaining buzz duration is too large.
* Remaining buzz duration is adjusted to a reasonable value at the same time. */
if(duration > GOOGL_FMR_MAX_DURATION){
app_fmr_set_event(GOOGLE_FMR_EVENT_DURATION_TOO_LONG);
app_fmr_set_duration(GOOGL_FMR_MAX_DURATION);
}else{
app_fmr_set_event(event);
app_fmr_set_duration(duration);
}
bls_att_pushNotifyData(APP_FMR_CMD_OUT_DP_H, (u8 *)app_fmr_data, sizeof(app_fmr_data));
printf("NOTIFY FMR DATA:");
array_printf((u8 *)app_fmr_data, sizeof(app_fmr_data));
}
/**********************************************************************
* GLOBAL FUNCTIONS
*/
u8 app_fmr_is_fmr_en(void){
return s_fmr_ctrl.en_fmr;
}
void app_fmr_set_fmr_en(u8 en){
s_fmr_ctrl.en_fmr = en;
}
int app_fmr_write_cb(void * p){
rf_packet_att_data_t *req = (rf_packet_att_data_t*)p;
u16 my_data_len = req->l2cap - 3;
// TODO
#define FMR_LOW_VOLTABE (2250) //mv
if(get_battery_voltage() < FMR_LOW_VOLTABE){
app_fmr_set_fmr_para(GOOGLE_FMR_EVENT_LOW_BATTERY, 0);
return 0;
}
/* Any write operation of length less than 3 bytes or where the last event field is not equal to PEER_REQUEST shall not be accepted
* and shall trigger INVALID_REQUEST notification (see Invalid request example). The failed write operations shall not alter
* the value of Remaining buzz duration field; */
if((my_data_len < 3) || (req->dat[0] != GOOGLE_FMR_EVENT_REQUEST)){
app_fmr_set_fmr_para(GOOGLE_FMR_EVENT_INVALID_REQUEST, app_fmr_get_duration());
return 0;
}
/* The writes of 3 bytes or more are accepted if the first byte of the last event field is set to PEER_REQUEST.
* The following 2 bytes shall be treated as the new value of remaining buzz duration (16-bit integer), all remaining bytes are ignored.
* Writing a non-zero value shall start the buzz mode for the provided duration. Writing 0 shall stop the buzz mode if active. */
u16 my_duration = MAKE_U16(req->dat[1], req->dat[2]);
if(my_duration){
/* buzzer ON. The value that is implied by every successful write operation.*/
s_fmr_counter_tick = clock_time()|1;
fmr_buzzer_play(app_buzzer_set_and_get_pwm_duty_by_voltage());
// app_led_pwm_enable(ALP_CFG_INDEX_FMR, 0);
app_fmr_set_fmr_para(GOOGLE_FMR_EVENT_REQUEST, my_duration);
}else{
/* buzzer OFF */
app_buzzer_stop();
// app_led_pwm_disable(ALP_CFG_INDEX_FMR);
app_fmr_set_fmr_para(GOOGLE_FMR_EVENT_REQUEST, 0);
}
return 0;
}
void app_fmr_parameter_init(void){
app_fmr_data[0] = 0x00;
app_fmr_data[1] = 0x00;
app_fmr_data[2] = 0x00;
s_fmr_counter_tick = 0;
app_buzzer_buffer_init();
}
u8 app_fmr_is_busy(void){
u8 my_event = app_fmr_get_event();
u16 my_duration = app_fmr_get_duration();
u8 my_flag_busy = 1;
if( (GOOGLE_FMR_EVENT_RCU_FOUND == my_event) || (0 == my_duration) ){
my_flag_busy = 0;
}
return my_flag_busy;
}
void app_fmr_on_button_event(void){
if(app_fmr_is_busy() == 0) return;
/* Set when the user picks up the remote or presses any button while in the buzz mode. */
app_buzzer_stop();
// app_led_pwm_disable(ALP_CFG_INDEX_FMR);
app_fmr_set_fmr_para(GOOGLE_FMR_EVENT_RCU_FOUND, 0);
}
void app_fmr_task(void){
if(0 == app_fmr_is_busy()){
if(app_buzzer_is_buzy()){
app_buzzer_stop();
}
// app_led_pwm_disable(ALP_CFG_INDEX_FMR);
return;
}
if(clock_time_exceed(s_fmr_counter_tick, 100*1000)){
s_fmr_counter_tick = clock_time()|1;
app_fmr_decrease_duration();
}
/* Set if and only if the remaining buzz duration is counted down to 0 */
if(app_fmr_get_duration() == 0){
app_buzzer_stop();
// app_led_pwm_disable(ALP_CFG_INDEX_FMR);
app_fmr_set_fmr_para(GOOGLE_FMR_EVENT_DURATION_ELAPSED, 0);
}
}
/**
* periodic wake-up ADV
*/
typedef struct {
u16 count;
u16 adv_interval_min;
u16 adv_interval_max;
u8 adv_type;
u8 reserved;
}app_fmr_adv_timer_table_t;
_attribute_data_retention_ u8 s_flag_fmr_adv = 0;
_attribute_data_retention_ u8 s_fmr_adv_table_index = 0;
_attribute_data_retention_ u16 s_fmr_adv_send_count = 0;
const app_fmr_adv_timer_table_t app_fmr_adv_timer_table[] = {
/* count adv_interval_min adv_interval_max adv_type reserved */
{15, ADV_INTERVAL_20MS, ADV_INTERVAL_25MS, ADV_TYPE_CONNECTABLE_DIRECTED_LOW_DUTY, 0},
{45, ADV_INTERVAL_100MS, ADV_INTERVAL_150MS, ADV_TYPE_CONNECTABLE_DIRECTED_LOW_DUTY, 0},
{120, ADV_INTERVAL_1S, ADV_INTERVAL_1S5, ADV_TYPE_CONNECTABLE_DIRECTED_LOW_DUTY, 0},
};
void app_fmr_set_periodic_wakeup(u8 en, u16 interval){
if(0 == en){
s_fmr_ctrl.en_periodic_wakeup = 0;
return;
}
if((interval <= 1440) && (interval != 0)){
/* 220707. The valid range is changed to 1~1440(min) */
s_fmr_ctrl.periodic_wakeup_interval = interval;
}else{
/* If the interval data in the flash is wrong, use 30 */
s_fmr_ctrl.periodic_wakeup_interval = 30;
}
s_fmr_ctrl.en_periodic_wakeup = 1;
printf("[SET] periodic wake-up:[%d] interval:[%d]", s_fmr_ctrl.en_periodic_wakeup, s_fmr_ctrl.periodic_wakeup_interval);
}
u8 app_fmr_is_periodic_wakeup_en(void){
return s_fmr_ctrl.en_periodic_wakeup;
}
u8 app_fmr_is_adv_busy(void){
return s_flag_fmr_adv;
}
void app_fmr_adv_settings(u16 interval_min, u16 interval_max){
u8 bond_number = blc_smp_param_getCurrentBondingDeviceNumber(); //get bonded device number
bls_ll_setAdvEnable(0); // ADV disable
if(bond_number){
smp_param_save_t bondInfo;
bls_smp_param_loadByIndex( bond_number - 1, &bondInfo); //get the latest bonding device (index: bond_number-1 )
ll_resolvingList_reset();
ll_resolvingList_add(bondInfo.peer_id_adrType,bondInfo.peer_id_addr,bondInfo.peer_irk,bondInfo.local_irk);
ll_whiteList_reset();
u8 status=0,rpa_flag=0;
if (memcmp(bondInfo.peer_id_addr, bondInfo.peer_addr, 6) == 0)
{
rpa_flag = 0;
}
else
{
extern u8 en_slave_rpa;
if(en_slave_rpa == 0)
rpa_flag = 0;
else
rpa_flag = 1;
}
// TODO check setting host address
if (0 == rpa_flag){
ll_whiteList_add(bondInfo.peer_addr_type,bondInfo.peer_addr);
status = bls_ll_setAdvParam(interval_min, interval_max,
ADV_TYPE_CONNECTABLE_DIRECTED_LOW_DUTY, OWN_ADDRESS_PUBLIC,
bondInfo.peer_addr_type, bondInfo.peer_addr,
BLT_ENABLE_ADV_ALL,
ADV_FP_NONE);
}else{
status = bls_ll_setAdvParam(interval_min, interval_max,
ADV_TYPE_CONNECTABLE_DIRECTED_LOW_DUTY, OWN_ADDRESS_RESOLVE_PRIVATE_PUBLIC,
bondInfo.peer_id_adrType, bondInfo.peer_id_addr,
BLT_ENABLE_ADV_ALL,
ADV_FP_NONE);
}
if(status != BLE_SUCCESS) { while(1); } //debug: adv setting err
bls_ll_setAdvEnable(1); //adv enable
s_flag_fmr_adv = 1;
}
}
int app_fmr_adv_timer(void){
if(!s_flag_fmr_adv){
printf("[NOTICE] FMS ADV flag is disabled !!! \n");
return -1;
}
s_fmr_adv_send_count++;
// printf("%s count: %d s_fmr_adv_count: %d \n",__FUNCTION__, s_fmr_adv_count, my_table_cnt);
if(s_fmr_adv_send_count >= app_fmr_adv_timer_table[s_fmr_adv_table_index].count){
s_fmr_adv_table_index++;
u8 my_table_cnt = sizeof(app_fmr_adv_timer_table)/sizeof(app_fmr_adv_timer_table_t);
if(s_fmr_adv_table_index >= my_table_cnt ){
bls_ll_setAdvEnable(0);
printf("End fmr direct adv \n");
s_flag_fmr_adv = 0;
return -1;
}
printf("FMS ADV parameter table_index: %d adv_interval_IDX MIN: %d MAX: %d \n",
s_fmr_adv_table_index,
app_fmr_adv_timer_table[s_fmr_adv_table_index].adv_interval_min,
app_fmr_adv_timer_table[s_fmr_adv_table_index].adv_interval_max);
app_fmr_adv_settings( app_fmr_adv_timer_table[s_fmr_adv_table_index].adv_interval_min,
app_fmr_adv_timer_table[s_fmr_adv_table_index].adv_interval_max);
}
return 0;
}
void app_fmr_try_connect(void){
if(!s_fmr_ctrl.en_periodic_wakeup) return;
printf("%s \n", __FUNCTION__ );
s_fmr_adv_send_count = 0;
s_fmr_adv_table_index = 0;
printf("FMS ADV parameter table_index: %d adv_interval_IDX MIN: %d MAX: %d \n",
s_fmr_adv_table_index,
app_fmr_adv_timer_table[s_fmr_adv_table_index].adv_interval_min,
app_fmr_adv_timer_table[s_fmr_adv_table_index].adv_interval_max);
app_fmr_adv_settings( app_fmr_adv_timer_table[s_fmr_adv_table_index].adv_interval_min,
app_fmr_adv_timer_table[s_fmr_adv_table_index].adv_interval_max);
blt_soft_timer_add(app_fmr_adv_timer, 1000000);
}
void app_fmr_delete_adv_task(void){
s_flag_fmr_adv = 0;
}
void app_fmr_periodic_wake_up(void){
/* FMS periodic wake up disabled */
printf("%s. %d \n", __FUNCTION__, s_fmr_ctrl.en_periodic_wakeup);
if(!s_fmr_ctrl.en_periodic_wakeup) return;
/* periodic wakeup interval out of range */
if( (0 == s_fmr_ctrl.periodic_wakeup_interval) || (s_fmr_ctrl.periodic_wakeup_interval > 1440) ) return;
u8 bond_number = blc_smp_param_getCurrentBondingDeviceNumber(); //get bonded device number
if(bond_number){
printf("[NOTICE] FMS periodic wake up time: %d min \n", s_fmr_ctrl.periodic_wakeup_interval);
// analog_write(USED_DEEP_ANA_REG, analog_read(USED_DEEP_ANA_REG) | (PERIODIC_WAKE_UP));
cpu_long_sleep_wakeup_32k_rc(DEEPSLEEP_MODE,PM_WAKEUP_TIMER|PM_WAKEUP_PAD, s_fmr_ctrl.periodic_wakeup_interval*CLOCK_32K_CLOCK_1MIN);
// cpu_long_sleep_wakeup_32k_rc(DEEPSLEEP_MODE,PM_WAKEUP_TIMER|PM_WAKEUP_PAD,g_p_app_fmr_ctrl->periodic_wakeup_interval*CLOCK_32K_CLOCK_1S); // for testing
}
}