diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 5176ecc9b7..69fa24fa77 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -1539,6 +1539,14 @@ msgstr "" msgid "Odd parity is not supported" msgstr "" +#: supervisor/shared/bluetooth/bluetooth.c +msgid "Off" +msgstr "" + +#: supervisor/shared/bluetooth/bluetooth.c +msgid "Ok" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/PDMIn.c #: ports/raspberrypi/common-hal/audiobusio/PDMIn.c msgid "Only 8 or 16 bit mono with " @@ -1791,6 +1799,10 @@ msgstr "" msgid "Received response was invalid" msgstr "" +#: supervisor/shared/bluetooth/bluetooth.c +msgid "Reconnecting" +msgstr "" + #: shared-bindings/displayio/EPaperDisplay.c msgid "Refresh too soon" msgstr "" diff --git a/ports/nrf/common-hal/_bleio/Adapter.c b/ports/nrf/common-hal/_bleio/Adapter.c index 5cebf2fa0f..49b5d638f3 100644 --- a/ports/nrf/common-hal/_bleio/Adapter.c +++ b/ports/nrf/common-hal/_bleio/Adapter.c @@ -449,15 +449,23 @@ bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_addre return sd_ble_gap_addr_set(&local_address) == NRF_SUCCESS; } +uint16_t bleio_adapter_get_name(char *buf, uint16_t len) { + uint16_t full_len = 0; + sd_ble_gap_device_name_get(NULL, &full_len); + + uint32_t err_code = sd_ble_gap_device_name_get((uint8_t *)buf, &len); + if (err_code != NRF_SUCCESS) { + return 0; + } + return full_len; +} + mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self) { uint16_t len = 0; sd_ble_gap_device_name_get(NULL, &len); - uint8_t buf[len]; - uint32_t err_code = sd_ble_gap_device_name_get(buf, &len); - if (err_code != NRF_SUCCESS) { - return NULL; - } - return mp_obj_new_str((char *)buf, len); + char buf[len]; + bleio_adapter_get_name(buf, len); + return mp_obj_new_str(buf, len); } void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *name) { diff --git a/shared-bindings/_bleio/Adapter.c b/shared-bindings/_bleio/Adapter.c index ce1080934e..53bd6be3c2 100644 --- a/shared-bindings/_bleio/Adapter.c +++ b/shared-bindings/_bleio/Adapter.c @@ -158,10 +158,10 @@ MP_PROPERTY_GETSET(bleio_adapter_address_obj, //| The name is "CIRCUITPY" + the last four hex digits of ``adapter.address``, //| to make it easy to distinguish multiple CircuitPython boards.""" //| -STATIC mp_obj_t bleio_adapter_get_name(mp_obj_t self) { +STATIC mp_obj_t _bleio_adapter_get_name(mp_obj_t self) { return MP_OBJ_FROM_PTR(common_hal_bleio_adapter_get_name(self)); } -MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_name_obj, bleio_adapter_get_name); +MP_DEFINE_CONST_FUN_OBJ_1(bleio_adapter_get_name_obj, _bleio_adapter_get_name); STATIC mp_obj_t bleio_adapter_set_name(mp_obj_t self, mp_obj_t new_name) { diff --git a/shared-bindings/_bleio/Adapter.h b/shared-bindings/_bleio/Adapter.h index 1dce615a40..49bb4421e7 100644 --- a/shared-bindings/_bleio/Adapter.h +++ b/shared-bindings/_bleio/Adapter.h @@ -50,6 +50,9 @@ extern bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self); extern bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *self); extern bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address); +// Copies the adapter name into the given buffer up to len and returns the full length (may be more +// than len if the buffer was too short.) +uint16_t bleio_adapter_get_name(char *buf, uint16_t len); extern mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self); extern void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *name); diff --git a/supervisor/shared/bluetooth/bluetooth.c b/supervisor/shared/bluetooth/bluetooth.c index 426aad9891..59fd1b87ed 100644 --- a/supervisor/shared/bluetooth/bluetooth.c +++ b/supervisor/shared/bluetooth/bluetooth.c @@ -39,8 +39,11 @@ #include "common-hal/_bleio/__init__.h" +#include "supervisor/serial.h" #include "supervisor/shared/status_leds.h" #include "supervisor/shared/tick.h" +#include "supervisor/shared/title_bar.h" +#include "supervisor/shared/translate/translate.h" #include "py/mpstate.h" @@ -75,18 +78,13 @@ const uint8_t public_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags const uint8_t private_advertising_data[] = { 0x02, 0x01, 0x06, // 0-2 Flags 0x02, 0x0a, 0x00 // 3-5 TX power level 0 }; -// This scan response advertises the full CIRCPYXXXX device name. -uint8_t circuitpython_scan_response_data[] = { - 0x0a, 0x09, 0x43, 0x49, 0x52, 0x50, 0x59, 0x00, 0x00, 0x00, 0x00, - #if CIRCUITPY_SERIAL_BLE - 0x11, 0x06, 0x6e, 0x68, 0x74, 0x79, 0x50, 0x74, 0x69, 0x75, 0x63, 0x72, 0x69, 0x43, 0x01, 0x00, 0xaf, 0xad - #endif -}; - +// This scan response advertises the full device name (if it fits.) +uint8_t circuitpython_scan_response_data[31]; #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE STATIC bool boot_in_discovery_mode = false; STATIC bool advertising = false; +STATIC bool _private_advertising = false; STATIC bool ble_started = false; #define WORKFLOW_UNSET 0 @@ -96,6 +94,36 @@ STATIC bool ble_started = false; STATIC uint8_t workflow_state = WORKFLOW_UNSET; STATIC bool was_connected = false; +// To detect when the title bar changes. +STATIC bool _last_connected = false; +STATIC bool _last_advertising = false; + +// Title bar status +bool supervisor_bluetooth_status_dirty(void) { + return _last_advertising != advertising || + _last_connected != was_connected; +} + +void supervisor_bluetooth_status(void) { + serial_write("BLE:"); + if (advertising) { + if (_private_advertising) { + serial_write_compressed(translate("Reconnecting")); + } else { + const char *name = (char *)circuitpython_scan_response_data + 2; + int len = MIN(strlen(name), sizeof(circuitpython_scan_response_data) - 2); + serial_write_substring(name, len); + } + } else if (was_connected) { + serial_write_compressed(translate("Ok")); + } else { + serial_write_compressed(translate("Off")); + } + + _last_connected = was_connected; + _last_advertising = advertising; +} + STATIC void supervisor_bluetooth_start_advertising(void) { if (workflow_state != WORKFLOW_ENABLED) { return; @@ -118,6 +146,7 @@ STATIC void supervisor_bluetooth_start_advertising(void) { size_t adv_len = sizeof(private_advertising_data); const uint8_t *scan_response = NULL; size_t scan_response_len = 0; + _private_advertising = true; // Advertise with less power when doing so publicly to reduce who can hear us. This will make it // harder for someone with bad intentions to pair from a distance. if (!bonded) { @@ -126,6 +155,20 @@ STATIC void supervisor_bluetooth_start_advertising(void) { adv_len = sizeof(public_advertising_data); scan_response = circuitpython_scan_response_data; scan_response_len = sizeof(circuitpython_scan_response_data); + uint16_t max_name_len = sizeof(circuitpython_scan_response_data) - 2; + uint16_t name_len = bleio_adapter_get_name((char *)circuitpython_scan_response_data + 2, + max_name_len); + if (name_len > max_name_len) { + circuitpython_scan_response_data[0] = max_name_len + 1; + circuitpython_scan_response_data[1] = 0x8; + } else { + circuitpython_scan_response_data[0] = name_len + 1; + circuitpython_scan_response_data[1] = 0x9; + } + scan_response_len = circuitpython_scan_response_data[0] + 1; + assert(scan_response_len < 32); + mp_printf(&mp_plat_print, "sr len %d\n", scan_response_len); + _private_advertising = false; } uint32_t status = _common_hal_bleio_adapter_start_advertising(&common_hal_bleio_adapter_obj, true, @@ -232,6 +275,9 @@ void supervisor_bluetooth_background(void) { supervisor_bluetooth_file_transfer_disconnected(); #endif } + if (was_connected != is_connected) { + supervisor_title_bar_request_update(false); + } was_connected = is_connected; if (!is_connected) { supervisor_bluetooth_start_advertising(); @@ -266,6 +312,7 @@ void supervisor_start_bluetooth(void) { // Kick off advertisements supervisor_bluetooth_background(); + supervisor_title_bar_request_update(false); #endif } diff --git a/supervisor/shared/bluetooth/bluetooth.h b/supervisor/shared/bluetooth/bluetooth.h index 9de82719a5..231fde3c54 100644 --- a/supervisor/shared/bluetooth/bluetooth.h +++ b/supervisor/shared/bluetooth/bluetooth.h @@ -38,4 +38,8 @@ void supervisor_stop_bluetooth(void); void supervisor_bluetooth_enable_workflow(void); void supervisor_bluetooth_disable_workflow(void); +// Title bar status +bool supervisor_bluetooth_status_dirty(void); +void supervisor_bluetooth_status(void); + #endif // MICROPY_INCLUDED_SUPERVISOR_SHARED_BLUETOOTH_H diff --git a/supervisor/shared/title_bar.c b/supervisor/shared/title_bar.c index 6a2462a399..cd519c5b7a 100644 --- a/supervisor/shared/title_bar.c +++ b/supervisor/shared/title_bar.c @@ -34,6 +34,11 @@ #if CIRCUITPY_WEB_WORKFLOW #include "supervisor/shared/web_workflow/web_workflow.h" #endif + +#if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE +#include "supervisor/shared/bluetooth/bluetooth.h" +#endif + static background_callback_t title_bar_background_cb; static bool _forced_dirty = false; @@ -55,6 +60,10 @@ void supervisor_title_bar_update(void) { supervisor_web_workflow_status(); serial_write(" | "); #endif + #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE + supervisor_bluetooth_status(); + serial_write(" | "); + #endif supervisor_execution_status(); serial_write(" | "); serial_write(MICROPY_GIT_TAG); @@ -75,6 +84,10 @@ static void title_bar_background(void *data) { dirty = dirty || supervisor_web_workflow_status_dirty(); #endif + #if CIRCUITPY_BLE_FILE_SERVICE || CIRCUITPY_SERIAL_BLE + dirty = dirty || supervisor_bluetooth_status_dirty(); + #endif + if (!dirty) { return; }