Commit bace0f82 authored by schneider's avatar schneider

feat(ble): pairing dialog in bluetooth app

parent 85627336
......@@ -39,8 +39,13 @@
#include "rscp/rscp_api.h"
#include "cccd.h"
#include "epicardium.h"
#include "api/interrupt-sender.h"
#include "modules/log.h"
static bool active;
static enum ble_event_type ble_event;
/**************************************************************************************************
Macros
**************************************************************************************************/
......@@ -191,6 +196,8 @@ static const attsCccSet_t bleCccSet[BLE_NUM_CCC_IDX] =
/*! WSF handler ID */
wsfHandlerId_t bleHandlerId;
static dmConnId_t pair_connId = DM_CONN_ID_NONE;
static uint32_t pair_confirm_value;
static void BleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
/*************************************************************************************************/
......@@ -348,27 +355,87 @@ static void bleSetup(bleMsg_t *pMsg)
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, NULL);
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, NULL);
#if 0
/* TODO: card10: until we have an BLE dialog, be discoverable and bondable always */
/* start advertising; automatically set connectable/discoverable mode and bondable mode */
AppAdvStart(APP_MODE_AUTO_INIT);
#else
/* enter discoverable and bondable mode mode by default */
AppSetBondable(TRUE);
AppAdvStart(APP_MODE_DISCOVERABLE);
#endif
/* We only want to be bondable when the appropriate dialog is open */
AppSetBondable(FALSE);
/* TODO: Sadly, not advertising leads to a higher current consumption... */
if(AppDbCheckBonded() == FALSE) {
AppAdvStop();
} else {
AppAdvStart(APP_MODE_CONNECTABLE);
}
active = true;
}
void epic_ble_set_bondable(bool bondable)
{
if(!active) {
return;
}
if(bondable) {
LOG_INFO("ble", "Making bondable and discoverable");
/* We need to stop advertising in between or the
* adv set will not be changed... */
AppAdvStop();
AppSetBondable(TRUE);
AppAdvStart(APP_MODE_DISCOVERABLE);
} else {
LOG_INFO("ble", "Making connectable");
AppAdvStop();
AppSetBondable(FALSE);
if(AppDbCheckBonded()) {
AppAdvStart(APP_MODE_CONNECTABLE);
}
}
}
void bleHandleNumericComparison(dmSecCnfIndEvt_t *pCnfInd)
uint32_t epic_ble_get_compare_value(void)
{
uint32_t confirm = DmSecGetCompareValue(pCnfInd->confirm);
dmConnId_t pair_connId;
APP_TRACE_INFO1(">>> Confirm Value: %d <<<", confirm);
LOG_INFO("ble", "Confirm Value: %ld", confirm);
return pair_confirm_value;
}
void epic_ble_compare_response(bool confirmed)
{
if(!active) {
return;
}
if(pair_connId != DM_CONN_ID_NONE) {
LOG_INFO("ble", "Value confirmed: %u", confirmed);
DmSecCompareRsp(pair_connId, confirmed);
} else {
/* error condition */
}
}
static void trigger_event(enum ble_event_type event)
{
bool enabled;
epic_interrupt_is_enabled(EPIC_INT_BLE, &enabled);
if(ble_event && enabled) {
LOG_WARN("ble", "Application missed event %u", ble_event);
}
ble_event = event;
api_interrupt_trigger(EPIC_INT_BLE);
}
enum ble_event_type epic_ble_get_event(void)
{
enum ble_event_type event = ble_event;
ble_event = 0;
return event;
}
static void bleHandleNumericComparison(dmSecCnfIndEvt_t *pCnfInd)
{
if(!active) {
return;
}
pair_connId = (dmConnId_t)pCnfInd->hdr.param;
/* TODO: Verify that local and peer confirmation values match */
LOG_INFO("ble", "Confirming");
DmSecCompareRsp(pair_connId, TRUE);
pair_confirm_value = DmSecGetCompareValue(pCnfInd->confirm);
LOG_INFO("ble", "Confirm Value: %ld", pair_confirm_value);
trigger_event(BLE_EVENT_HANDLE_NUMERIC_COMPARISON);
}
/*************************************************************************************************/
......@@ -454,6 +521,8 @@ static void bleProcMsg(bleMsg_t *pMsg)
case DM_SEC_PAIR_CMPL_IND:
LOG_INFO("ble", "Secure pairing successful, auth: 0x%02X",
pMsg->dm.pairCmpl.auth);
pair_connId = DM_CONN_ID_NONE;
trigger_event(BLE_EVENT_PAIRING_COMPLETE);
/* After a successful pairing, bonding is disabled again.
* We don't want that for now. */
AppSetBondable(TRUE);
......@@ -474,6 +543,8 @@ static void bleProcMsg(bleMsg_t *pMsg)
pMsg->hdr.status);
break;
}
pair_connId = DM_CONN_ID_NONE;
trigger_event(BLE_EVENT_PAIRING_FAILED);
break;
case DM_SEC_ENCRYPT_IND:
......
......@@ -147,6 +147,12 @@ typedef _Bool bool;
#define API_CONFIG_GET_INTEGER 0x131
#define API_CONFIG_GET_BOOLEAN 0x132
#define API_CONFIG_SET_STRING 0x133
#define API_BLE_GET_COMPARE_VALUE 0x140
#define API_BLE_COMPARE_RESPONSE 0x141
#define API_BLE_SET_BONDABLE 0x142
#define API_BLE_GET_EVENT 0x143
/* clang-format on */
typedef uint32_t api_int_id_t;
......@@ -223,9 +229,10 @@ API(API_INTERRUPT_IS_ENABLED, int epic_interrupt_is_enabled(api_int_id_t int_id,
#define EPIC_INT_BHI160_MAGNETOMETER 8
/** MAX86150 ECG and PPG sensor. See :c:func:`epic_isr_max86150`. */
#define EPIC_INT_MAX86150 9
/** Bluetooth Low Energy event. See :c:func:`epic_isr_ble`. */
#define EPIC_INT_BLE 10
/* Number of defined interrupts. */
#define EPIC_INT_NUM 10
#define EPIC_INT_NUM 11
/* clang-format on */
/*
......@@ -2095,5 +2102,108 @@ API(API_CONFIG_GET_STRING, int epic_config_get_string(const char *key, char *buf
* .. versionadded:: 1.16
*/
API(API_CONFIG_SET_STRING, int epic_config_set_string(const char *key, const char *value));
/**
* Bluetooth Low Energy (BLE)
* ==========================
*/
/**
* BLE event type
*/
enum ble_event_type {
/** No event pending */
BLE_EVENT_NONE = 0,
/** Numeric comparison requested */
BLE_EVENT_HANDLE_NUMERIC_COMPARISON = 1,
/** A pairing procedure has failed */
BLE_EVENT_PAIRING_FAILED = 2,
/** A pairing procedure has successfully completed */
BLE_EVENT_PAIRING_COMPLETE = 3,
};
/**
* **Interrupt Service Routine** for :c:data:`EPIC_INT_BLE`
*
* :c:func:`epic_isr_ble` is called when the BLE stack wants to signal an
* event to the application. You can use :c:func:`epic_ble_get_event` to obtain
* the event which triggered this interrupt.
*
* Currently supported events:
*
* :c:data:`BLE_EVENT_HANDLE_NUMERIC_COMPARISON`:
* An ongoing pairing procedure requires a numeric comparison to complete.
* The compare value can be retreived using :c:func:`epic_ble_get_compare_value`.
*
* :c:data:`BLE_EVENT_PAIRING_FAILED`:
* A pairing procedure failed. The stack automatically went back advertising
* and accepting new pairings.
*
* :c:data:`BLE_EVENT_PAIRING_COMPLETE`:
* A pairing procedure has completed sucessfully.
* The stack automatically persists the pairing information, creating a bond.
*
* .. versionadded:: 1.16
*/
API_ISR(EPIC_INT_BLE, epic_isr_ble);
/**
* Retreive the event which triggered :c:func:`epic_isr_ble`
*
* The handling code needs to ensure to handle interrupts in a timely
* manner as new events will overwrite each other. Reading the event
* automatically resets it to :c:data:`BLE_EVENT_NONE`.
*
* :return: Event which triggered the interrupt.
*
* .. versionadded:: 1.16
*/
API(API_BLE_GET_EVENT, enum ble_event_type epic_ble_get_event(void));
/**
* Retrieve the compare value of an ongoing pairing procedure.
*
* If no pairing procedure is ongoing, the returned value is undefined.
*
* :return: 6 digit long compare value
*
* .. versionadded:: 1.16
*/
API(API_BLE_GET_COMPARE_VALUE, uint32_t epic_ble_get_compare_value(void));
/**
* Indicate wether the user confirmed the compare value.
*
* If a pariring procedure involving a compare value is ongoing and this
* function is called with confirmed set to ``true``, it will try to
* proceed and complete the pairing process. If called with ``false``, the
* pairing procedure will be aborted.
*
* :param bool confirmed: `true` if the user confirmed the compare value.
*
* .. versionadded:: 1.16
*/
API(API_BLE_COMPARE_RESPONSE, void epic_ble_compare_response(bool confirmed));
/**
* Allow or disallow new bondings to happen
*
* By default the card10 will not allow new bondings to be made. New
* bondings have to explicitly allowed by calling this function.
*
* While bonadable the card10 will change its advertisements to
* indicate to scanning hosts that it is available for discovery.
*
* When switching applications new bondings are automatically
* disallowed.
*
* :param bool bondable: `true` if new bondings should be allowed.
*
* .. versionadded:: 1.16
*/
API(API_BLE_SET_BONDABLE, void epic_ble_set_bondable(bool bondable));
#endif /* _EPICARDIUM_H */
......@@ -289,5 +289,10 @@ int hardware_reset(void)
epic_max86150_disable_sensor();
/*
* BLE
*/
epic_ble_set_bondable(false);
return 0;
}
......@@ -2,17 +2,28 @@ import os
import display
import time
import buttons
import sys_ble
import interrupt
CONFIG_NAME = "ble.txt"
MAC_NAME = "mac.txt"
ACTIVE_STRING = "active=true"
INACTIVE_STRING = "active=false"
ble_event = None
def ble_callback(_):
global ble_event
ble_event = sys_ble.get_event()
def init():
if CONFIG_NAME not in os.listdir("."):
with open(CONFIG_NAME, "w") as f:
f.write(INACTIVE_STRING)
interrupt.set_callback(interrupt.BLE, ble_callback)
interrupt.enable_callback(interrupt.BLE)
sys_ble.set_bondable(True)
def load_mac():
......@@ -67,21 +78,74 @@ def selector():
disp.print("toggle", posx=25, posy=40, fg=[255, 255, 255])
disp = display.open()
button_pressed = True
init()
disp = display.open()
state = 1
v_old = buttons.read()
while True:
disp.clear()
headline()
v = buttons.read(buttons.TOP_RIGHT)
if v == 0:
button_pressed = False
if not button_pressed and v & buttons.TOP_RIGHT != 0:
button_pressed = True
toggle()
v_new = buttons.read()
v = ~v_old & v_new
v_old = v_new
if state == 1:
# print config screen
disp.clear()
headline()
selector()
disp.update()
state = 2
elif state == 2:
# wait for button press or ble_event
if ble_event == sys_ble.EVENT_HANDLE_NUMERIC_COMPARISON:
ble_event = None
state = 3
if v & buttons.TOP_RIGHT:
toggle()
state = 1
elif state == 3:
# print confirmation value
compare_value = sys_ble.get_compare_value()
disp.clear()
disp.print("confirm:", posy=0, fg=[0, 255, 255])
disp.print("%06d" % compare_value, posy=20, fg=[255, 0, 0])
disp.update()
state = 4
elif state == 4:
# wait for button press or ble_event
if ble_event == sys_ble.EVENT_PAIRING_FAILED:
ble_event = None
state = 6
if v & buttons.BOTTOM_RIGHT:
sys_ble.confirm_compare_value(True)
disp.clear()
disp.print("Wait", posy=0, fg=[0, 255, 255])
disp.update()
state = 5
elif v & buttons.BOTTOM_LEFT:
sys_ble.confirm_compare_value(False)
state = 1
elif state == 5:
# Wait for pairing to complete
if ble_event == sys_ble.EVENT_PAIRING_FAILED:
ble_event = None
state = 6
elif ble_event == sys_ble.EVENT_PAIRING_COMPLETE:
ble_event = None
disp.clear()
disp.print("OK", posy=0, fg=[0, 255, 255])
disp.update()
time.sleep(5)
state = 1
elif state == 6:
# display fail screen and wait 5 seconds
disp.clear()
disp.print("Fail", posy=0, fg=[0, 255, 255])
disp.update()
time.sleep(5)
state = 1
selector()
disp.update()
time.sleep(0.1)
......@@ -14,6 +14,7 @@ modsrc = files(
'modules/os.c',
'modules/personal_state.c',
'modules/power.c',
'modules/sys_ble.c',
'modules/sys_bme680.c',
'modules/sys_display.c',
'modules/sys_leds.c',
......
......@@ -99,6 +99,7 @@ static const mp_rom_map_elem_t interrupt_module_globals_table[] = {
MP_OBJ_NEW_SMALL_INT(EPIC_INT_MAX30001_ECG) },
{ MP_ROM_QSTR(MP_QSTR_MAX86150),
MP_OBJ_NEW_SMALL_INT(EPIC_INT_MAX86150) },
{ MP_ROM_QSTR(MP_QSTR_BLE), MP_OBJ_NEW_SMALL_INT(EPIC_INT_BLE) },
};
static MP_DEFINE_CONST_DICT(
......
......@@ -189,6 +189,20 @@ Q(MAX86150)
Q(ws2812)
Q(set_all)
/* config */
Q(config)
Q(set_string)
Q(get_string)
/* BLE */
Q(BLE)
Q(ble)
Q(get_compare_value)
Q(confirm_compare_value)
Q(set_bondable)
Q(get_event)
Q(EVENT_NONE)
Q(EVENT_HANDLE_NUMERIC_COMPARISON)
Q(EVENT_PAIRING_COMPLETE)
Q(EVENT_PAIRING_FAILED)
#include "epicardium.h"
#include "py/obj.h"
#include "py/objlist.h"
#include "py/runtime.h"
#include <stdint.h>
static mp_obj_t mp_ble_confirm_compare_value(mp_obj_t confirmed_obj)
{
bool confirmed = mp_obj_is_true(confirmed_obj);
epic_ble_compare_response(confirmed);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(
ble_confirm_compare_value_obj, mp_ble_confirm_compare_value
);
static mp_obj_t mp_ble_get_compare_value(void)
{
return mp_obj_new_int(epic_ble_get_compare_value());
}
static MP_DEFINE_CONST_FUN_OBJ_0(
ble_get_compare_value_obj, mp_ble_get_compare_value
);
static mp_obj_t mp_ble_get_event(void)
{
return mp_obj_new_int(epic_ble_get_event());
}
static MP_DEFINE_CONST_FUN_OBJ_0(ble_get_event_obj, mp_ble_get_event);
static mp_obj_t mp_ble_set_bondable(mp_obj_t bondable_obj)
{
bool bondable = mp_obj_is_true(bondable_obj);
epic_ble_set_bondable(bondable);
return mp_const_none;
}
static MP_DEFINE_CONST_FUN_OBJ_1(ble_set_bondable_obj, mp_ble_set_bondable);
static const mp_rom_map_elem_t ble_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys_ble) },
{ MP_ROM_QSTR(MP_QSTR_confirm_compare_value),
MP_ROM_PTR(&ble_confirm_compare_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_compare_value),
MP_ROM_PTR(&ble_get_compare_value_obj) },
{ MP_ROM_QSTR(MP_QSTR_get_event), MP_ROM_PTR(&ble_get_event_obj) },
{ MP_ROM_QSTR(MP_QSTR_set_bondable),
MP_ROM_PTR(&ble_set_bondable_obj) },
/* Event Numbers */
{ MP_ROM_QSTR(MP_QSTR_EVENT_NONE),
MP_OBJ_NEW_SMALL_INT(BLE_EVENT_NONE) },
{ MP_ROM_QSTR(MP_QSTR_EVENT_HANDLE_NUMERIC_COMPARISON),
MP_OBJ_NEW_SMALL_INT(BLE_EVENT_HANDLE_NUMERIC_COMPARISON) },
{ MP_ROM_QSTR(MP_QSTR_EVENT_PAIRING_FAILED),
MP_OBJ_NEW_SMALL_INT(BLE_EVENT_PAIRING_FAILED) },
{ MP_ROM_QSTR(MP_QSTR_EVENT_PAIRING_COMPLETE),
MP_OBJ_NEW_SMALL_INT(BLE_EVENT_PAIRING_COMPLETE) },
};
static MP_DEFINE_CONST_DICT(ble_module_globals, ble_module_globals_table);
const mp_obj_module_t ble_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&ble_module_globals,
};
/* Register the module to make it available in Python */
/* clang-format off */
MP_REGISTER_MODULE(MP_QSTR_sys_ble, ble_module, MODULE_BLE_ENABLED);
......@@ -71,6 +71,7 @@ int mp_hal_trng_read_int(void);
#define MODULE_VIBRA_ENABLED (1)
#define MODULE_WS2812_ENABLED (1)
#define MODULE_CONFIG_ENABLED (1)
#define MODULE_BLE_ENABLED (1)
/*
* This port is intended to be 32-bit, but unfortunately, int32_t for
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment