diff --git a/libs/BLEKit/CMakeLists.txt b/libs/BLEKit/CMakeLists.txt index eb2f2de7b4..9a83e7efb4 100644 --- a/libs/BLEKit/CMakeLists.txt +++ b/libs/BLEKit/CMakeLists.txt @@ -26,6 +26,7 @@ target_link_libraries(BLEKit if (${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests") leka_unit_tests_sources( + tests/AdvertisingData_test.cpp tests/CoreGapEventHandler_test.cpp tests/CoreGap_test.cpp tests/CoreGattServerEventHandler_test.cpp diff --git a/libs/BLEKit/include/AdvertisingData.h b/libs/BLEKit/include/AdvertisingData.h new file mode 100644 index 0000000000..d1edf66dc3 --- /dev/null +++ b/libs/BLEKit/include/AdvertisingData.h @@ -0,0 +1,32 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#pragma once + +#include +#include +#include + +namespace leka { + +struct AdvertisingData { + const char *name = "Leka"; // TODO: Get default name from configuration files + uint8_t battery {}; + uint8_t is_charging {}; + + auto data() + { + updateValues(); + return _internal_values.data(); + } + + [[nodiscard]] auto size() const -> uint32_t { return _internal_values.size(); } + + // private: + void updateValues() { _internal_values = {battery, is_charging}; } + + std::array _internal_values = {}; +}; + +} // namespace leka diff --git a/libs/BLEKit/include/BLEKit.h b/libs/BLEKit/include/BLEKit.h index b8d24c76b2..4663488441 100644 --- a/libs/BLEKit/include/BLEKit.h +++ b/libs/BLEKit/include/BLEKit.h @@ -22,12 +22,17 @@ class BLEKit void init(); + void setAdvertisingData(const AdvertisingData &advertising_data); + [[nodiscard]] auto getAdvertisingData() const -> AdvertisingData; + private: // ? mbed::BLE specific function void processEvents(BLE::OnEventsToProcessCallbackContext *context); CoreEventQueue _event_queue {}; + AdvertisingData _advertising_data {}; + BLE &_ble = BLE::Instance(); CoreGap _core_gap {_ble.gap()}; CoreGattServer _core_gatt_server {_ble.gattServer()}; diff --git a/libs/BLEKit/include/CoreGap.h b/libs/BLEKit/include/CoreGap.h index 37054469bb..569874d8ba 100644 --- a/libs/BLEKit/include/CoreGap.h +++ b/libs/BLEKit/include/CoreGap.h @@ -9,6 +9,7 @@ #include "ble/BLE.h" #include "ble/Gap.h" +#include "AdvertisingData.h" #include "CoreGapEventHandler.h" namespace leka { @@ -24,9 +25,8 @@ class CoreGap void onInitializationComplete(BLE::InitializationCompleteCallbackContext *params); // void onInit(std::function cb) { _post_init = cb; } - void setDeviceName(const char *name); - void startAdvertising(); + void setAdvertising(AdvertisingData advertising_data); private: ble::advertising_handle_t _advertising_handle {ble::LEGACY_ADVERTISING_HANDLE}; diff --git a/libs/BLEKit/source/BLEKit.cpp b/libs/BLEKit/source/BLEKit.cpp index 8c3c3a7b44..93ba22cbe8 100644 --- a/libs/BLEKit/source/BLEKit.cpp +++ b/libs/BLEKit/source/BLEKit.cpp @@ -19,7 +19,6 @@ void BLEKit::init() _ble.onEventsToProcess({this, &BLEKit::processEvents}); _core_gap.setDefaultAdvertising(); - _core_gap.setDeviceName("Leka"); _ble.init(&_core_gap, &CoreGap::onInitializationComplete); @@ -30,3 +29,15 @@ void BLEKit::processEvents(BLE::OnEventsToProcessCallbackContext *context) { _event_queue.callMbedCallback(mbed::callback(&context->ble, &BLE::processEvents)); } + +void BLEKit::setAdvertisingData(const AdvertisingData &advertising_data) +{ + _advertising_data = advertising_data; + + _core_gap.setAdvertising(_advertising_data); +} + +auto BLEKit::getAdvertisingData() const -> AdvertisingData +{ + return _advertising_data; +} diff --git a/libs/BLEKit/source/CoreGap.cpp b/libs/BLEKit/source/CoreGap.cpp index 8f489dbe1f..2ec4f8c3ad 100644 --- a/libs/BLEKit/source/CoreGap.cpp +++ b/libs/BLEKit/source/CoreGap.cpp @@ -7,12 +7,12 @@ using namespace ble; void CoreGap::setDefaultAdvertising() { - _advertising_data_builder.setAppearance(adv_data_appearance_t::GENERIC_HEART_RATE_SENSOR); - _advertising_data_builder.setFlags(); - _advertising_data_builder.setManufacturerSpecificData({{0x2A, 0x2B, 0x2C, 0x2D}}); - _advertising_data_builder.setAdvertisingInterval(adv_interval_t::min()); - _advertising_data_builder.setServiceData(GattService::UUID_BATTERY_SERVICE, {{0x42}}); - _advertising_data_builder.setServiceData(leka::service::commands::uuid, {{0}}); + auto default_advertising_data = AdvertisingData {}; + + _advertising_data_builder.setName(default_advertising_data.name); + _advertising_data_builder.setServiceData( + leka::service::commands::uuid, // TODO: commands::uuid only for compatibility with LekaApp + {default_advertising_data.data(), default_advertising_data.size()}); } void CoreGap::setEventHandler() @@ -27,11 +27,6 @@ void CoreGap::onInitializationComplete(BLE::InitializationCompleteCallbackContex _gap_event_handler.onInitializationComplete(params); } -void CoreGap::setDeviceName(const char *name) -{ - _advertising_data_builder.setName(name); -} - void CoreGap::startAdvertising() { if (_gap.isAdvertisingActive(_advertising_handle)) { @@ -43,3 +38,13 @@ void CoreGap::startAdvertising() _gap.startAdvertising(_advertising_handle, adv_duration_t(millisecond_t(4000))); } + +void CoreGap::setAdvertising(AdvertisingData advertising_data) +{ + _advertising_data_builder.setName(advertising_data.name); + _advertising_data_builder.setServiceData( + leka::service::commands::uuid, // TODO: commands::uuid only for compatibility with LekaApp + {advertising_data.data(), advertising_data.size()}); + + _gap.setAdvertisingPayload(_advertising_handle, _advertising_data_builder.getAdvertisingData()); +} diff --git a/libs/BLEKit/tests/AdvertisingData_test.cpp b/libs/BLEKit/tests/AdvertisingData_test.cpp new file mode 100644 index 0000000000..2ea6cb7628 --- /dev/null +++ b/libs/BLEKit/tests/AdvertisingData_test.cpp @@ -0,0 +1,28 @@ +// Leka - LekaOS +// Copyright 2022 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +#include "AdvertisingData.h" + +#include "gtest/gtest.h" + +using namespace leka; + +TEST(AdvertisingDataTest, initialisation) +{ + auto advertising_data = AdvertisingData {}; + + EXPECT_NE(&advertising_data, nullptr); +} + +TEST(AdvertisingDataTest, data) +{ + auto advertising_data = AdvertisingData {.battery = 0x2A, .is_charging = 0x2B}; + + auto expected_data_array = std::to_array({advertising_data.battery, advertising_data.is_charging}); + + auto actual_data_array = std::span {advertising_data.data(), advertising_data.size()}; + + EXPECT_EQ(std::size(actual_data_array), std::size(expected_data_array)); + EXPECT_TRUE(0 == memcmp(expected_data_array.data(), actual_data_array.data(), std::size(expected_data_array))); +} diff --git a/libs/BLEKit/tests/BLEKit_test.cpp b/libs/BLEKit/tests/BLEKit_test.cpp index e0b9dbf483..60eb14ba08 100644 --- a/libs/BLEKit/tests/BLEKit_test.cpp +++ b/libs/BLEKit/tests/BLEKit_test.cpp @@ -107,3 +107,13 @@ TEST_F(BLEKitTest, callOnEventsToProcess) EXPECT_TRUE(spy_CoreEventQueue_did_call_function); } + +TEST_F(BLEKitTest, getAdvertisingDataThenSetAdvertisingData) +{ + auto advertising_data = ble.getAdvertisingData(); + advertising_data.name = "NewLeka"; + + EXPECT_CALL(mbed_mock_gap, setAdvertisingPayload).Times(1); + + ble.setAdvertisingData(advertising_data); +} diff --git a/libs/BLEKit/tests/CoreGap_test.cpp b/libs/BLEKit/tests/CoreGap_test.cpp index 06579ffe27..a53abe9b16 100644 --- a/libs/BLEKit/tests/CoreGap_test.cpp +++ b/libs/BLEKit/tests/CoreGap_test.cpp @@ -93,14 +93,12 @@ TEST_F(CoreGapTest, defaultAdvertisingParameters) TEST_F(CoreGapTest, defaultAdvertisingPayload) { std::array buffer {}; - auto data_builder = AdvertisingDataBuilder {{buffer.begin(), buffer.end()}}; + auto data_builder = AdvertisingDataBuilder {{buffer.begin(), buffer.end()}}; + auto default_advertising_data = AdvertisingData {}; - data_builder.setAppearance(ble::adv_data_appearance_t::GENERIC_HEART_RATE_SENSOR); - data_builder.setFlags(); - data_builder.setManufacturerSpecificData({{0x2A, 0x2B, 0x2C, 0x2D}}); - data_builder.setAdvertisingInterval(ble::adv_interval_t::min()); - data_builder.setServiceData(GattService::UUID_BATTERY_SERVICE, {{0x42}}); - data_builder.setServiceData(service::commands::uuid, {{0}}); + data_builder.setName(default_advertising_data.name); + data_builder.setServiceData(service::commands::uuid, + {{default_advertising_data.battery, default_advertising_data.is_charging}}); EXPECT_CALL(mbed_mock_gap, setAdvertisingPayload(LEGACY_ADVERTISING_HANDLE, compareAdvertisingPayload(data_builder))) @@ -113,26 +111,6 @@ TEST_F(CoreGapTest, defaultAdvertisingPayload) coregap.startAdvertising(); } -TEST_F(CoreGapTest, setDeviceName) -{ - auto expected_device_name = "LekaCoreGap"; - - std::array buffer {}; - auto data_builder = AdvertisingDataBuilder {{buffer.begin(), buffer.end()}}; - - data_builder.setName(expected_device_name); - - EXPECT_CALL(mbed_mock_gap, - setAdvertisingPayload(LEGACY_ADVERTISING_HANDLE, compareAdvertisingPayload(data_builder))) - .Times(1); - EXPECT_CALL(mbed_mock_gap, isAdvertisingActive).WillOnce(Return(false)); - EXPECT_CALL(mbed_mock_gap, startAdvertising).Times(1); - EXPECT_CALL(mbed_mock_gap, setAdvertisingParameters).Times(1); - - coregap.setDeviceName(expected_device_name); - coregap.startAdvertising(); -} - TEST_F(CoreGapTest, startAdvertisingAdvertisingWasInactive) { Sequence seq1, seq2; @@ -166,3 +144,19 @@ TEST_F(CoreGapTest, onInitializationComplete) // coregap.onInitializationComplete(&context); // Alternative } + +TEST_F(CoreGapTest, setAdvertising) +{ + std::array buffer {}; + auto data_builder = AdvertisingDataBuilder {{buffer.begin(), buffer.end()}}; + auto new_advertising_data = AdvertisingData {.name = "NewLeka", .battery = 0x42, .is_charging = 0x01}; + + data_builder.setName(new_advertising_data.name); + data_builder.setServiceData(service::commands::uuid, {new_advertising_data.data(), new_advertising_data.size()}); + + EXPECT_CALL(mbed_mock_gap, + setAdvertisingPayload(LEGACY_ADVERTISING_HANDLE, compareAdvertisingPayload(data_builder))) + .Times(1); + + coregap.setAdvertising(new_advertising_data); +} diff --git a/spikes/lk_ble/main.cpp b/spikes/lk_ble/main.cpp index a2fd213e64..9d877c6e2b 100644 --- a/spikes/lk_ble/main.cpp +++ b/spikes/lk_ble/main.cpp @@ -106,5 +106,11 @@ auto main() -> int auto version = service_update.getVersion(); log_info("Requested version: %d.%d.%d", version.major, version.minor, version.revision); + + auto advertising_data = blekit.getAdvertisingData(); + advertising_data.name = "NewLeka"; + advertising_data.battery = level; + advertising_data.is_charging = charging_status; + blekit.setAdvertisingData(advertising_data); } }