NRF51822新手教程(1)——自定义服务

本文详细介绍如何在NRF51822设备上自定义蓝牙服务,包括创建私有服务UUID,定义服务特性和数据结构,实现温湿度数据定时发送至手机端的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NRF51822新手教程(1)——自定义服务

实验目的:自定义Service私有服务并传输温湿度传感器数据。本实验基于SDK12.0中ble_app_blink示例代码进行更改。

1.定制私有服务UUID和特性UUID

1.1 背景知识——UUID

蓝牙技术联盟规定了一系列制定的UUID,其基本UUID:

0x0000xxxx-0000-1000-8000-00805F9B34FB

为进一步简化基本UUID,每一个蓝牙技术联盟(官方)定义的属性都有一个唯一16位UUID,以代替上面基本UUID中的x部分。例如,心率测量特性使用0X2A37 作为它的16 位
UUID,因此它完整的128 位UUID 为:

0x00002A37-0000-1000-8000-00805F9B34FB

蓝牙技术联盟所用的基本UUID 不能用于任何定制的属性、服务和特性。
对于定制的属性,必须使用另外完整的128 位UUID (即需要自定义UUID)。

1.2 自定义UUID

自定义的基本UUID本质上可以任意定义,一般可用nRF Studio自动生成。本例中采用的基本UUID为:

0x0000xxxx-1212-EFDE-1523-785FEABCD123 (大小写无关)

定义完基本UUID后即可定义特性UUID,用来填补其中的x
本例中定义的特性温湿度特性UUID:

0x1526

实际keil工程中建议新建.h文件(ble_lbs.h),将上面的UUID定义:

#define LBS_UUID_BASE        {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, \
                              0xDE, 0xEF, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00}                              //base UUID 
#define LBS_UUID_TEM_CHAR    0x1526	 // characrestic UUID

以上两个自定义的UUID需要记住,后续会经常用到。
UUID的作用就类似开门的钥匙,不同的UUID对应不同的传输数据

2 私有服务的特性

2.1 自定义数据结构体

在ble_lbs.h头文件中定义下列服务结构体:

struct ble_lbs_s
{
    uint16_t                    service_handle;      /**< Handle of LED Button Service (as provided by the BLE stack). */
	ble_gatts_char_handles_t    temperature_char_handles; //自定义服务的handle
    uint8_t                     uuid_type;           /**< UUID type for the LED Button Service. */
    uint16_t                    conn_handle;         /**< Handle of the current connection (as provided by the BLE stack). BLE_CONN_HANDLE_INVALID if not in a connection. */

};
typedef struct ble_lbs_s ble_lbs_t;

这个结构体是为了记录service/characteristic handles,方便其他位置调用.

2.2 定义服务初始化

服务的添加通过sd_ble_gatts_service_add()进行添加,该函数确定了服务的UUID和回调handle

err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_lbs->service_handle);

添加完服务后还需对数据特性进行添加
通过调用tem_char_add(p_lbs, p_lbs_init)定义具体的特性。

err_code = tem_char_add(p_lbs, p_lbs_init);
uint32_t ble_lbs_init(ble_lbs_t * p_lbs, const ble_lbs_init_t * p_lbs_init)
{
    uint32_t   err_code;
    ble_uuid_t ble_uuid;

    // Initialize service structure.
    p_lbs->conn_handle       = BLE_CONN_HANDLE_INVALID;
    p_lbs->led_write_handler = p_lbs_init->led_write_handler;

    // Add service.
    ble_uuid128_t base_uuid = {LBS_UUID_BASE};
    err_code = sd_ble_uuid_vs_add(&base_uuid, &p_lbs->uuid_type);
    VERIFY_SUCCESS(err_code);

    ble_uuid.type = p_lbs->uuid_type;
    ble_uuid.uuid = LBS_UUID_SERVICE;

    err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_lbs->service_handle);
    VERIFY_SUCCESS(err_code);

    // Add characteristics.
	err_code = tem_char_add(p_lbs, p_lbs_init);
    VERIFY_SUCCESS(err_code);

    return NRF_SUCCESS;
}

2.3 定义具体特性

温湿度传感器数据特性需要可读和可通知,无需可写特性。

static uint32_t tem_char_add(ble_lbs_t * p_lbs, const ble_lbs_init_t * p_lbs_init)
{
	
		ble_gatts_char_md_t char_md;
    ble_gatts_attr_md_t cccd_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;

    memset(&cccd_md, 0, sizeof(cccd_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&cccd_md.write_perm);
    cccd_md.vloc = BLE_GATTS_VLOC_STACK;

    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read   = 1;//可读
    char_md.char_props.notify = 1;//可通知
    char_md.p_char_user_desc  = NULL;
    char_md.p_char_pf         = NULL;
    char_md.p_user_desc_md    = NULL;
    char_md.p_cccd_md         = &cccd_md;
    char_md.p_sccd_md         = NULL;

    ble_uuid.type = p_lbs->uuid_type;
    ble_uuid.uuid = LBS_UUID_TEM_CHAR; // 自定义的char uuid

    memset(&attr_md, 0, sizeof(attr_md));

    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.write_perm);
    attr_md.vloc       = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth    = 0;
    attr_md.wr_auth    = 0;
    attr_md.vlen       = 0;

    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid       = &ble_uuid;
    attr_char_value.p_attr_md    = &attr_md;
    attr_char_value.init_len     = 2; //温、湿度两个8位数据,长度为2
    attr_char_value.init_offs    = 0;
    attr_char_value.max_len      = 2;
    attr_char_value.p_value      = NULL;

    return sd_ble_gatts_characteristic_add(p_lbs->service_handle,
                                               &char_md,
                                               &attr_char_value,
                                               &p_lbs->temperature_char_handles);

}

至此,自定义的服务和特性已经完成。接下来该考虑如何将温湿度传感器得到的数据发送到上位机(手机)中了。

3 温、湿度数据发送

3.1 nRF51822 定时器中断

要使数据定时发送,必然要用到内置的定时器和定时器中断处理函数。
nRF 的定时器配置在main()函数的初始化中分为两块,一块是定时器初始化函数timers_init(),一块是定时器开始计时函数application_timers_start();

3.1.1 定时器初始化timers_init()

需要添加的代码代码就是app_timer_create(),该函数入口有三个参数,分别为:计时器ID;计时模式;中断回调函数。
计时器ID在main.c文件中定义:

APP_TIMER_DEF(tempreture_humidity_timer_id);                 /**< measure_temperature_and_humidity. */

计时模式选择APP_TIMER_MODE_REPEATED,重复模式
中断回调函数需另外定义,这里传入回调函数名称。当计时溢出时会自动转入回调函数进行处理。

static void timers_init(void)
{
		uint32_t err_code;
    // Initialize timer module, making it use the scheduler
    APP_TIMER_INIT(APP_TIMER_PRESCALER, APP_TIMER_OP_QUEUE_SIZE, false);
	//添加温湿度测量中断	
	err_code = app_timer_create(&tempreture_humidity_timer_id,
                                APP_TIMER_MODE_REPEATED,
                                measurement_timer_handler);
    APP_ERROR_CHECK(err_code);
}
static void measurement_timer_handler(void * p_context)
{
		measure_temperature_and_humidity();//测量温湿度
}
static ble_lbs_t                        m_lbs; 
void measure_temperature_and_humidity()
{
		....
		为了简洁,这里省略了IIC通讯读取传感器数据
		....
		uint8_t humidity = (uint8_t) humidityRH;
		uint8_t temperature = (uint8_t) temperatureC;
    DelayMicroSeconds(300000);     // wait 0.3s for next measurement
		ble_tem_change(&m_lbs,temperature,humidity);//蓝牙数据发送
}

可以看到,在定时器中断中将调用温湿度测量函数。为了简洁,在温湿度测量函数中省略的IIC的读取过程。当完成数据读取后,通过ble_tem_change()函数将温湿度数据发送。

3.1.2定时器开启

定时器开启函数一定不用忘记定义,并加入相应的计时器ID,不开启定时器一切白给。

static void application_timers_start(void)
{
    uint32_t err_code;
    // Start application timers.
    err_code = app_timer_start(tempreture_humidity_timer_id, Temperature_Humidty_INTERVAL, NULL);
    APP_ERROR_CHECK(err_code);
}

至此,如果一切顺利的话程序已经可以定时测量温湿度并调用数据发送函数ble_tem_change().接下来就是定义具体的数据发送。

4 数据更新发送

蓝牙数据发送通过sd_ble_gatts_hvx()函数

// 数据更新函数
uint16_t ble_tem_change(ble_lbs_t * p_lbs, uint8_t temperature,uint8_t humidity)
{
	uint32_t err_code;
	ble_gatts_hvx_params_t tem_params;
	uint16_t len = 2;
	uint8_t SHT_val[2];
	SHT_val[0] = (uint8_t) temperature;
	SHT_val[1] = (uint8_t) humidity;
	if(p_lbs -> conn_handle != BLE_CONN_HANDLE_INVALID)
	{
    memset(&tem_params, 0, sizeof(tem_params));
    tem_params.type = BLE_GATT_HVX_NOTIFICATION;
    tem_params.handle = p_lbs->temperature_char_handles.value_handle;
	  tem_params.p_data = SHT_val;
    tem_params.p_len = &len;
    return sd_ble_gatts_hvx(p_lbs->conn_handle, &tem_params); // 发送数据更新
	}
	else{
		err_code = NRF_ERROR_INVALID_STATE;
		SEGGER_RTT_printf(0,"params.p_data error %% \n");
	}
}

至此即可在手机端看到实际数据

在这里插入图片描述
在这里插入图片描述
可以看到在自定义的服务中,Value: (0x)18-44对应温湿度数值24-68.
自定义服务成功!

### Nordic 蓝牙 Mesh 开发指南 #### 1. nRF5 SDK for Mesh 的概述 nRF5 SDK for Mesh 是一个开源软件开发套件,旨在帮助开发者在 Nordic 的 nRF5 微控制器上构建基于蓝牙网状网络的应用程序[^1]。该工具包提供了完整的文档和支持材料,使开发者可以轻松创建并管理大型设备网络。 #### 2. 安装与设置环境 为了开始使用 nRF5 SDK for Mesh 进行开发工作,首先需要下载 GitHub 上提供的最新版本源码库,并按照官方说明完成必要的编译器和其他依赖项的安装。确保所选硬件兼容指定系列的 nRF5 SoC 或者模块产品线。 #### 3. 创建第一个项目 对于初次接触蓝牙 mesh 技术的新手来说,可以从简单的例子入手——比如点灯实验。这类基础实例不仅展示了如何定义自己的模型(Model),还介绍了怎样利用现有的通用 OnOff 模型来实现基本功能[^4]。在这个过程中需要注意的是,在正式的产品里应当替换为自己公司的 Bluetooth SIG 注册 ID 来代替示例中的临时编号[^2]。 #### 4. 编写自定义 Model 当熟悉了框架之后,则可以根据具体需求扩展更多特性。例如,可以通过继承标准接口来自定义行为逻辑;同时也要遵循蓝牙技术联盟制定的相关规范以保证互操作性和安全性。 ```c // 示例:注册一个新的 vendor-specific model static const struct bt_mesh_model_pub pub = { .msg = NET_BUF_SIMPLE(2 + 3), }; static void message_received(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx, uint8_t *data, uint16_t length) { // 处理接收到的消息... } const struct bt_mesh_model_ops my_custom_model_op = { .message_recv = message_received, }; struct bt_mesh_model custom_model = { .id = MY_COMPANY_ID | BT_MESH_MODEL_ID_GEN_ONOFF_SRV, .pub = &pub, .op = &my_custom_model_op, }; ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值