Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ (ble): Add CoreGattServer #485

Closed
wants to merge 9 commits into from
4 changes: 4 additions & 0 deletions libs/BLEKit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ target_sources(BLEKit
source/BLEKit.cpp
source/CoreGapEventHandler.cpp
source/CoreGap.cpp
source/CoreGattServerEventHandler.cpp
source/CoreGattServer.cpp
)

target_link_libraries(BLEKit
Expand All @@ -26,6 +28,8 @@ if (${CMAKE_PROJECT_NAME} STREQUAL "LekaOSUnitTests")
leka_unit_tests_sources(
tests/CoreGapEventHandler_test.cpp
tests/CoreGap_test.cpp
tests/CoreGattServerEventHandler_test.cpp
tests/CoreGattServer_test.cpp
tests/BLEKit_test.cpp
)

Expand Down
5 changes: 5 additions & 0 deletions libs/BLEKit/include/BLEKit.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
#ifndef _LEKA_OS_LIB_BLE_KIT_H_
#define _LEKA_OS_LIB_BLE_KIT_H_

#include "BLEServiceBattery.h"
#include "ble/BLE.h"

#include "CoreEventQueue.h"
#include "CoreGap.h"
#include "CoreGattServer.h"

namespace leka {

Expand All @@ -17,6 +19,8 @@ class BLEKit
public:
BLEKit() = default;

void setServices(std::span<interface::BLEService *> const &services);

void init();

private:
Expand All @@ -27,6 +31,7 @@ class BLEKit

BLE &_ble = BLE::Instance();
CoreGap _core_gap {_ble.gap()};
CoreGattServer _core_gatt_server {_ble.gattServer()};
};

} // namespace leka
Expand Down
53 changes: 53 additions & 0 deletions libs/BLEKit/include/BLEServiceBattery.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Leka - LekaOS
// Copyright 2021 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#ifndef _LEKA_OS_LIB_BLE_SERVICE_BATTERY_H_
#define _LEKA_OS_LIB_BLE_SERVICE_BATTERY_H_

#include "internal/BLEService.h"

namespace leka {

class BLEServiceBattery : public interface::BLEService
{
const static uint16_t SERVICE_BATTERY_UUID = GattService::UUID_BATTERY_SERVICE;
const static uint16_t BATTERY_LEVEL_WRITABLE_CHARACTERISTIC_UUID = GattCharacteristic::UUID_BATTERY_LEVEL_CHAR;

public:
BLEServiceBattery() : interface::BLEService(SERVICE_BATTERY_UUID, _characteristic_table) {};

void setBatteryLevel(uint8_t value)
{
if (value <= 100) {
_battery_level_characteristic_value = value;
}
sendData();
}

void onDataReceived(const data_received_handle_t &params) final {
// do nothing
};

void onDataReadyToSend(const data_to_send_handle_t &function) final { send_data_function = function; };

void sendData() final
{
send_data_function(_battery_level_writable_characteristic.getValueHandle(),
&_battery_level_characteristic_value, 1);
};

private:
data_to_send_handle_t send_data_function;

uint8_t _battery_level_characteristic_value {};
ReadOnlyGattCharacteristic<uint8_t> _battery_level_writable_characteristic {
BATTERY_LEVEL_WRITABLE_CHARACTERISTIC_UUID, &_battery_level_characteristic_value,
GattCharacteristic::BLE_GATT_CHAR_PROPERTIES_NOTIFY};

std::array<GattCharacteristic *, 1> _characteristic_table {&_battery_level_writable_characteristic};
};

} // namespace leka

#endif // _LEKA_OS_LIB_BLE_SERVICE_BATTERY_H_
3 changes: 3 additions & 0 deletions libs/BLEKit/include/CoreGap.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class CoreGap

void setEventHandler();
void onInitializationComplete(BLE::InitializationCompleteCallbackContext *params);
// void onInit(std::function<void()> cb) { _post_init = cb; }

void setDeviceName(const char *name);

Expand All @@ -36,6 +37,8 @@ class CoreGap

CoreGapEventHandler _gap_event_handler;
ble::Gap &_gap;

// std::function<void()> _post_init;
};

} // namespace leka
Expand Down
36 changes: 36 additions & 0 deletions libs/BLEKit/include/CoreGattServer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Leka - LekaOS
// Copyright 2022 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#ifndef _LEKA_OS_LIB_CORE_GATT_SERVER_H_
#define _LEKA_OS_LIB_CORE_GATT_SERVER_H_

#include <span>

#include "BLEServiceBattery.h"
#include "ble/BLE.h"
#include "ble/GattServer.h"

#include "CoreGattServerEventHandler.h"
#include "internal/BLEService.h"

namespace leka {

class CoreGattServer
{
public:
explicit CoreGattServer(ble::GattServer &gatt_server) : _gatt_server(gatt_server) {};

void setEventHandler();
void setServices(std::span<interface::BLEService *> services);

private:
void write(GattAttribute::Handle_t characteristic_updated, const uint8_t *data, uint16_t n_data_bytes);

ble::GattServer &_gatt_server;
leka::CoreGattServerEventHandler _gatt_server_event_handler;
};

} // namespace leka

#endif // _LEKA_OS_LIB_CORE_GATT_SERVER_H_
32 changes: 32 additions & 0 deletions libs/BLEKit/include/CoreGattServerEventHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Leka - LekaOS
// Copyright 2022 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#ifndef _LEKA_OS_LIB_CORE_GATT_SERVER_EVENT_HANDLER_H_
#define _LEKA_OS_LIB_CORE_GATT_SERVER_EVENT_HANDLER_H_

#include <span>

#include "ble/BLE.h"
#include "ble/GattServer.h"

#include "internal/BLEService.h"

namespace leka {

class CoreGattServerEventHandler : public ble::GattServer::EventHandler
{
public:
explicit CoreGattServerEventHandler() = default;

void setServices(std::span<interface::BLEService *> const &services);

void onDataWritten(const GattWriteCallbackParams &params) override;

private:
std::span<interface::BLEService *> _services {};
};

} // namespace leka

#endif // _LEKA_OS_LIB_CORE_GATT_SERVER_EVENT_HANDLER_H_
39 changes: 39 additions & 0 deletions libs/BLEKit/include/internal/BLEService.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Leka - LekaOS
// Copyright 2022 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#ifndef _LEKA_OS_LIB_BLE_SERVICE_INTERFACE_H_
#define _LEKA_OS_LIB_BLE_SERVICE_INTERFACE_H_

#include <span>

#include "ble/gatt/GattCharacteristic.h"
#include "ble/gatt/GattService.h"

namespace leka::interface {

class BLEService : public GattService
{
public:
using data_received_handle_t = GattWriteCallbackParams;
using data_to_send_handle_t =
std::function<void(GattAttribute::Handle_t characteristic_updated, const uint8_t *data, uint16_t n_data_bytes)>;

BLEService(const UUID &uuid, std::span<GattCharacteristic *> characteristics)
: GattService(uuid, characteristics.data(), std::size(characteristics)) {};

[[nodiscard]] auto getCharacteristicCount() const -> uint8_t { return GattService::getCharacteristicCount(); };
[[nodiscard]] auto getCharacteristic(uint8_t index) -> GattCharacteristic *
{
return GattService::getCharacteristic(index);
};

virtual void onDataReceived(const data_received_handle_t &handle) = 0;

virtual void onDataReadyToSend(const data_to_send_handle_t &function) = 0;
virtual void sendData() = 0;
};

} // namespace leka::interface

#endif // _LEKA_OS_LIB_BLE_SERVICE_INTERFACE_H_
10 changes: 8 additions & 2 deletions libs/BLEKit/source/BLEKit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,28 @@

using namespace leka;

void BLEKit::setServices(std::span<interface::BLEService *> const &services)
{
_core_gatt_server.setServices(services);
}

void BLEKit::init()
{
if (_ble.hasInitialized()) {
return;
}

_core_gap.setEventHandler();
_core_gatt_server.setEventHandler();

_ble.onEventsToProcess({this, &BLEKit::processEvents});

_event_queue.dispatch_forever();

_core_gap.setDefaultAdvertising();
_core_gap.setDeviceName("Leka");

_ble.init(&_core_gap, &CoreGap::onInitializationComplete);

_event_queue.dispatch_forever();
}

void BLEKit::processEvents(BLE::OnEventsToProcessCallbackContext *context)
Expand Down
4 changes: 4 additions & 0 deletions libs/BLEKit/source/CoreGap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ void CoreGap::setEventHandler()
void CoreGap::onInitializationComplete(BLE::InitializationCompleteCallbackContext *params)
{
_gap_event_handler.onInitializationComplete(params);

// if (_post_init) {
// _post_init();
// }
}

void CoreGap::setDeviceName(const char *name)
Expand Down
26 changes: 26 additions & 0 deletions libs/BLEKit/source/CoreGattServer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include "CoreGattServer.h"

using namespace leka;
using namespace std::chrono_literals;

void CoreGattServer::setEventHandler()
{
_gatt_server.setEventHandler(&_gatt_server_event_handler);
}

void CoreGattServer::setServices(std::span<interface::BLEService *> services)
{
for (auto &service: services) {
_gatt_server.addService(*service);

service->onDataReadyToSend([this](GattAttribute::Handle_t &&PH1, const uint8_t *&&PH2, uint16_t &&PH3) {
write(std::forward<decltype(PH1)>(PH1), std::forward<decltype(PH2)>(PH2), std::forward<decltype(PH3)>(PH3));
});
}
_gatt_server_event_handler.setServices(services);
}

void CoreGattServer::write(GattAttribute::Handle_t characteristic_updated, const uint8_t *data, uint16_t n_data_bytes)
{
_gatt_server.write(characteristic_updated, data, n_data_bytes);
}
27 changes: 27 additions & 0 deletions libs/BLEKit/source/CoreGattServerEventHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Leka - LekaOS
// Copyright 2022 APF France handicap
// SPDX-License-Identifier: Apache-2.0

#include "CoreGattServerEventHandler.h"

using namespace leka;
using namespace std::chrono_literals;

void CoreGattServerEventHandler::setServices(std::span<interface::BLEService *> const &services)
{
_services = services;
}

void CoreGattServerEventHandler::onDataWritten(const GattWriteCallbackParams &params)
{
auto call_on_data_written = [&params](interface::BLEService *service) {
auto characteristics_count = service->getCharacteristicCount();
for (uint8_t index = 0; index < characteristics_count; index++) {
if (params.handle == service->getCharacteristic(index)->getValueHandle()) {
service->onDataReceived(params);
}
}
};

std::for_each(_services.begin(), _services.end(), call_on_data_written);
}
25 changes: 24 additions & 1 deletion libs/BLEKit/tests/BLEKit_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "mocks/leka/BLEService.h"
#include "stubs/leka/CoreEventQueue.h"
#include "stubs/mbed/BLE.h"

Expand All @@ -28,7 +29,8 @@ class BLEKitTest : public testing::Test
void TearDown() override { ble::delete_mocks(); }

BLEKit ble {};
GapMock &mock_gap = gap_mock();
GapMock &mock_gap = gap_mock();
GattServerMock &mock_gatt = gatt_server_mock();

void expectStartAdvertisingCall()
{
Expand All @@ -49,6 +51,7 @@ TEST_F(BLEKitTest, init)
spy_ble_hasInitialized_return_value = false;

EXPECT_CALL(mock_gap, setEventHandler).Times(1);
EXPECT_CALL(mock_gatt, setEventHandler).Times(1);
expectStartAdvertisingCall();

ble.init();
Expand All @@ -71,12 +74,32 @@ TEST_F(BLEKitTest, initBLEAlreadyInitialized)
EXPECT_FALSE(spy_ble_did_call_initialization);
}

TEST_F(BLEKitTest, setServices)
{
auto characteristic_value = uint8_t {};
auto characteristic = GattCharacteristic {0x1234, &characteristic_value};
auto service_characteristic_table = std::to_array<GattCharacteristic *>({&characteristic});

auto mock_service_1 = mock::BLEService(0x01, service_characteristic_table);
auto mock_service_2 = mock::BLEService(0x02, service_characteristic_table);

auto services = std::to_array<interface::BLEService *>({&mock_service_1, &mock_service_2});

EXPECT_CALL(mock_gatt, addService).Times(std::size(services));
EXPECT_CALL(mock_service_1, onDataReadyToSend).Times(1);
EXPECT_CALL(mock_service_2, onDataReadyToSend).Times(1);

ble.setServices(services);
}

TEST_F(BLEKitTest, callOnEventsToProcess)
{
spy_ble_hasInitialized_return_value = false;
spy_CoreEventQueue_did_call_function = false;

EXPECT_CALL(mock_gap, setEventHandler).Times(AnyNumber());
EXPECT_CALL(mock_gatt, setEventHandler).Times(AnyNumber());
EXPECT_CALL(mock_gatt, addService).Times(AnyNumber());

ble.init();

Expand Down
Loading