一、介绍
1.1 AirSync概述
AirSync是微信硬件平台提供的一种微信客户端与蓝牙设备间通讯的技术协议,它允许蓝牙设备与微信客户端之间收发数据,并支持通过微信客户端透传到远程服务器。该技术在支持微信互联的蓝牙手环、血压计、智能秤、血糖仪等设备上有比较多的应用。
AirSync支持经典蓝牙和BLE低功耗蓝牙技术。
AirSync开发文档
- 主要功能
该协议打通了设备和厂商服务器之间的数据链路,也就是支持将设备上的数据发送到厂商的服务器上,也支持将厂商的数据发送到设备。厂商的数据对于微信来说,是黑盒,微信不对设备数据做分析。
该协议也打通了设备和微信服务器之间的数据链路。设备和微信服务器之间的数据格式由微信规定,例如登录,新消息通知等。
1.2 蓝牙广播
为了和微信能够通信,设备的广播包需符合以下的格式:
- 微信规定的service uuid(0xFEE7)
- 厂商自定义字段里,包含MAC地址(0xFF + company_identifier + MAC)
1.3 蓝牙BLE模拟成流
微信规定了蓝牙BLE设备需要先模拟成流(即stream,输入输出流)。经典蓝牙的RFCOMM,就是一个流。流具有的特性有:
- 可以传输无限长度的数据
- 双工,读写可以并发,互不干扰
显然,蓝牙BLE无法传输无限长度的数据,为了实现这个目的,需要定义一个规范。
蓝牙设备需暴露两个特征值(Characteristics):Write特征值,Indication特征值。蓝牙设备从Write特征值接受数据,从Indication特征值发送数据。
Indication特征值类型是bytes。
这里我们约定,把一个特征值一次传输的数据,称为一帧(不同类型的特征值一次传输的数据长度是不一样的)。
注意:应用层上的数据包(例如1k大小),会分散成许多帧来传输。
1.3.1 蓝牙设备写过程
- 分帧:假设蓝牙手环上有1k数据,要发给手机微信。由于一个特征值长度有限(如20个字节),显然需要分多次才能传输完成。1k数据,要分成1024字节/ 20字节=51 个帧。剩下的4个字节,不足一帧(20个字节),需补齐为一帧并对剩下的16个字节赋0。总共是52帧。
- 发送第一个帧:把第一个帧的内容放入特征值里面。然后通知手机读取数据,通知有两种方式,Indication 和notify,这里使用Indication方式,即带响应的通知。当通知完成的时候,可以认为手机已经读完数据。这就完成了发送第一个帧
- 按照2的步骤,依次发送剩下的帧。
注意:
蓝牙设备必须等微信app订阅了Characteristics之后,才能indicate数据,否则会造成设备发送数据丢失的问题。
1.3.2 蓝牙设备读过程
当蓝牙设备发现读特征值收到数据的时候,就接收数据,并追加到设备的buf里。
二、AirSync服务添加
Airsync V0.2源码下载地址: https://iot.weixin.qq.com/wiki/new/index.html?page=6-1
2.1 添加文件
创建三个文件夹 wechatapp、wechatproduct、wechatservice,然后分别添加以下文件
2.2 添加代码
- 添加头文件
// WeChat
#include "ble_wechat_service.h"
#include "mpbledemo2.h"
- 添加声明
extern ble_wechat_t m_ble_wechat; // 定义微信服务
extern data_handler *m_mpbledemo2_handler;
-
修改UUID
static ble_uuid_t m_adv_uuids[] = /**< Universally unique service identifier. */
{
{BLE_UUID_WECHAT_SERVICE, NUS_SERVICE_UUID_TYPE}
};
- 修改蓝牙事件处理函数 ble_evt_dispatch() 或 ble_evt_handler()
在最后添加微信服务事件处理函数
- 修改服务初始化函数 services_init()
- 添加获取MAC地址函数
void GetMacAddress(uint8 *pAddress)
{
uint32 errCode;
ble_gap_addr_t macAddr;
errCode = sd_ble_gap_addr_get(&macAddr);
APP_ERROR_CHECK(errCode);
uint8 i;
for(i = 0; i < 6; i++)
{
pAddress[i] = macAddr.addr[5 - i];// 逆序
}
}
- 修改广播初始化函数 advertising_init()
static void advertising_init(void)
{
uint32_t err_code;
ble_advdata_t advdata;
// Build and set advertising data
memset(&advdata, 0, sizeof(advdata));
advdata.name_type = BLE_ADVDATA_FULL_NAME;
advdata.include_appearance = false;
advdata.flags = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
advdata.uuids_complete.p_uuids = m_adv_uuids;
uint8 macAddr[6] = {0};
GetMacAddress(macAddr);
memcpy(g_advertisingDataCustomData, macAddr, 6);
ble_advdata_manuf_data_t manuf_data;
manuf_data.company_identifier = COMPANY_IDENTIFIER;
manuf_data.data.size = 6;
manuf_data.data.p_data = macAddr;
advdata.p_manuf_specific_data = &manuf_data;
ble_adv_modes_config_t options = {0};
options.ble_adv_fast_enabled = BLE_ADV_FAST_ENABLED;
options.ble_adv_fast_interval = APP_ADV_INTERVAL;
options.ble_adv_fast_timeout = APP_ADV_TIMEOUT_IN_SECONDS;
err_code = ble_advertising_init(&advdata, NULL, &options, on_adv_evt, NULL);
APP_ERROR_CHECK(err_code);
}
- 添加获取一个事件处理函数的函数
static void data_handler_init(data_handler **p_data_handler, uint8_t product_type)
{
if(*p_data_handler == NULL)
{
*p_data_handler = get_handler_by_type(product_type);
}
}
- 添加一个注册产品函数
static void register_all_products(void)
{
REGISTER(mpbledemo2);
}
- 在 main 函数中添加如下代码
register_all_products(); // 注册微信服务应用处理
data_handler_init(&m_mpbledemo2_handler, PRODUCT_TYPE_MPBLEDEMO2);
- 在 ble_wechat_service.h 中屏蔽掉如下代码
#include <string.h>
#include <stdint.h>
#include "ble.h"
//#include "nrf_gpio.h"
#include "ble_conn_params.h"
//#include "ble_stack_handler.h"
#include "ble_types.h"
#include "ble_wechat_util.h"
#define BLE_UUID_WECHAT_SERVICE 0xFEE7
#define BLE_UUID_WECHAT_WRITE_CHARACTERISTICS 0xFEC7
#define BLE_UUID_WECHAT_INDICATE_CHARACTERISTICS 0xFEC8
#define BLE_UUID_WECHAT_READ_CHARACTERISTICS 0xFEC9
//#define APP_ADV_INTERVAL 40
//#define APP_ADV_TIMEOUT_IN_SECONDS 0
#define BLE_WECHAT_MAX_DATA_LEN (GATT_MTU_SIZE_DEFAULT - 3)
- 在 ble_wechat_service.c 中屏蔽掉如下代码
#include "ble_wechat_service.h"
#include "nrf_gpio.h"
#include "ble_conn_params.h"
//#include "ble_stack_handler.h"
#include "ble_types.h"
#include "mpbledemo2.h"
#include "ble_wechat_util.h"
- 在 ble_wechat_service.c 中添加代码
extern mpbledemo2_state mpbledemo2Sta;
• 由 Leung 写于 2020 年 10 月 12 日
• 参考:[Nordic BLE]nRF51822 基于nRF5_SDK_11.0.0移植Airsync到ble_app_uart工程
微信蓝牙BLE接入调试指引 硬件篇
以蓝牙开发的视觉解读微信Airsync协议