diff --git a/source/application/bluetooth.c b/source/application/bluetooth.c index 9388dbd0..f36f289e 100644 --- a/source/application/bluetooth.c +++ b/source/application/bluetooth.c @@ -39,13 +39,13 @@ nrf_nvic_state_t nrf_nvic_state = {{0}, 0}; extern uint32_t __ram_start; static uint32_t ram_start = (uint32_t)&__ram_start; -static struct ble_handles_t +static struct ble_peripheral_handles_t { uint16_t connection; uint8_t advertising; ble_gatts_char_handles_t repl_rx_write; ble_gatts_char_handles_t repl_tx_notification; -} ble_handles = { +} ble_peripheral_handles = { .connection = BLE_CONN_HANDLE_INVALID, .advertising = BLE_GAP_ADV_SET_HANDLE_NOT_SET, }; @@ -80,15 +80,73 @@ static struct bond_information_t }, }; +ble_gap_sec_params_t central_sec_param = { + .bond = 1, + .keypress = 0, + .io_caps = BLE_GAP_IO_CAPS_NONE, + .min_key_size = 7, + .max_key_size = 16, + .kdist_own.enc = 1, + .kdist_own.id = 1, + .kdist_peer.enc = 1, + .kdist_peer.id = 1, +}; + +ble_gap_enc_key_t own_enc_key; /**< Encryption Key, or NULL. */ +ble_gap_id_key_t own_id_key; /**< Identity Key, or NULL. */ +ble_gap_sign_info_t own_sign_key; /**< Signing Key, or NULL. */ +ble_gap_lesc_p256_pk_t own_pk; + +ble_gap_enc_key_t peer_enc_key; /**< Encryption Key, or NULL. */ +ble_gap_id_key_t peer_id_key; /**< Identity Key, or NULL. */ +ble_gap_sign_info_t peer_sign_key; /**< Signing Key, or NULL. */ +ble_gap_lesc_p256_pk_t peer_pk; + +ble_gap_sec_keyset_t central_keyset = { + .keys_own.p_enc_key = &own_enc_key, + .keys_own.p_id_key = &own_id_key, + .keys_own.p_sign_key = &own_sign_key, + .keys_own.p_pk = &own_pk, + + .keys_peer.p_enc_key = &peer_enc_key, + .keys_peer.p_id_key = &peer_id_key, + .keys_peer.p_sign_key = &peer_sign_key, + .keys_peer.p_pk = &peer_pk, +}; + bool flash_write_in_progress = false; uint16_t ble_negotiated_mtu; +uint8_t scan_buffer_data[NRF_BLE_SCAN_BUFFER]; + static void softdevice_assert_handler(uint32_t id, uint32_t pc, uint32_t info) { error_with_message("Softdevice crashed"); } +static uint8_t find_ad_type(uint16_t ad_type, uint8_t* p_data, uint8_t data_len, uint8_t* offset) +{ + size_t i=0; + uint16_t this_ad_type; + uint8_t ad_type_len = 0; + + while (i+1 < data_len) + { + ad_type_len = p_data[i]; + this_ad_type = p_data[i+1]; + + if (this_ad_type == ad_type) + { + *offset = i+2; + return ad_type_len-1; + } + else i += ad_type_len+1; // +1 offset for ad_type_len + } + + return 0; +} + void SD_EVT_IRQHandler(void) { uint32_t evt_id; @@ -136,41 +194,57 @@ void SD_EVT_IRQHandler(void) case BLE_GAP_EVT_CONNECTED: { - ble_handles.connection = ble_evt - ->evt - .gap_evt - .conn_handle; + if (ble_evt->evt.gap_evt.params.connected.role == BLE_GAP_ROLE_PERIPH) + { + ble_peripheral_handles.connection = ble_evt + ->evt + .gap_evt + .conn_handle; - ble_gap_conn_params_t conn_params; + ble_gap_conn_params_t conn_params; - check_error(sd_ble_gap_ppcp_get(&conn_params)); + check_error(sd_ble_gap_ppcp_get(&conn_params)); - check_error(sd_ble_gap_conn_param_update(ble_handles.connection, - &conn_params)); + check_error(sd_ble_gap_conn_param_update(ble_peripheral_handles.connection, + &conn_params)); - check_error(sd_ble_gatts_sys_attr_set(ble_handles.connection, - NULL, - 0, - 0)); + check_error(sd_ble_gatts_sys_attr_set(ble_peripheral_handles.connection, + NULL, + 0, + 0)); - check_error(sd_ble_gap_authenticate(ble_handles.connection, - &bond.sec_param)); + check_error(sd_ble_gap_authenticate(ble_peripheral_handles.connection, + &bond.sec_param)); + } + + if (ble_evt->evt.gap_evt.params.connected.role == BLE_GAP_ROLE_CENTRAL) + { + central_conn_handle = ble_evt->evt.gap_evt.conn_handle; + } break; } case BLE_GAP_EVT_DISCONNECTED: { - ble_handles.connection = BLE_CONN_HANDLE_INVALID; + if (ble_evt->evt.gap_evt.conn_handle == ble_peripheral_handles.connection) + { + ble_peripheral_handles.connection = BLE_CONN_HANDLE_INVALID; + + check_error(sd_ble_gap_adv_start(ble_peripheral_handles.advertising, 1)); + } - check_error(sd_ble_gap_adv_start(ble_handles.advertising, 1)); + if (ble_evt->evt.gap_evt.conn_handle == central_conn_handle) + { + central_conn_handle = BLE_CONN_HANDLE_INVALID; + } break; } case BLE_GAP_EVT_AUTH_KEY_REQUEST: { - check_error(sd_ble_gap_auth_key_reply(ble_handles.connection, + check_error(sd_ble_gap_auth_key_reply(ble_peripheral_handles.connection, BLE_GAP_AUTH_KEY_TYPE_NONE, NULL)); } @@ -199,7 +273,7 @@ void SD_EVT_IRQHandler(void) .client_rx_mtu; // Respond with our max MTU size - sd_ble_gatts_exchange_mtu_reply(ble_handles.connection, + sd_ble_gatts_exchange_mtu_reply(ble_peripheral_handles.connection, BLE_PREFERRED_MAX_MTU); // Choose the smaller MTU as the final length we'll use @@ -215,12 +289,12 @@ void SD_EVT_IRQHandler(void) { // If REPL service if (ble_evt->evt.gatts_evt.params.write.handle == - ble_handles.repl_rx_write.value_handle) + ble_peripheral_handles.repl_rx_write.value_handle) { // Handle raw data if (ble_evt->evt.gatts_evt.params.write.data[0] == 0x01) { - lua_bluetooth_data_interrupt( + lua_bluetooth_peripheral_data_interrupt( ble_evt->evt.gatts_evt.params.write.data + 1, ble_evt->evt.gatts_evt.params.write.len - 1); } @@ -248,7 +322,7 @@ void SD_EVT_IRQHandler(void) case BLE_GATTS_EVT_TIMEOUT: { check_error(sd_ble_gap_disconnect( - ble_handles.connection, + ble_peripheral_handles.connection, BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION)); break; @@ -256,7 +330,7 @@ void SD_EVT_IRQHandler(void) case BLE_GATTS_EVT_SYS_ATTR_MISSING: { - check_error(sd_ble_gatts_sys_attr_set(ble_handles.connection, + check_error(sd_ble_gatts_sys_attr_set(ble_peripheral_handles.connection, NULL, 0, 0)); @@ -266,7 +340,7 @@ void SD_EVT_IRQHandler(void) case BLE_GAP_EVT_DATA_LENGTH_UPDATE_REQUEST: { - check_error(sd_ble_gap_data_length_update(ble_handles.connection, + check_error(sd_ble_gap_data_length_update(ble_peripheral_handles.connection, NULL, NULL)); @@ -275,38 +349,51 @@ void SD_EVT_IRQHandler(void) case BLE_GAP_EVT_SEC_PARAMS_REQUEST: { - size_t bonded = 0; - - for (size_t i = 0; - i < sizeof(bond.keyset.keys_own.p_enc_key->enc_info.ltk); - i++) + if (ble_evt->evt.gap_evt.conn_handle == ble_peripheral_handles.connection) { - if (bond.keyset.keys_own.p_enc_key->enc_info.ltk[i] == 0xff) + size_t bonded = 0; + + for (size_t i = 0; + i < sizeof(bond.keyset.keys_own.p_enc_key->enc_info.ltk); + i++) { - bonded++; + if (bond.keyset.keys_own.p_enc_key->enc_info.ltk[i] == 0xff) + { + bonded++; + } } - } - if (bonded == sizeof(bond.keyset.keys_own.p_enc_key->enc_info.ltk)) - { - check_error(sd_ble_gap_sec_params_reply( - ble_handles.connection, - BLE_GAP_SEC_STATUS_SUCCESS, - &bond.sec_param, - &bond.keyset)); + if (bonded == sizeof(bond.keyset.keys_own.p_enc_key->enc_info.ltk)) + { + check_error(sd_ble_gap_sec_params_reply( + ble_peripheral_handles.connection, + BLE_GAP_SEC_STATUS_SUCCESS, + &bond.sec_param, + &bond.keyset)); + } + + else + { + check_error(sd_ble_gap_sec_params_reply( + ble_peripheral_handles.connection, + BLE_GAP_SEC_STATUS_AUTH_REQ, + NULL, + NULL)); + + check_error(sd_ble_gap_disconnect( + ble_peripheral_handles.connection, + BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION)); + } } - else + if (ble_evt->evt.gap_evt.conn_handle == central_conn_handle) { check_error(sd_ble_gap_sec_params_reply( - ble_handles.connection, - BLE_GAP_SEC_STATUS_AUTH_REQ, + central_conn_handle, + BLE_GAP_SEC_STATUS_SUCCESS, NULL, - NULL)); - - check_error(sd_ble_gap_disconnect( - ble_handles.connection, - BLE_HCI_REMOTE_USER_TERMINATED_CONNECTION)); + ¢ral_keyset + )); } break; @@ -315,7 +402,7 @@ void SD_EVT_IRQHandler(void) case BLE_GAP_EVT_SEC_INFO_REQUEST: { check_error(sd_ble_gap_sec_info_reply( - ble_handles.connection, + ble_peripheral_handles.connection, &bond.keyset.keys_own.p_enc_key->enc_info, NULL, NULL)); @@ -325,13 +412,16 @@ void SD_EVT_IRQHandler(void) case BLE_GAP_EVT_AUTH_STATUS: { - if (ble_evt->evt.gap_evt.params.auth_status.auth_status == - BLE_GAP_SEC_STATUS_SUCCESS) + if (ble_evt->evt.gap_evt.conn_handle == ble_peripheral_handles.connection) { - flash_write( - bond_storage, - (uint32_t *)&bond.keyset.keys_own.p_enc_key->enc_info, - sizeof(bond.keyset.keys_own.p_enc_key->enc_info)); + if (ble_evt->evt.gap_evt.params.auth_status.auth_status == + BLE_GAP_SEC_STATUS_SUCCESS) + { + flash_write( + bond_storage, + (uint32_t *)&bond.keyset.keys_own.p_enc_key->enc_info, + sizeof(bond.keyset.keys_own.p_enc_key->enc_info)); + } } break; @@ -347,6 +437,71 @@ void SD_EVT_IRQHandler(void) break; } + case BLE_GAP_EVT_ADV_REPORT: + { + ble_gap_evt_adv_report_t *adv_report = &ble_evt->evt.gap_evt.params.adv_report; + bool is_unique = true; + + uint8_t name_offset = 0; + uint8_t name_len = find_ad_type(BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME, + adv_report->data.p_data, adv_report->data.len, &name_offset); + + for (size_t i=0; ipeer_addr.addr, + scan_data.address[i].addr, BLE_GAP_ADDR_LEN) == 0) { + is_unique = false; + break; + } + } + + if (is_unique && name_len != 0) + { + memcpy(&scan_data.address[scan_data.len].addr, + adv_report->peer_addr.addr, + BLE_GAP_ADDR_LEN); + + scan_data.name_len[scan_data.len] = name_len; + + // offset +2 to exclude packet size and ad_type + memcpy(&scan_data.name[scan_data.len], + &adv_report->data.p_data[name_offset], + scan_data.name_len[scan_data.len]); + + scan_data.len++; + } + break; + } + + case BLE_GAP_EVT_TIMEOUT: + { + break; + } + + case BLE_GAP_EVT_SEC_REQUEST: + { + uint32_t error = sd_ble_gap_authenticate(ble_evt->evt.gap_evt.conn_handle, ¢ral_sec_param); + break; + } + + case BLE_GAP_EVT_CONN_PARAM_UPDATE_REQUEST: + { + sd_ble_gap_conn_param_update(ble_evt->evt.gap_evt.conn_handle, + &ble_evt->evt.gap_evt.params.conn_param_update_request.conn_params); + + break; + } + + case BLE_GATTC_EVT_HVX: + { + + lua_bluetooth_central_data_interrupt( + ble_evt->evt.gattc_evt.params.hvx.data, + ble_evt->evt.gattc_evt.params.hvx.len); + + break; + } + default: { LOG("Unhandled BLE event: %u", ble_evt->header.evt_id); @@ -364,16 +519,25 @@ void bluetooth_setup(bool factory_reset) // Enable softdevice interrupt check_error(sd_nvic_EnableIRQ((IRQn_Type)SD_EVT_IRQn)); - // Add GAP configuration to the BLE stack + // Add GAP configuration for peripheral to the BLE stack ble_cfg_t cfg; cfg.conn_cfg.conn_cfg_tag = 1; - cfg.conn_cfg.params.gap_conn_cfg.conn_count = 1; + cfg.conn_cfg.params.gap_conn_cfg.conn_count = 2; cfg.conn_cfg.params.gap_conn_cfg.event_length = 300; check_error(sd_ble_cfg_set(BLE_CONN_CFG_GAP, &cfg, ram_start)); - // Set BLE role to peripheral only + // Add GAP configuration for central to the BLE stack + memset(&cfg, 0, sizeof(cfg)); + cfg.conn_cfg.conn_cfg_tag = 2; + cfg.conn_cfg.params.gap_conn_cfg.conn_count = 1; + cfg.conn_cfg.params.gap_conn_cfg.event_length = BLE_GAP_EVENT_LENGTH_DEFAULT; + check_error(sd_ble_cfg_set(BLE_CONN_CFG_GAP, &cfg, ram_start)); + + // Set BLE role memset(&cfg, 0, sizeof(cfg)); cfg.gap_cfg.role_count_cfg.periph_role_count = 1; + cfg.gap_cfg.role_count_cfg.central_role_count = 1; + cfg.gap_cfg.role_count_cfg.central_sec_count = 1; check_error(sd_ble_cfg_set(BLE_GAP_CFG_ROLE_COUNT, &cfg, ram_start)); // Set max MTU size @@ -403,11 +567,11 @@ void bluetooth_setup(bool factory_reset) cfg.gatts_cfg.service_changed.service_changed = 0; check_error(sd_ble_cfg_set(BLE_GATTS_CFG_SERVICE_CHANGED, &cfg, ram_start)); + LOG("Softdevice using 0x%lx bytes of RAM", ram_start - 0x20000000); + // Start the Softdevice check_error(sd_ble_enable(&ram_start)); - LOG("Softdevice using 0x%lx bytes of RAM", ram_start - 0x20000000); - // Set device name ble_gap_conn_sec_mode_t write_permission; BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&write_permission); @@ -513,12 +677,12 @@ void bluetooth_setup(bool factory_reset) check_error(sd_ble_gatts_characteristic_add(repl_service_handle, &rx_char_md, &rx_attr, - &ble_handles.repl_rx_write)); + &ble_peripheral_handles.repl_rx_write)); check_error(sd_ble_gatts_characteristic_add(repl_service_handle, &tx_char_md, &tx_attr, - &ble_handles.repl_tx_notification)); + &ble_peripheral_handles.repl_tx_notification)); // Add name to advertising payload adv.payload[adv.length++] = strlen((const char *)device_name) + 1; @@ -558,34 +722,51 @@ void bluetooth_setup(bool factory_reset) adv_params.interval = (20 * 1000) / 625; // Configure the advertising set - check_error(sd_ble_gap_adv_set_configure(&ble_handles.advertising, + check_error(sd_ble_gap_adv_set_configure(&ble_peripheral_handles.advertising, &adv_data, &adv_params)); // Start advertising - check_error(sd_ble_gap_adv_start(ble_handles.advertising, 1)); + check_error(sd_ble_gap_adv_start(ble_peripheral_handles.advertising, 1)); + + memset(&scan_data, 0, sizeof(scan_data)); + + scan_data.scan_params.active = 1; + scan_data.scan_params.interval = 160; + scan_data.scan_params.window = 80; + scan_data.scan_params.timeout = 300; + scan_data.scan_params.filter_policy = BLE_GAP_SCAN_FP_ACCEPT_ALL; + scan_data.scan_params.scan_phys = BLE_GAP_PHY_1MBPS; + scan_data.scan_params.extended = 1; + + scan_data.conn_params.conn_sup_timeout = 620; + scan_data.conn_params.min_conn_interval = 80; + scan_data.conn_params.max_conn_interval = 400; + scan_data.conn_params.slave_latency = 5; + scan_data.scan_buffer.p_data = (uint8_t *) &scan_buffer_data; + scan_data.scan_buffer.len = sizeof(scan_buffer_data); } bool bluetooth_is_connected(void) { - return ble_handles.connection == BLE_CONN_HANDLE_INVALID ? false : true; + return ble_peripheral_handles.connection == BLE_CONN_HANDLE_INVALID ? false : true; } bool bluetooth_send_data(const uint8_t *data, size_t length) { - if (ble_handles.connection == BLE_CONN_HANDLE_INVALID) + if (ble_peripheral_handles.connection == BLE_CONN_HANDLE_INVALID) { return true; } // Initialise the handle value parameters ble_gatts_hvx_params_t hvx_params = {0}; - hvx_params.handle = ble_handles.repl_tx_notification.value_handle; + hvx_params.handle = ble_peripheral_handles.repl_tx_notification.value_handle; hvx_params.p_data = data; hvx_params.p_len = (uint16_t *)&length; hvx_params.type = BLE_GATT_HVX_NOTIFICATION; - uint32_t status = sd_ble_gatts_hvx(ble_handles.connection, &hvx_params); + uint32_t status = sd_ble_gatts_hvx(ble_peripheral_handles.connection, &hvx_params); if (status == NRF_SUCCESS) { diff --git a/source/application/bluetooth.h b/source/application/bluetooth.h index b8a919dd..0cb82617 100644 --- a/source/application/bluetooth.h +++ b/source/application/bluetooth.h @@ -27,12 +27,68 @@ #include #include #include +#include "ble.h" #define BLE_PREFERRED_MAX_MTU 256 extern uint16_t ble_negotiated_mtu; +#define NRF_BLE_SCAN_BUFFER 255 + +#define SCAN_LIST_SIZE 10 +struct scan_data_t { + ble_data_t scan_buffer; + ble_gap_scan_params_t scan_params; + ble_gap_conn_params_t conn_params; + ble_gap_addr_t address[SCAN_LIST_SIZE]; + char name[SCAN_LIST_SIZE][32]; + uint8_t name_len[SCAN_LIST_SIZE]; + uint8_t len; +}; + +extern struct scan_data_t scan_data; + +extern uint16_t central_conn_handle; + void bluetooth_setup(bool factory_reset); bool bluetooth_is_connected(void); -bool bluetooth_send_data(const uint8_t *data, size_t length); \ No newline at end of file +bool bluetooth_send_data(const uint8_t *data, size_t length); + +/* BLE_GAP_AD_TYPE_DEFINITIONS GAP Advertising and Scan Response Data format + * Found at https://www.bluetooth.org/Technical/AssignedNumbers/generic_access_profile.htm + */ +#define BLE_GAP_AD_TYPE_FLAGS 0x01 /**< Flags for discoverability. */ +#define BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_MORE_AVAILABLE 0x02 /**< Partial list of 16 bit service UUIDs. */ +#define BLE_GAP_AD_TYPE_16BIT_SERVICE_UUID_COMPLETE 0x03 /**< Complete list of 16 bit service UUIDs. */ +#define BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_MORE_AVAILABLE 0x04 /**< Partial list of 32 bit service UUIDs. */ +#define BLE_GAP_AD_TYPE_32BIT_SERVICE_UUID_COMPLETE 0x05 /**< Complete list of 32 bit service UUIDs. */ +#define BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_MORE_AVAILABLE 0x06 /**< Partial list of 128 bit service UUIDs. */ +#define BLE_GAP_AD_TYPE_128BIT_SERVICE_UUID_COMPLETE 0x07 /**< Complete list of 128 bit service UUIDs. */ +#define BLE_GAP_AD_TYPE_SHORT_LOCAL_NAME 0x08 /**< Short local device name. */ +#define BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME 0x09 /**< Complete local device name. */ +#define BLE_GAP_AD_TYPE_TX_POWER_LEVEL 0x0A /**< Transmit power level. */ +#define BLE_GAP_AD_TYPE_CLASS_OF_DEVICE 0x0D /**< Class of device. */ +#define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C 0x0E /**< Simple Pairing Hash C. */ +#define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R 0x0F /**< Simple Pairing Randomizer R. */ +#define BLE_GAP_AD_TYPE_SECURITY_MANAGER_TK_VALUE 0x10 /**< Security Manager TK Value. */ +#define BLE_GAP_AD_TYPE_SECURITY_MANAGER_OOB_FLAGS 0x11 /**< Security Manager Out Of Band Flags. */ +#define BLE_GAP_AD_TYPE_SLAVE_CONNECTION_INTERVAL_RANGE 0x12 /**< Slave Connection Interval Range. */ +#define BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_16BIT 0x14 /**< List of 16-bit Service Solicitation UUIDs. */ +#define BLE_GAP_AD_TYPE_SOLICITED_SERVICE_UUIDS_128BIT 0x15 /**< List of 128-bit Service Solicitation UUIDs. */ +#define BLE_GAP_AD_TYPE_SERVICE_DATA 0x16 /**< Service Data - 16-bit UUID. */ +#define BLE_GAP_AD_TYPE_PUBLIC_TARGET_ADDRESS 0x17 /**< Public Target Address. */ +#define BLE_GAP_AD_TYPE_RANDOM_TARGET_ADDRESS 0x18 /**< Random Target Address. */ +#define BLE_GAP_AD_TYPE_APPEARANCE 0x19 /**< Appearance. */ +#define BLE_GAP_AD_TYPE_ADVERTISING_INTERVAL 0x1A /**< Advertising Interval. */ +#define BLE_GAP_AD_TYPE_LE_BLUETOOTH_DEVICE_ADDRESS 0x1B /**< LE Bluetooth Device Address. */ +#define BLE_GAP_AD_TYPE_LE_ROLE 0x1C /**< LE Role. */ +#define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_HASH_C256 0x1D /**< Simple Pairing Hash C-256. */ +#define BLE_GAP_AD_TYPE_SIMPLE_PAIRING_RANDOMIZER_R256 0x1E /**< Simple Pairing Randomizer R-256. */ +#define BLE_GAP_AD_TYPE_SERVICE_DATA_32BIT_UUID 0x20 /**< Service Data - 32-bit UUID. */ +#define BLE_GAP_AD_TYPE_SERVICE_DATA_128BIT_UUID 0x21 /**< Service Data - 128-bit UUID. */ +#define BLE_GAP_AD_TYPE_LESC_CONFIRMATION_VALUE 0x22 /**< LE Secure Connections Confirmation Value */ +#define BLE_GAP_AD_TYPE_LESC_RANDOM_VALUE 0x23 /**< LE Secure Connections Random Value */ +#define BLE_GAP_AD_TYPE_URI 0x24 /**< URI */ +#define BLE_GAP_AD_TYPE_3D_INFORMATION_DATA 0x3D /**< 3D Information Data. */ +#define BLE_GAP_AD_TYPE_MANUFACTURER_SPECIFIC_DATA 0xFF /**< Manufacturer Specific Data. */ \ No newline at end of file diff --git a/source/application/lua_libraries/bluetooth.c b/source/application/lua_libraries/bluetooth.c index 78b19412..90593933 100644 --- a/source/application/lua_libraries/bluetooth.c +++ b/source/application/lua_libraries/bluetooth.c @@ -25,12 +25,16 @@ #include #include "ble_gap.h" #include "bluetooth.h" +#include "nrfx_systick.h" #include "error_logging.h" #include "frame_lua_libraries.h" #include "lauxlib.h" #include "lua.h" #include "luaport.h" +struct scan_data_t scan_data; +uint16_t central_conn_handle = BLE_CONN_HANDLE_INVALID; + static int lua_bluetooth_is_connected(lua_State *L) { lua_pushboolean(L, bluetooth_is_connected()); @@ -82,24 +86,26 @@ static int lua_bluetooth_send(lua_State *L) return 0; } -static struct lua_bluetooth_callback +struct lua_bluetooth_callback { int function; uint8_t data[BLE_PREFERRED_MAX_MTU]; size_t length; -} lua_bluetooth_callback = { +}; + +static struct lua_bluetooth_callback lua_bluetooth_peripheral_callback = { .function = 0, }; -static void lua_bluetooth_receive_callback_handler(lua_State *L, lua_Debug *ar) +static void lua_bluetooth_peripheral_receive_callback_handler(lua_State *L, lua_Debug *ar) { lua_sethook(L, NULL, 0, 0); - lua_rawgeti(L, LUA_REGISTRYINDEX, lua_bluetooth_callback.function); + lua_rawgeti(L, LUA_REGISTRYINDEX, lua_bluetooth_peripheral_callback.function); lua_pushlstring(L, - (char *)lua_bluetooth_callback.data, - lua_bluetooth_callback.length); + (char *)lua_bluetooth_peripheral_callback.data, + lua_bluetooth_peripheral_callback.length); if (lua_pcall(L, 1, 0, 0) != LUA_OK) { @@ -107,33 +113,168 @@ static void lua_bluetooth_receive_callback_handler(lua_State *L, lua_Debug *ar) } } -void lua_bluetooth_data_interrupt(uint8_t *data, size_t length) +void lua_bluetooth_peripheral_data_interrupt(uint8_t *data, size_t length) { - if (lua_bluetooth_callback.function == 0) + if (lua_bluetooth_peripheral_callback.function == 0) { return; } - memcpy(lua_bluetooth_callback.data, data, length); - lua_bluetooth_callback.length = length; + memcpy(lua_bluetooth_peripheral_callback.data, data, length); + lua_bluetooth_peripheral_callback.length = length; lua_sethook(L_global, - lua_bluetooth_receive_callback_handler, + lua_bluetooth_peripheral_receive_callback_handler, LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT, 1); } -static int lua_bluetooth_receive_callback(lua_State *L) +static int lua_bluetooth_peripheral_receive_callback(lua_State *L) { if (lua_isnil(L, 1)) { - lua_bluetooth_callback.function = 0; + lua_bluetooth_peripheral_callback.function = 0; return 0; } if (lua_isfunction(L, 1)) { - lua_bluetooth_callback.function = luaL_ref(L, LUA_REGISTRYINDEX); + lua_bluetooth_peripheral_callback.function = luaL_ref(L, LUA_REGISTRYINDEX); + return 0; + } + + luaL_error(L, "expected nil or function"); + + return 0; +} + +static int lua_bluetooth_start_scan(lua_State *L) { + uint16_t timeout = luaL_checkinteger(L, 1); + if (timeout <= 0) { + luaL_error(L, "timeout must be greater than 0"); + } + if (timeout <= 0) { + luaL_error(L, "timeout must be less than 10000"); + } + scan_data.scan_params.timeout = timeout; + check_error(sd_ble_gap_scan_start(&scan_data.scan_params, &scan_data.scan_buffer)); + return 1; +} + +static int lua_bluetooth_scan_list(lua_State *L) { + + lua_newtable(L); + + char mac_address[18]; + char name[32]; + + for (size_t i=0; i 0) + { + check_error(sd_ble_gap_connect(&scan_data.address[index], + &scan_data.scan_params, &scan_data.conn_params, 2)); + + nrfx_systick_delay_ms(1000); + + if (central_conn_handle != BLE_CONN_HANDLE_INVALID) + { + break; + } + else + { + retry_count--; + } + } + + if (retry_count == 0) + luaL_error(L, "failed to connect"); + + return 1; +} + +static struct lua_bluetooth_callback lua_bluetooth_central_callback = { + .function = 0, +}; + +static void lua_bluetooth_central_receive_callback_handler(lua_State *L, lua_Debug *ar) +{ + lua_sethook(L, NULL, 0, 0); + + lua_rawgeti(L, LUA_REGISTRYINDEX, lua_bluetooth_central_callback.function); + + lua_pushlstring(L, + (char *)lua_bluetooth_central_callback.data, + lua_bluetooth_central_callback.length); + + if (lua_pcall(L, 1, 0, 0) != LUA_OK) + { + luaL_error(L, "%s", lua_tostring(L, -1)); + } +} + +void lua_bluetooth_central_data_interrupt(uint8_t *data, size_t length) +{ + if (lua_bluetooth_central_callback.function == 0) + { + return; + } + + memcpy(lua_bluetooth_central_callback.data, data, length); + lua_bluetooth_central_callback.length = length; + + lua_sethook(L_global, + lua_bluetooth_central_receive_callback_handler, + LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT, + 1); +} + +static int lua_bluetooth_central_receive_callback(lua_State *L) +{ + if (lua_isnil(L, 1)) + { + lua_bluetooth_central_callback.function = 0; + return 0; + } + + if (lua_isfunction(L, 1)) + { + lua_bluetooth_central_callback.function = luaL_ref(L, LUA_REGISTRYINDEX); return 0; } @@ -160,8 +301,23 @@ void lua_open_bluetooth_library(lua_State *L) lua_pushcfunction(L, lua_bluetooth_send); lua_setfield(L, -2, "send"); - lua_pushcfunction(L, lua_bluetooth_receive_callback); - lua_setfield(L, -2, "receive_callback"); + lua_pushcfunction(L, lua_bluetooth_peripheral_receive_callback); + lua_setfield(L, -2, "peripheral_receive_callback"); + + lua_pushcfunction(L, lua_bluetooth_start_scan); + lua_setfield(L, -2, "start_scan"); + + lua_pushcfunction(L, lua_bluetooth_scan_list); + lua_setfield(L, -2, "scan_list"); + + lua_pushcfunction(L, lua_bluetooth_scan_clear); + lua_setfield(L, -2, "scan_clear"); + + lua_pushcfunction(L, lua_bluetooth_connect); + lua_setfield(L, -2, "connect"); + + lua_pushcfunction(L, lua_bluetooth_central_receive_callback); + lua_setfield(L, -2, "central_receive_callback"); lua_setfield(L, -2, "bluetooth"); diff --git a/source/application/lua_libraries/frame_lua_libraries.h b/source/application/lua_libraries/frame_lua_libraries.h index 599912ae..49af268b 100644 --- a/source/application/lua_libraries/frame_lua_libraries.h +++ b/source/application/lua_libraries/frame_lua_libraries.h @@ -29,7 +29,8 @@ extern lua_State *L_global; -void lua_bluetooth_data_interrupt(uint8_t *data, size_t length); +void lua_bluetooth_peripheral_data_interrupt(uint8_t *data, size_t length); +void lua_bluetooth_central_data_interrupt(uint8_t *data, size_t length); void lua_open_bluetooth_library(lua_State *L); void lua_open_camera_library(lua_State *L); diff --git a/source/memory_layout.ld b/source/memory_layout.ld index 749b5919..36ec6e1e 100644 --- a/source/memory_layout.ld +++ b/source/memory_layout.ld @@ -51,7 +51,7 @@ ENTRY(Reset_Handler) MEMORY { APPLICATION_FLASH (rx) : ORIGIN = 0x27000, LENGTH = 0xCE000 - APPLICATION_RAM (rwx) : ORIGIN = 0x20002A08, LENGTH = 256K - 0x2A08 + APPLICATION_RAM (rwx) : ORIGIN = 0x20003CA0, LENGTH = 256K - 0x3CA0 BOOTLOADER_FLASH (rx) : ORIGIN = 0xF5000, LENGTH = 0x9000 BOOTLOADER_RAM (rwx) : ORIGIN = 0x20002AE8, LENGTH = 256K - 0x2AE8 diff --git a/tests/test_api.py b/tests/test_api.py index 9012cf6e..f84ceb8e 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -93,10 +93,10 @@ async def main(): ## Send and receive callback await test.lua_send( - "frame.bluetooth.receive_callback((function(d)frame.bluetooth.send(d)end))" + "frame.bluetooth.peripheral_receive_callback((function(d)frame.bluetooth.send(d)end))" ) await test.data_equal(b"test", b"test") - await test.lua_send("frame.bluetooth.receive_callback(nil)") + await test.lua_send("frame.bluetooth.peripheral_receive_callback(nil)") ## MTU size max_length = test.max_data_payload() diff --git a/tests/test_bluetooth_callback_api.py b/tests/test_bluetooth_callback_api.py index f7e581a7..430cc765 100644 --- a/tests/test_bluetooth_callback_api.py +++ b/tests/test_bluetooth_callback_api.py @@ -12,7 +12,7 @@ async def main(): await bluetooth.send_reset_signal() await bluetooth.send_lua("function ble_event(d)frame.bluetooth.send(d)end") - await bluetooth.send_lua("frame.bluetooth.receive_callback(ble_event)") + await bluetooth.send_lua("frame.bluetooth.peripheral_receive_callback(ble_event)") await bluetooth.send_lua("for i=1,10 do print(i); frame.sleep(1) end") await asyncio.sleep(1)