logging: Added BLE Backend Service

Added a new logger backend to recieve data over a BLE
notification characteristic.
The characteristic is based on the UUID from the Nordic connect SDK
service NUS, which allows to have a UART shell over BLE.
The idea behind this, is that this logger can be used directly
with the NRF apps or any other BLE UART terminal app.

Signed-off-by: Victor Chavez <chavez-bermudez@fh-aachen.de>
This commit is contained in:
Victor Chavez 2023-03-29 01:00:29 +02:00 committed by Carles Cufí
parent 872907f42c
commit a18aa915ce
10 changed files with 388 additions and 0 deletions

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2023 Victor Chavez
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_LOG_BACKEND_BLE_H_
#define ZEPHYR_LOG_BACKEND_BLE_H_
#include <stdbool.h>
/**
* @brief Raw adv UUID data to add the ble backend for the use with apps
* such as the NRF Toolbox
*
*/
#define LOGGER_BACKEND_BLE_ADV_UUID_DATA \
0x9E, 0xCA, 0xDC, 0x24, 0x0E, 0xE5, 0xA9, 0xE0, 0x93, 0xF3, 0xA3, 0xB5, 0x01, 0x00, 0x40, \
0x6E
/**
* @brief Hook for application to know when the ble backend
* is enabled or disabled.
* @param backend_status True if the backend is enabled or false if disabled
* @param ctx User context
*
*/
typedef void (*logger_backend_ble_hook)(bool backend_status, void *ctx);
/**
* @brief Allows application to add a hook for the status of the BLE
* logger backend.
* @details The BLE logger backend is enabled or disabled auomatically by
* the subscription of the notification characteristic of this BLE
* Logger backend service.
*
* @param hook The hook that will be called when the status of the backend changes
* @param ctx User context for whenever the hook is called
*/
void logger_backend_ble_set_hook(logger_backend_ble_hook hook, void *ctx);
#endif /* ZEPHYR_LOG_BACKEND_BLE_H_ */

View file

@ -0,0 +1,10 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(logger_ble_backend)
target_sources(app PRIVATE
src/main.c
)

View file

@ -0,0 +1,26 @@
.. _logger_ble_backend:
Logging: BLE Backend
########################
Overview
********
Sample that demonstrates how to setup and use the BLE Logging backend. The
BLE Logger uses the NRF Connect SDK NUS service as UUID to make it compatible
with already existing apps to debug BLE connections over UART.
Requirements
************
* A board with BLE support
Building and Running
********************
This sample can be found under :zephyr_file:`samples/subsys/logging/ble_backend` in the
Zephyr tree.
The BLE logger can be tested with the NRF Toolbox app or any similar app that can connect over
BLE and detect the NRF NUS UUID service.

View file

@ -0,0 +1,11 @@
CONFIG_BT=y
CONFIG_BT_SMP=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="Zephyr Logger Backend BLE"
CONFIG_LOG_BACKEND_BLE=y
CONFIG_LOG=y
CONFIG_LOG_PROCESS_THREAD_STACK_SIZE=2048
# Uncomment to use the maximum buffer size
# CONFIG_BT_L2CAP_TX_MTU=600
# CONFIG_BT_BUF_ACL_RX_SIZE=600
# CONFIG_LOG_BACKEND_BLE_BUF_SIZE=512

View file

@ -0,0 +1,11 @@
sample:
description: A simple application that demonstrates the
use of the logging ble backend.
name: logger backend ble
tests:
sample.logging.ble_backend.nrf52833:
harness: bluetooth
platform_allow: qemu_cortex_m3 qemu_x86
integration_platforms:
- qemu_cortex_m3
tags: bluetooth

View file

@ -0,0 +1,100 @@
/*
* Copyright (c) 2023 Victor Chavez
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/logging/log.h>
#include <zephyr/logging/log_backend_ble.h>
LOG_MODULE_REGISTER(ble_backend);
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA_BYTES(BT_DATA_UUID128_ALL, LOGGER_BACKEND_BLE_ADV_UUID_DATA)
};
static void start_adv(void)
{
int err;
err = bt_le_adv_start(BT_LE_ADV_CONN_NAME, ad, ARRAY_SIZE(ad), NULL, 0);
if (err) {
LOG_ERR("Advertising failed to start (err %d)", err);
return;
}
LOG_INF("Advertising successfully started");
}
static void connected(struct bt_conn *conn, uint8_t err)
{
if (err) {
LOG_ERR("Connection failed (err 0x%02x)", err);
} else {
LOG_INF("Connected");
}
}
static void disconnected(struct bt_conn *conn, uint8_t reason)
{
LOG_INF("Disconnected (reason 0x%02x)", reason);
start_adv();
}
BT_CONN_CB_DEFINE(conn_callbacks) = {
.connected = connected,
.disconnected = disconnected,
};
static void auth_cancel(struct bt_conn *conn)
{
char addr[BT_ADDR_LE_STR_LEN];
bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr));
LOG_INF("Pairing cancelled: %s", addr);
}
static struct bt_conn_auth_cb auth_cb_display = {
.cancel = auth_cancel,
};
void backend_ble_hook(bool status, void *ctx)
{
ARG_UNUSED(ctx);
if (status) {
LOG_INF("BLE Logger Backend enabled.");
} else {
LOG_INF("BLE Logger Backend disabled.");
}
}
void main(void)
{
int err;
LOG_INF("BLE LOG Demo");
logger_backend_ble_set_hook(backend_ble_hook, NULL);
err = bt_enable(NULL);
if (err) {
LOG_ERR("Bluetooth init failed (err %d)", err);
return;
}
bt_conn_auth_cb_register(&auth_cb_display);
start_adv();
while (1) {
uint32_t uptime_secs = k_uptime_get_32()/1000U;
LOG_INF("Uptime %d secs", uptime_secs);
k_sleep(K_MSEC(1000));
}
}

View file

@ -15,6 +15,11 @@ zephyr_sources_ifdef(
log_backend_adsp_mtrace.c
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_BLE
log_backend_ble.c
)
zephyr_sources_ifdef(
CONFIG_LOG_BACKEND_EFI_CONSOLE
log_backend_efi_console.c

View file

@ -6,6 +6,7 @@ menu "Backends"
rsource "Kconfig.adsp"
rsource "Kconfig.adsp_hda"
rsource "Kconfig.adsp_mtrace"
rsource "Kconfig.ble"
rsource "Kconfig.efi_console"
rsource "Kconfig.fs"
rsource "Kconfig.native_posix"

View file

@ -0,0 +1,34 @@
# Copyright (c) 2023 Victor Chavez
# SPDX-License-Identifier: Apache-2.0
config LOG_BACKEND_BLE
bool "Bluetooth Low Energy (BLE) backend"
depends on BT
depends on LOG_PROCESS_THREAD_STACK_SIZE>=2048
select LOG_OUTPUT
select EXPERIMENTAL
help
Backend that sends log messages over Bluetooth LE Notifications. This
characteristic and its service are compatible with the Nordic UART
Service (NUS), from the nRF Connect SDK.
This allows to use this BLE Logger directly with a compatible app such
as the nRF UART 2.0 or nRF Toolbox app.
if LOG_BACKEND_BLE
config LOG_BACKEND_BLE_BUF_SIZE
int "BLE Logger Backend Buffer size"
range 20 512
default 20
help
Maximum buffer size that can be transmitted over the BLE Logger
notification characteristic. The minimum size is 20 for the smallest
MTU packet. Be sure to increase the MTU size in your application to use
bigger values.
Both BT_L2CAP_TX_MTU and BT_BUF_ACL_RX_SIZE will need to be increased.
backend = BLE
backend-str = ble
source "subsys/logging/Kconfig.template.log_format_config"
endif # LOG_BACKEND_BLE

View file

@ -0,0 +1,148 @@
/*
* Copyright (c) 2023 Victor Chavez
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/logging/log_backend.h>
#include <zephyr/logging/log_output.h>
#include <zephyr/logging/log_backend_ble.h>
#include <zephyr/bluetooth/gatt.h>
static uint8_t output_buf[CONFIG_LOG_BACKEND_BLE_BUF_SIZE];
static bool panic_mode;
static uint32_t log_format_current = CONFIG_LOG_BACKEND_BLE_OUTPUT_DEFAULT;
static logger_backend_ble_hook user_hook;
static void *user_ctx;
/* Forward declarations*/
const struct log_backend *log_backend_ble_get(void);
/**
* @brief Callback for the subscription to the ble logger notification characteristic
* @details This callback enables/disables automatically the logger when the notification
* is subscribed.
* @param attr The attribute that's changed value
* @param value New value
*/
static void log_notify_changed(const struct bt_gatt_attr *attr, uint16_t value);
/** BLE Logger based on the UUIDs for the NRF Connect SDK NUS service
* https://developer.nordicsemi.com/nRF_Connect_SDK/doc/2.3.0/nrf/libraries/bluetooth_services/services/nus.html
*/
#define NUS_SERVICE_UUID \
BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x6E400001, 0xB5A3, 0xF393, 0xE0A9, 0xE50E24DCCA9E))
#define LOGGER_TX_SERVICE_UUID \
BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x6E400003, 0xB5A3, 0xF393, 0xE0A9, 0xE50E24DCCA9E))
#define LOGGER_RX_SERVICE_UUID \
BT_UUID_DECLARE_128(BT_UUID_128_ENCODE(0x6E400002, 0xB5A3, 0xF393, 0xE0A9, 0xE50E24DCCA9E))
/**
* @brief BLE Service that represents this backend
* @note Only transmission characteristic is used. The RX characteristic
* is added to make the backend usable with the NRF toolbox app
* which expects both characteristics.
*/
BT_GATT_SERVICE_DEFINE(ble_log_svc, BT_GATT_PRIMARY_SERVICE(NUS_SERVICE_UUID),
BT_GATT_CHARACTERISTIC(LOGGER_TX_SERVICE_UUID, BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ, NULL, NULL, NULL),
BT_GATT_CCC(log_notify_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
BT_GATT_CHARACTERISTIC(LOGGER_RX_SERVICE_UUID, BT_GATT_CHRC_WRITE, 0,
NULL, NULL, NULL),
);
/* Log characteristic attribute is defined after the first attribute (i.e. the service) */
const struct bt_gatt_attr *log_characteristic = &ble_log_svc.attrs[1];
void logger_backend_ble_set_hook(logger_backend_ble_hook hook, void *ctx)
{
user_hook = hook;
user_ctx = ctx;
}
void log_notify_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
ARG_UNUSED(attr);
const bool notify_enabled = value == BT_GATT_CCC_NOTIFY;
if (notify_enabled) {
log_backend_activate(log_backend_ble_get(), NULL);
} else {
log_backend_deactivate(log_backend_ble_get());
}
if (user_hook != NULL) {
user_hook(notify_enabled, user_ctx);
}
}
static int line_out(uint8_t *data, size_t length, void *output_ctx)
{
const int notify_res = bt_gatt_notify(NULL, log_characteristic, data, length);
if (notify_res == 0) {
return length;
} else {
return 0;
}
}
LOG_OUTPUT_DEFINE(log_output_ble, line_out, output_buf, sizeof(output_buf));
static void process(const struct log_backend *const backend, union log_msg_generic *msg)
{
uint32_t flags = LOG_OUTPUT_FLAG_FORMAT_SYSLOG | LOG_OUTPUT_FLAG_TIMESTAMP;
if (panic_mode) {
return;
}
log_format_func_t log_output_func = log_format_func_t_get(log_format_current);
log_output_func(&log_output_ble, &msg->log, flags);
}
static int format_set(const struct log_backend *const backend, uint32_t log_type)
{
log_format_current = log_type;
return 0;
}
static void init_ble(struct log_backend const *const backend)
{
log_backend_deactivate(log_backend_ble_get());
}
static void panic(struct log_backend const *const backend)
{
panic_mode = true;
}
/**
* @brief Backend ready function for ble logger
* @details After initialization of the logger, this function avoids
* the logger subys to enable it. The logger is enabled automatically
* via the notification changed callback.
* @param backend Logger backend
* @return Zephyr permission denied
*/
static int backend_ready(const struct log_backend *const backend)
{
return -EACCES;
}
const struct log_backend_api log_backend_ble_api = {.process = process,
.dropped = NULL,
.panic = panic,
.init = init_ble,
.is_ready = backend_ready,
.format_set = format_set,
.notify = NULL};
LOG_BACKEND_DEFINE(log_backend_ble, log_backend_ble_api, true);
const struct log_backend *log_backend_ble_get(void)
{
return &log_backend_ble;
}