Commit bace0f82 authored by schneider's avatar schneider

feat(ble): pairing dialog in bluetooth app

parent 85627336
...@@ -39,8 +39,13 @@ ...@@ -39,8 +39,13 @@
#include "rscp/rscp_api.h" #include "rscp/rscp_api.h"
#include "cccd.h" #include "cccd.h"
#include "epicardium.h"
#include "api/interrupt-sender.h"
#include "modules/log.h" #include "modules/log.h"
static bool active;
static enum ble_event_type ble_event;
/************************************************************************************************** /**************************************************************************************************
Macros Macros
**************************************************************************************************/ **************************************************************************************************/
...@@ -191,6 +196,8 @@ static const attsCccSet_t bleCccSet[BLE_NUM_CCC_IDX] = ...@@ -191,6 +196,8 @@ static const attsCccSet_t bleCccSet[BLE_NUM_CCC_IDX] =
/*! WSF handler ID */ /*! WSF handler ID */
wsfHandlerId_t bleHandlerId; 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); static void BleHandler(wsfEventMask_t event, wsfMsgHdr_t *pMsg);
/*************************************************************************************************/ /*************************************************************************************************/
...@@ -348,27 +355,87 @@ static void bleSetup(bleMsg_t *pMsg) ...@@ -348,27 +355,87 @@ static void bleSetup(bleMsg_t *pMsg)
AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, NULL); AppAdvSetData(APP_ADV_DATA_CONNECTABLE, 0, NULL);
AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, NULL); AppAdvSetData(APP_SCAN_DATA_CONNECTABLE, 0, NULL);
#if 0 /* We only want to be bondable when the appropriate dialog is open */
/* TODO: card10: until we have an BLE dialog, be discoverable and bondable always */ AppSetBondable(FALSE);
/* start advertising; automatically set connectable/discoverable mode and bondable mode */ /* TODO: Sadly, not advertising leads to a higher current consumption... */
AppAdvStart(APP_MODE_AUTO_INIT); if(AppDbCheckBonded() == FALSE) {
#else AppAdvStop();
/* enter discoverable and bondable mode mode by default */ } else {
AppSetBondable(TRUE); AppAdvStart(APP_MODE_CONNECTABLE);
AppAdvStart(APP_MODE_DISCOVERABLE); }
#endif 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); return pair_confirm_value;
dmConnId_t pair_connId; }
APP_TRACE_INFO1(">>> Confirm Value: %d <<<", confirm);
LOG_INFO("ble", "Confirm Value: %ld", confirm); 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; pair_connId = (dmConnId_t)pCnfInd->hdr.param;
/* TODO: Verify that local and peer confirmation values match */ pair_confirm_value = DmSecGetCompareValue(pCnfInd->confirm);
LOG_INFO("ble", "Confirming"); LOG_INFO("ble", "Confirm Value: %ld", pair_confirm_value);
DmSecCompareRsp(pair_connId, TRUE); trigger_event(BLE_EVENT_HANDLE_NUMERIC_COMPARISON);
} }
/*************************************************************************************************/ /*************************************************************************************************/
...@@ -454,6 +521,8 @@ static void bleProcMsg(bleMsg_t *pMsg) ...@@ -454,6 +521,8 @@ static void bleProcMsg(bleMsg_t *pMsg)
case DM_SEC_PAIR_CMPL_IND: case DM_SEC_PAIR_CMPL_IND:
LOG_INFO("ble", "Secure pairing successful, auth: 0x%02X", LOG_INFO("ble", "Secure pairing successful, auth: 0x%02X",
pMsg->dm.pairCmpl.auth); pMsg->dm.pairCmpl.auth);
pair_connId = DM_CONN_ID_NONE;
trigger_event(BLE_EVENT_PAIRING_COMPLETE);
/* After a successful pairing, bonding is disabled again. /* After a successful pairing, bonding is disabled again.
* We don't want that for now. */ * We don't want that for now. */
AppSetBondable(TRUE); AppSetBondable(TRUE);
...@@ -474,6 +543,8 @@ static void bleProcMsg(bleMsg_t *pMsg) ...@@ -474,6 +543,8 @@ static void bleProcMsg(bleMsg_t *pMsg)
pMsg->hdr.status); pMsg->hdr.status);
break; break;
} }
pair_connId = DM_CONN_ID_NONE;
trigger_event(BLE_EVENT_PAIRING_FAILED);
break; break;
case DM_SEC_ENCRYPT_IND: case DM_SEC_ENCRYPT_IND:
......
...@@ -147,6 +147,12 @@ typedef _Bool bool; ...@@ -147,6 +147,12 @@ typedef _Bool bool;
#define API_CONFIG_GET_INTEGER 0x131 #define API_CONFIG_GET_INTEGER 0x131
#define API_CONFIG_GET_BOOLEAN 0x132 #define API_CONFIG_GET_BOOLEAN 0x132
#define API_CONFIG_SET_STRING 0x133 #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 */ /* clang-format on */
typedef uint32_t api_int_id_t; 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, ...@@ -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 #define EPIC_INT_BHI160_MAGNETOMETER 8
/** MAX86150 ECG and PPG sensor. See :c:func:`epic_isr_max86150`. */ /** MAX86150 ECG and PPG sensor. See :c:func:`epic_isr_max86150`. */
#define EPIC_INT_MAX86150 9 #define EPIC_INT_MAX86150 9
/** Bluetooth Low Energy event. See :c:func:`epic_isr_ble`. */
#define EPIC_INT_BLE 10
/* Number of defined interrupts. */ /* Number of defined interrupts. */
#define EPIC_INT_NUM 10 #define EPIC_INT_NUM 11
/* clang-format on */ /* clang-format on */
/* /*
...@@ -2095,5 +2102,108 @@ API(API_CONFIG_GET_STRING, int epic_config_get_string(const char *key, char *buf ...@@ -2095,5 +2102,108 @@ API(API_CONFIG_GET_STRING, int epic_config_get_string(const char *key, char *buf
* .. versionadded:: 1.16 * .. versionadded:: 1.16
*/ */
API(API_CONFIG_SET_STRING, int epic_config_set_string(const char *key, const char *value)); 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 */ #endif /* _EPICARDIUM_H */
...@@ -289,5 +289,10 @@ int hardware_reset(void) ...@@ -289,5 +289,10 @@ int hardware_reset(void)
epic_max86150_disable_sensor(); epic_max86150_disable_sensor();
/*
* BLE
*/
epic_ble_set_bondable(false);
return 0; return 0;
} }
...@@ -2,17 +2,28 @@ import os ...@@ -2,17 +2,28 @@ import os
import display import display
import time import time
import buttons import buttons
import sys_ble
import interrupt
CONFIG_NAME = "ble.txt" CONFIG_NAME = "ble.txt"
MAC_NAME = "mac.txt" MAC_NAME = "mac.txt"
ACTIVE_STRING = "active=true" ACTIVE_STRING = "active=true"
INACTIVE_STRING = "active=false" INACTIVE_STRING = "active=false"
ble_event = None
def ble_callback(_):
global ble_event
ble_event = sys_ble.get_event()
def init(): def init():
if CONFIG_NAME not in os.listdir("."): if CONFIG_NAME not in os.listdir("."):
with open(CONFIG_NAME, "w") as f: with open(CONFIG_NAME, "w") as f:
f.write(INACTIVE_STRING) f.write(INACTIVE_STRING)
interrupt.set_callback(interrupt.BLE, ble_callback)
interrupt.enable_callback(interrupt.BLE)
sys_ble.set_bondable(True)
def load_mac(): def load_mac():
...@@ -67,21 +78,74 @@ def selector(): ...@@ -67,21 +78,74 @@ def selector():
disp.print("toggle", posx=25, posy=40, fg=[255, 255, 255]) disp.print("toggle", posx=25, posy=40, fg=[255, 255, 255])
disp = display.open()
button_pressed = True
init() init()
disp = display.open()
state = 1
v_old = buttons.read()
while True: while True:
disp.clear() v_new = buttons.read()
headline() v = ~v_old & v_new
v = buttons.read(buttons.TOP_RIGHT) v_old = v_new
if v == 0:
button_pressed = False if state == 1:
# print config screen
if not button_pressed and v & buttons.TOP_RIGHT != 0: disp.clear()
button_pressed = True headline()
toggle() 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) time.sleep(0.1)
...@@ -14,6 +14,7 @@ modsrc = files( ...@@ -14,6 +14,7 @@ modsrc = files(
'modules/os.c', 'modules/os.c',
'modules/personal_state.c', 'modules/personal_state.c',
'modules/power.c', 'modules/power.c',
'modules/sys_ble.c',
'modules/sys_bme680.c', 'modules/sys_bme680.c',
'modules/sys_display.c', 'modules/sys_display.c',
'modules/sys_leds.c', 'modules/sys_leds.c',
......
...@@ -99,6 +99,7 @@ static const mp_rom_map_elem_t interrupt_module_globals_table[] = { ...@@ -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_OBJ_NEW_SMALL_INT(EPIC_INT_MAX30001_ECG) },
{ MP_ROM_QSTR(MP_QSTR_MAX86150), { MP_ROM_QSTR(MP_QSTR_MAX86150),
MP_OBJ_NEW_SMALL_INT(EPIC_INT_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( static MP_DEFINE_CONST_DICT(
......
...@@ -189,6 +189,20 @@ Q(MAX86150) ...@@ -189,6 +189,20 @@ Q(MAX86150)
Q(ws2812) Q(ws2812)
Q(set_all) Q(set_all)
/* config */
Q(config) Q(config)
Q(set_string) Q(set_string)
Q(get_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); ...@@ -71,6 +71,7 @@ int mp_hal_trng_read_int(void);
#define MODULE_VIBRA_ENABLED (1) #define MODULE_VIBRA_ENABLED (1)
#define MODULE_WS2812_ENABLED (1) #define MODULE_WS2812_ENABLED (1)
#define MODULE_CONFIG_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 * 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