简介
Adafruit Feather nRF52 Bluefruit 是我们最新的易于使用的多合一蓝牙低功耗板,带有本机蓝牙芯片,nRF52832!这是我们对“兼容所有”的Arduino兼容+蓝牙低功耗技术的要求,内置USB和电池充电功能。
该芯片的闪存,SRAM和性能是早期基于nRF51的两倍Bluefruit模块。最棒的是它具有Arduino IDE支持,因此没有像ATmega32u4或ATSAMD21这样的“辅助”芯片。而是直接对该芯片进行编程!它有无数很棒的外设:大量的GPIO,模拟输入,PWM,计时器等。省去了额外的微控制器意味着价格,复杂性和功耗都更低/更好。它允许您直接在Arduino IDE上直接在nRF52832上运行代码,就像使用任何其他MCU或Arduino兼容设备一样。如果您想基于Bluefruit nRF52 Feather项目设计自己的硬件,则单个MCU意味着更好的性能,更低的总体功耗以及更低的生产成本!
这些芯片已预先进行了自动编程,重置引导加载程序,以便您无需按任何按钮即可在Arduino IDE中快速上载。是否要直接对芯片进行编程?您可以将我们的命令行工具与您喜欢的编辑器和工具链一起使用。
为了让您快速起步并运行,我们已经完成了所有繁重的工作,以使低级BLE堆栈成形,从而使您可以集中精力从第一天开始就在您的项目中!
nRF52832技术详细信息
ARM Cortex M4F(具有硬件浮点加速),运行频率为64MHz
512KB闪存和64KB SRAM
内置USB串行转换器,用于快速,高效的编程和调试
兼容蓝牙低功耗的2.4GHz无线电(有关详细信息,请参见RF52832产品规格)
使用内部线性和DC/DC电压调节器,最高输出功率为+ 4dBm
1.7v至3.3v
19 GPIO, 8个12位ADC引脚,最多12个PWM输出(3个PWM模块,每个模块具有4个输出)
引脚#17红色LED用于通用闪烁i ng
电源/启用引脚
尺寸为2.0“ x 0.9” x 0.28“(51mm x 23mm x 8mm),没有焊接接头,
羽毛)-5.7克
4个安装孔
重置按钮
用于调试的可选SWD连接器
开箱即用几乎与我们所有的Adafruit FeatherWings一样! (需要GPS FeatherWing之类的需要UART的翼将无法工作)
nRF52832产品规格中提供了更多技术细节。
与我们所有的Feather板一样,Bluefruit nRF52 Feather包括板载基于USB的LIPO充电功能,并具有标准的LIPO电池连接器,可让您无线项目真正是“无线”项目,无需额外费用(除了LIPO单元本身)。
div》 nRF51或nRF52 Bluefruit设备?
Bluefruit nRF52 Feather(基于nRF52832 SoC)与基于nRF51822的早期Bluefruit产品(Bluefruit M0 Feather等)有很大不同。
从硬件的角度来看,nRF52 Feather基于功能更强大的ARM Cortex M4F处理器,具有512KB闪存,64KB SRAM。和硬件浮点加速。..而早期的nRF51822基于较小的ARM Cortex M0内核(内部指令较少),具有256KB闪存和16KB或32KB SRAM。
更重要的是,设计方法我们使用nRF52进行处理的方式完全不同:
基于nRF51的Bluefruit板作为模块运行,您可以通过外部MCU(通常是Atmel 32u4或SAMD21)连接到该模块,并通过SPI或UART。
使用nRF52,您可以直接在nRF52832上运行所有代码,而无需使用或不需要外部MCU!
这种设计上的变化有助于降低总体成本,由于不受SPI或UART传输通道的限制,因此可以提供更好的性能,并且可以帮助改善总体功耗。
,但这也意味着完全不同的API和开发过程!
nRF51 Bluefruit草图未经修改,es将无法在nRF52 Bluefruit硬件上运行!这两个设备系列具有不同的API和编程模型,旨在以两种不同的方式解决您的无线问题。
设备引脚排列
请注意,nRF52832使用USB串行适配器将RXD/TXD相对于nRF52
特殊说明
以下引脚有一些限制,需要考虑使用它们时:
PIN_DFU/P0.20 :如果在启动时检测到此引脚处于GND电平,则该板将进入特殊的串行引导加载程序模式,并且不会执行任何用户代码,直接进入引导加载程序模式。如果希望将此引脚用作标准GPIO,请确保使用上拉电阻将其拉高,以便在MCU启动时代码可以正常执行。
P0.31/A7 :此引脚硬连线至LIPO电池输入上的分压器,可让您安全地测量设备上LIPO电池的电量。如有可能,应避免将此引脚用作输入,因为您将失去读取电池电压的能力。您可以将其用作输出,只要确保要读取电池时将其切换为模拟输入,然后在切换引脚时将其切换回输出
FRST/P0.22 :启动时将此引脚设置为GND将导致设备在启动时执行出厂重置,擦除和配置数据以及用户草图。下次重置时,默认情况下应进入串行引导加载程序模式,因为将不存在用户草图。您可以使用它来恢复“砖状”电路板,但是如果您不希望这样做,请注意不要在启动时将FRST设置为低电平。默认情况下,在引导加载程序阶段会在此引脚上启用一个弱内部上拉电阻。 FRST与Mynewt引导加载程序不兼容!
电源引脚
3.3V输出:这两个引脚连接到板载3.3V稳压器的输出。它们可用于为外部传感器,分支或羽翼提供3.3V电源。
LIPO输入(VBAT):这是可选LIPO电池的电源,可以通过JST PH连接器连接。标称值为〜3.5-4.2V。
VREG启用:可以将该引脚设置为GND,以禁用板载稳压器的3.3V输出。默认情况下,它通过上拉电阻设置为高电平。
USB电源(VBUS):这是USB连接器的电源电压,标称值为4.5-5.2V。
模拟输入
可将8个可用模拟输入配置为生成8、10或12位数据(或14位带过采样),速度最高可达200kHz(取决于所生成值的位宽),具体取决于内部0.6V参考电压或外部电源。
使用以下默认值:
默认电压范围:0-3.6V (使用具有1/6增益的内部0.6V基准)
默认分辨率:10位(0..1023)
与可以重新映射到任何GPIO/数字引脚的数字功能不同,ADC功能绑定到指定的引脚,在上图(A0,A1等)中标记为A *。
PWM输出
任何GPIO引脚都可以配置为PWM输出,
三个PWM模块可以提供多达12个PWM通道,并以多达四个通道的组的形式进行单独的频率控制。
请注意,基于DMA的PWM输出在nR52 BSP的初始版本中仍在进行中,并计划在此处进行进一步的改进。
I2C引脚
nRF52832上的I2C引脚需要外部上拉电阻才能工作,默认情况下Adafruit nRF52 Feather上不存在这些电阻。您将需要提供外部上拉电阻才能使用它们。所有的Adafruit I2C分支都已经具有适当的上拉电阻,因此这通常对您来说不是问题。
装配体
我们出厂的Feathers经过了全面测试,但未连接标头-这使您在选择如何使用和配置Feather时更具灵活性
标题选项!
焊接时,有一些选择要考虑!
第一个选择是焊接普通的公头,这使您可以插入羽毛变成无焊面包板
另一种选择是使用插座母头。这不会让您将Feather插入面包板,但可以非常轻松地连接羽翼
我们也有“ slim”母头的版本短一些,并且形状更紧凑
最后,有“ Stacking Header”选项。这是两全其美的。您可以插入无焊面包板 并在顶部插入一个羽翼。但是它有点笨重
以普通标题进行焊接
准备标题栏:
如有必要,将标题栏剪切成一定长度。如果将其插入面包板,则焊接会更容易-长按针脚
添加分线板:
将分线板放置在插针上,以使短的插针穿过分线垫
然后焊接!
请务必焊接所有引脚以实现可靠的电接触。
(有关焊接的技巧,请务必查看我们的 优良焊接指南 )。
将另一条带焊接为
您完成了!目视检查焊点,然后继续进行下一步
在女性头上焊接
原位录音
对于插座,您需要将其绑在适当的位置,因此当您翻转电路板时,它们不会掉落
Flip&Tack Solder Fack&Tack Solder
After翻转,在每个条上焊接一个或两个点,以将接头“固定”到位
并焊接!
请务必焊接所有引脚以确保可靠的电接触。
(有关焊接的技巧,请务必查看我们的 优秀焊接指南 )。
您完成了!目视检查焊点,然后继续进行下一步
Arduino支持设置
您可以分两个步骤安装Adafruit Bluefruit nRF52 BSP(板级支持包):
nRF52支持至少需要Arduino IDE版本1.8.6!在继续阅读本指南之前,请确保您具有最新版本!
如果在安装或使用此BSP时遇到任何问题,请查阅本页底部的“常见问题解答”部分!
1。 BSP安装 推荐:通过板管理器安装BSP
下载并安装Arduino IDE(至少 v1.8 )
启动Arduino IDE
进入首选项
将https://www.adafruit.com/package_adafruit_index.json添加为“ 附加板管理器URL ”(请参见下图) )
重新启动Arduino IDE
从 Tools-》 Board 菜单中打开 Boards Manager 选项并安装‘ Adafruit的Adafruit nRF52 ”(见下图)
最多需要几分钟才能完成交叉编译工具链和与此BSP相关的工具的安装。
下图所示的安装阶段延迟是正常的,请耐心等待,并让安装正常终止:
已安装BSP,然后从中选择“ Adafruit Bluefruit nRF52840 Feather Express” (对于nRF52840 Feather)的“ Adafruit Bluefruit nRF52832 Featherger ”(对于nRF52 Feathger)。工具-》板菜单,它将更新您的系统配置,以使用正确的nRF52编译器和设置:
2。仅限LINUX:adafruit-nrfutil工具安装
adafruit-nrfutil是Nordic的nrfutil的修改版本,用于使用内置的串行引导程序来刷新板。它最初是为python2编写的,但自BSP版本0.8.5起,已迁移到python3并重命名为 adafruit-nrfutil 。
仅在Linux上才需要执行此步骤,BSP中已包含针对Windows和MacOS的adafruit-nrfutil的预构建二进制文件。对于大多数设置来说,这应该是开箱即用的。
如果尚未在系统中安装python3,请安装
下载:文件
复制代码
$ sudo apt-get install python3 $ sudo apt-get install python3
然后运行以下命令从PyPi安装该工具
下载:file
复制代码
$ pip3 install --user adafruit-nrfutil $ pip3 install --user adafruit-nrfutil
将pip3安装目录添加到您的 PATH 中它尚未添加。通过运行
,确保adafruit-nrfutil可以在终端中执行下载:文件
复制代码
$ adafruit-nrfutil version
adafruit-nrfutil version 0.5.3.post12 $ adafruit-nrfutil version
adafruit-nrfutil version 0.5.3.post12
3。更新引导程序(仅nRF52832)
要与Nordic的SoftDevice保持同步,如果使用的是原始的基于nRF52832的 Bluefruit nRF52 Feather 板,则可能需要更新引导程序。
点击此链接以获取有关操作方法的说明
较新的nRF52840 Feather Express不需要执行此步骤,后者完全具有不同的引导程序!
更新nRF52832引导程序
高级选项:如果需要,通过’git‘手动安装BSP
针对核心代码库进行任何开发(生成提取请求等),您还可以选择使用“ git”手动安装Adafruit nRF52 BSP,如下所述:
通过git的Adafruit nRF52 BSP(对于核心
通过如上所述的Board Manager安装BSP,以安装编译器和工具。
》
删除由Board Manager在Adruino15中安装的核心文件夹 nrf52 ,具体取决于您的操作系统。可能是
macOS :~/Library/Arduino15/packages/adafruit/hardware/nrf52
Linux :~/.arduino15/packages/adafruit/hardware/nrf52
Windows :%APPDATA%\Local\Arduino15\packagesdafruit\hardware rf52
转到命令行上的sketchbook文件夹,该文件夹应为以下其中之一:
macOS :~/Documents/Arduino
Linux :~/Arduino
Windows :~/Documents/Arduino
创建一个名为hardware/Adafruit的文件夹(如果该文件夹不存在),并将目录更改为该文件夹。
将Adafruit_nRF52_Arduino存储库克隆到步骤2中所述的文件夹中:
git clone [email protected]:adafruit/Adafruit_nRF52_Arduino.git
这应产生一个最终的文件夹名称,例如~/Documents/Arduino/hardware/Adafruit/Adafruit_nRF52_Arduino(macOS)
重新启动Arduino IDE
Arduino开发板测试
一旦在系统上安装了Bluefruit nRF52 BSP,就需要选择合适的开发板,这将确定编译器并提供一些新菜单选项:
1。选择木板目标
转到工具菜单
选择工具》木板》 Adafruit Bluefruit nRF52 Feather 基于nRF52840的板
选择工具》用于基于 nRF52840的板的板Adafruit Bluefruit nRF52840 Feather Express
最后,您需要设置串行监视器和串行引导程序使用的串行端口:
转到工具 》》 端口,然后选择适当的设备
安装CP2104驱动程序(nRF52832)
对于羽毛nRF52832如果您没有看到列出的SiLabs设备,则可能需要在系统上安装SiLabs CP2104驱动程序。
在MacOS上如果您在安装驱动程序时会看到此对话框消息
在MacOS上,如果在安装驱动程序时看到此对话框消息,则系统扩展被阻止
并且找不到CP2104的串行端口,很有可能帽子驱动程序被阻止。
要启用它,请转到系统偏好设置-》安全和隐私,如果在开发人员名称中看到Silab,则单击允许。
下载并安装Adafruit驱动程序(nRF52840 Windows)
对于Feather nRF52840,如果您使用的是Windows,则需要按照Windows驱动程序安装来下载并安装驱动程序。
3。更新引导程序(仅适用于nRF52832 Feather)
要与Nordic的SoftDevice保持同步,您可能需要更新引导程序
按照此链接获取有关操作方法的说明
此步骤仅在基于nRF52832的设备上是必需的,而在较新的nRF52840 Feather上则不需要Express。
更新Bootloader
4。运行测试草图
这时,您应该能够从 Examples 文件夹运行测试草图,或者只需从Arduino IDE中刷新以下闪烁代码:
下载:文件
复制代码
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
} void setup() {
pinMode(LED_BUILTIN, OUTPUT);
}
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
这将使羽毛上USB端口旁边的红色LED闪烁
如果Arduino无法将草图上传到羽毛上 》
如果出现此错误:
Timed out waiting for acknowledgement from device.
Failed to upgrade target. Error is: No data received on serial port. Not able to proceed.
Traceback (most recent call last):
File “nordicsemi\__main__.py”, line 294, in serial
File “nordicsemi\dfu\dfu.py”, line 235, in dfu_send_images
File “nordicsemi\dfu\dfu.py”, line 203, in _dfu_send_image
File “nordicsemi\dfu\dfu_transport_serial.py”, line 155, in send_init_packet
File “nordicsemi\dfu\dfu_transport_serial.py”, line 243, in send_packet
File “nordicsemi\dfu\dfu_transport_serial.py”, line 282, in get_ack_nr
nordicsemi.exceptions.NordicSemiException: No data received on serial port. Not able to proceed.
这可能是由于 bootloader 版本与您的羽毛和安装的BSP不匹配导致的。由于闪存布局(更多详细信息)和Softdevice API(与引导加载程序捆绑在一起)的差异,使用选定的引导加载程序构建的草图只能上传到具有相同版本的电路板上。简而言之,您需要在羽毛上升级/刻录引导程序以匹配,请按照上述步骤进行更新。Bootloader指南
只需更新一次羽毛即可
在Linux上,即使路径中存在“ arm-none-eabi-g ++”,我也得到“ arm-none-eabi-g ++:没有这样的文件或目录”指定。我该怎么办?
这可能是由于32-位和64位版本的编译器,libc和IDE。编译器使用32位二进制文件,因此您还需要在系统上安装32位版本的libc(详细信息)。尝试从命令行运行以下命令来解决此问题:
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libc6:i386
使用Bootloader
此页面仅供参考。通常,引导程序会从Arduino IDE透明且自动地工作,不需要您手动干预。
Bluefruit nRF52 Feather包含定制版本的Nordic引导程序可提供串行支持,空中(OTA)DFU支持以及各种故障保护功能,例如在启动时将FRST引脚接地时恢复出厂设置。仅使用板上安装的CP2104 USB到串行适配器将用户草图刷新到nRF52832。
引导加载程序源代码可在此处找到Adafruit_nRF52_Bootloader
强制串行引导模式
Bluefruit nRF52 Feather旨在短暂进入串行引导程序模式每次设备复位后都会有短暂的延迟,并且每次Seri上CP2104 USB到串行适配器上的DTR线都会触发复位al监视器已打开。这意味着您通常可以在HW级别上将用户草图刷新到nRF52,而无需人工干预。
如果您需要强制使用串行引导程序模式,则可以连接 DFU 引脚连接到 GND ,这将迫使您进入串行Bootloader模式并保持该模式,直到您重置或重启电路板为止。
此操作可以用来恢复上传了错误的用户草图的砖墙,因为您无需执行用户草图即可进入串行引导加载程序模式,并且可以直接从Arduino IDE刷新新草图。
通常可以使用强制串行引导加载程序来恢复砖化设备。
基于nRF52840的主板还可以使用双重重置技巧来强制进入串行引导程序
出厂重置
Bluefruit nRF52 Feather具有一个
如果对设备进行砌块,则可以将导线焊接到 FRST 焊盘上,然后将其连接到 GND 》。开机时检测到GND状态时,将执行以下操作:
用户应用程序闪存部分将被擦除
存储了用户“应用程序数据”部分非易失性配置数据将被擦除
这将导致设备在启动时进入串行引导加载程序模式,并应删除导致设备停止响应的用户草图或配置数据。
在成功恢复出厂设置后,请确保将引脚从GND断开!
高级:OTA DFU引导加载程序
虽然仅建议高级用户使用,但您也可以强制OTA(空中下载)DFU引导加载程序模式,以使用BLE和Nordic的专有更新协议(这两个Nordic移动应用程序均支持该功能,以及
要强制执行OTA DFU模式,请将FRST和DFU都设置为GND启动。对电路板重新通电会导致设备启动到OTA DFU模式。
Adafruit并未积极支持此选项,Adafruit也不推荐使用此选项,并且我们仍在通过Bluefruit LE Connect应用程序为用户提供尽可能安全的选项。使用OTA DFU需您自担风险,因为您知道自己可以对设备进行砌块,并且可能需要Segger J-Link或类似设备才能重新获得对其的控制!
更新Bootloader
仅从BSP版本0.8开始才可以升级Bootloader。 0或更高。
升级现有的Bootloader
Adafruit Feather nRF52 Bootloader二进制文件不仅包含DFU代码,还包含蓝牙堆栈(akaSoftDevice),以确保它们能够可靠地协同工作。为了从堆栈中获取最新和最出色的功能,例如具有更高吞吐量的Bluetooth 5.0,增加的广播容量或更大的MTU,必须升级Bootloader以获得最新的堆栈。
因此,最新的BSP版本可以支持更新的BSP。当前板上的SoftDevice版本不超过该版本。由于SoftDevice主要版本之间的闪存和API有所不同,因此需要升级电路板的自举程序以匹配BSP支持的自举程序,以上传已编译的草图。幸运的是,Bluefruit nRF52 Bootloader可以在没有任何其他硬件的情况下进行升级/降级,我们甚至可以在Arduino IDE中完成此操作,而不会出现输入错误或常见用户错误的风险。
在单击“刻录启动加载程序”之前,关闭串行监视器。之后,您不应该关闭Arduino IDE,拔下Feather,启动Serial Monitor等。..以中止该过程。很有可能会使您的设备变砖!
首先在工具-》引导程序
然后为工具选择“ 用于Bluefruit nRF52的Bootloader DFU ” -》程序员
仔细检查以下所有内容:开发板,引导加载程序版本,编程器。 。.
选择工具-》刻录Bootloader 开始升级。
在通过串行连接接收到新的Bootloader之后,旧的Bootloader会自行擦除!然后将刷新新的引导程序。该过程通常需要30-60秒才能完成。启动串行监视器或上传新草图之前,请确保在输出日志中看到“ 设备已编程”。
如果您想知道,IDE用于升级引导加载程序的命令如下(引导加载程序zip文件
Download:file
Copy Code
》
$ adafruit-nrfutil --verbose dfu serial --package feather_nrf52832_bootloader-0.2.8_s132_6.1.1.zip
-p /dev/ttyACM0 -b 115200 --singlebank --touch 1200 $ adafruit-nrfutil --verbose dfu serial --package feather_nrf52832_bootloader-0.2.8_s132_6.1.1.zip
-p /dev/ttyACM0 -b 115200 --singlebank --touch 1200
刷新引导程序
所有带有引导程序预加载的Adafruit nRF52板芯片。本页仅供参考!
所有Bluefruit nRF52羽毛板和Bluefruit nRF52模块出厂时均已预刷新了串行引导加载程序,因此此页面通常是
此处提供的信息仅适用于极少数情况,您可能想要或需要自己重新刷新引导加载程序,并能够访问所需的硬件。
1。使用Segger J-Link和Arduino IDE更新Bootloader
要使用Segger J-Link在Arduino IDE中刻录Bootloader,您将需要在系统上安装以下工具并在系统中可用路径:
JLink驱动程序和工具
从Segger下载并安装JLink软件和文档包,该工具包还将安装一组命令行工具。
从Arduino IDE刻录引导加载程序
从Arduino IDE安装完上述工具并将其添加到系统路径后,即可:
选择`工具》板》 Adafruit Bluefruit Feather52 `
选择`工具》程序员》 J-Link for Feather52 `
选择“ 工具》刻录引导加载程序”,并连接板子和J-Link
适当的程序员目标和刻录Bootloader 按钮如下所示:
您将需要Segger J-Link将引导加载程序刷新到nRF52832/nRF52840 SoC!
2。通过nrfjprog手动刻录引导加载程序
您还可以使用Nordic的nrfjprog`从命令行手动刻录引导加载程序。
您可以下载nRF5x-Command-Line-Tools (适用于OSX/Linux/Win32),或使用 tools/nrf5x-command-line-tools 文件夹中BSP附带的版本。
运行以下命令,更新.hex文件的路径:
下载:文件
复制代码
$ nrfjprog --program bootloader_binary.hex --chiperase -f nrf52 --reset $ nrfjprog --program bootloader_binary.hex --chiperase -f nrf52 --reset
您应该看到与以下输出类似的内容,然后状态LED上快速闪烁表示您正在在DFU/bootloader模式下,因为在重置设备后未找到用户草图:
以下所有命令均从’tools/nrf5x-command-line-tools/osx/nrfjprog‘
下载:文件
复制代码
$ 。/nrfjprog -e -f nrf52
Erasing code and UICR flash areas.
Applying system reset.
$ 。/nrfjprog --program 。./。./。./。./bin/bootloader/bootloader_v050_s132_v201.hex -f nrf52
Parsing hex file.
Reading flash area to program to guarantee it is erased.
Checking that the area to write is not protected.
Programing device.
$ 。/nrfjprog --reset -f nrf52
Applying system reset.
Run. $ 。/nrfjprog -e -f nrf52
Erasing code and UICR flash areas.
Applying system reset.
$ 。/nrfjprog --program 。./。./。./。./bin/bootloader/bootloader_v050_s132_v201.hex -f nrf52
Parsing hex file.
Reading flash area to program to guarantee it is erased.
Checking that the area to write is not protected.
Programing device.
$ 。/nrfjprog --reset -f nrf52
Applying system reset.
Run.
OS X注意:您可能需要在`/usr/local/bin`中创建到
nrfjprog的符号链接`工具,无论您在何处添加了它。您可以运行以下命令,例如:
$ ln -s $HOME/prog/nordic/nrfjprog/nrfjprog /usr/local/bin/nrfjprog
3。通过AdaLink手动刻录引导加载程序
或者,您可以使用AdaLink通过Segger J-Link刷新引导加载程序:
下载:文件
复制代码
# First erase the device’s flash contents
$ adalink nrf52832 -p jlink -w
# Then flash the bootloader and SoftDevice .hex file
$ adalink nrf52832 -p jlink -h feather52_bootloader_v050_s132_v201.hex # First erase the device‘s flash contents
$ adalink nrf52832 -p jlink -w
# Then flash the bootloader and SoftDevice .hex file
$ adalink nrf52832 -p jlink -h feather52_bootloader_v050_s132_v201.hex
Arduino示例
nRF52 BSP的示例菜单中有许多适用于Bluefruit nRF52/nRF52840 Feather的示例。最新。您首先停止寻找示例代码应该存在的地方:
示例源代码
最新的示例源代码始终可用并在Github上可见,并且公共git存储库应被视为该板的示例代码的权威源。
单击此处浏览示例Github上的源代码
文档示例
为帮助解释nRF52 BLE API的一些常见用例,请随时参考学习指南本部分中的示例文档:
广告:信标-显示如何使用BLEBeacon帮助程序类将Bleufruit nRF52 Feather配置为信标
BLE UART:控制器-显示如何使用 Controller 实用程序在我们的Bluefruit LE Co nnect应用程序,以在您的外围设备与手机或平板电脑之间发送基本数据。
自定义:HRM -显示如何使用正式版定义和使用自定义GATT服务和特征
BLE针脚I/O (StandardFirmataBLE)展示了如何使用Firmata协议控制nRF52的针脚I/O
》
广告:信标
此示例显示了如何使用BLEBeacon帮助程序类和广告API将Bluefruit nRF52开发板配置为“信标”。
完整代码
下载:Project Zip 或 beacon.ino | 在Github上查看
复制代码
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include
// Beacon uses the Manufacturer Specific Data field in the advertising
// packet, which means you must provide a valid Manufacturer ID. Update
// the field below to an appropriate value. For a list of valid IDs see:
// https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
// 0x004C is Apple (for example)
#define MANUFACTURER_ID 0x004C
// AirLocate UUID: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0
uint8_t beaconUuid[16] =
{
0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2,
0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0,
};
// A valid Beacon packet consists of the following information:
// UUID, Major, Minor, RSSI @ 1M
BLEBeacon beacon(beaconUuid, 0x0000, 0x0000, -54);
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(“Bluefruit52 Beacon Example”);
Serial.println(“-------------------------- ”);
Bluefruit.begin();
// off Blue LED for lowest power consumption
Bluefruit.autoConnLed(false);
Bluefruit.setTxPower(0); // Check bluefruit.h for supported values
Bluefruit.setName(“Bluefruit52”);
// Manufacturer ID is required for Manufacturer Specific Data
beacon.setManufacturer(MANUFACTURER_ID);
// Setup the advertising packet
startAdv();
Serial.println(“Broadcasting beacon, open your beacon app to test”);
// Suspend Loop() to save power, since we didn’t have any code there
suspendLoop();
}
void startAdv(void)
{
// Advertising packet
// Set the beacon payload using the BLEBeacon class populated
// earlier in this example
Bluefruit.Advertising.setBeacon(beacon);
// Secondary Scan Response packet (optional)
// Since there is no room for ‘Name’ in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* Apple Beacon specs
* - Type: Non connectable, undirected
* - Fixed interval: 100 ms -》 fast = slow = 100 ms
*/
//Bluefruit.Advertising.setType(BLE_GAP_ADV_TYPE_ADV_NONCONN_IND);
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(160, 160); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don‘t stop advertising after n seconds
}
void loop()
{
// loop is already suspended, CPU will not run loop() at all
}
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include
// Beacon uses the Manufacturer Specific Data field in the advertising
// packet, which means you must provide a valid Manufacturer ID. Update
// the field below to an appropriate value. For a list of valid IDs see:
// https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
// 0x004C is Apple (for example)
#define MANUFACTURER_ID 0x004C
// AirLocate UUID: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0
uint8_t beaconUuid[16] =
{
0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2,
0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0,
};
// A valid Beacon packet consists of the following information:
// UUID, Major, Minor, RSSI @ 1M
BLEBeacon beacon(beaconUuid, 0x0000, 0x0000, -54);
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(“Bluefruit52 Beacon Example”);
Serial.println(“-------------------------- ”);
Bluefruit.begin();
// off Blue LED for lowest power consumption
Bluefruit.autoConnLed(false);
Bluefruit.setTxPower(0); // Check bluefruit.h for supported values
Bluefruit.setName(“Bluefruit52”);
// Manufacturer ID is required for Manufacturer Specific Data
beacon.setManufacturer(MANUFACTURER_ID);
// Setup the advertising packet
startAdv();
Serial.println(“Broadcasting beacon, open your beacon app to test”);
// Suspend Loop() to save power, since we didn’t have any code there
suspendLoop();
}
void startAdv(void)
{
// Advertising packet
// Set the beacon payload using the BLEBeacon class populated
// earlier in this example
Bluefruit.Advertising.setBeacon(beacon);
// Secondary Scan Response packet (optional)
// Since there is no room for ‘Name’ in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* Apple Beacon specs
* - Type: Non connectable, undirected
* - Fixed interval: 100 ms -》 fast = slow = 100 ms
*/
//Bluefruit.Advertising.setType(BLE_GAP_ADV_TYPE_ADV_NONCONN_IND);
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(160, 160); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don‘t stop advertising after n seconds
}
void loop()
{
// loop is already suspended, CPU will not run loop() at all
}
输出
您可以使用Nordic Semiconductors的nRF Beacons应用程序来测试此草图:
iOS的nRF信标
Android的nRF信标
请确保您设置了UUID,Major和Minor值以匹配上面的草图,并且然后与nRF Beacons应用程序同时运行草图。
使用默认设置,在检测到信标时,您应该会看到Mona Lisa图标。如果看不到,请仔细检查UUID,主要和次要值,以确保它们完全匹配。
BLE UART:控制器
此示例显示了您可以使用BLEUart帮助器类和Bluefruit LE Connect应用程序将基于键盘和传感器数据发送到nRF52。 p》
设置
按顺序要使用此草图,您需要使用我们的免费iOS,Android或OS X应用程序在移动设备上打开Bluefruit LE Connect。
加载 Controller 示例草图在Arduino IDE中
编译草图并将其刷新到基于nRF52的Feather
上传完成后,打开串行监视器(工具》串行监视器)
在移动设备上打开 Bluefruit LE Connect 应用程序
连接到适当的目标(可能是“ Bluefruit52 ” )
一旦将开关连接到应用程序内的 Controller 应用程序
启用适当的控制界面。例如,拾色器控制面如下所示(从iOS应用程序中截取的屏幕截图):
更改颜色(或提供其他数据)时,您应该收到nRF52上的数据,并在串行监视器输出中看到它:
完整代码
下载:Project Zip 或 controller.ino | 在Github上查看
复制代码
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include
// OTA DFU service
BLEDfu bledfu;
// Uart over BLE service
BLEUart bleuart;
// Function prototypes for packetparser.cpp
uint8_t readPacket (BLEUart *ble_uart, uint16_t timeout);
float parsefloat (uint8_t *buffer);
void printHex (const uint8_t * data, const uint32_t numBytes);
// Packet buffer
extern uint8_t packetbuffer[];
void setup(void)
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(F(“Adafruit Bluefruit52 Controller App Example”));
Serial.println(F(“-------------------------------------------”));
Bluefruit.begin();
Bluefruit.setTxPower(4); // Check bluefruit.h for supported values
Bluefruit.setName(“Bluefruit52”);
// To be consistent OTA DFU should be added first if it exists
bledfu.begin();
// Configure and start the BLE Uart service
bleuart.begin();
// Set up and start advertising
startAdv();
Serial.println(F(“Please use Adafruit Bluefruit LE app to connect in Controller mode”));
Serial.println(F(“Then activate/use the sensors, color picker, game controller, etc!”));
Serial.println();
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include the BLE UART (AKA ’NUS‘) 128-bit UUID
Bluefruit.Advertising.addService(bleuart);
// Secondary Scan Response packet (optional)
// Since there is no room for ’Name‘ in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don’t stop advertising after n seconds
}
/**************************************************************************/
/*!
@brief Constantly poll for new command or response data
*/
/**************************************************************************/
void loop(void)
{
// Wait for new data to arrive
uint8_t len = readPacket(&bleuart, 500);
if (len == 0) return;
// Got a packet!
// printHex(packetbuffer, len);
// Color
if (packetbuffer[1] == ‘C’) {
uint8_t red = packetbuffer[2];
uint8_t green = packetbuffer[3];
uint8_t blue = packetbuffer[4];
Serial.print (“RGB #”);
if (red 《 0x10) Serial.print(“0”);
Serial.print(red, HEX);
if (green 《 0x10) Serial.print(“0”);
Serial.print(green, HEX);
if (blue 《 0x10) Serial.print(“0”);
Serial.println(blue, HEX);
}
// Buttons
if (packetbuffer[1] == ‘B’) {
uint8_t buttnum = packetbuffer[2] - ‘0’;
boolean pressed = packetbuffer[3] - ‘0’;
Serial.print (“Button ”); Serial.print(buttnum);
if (pressed) {
Serial.println(“ pressed”);
} else {
Serial.println(“ released”);
}
}
// GPS Location
if (packetbuffer[1] == ‘L’) {
float lat, lon, alt;
lat = parsefloat(packetbuffer+2);
lon = parsefloat(packetbuffer+6);
alt = parsefloat(packetbuffer+10);
Serial.print(“GPS Location ”);
Serial.print(“Lat: ”); Serial.print(lat, 4); // 4 digits of precision!
Serial.print(‘ ’);
Serial.print(“Lon: ”); Serial.print(lon, 4); // 4 digits of precision!
Serial.print(‘ ’);
Serial.print(alt, 4); Serial.println(“ meters”);
}
// Accelerometer
if (packetbuffer[1] == ‘A’) {
float x, y, z;
x = parsefloat(packetbuffer+2);
y = parsefloat(packetbuffer+6);
z = parsefloat(packetbuffer+10);
Serial.print(“Accel ”);
Serial.print(x); Serial.print(‘ ’);
Serial.print(y); Serial.print(‘ ’);
Serial.print(z); Serial.println();
}
// Magnetometer
if (packetbuffer[1] == ‘M’) {
float x, y, z;
x = parsefloat(packetbuffer+2);
y = parsefloat(packetbuffer+6);
z = parsefloat(packetbuffer+10);
Serial.print(“Mag ”);
Serial.print(x); Serial.print(‘ ’);
Serial.print(y); Serial.print(‘ ’);
Serial.print(z); Serial.println();
}
// Gyroscope
if (packetbuffer[1] == ‘G’) {
float x, y, z;
x = parsefloat(packetbuffer+2);
y = parsefloat(packetbuffer+6);
z = parsefloat(packetbuffer+10);
Serial.print(“Gyro ”);
Serial.print(x); Serial.print(‘ ’);
Serial.print(y); Serial.print(‘ ’);
Serial.print(z); Serial.println();
}
// Quaternions
if (packetbuffer[1] == ‘Q’) {
float x, y, z, w;
x = parsefloat(packetbuffer+2);
y = parsefloat(packetbuffer+6);
z = parsefloat(packetbuffer+10);
w = parsefloat(packetbuffer+14);
Serial.print(“Quat ”);
Serial.print(x); Serial.print(‘ ’);
Serial.print(y); Serial.print(‘ ’);
Serial.print(z); Serial.print(‘ ’);
Serial.print(w); Serial.println();
}
}
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include
// OTA DFU service
BLEDfu bledfu;
// Uart over BLE service
BLEUart bleuart;
// Function prototypes for packetparser.cpp
uint8_t readPacket (BLEUart *ble_uart, uint16_t timeout);
float parsefloat (uint8_t *buffer);
void printHex (const uint8_t * data, const uint32_t numBytes);
// Packet buffer
extern uint8_t packetbuffer[];
void setup(void)
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(F(“Adafruit Bluefruit52 Controller App Example”));
Serial.println(F(“-------------------------------------------”));
Bluefruit.begin();
Bluefruit.setTxPower(4); // Check bluefruit.h for supported values
Bluefruit.setName(“Bluefruit52”);
// To be consistent OTA DFU should be added first if it exists
bledfu.begin();
// Configure and start the BLE Uart service
bleuart.begin();
// Set up and start advertising
startAdv();
Serial.println(F(“Please use Adafruit Bluefruit LE app to connect in Controller mode”));
Serial.println(F(“Then activate/use the sensors, color picker, game controller, etc!”));
Serial.println();
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include the BLE UART (AKA ‘NUS’) 128-bit UUID
Bluefruit.Advertising.addService(bleuart);
// Secondary Scan Response packet (optional)
// Since there is no room for ‘Name’ in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don‘t stop advertising after n seconds
}
/**************************************************************************/
/*!
@brief Constantly poll for new command or response data
*/
/**************************************************************************/
void loop(void)
{
// Wait for new data to arrive
uint8_t len = readPacket(&bleuart, 500);
if (len == 0) return;
// Got a packet!
// printHex(packetbuffer, len);
// Color
if (packetbuffer[1] == ’C‘) {
uint8_t red = packetbuffer[2];
uint8_t green = packetbuffer[3];
uint8_t blue = packetbuffer[4];
Serial.print (“RGB #”);
if (red 《 0x10) Serial.print(“0”);
Serial.print(red, HEX);
if (green 《 0x10) Serial.print(“0”);
Serial.print(green, HEX);
if (blue 《 0x10) Serial.print(“0”);
Serial.println(blue, HEX);
}
// Buttons
if (packetbuffer[1] == ’B‘) {
uint8_t buttnum = packetbuffer[2] - ’0‘;
boolean pressed = packetbuffer[3] - ’0‘;
Serial.print (“Button ”); Serial.print(buttnum);
if (pressed) {
Serial.println(“ pressed”);
} else {
Serial.println(“ released”);
}
}
// GPS Location
if (packetbuffer[1] == ’L‘) {
float lat, lon, alt;
lat = parsefloat(packetbuffer+2);
lon = parsefloat(packetbuffer+6);
alt = parsefloat(packetbuffer+10);
Serial.print(“GPS Location ”);
Serial.print(“Lat: ”); Serial.print(lat, 4); // 4 digits of precision!
Serial.print(’ ‘);
Serial.print(“Lon: ”); Serial.print(lon, 4); // 4 digits of precision!
Serial.print(’ ‘);
Serial.print(alt, 4); Serial.println(“ meters”);
}
// Accelerometer
if (packetbuffer[1] == ’A‘) {
float x, y, z;
x = parsefloat(packetbuffer+2);
y = parsefloat(packetbuffer+6);
z = parsefloat(packetbuffer+10);
Serial.print(“Accel ”);
Serial.print(x); Serial.print(’ ‘);
Serial.print(y); Serial.print(’ ‘);
Serial.print(z); Serial.println();
}
// Magnetometer
if (packetbuffer[1] == ’M‘) {
float x, y, z;
x = parsefloat(packetbuffer+2);
y = parsefloat(packetbuffer+6);
z = parsefloat(packetbuffer+10);
Serial.print(“Mag ”);
Serial.print(x); Serial.print(’ ‘);
Serial.print(y); Serial.print(’ ‘);
Serial.print(z); Serial.println();
}
// Gyroscope
if (packetbuffer[1] == ’G‘) {
float x, y, z;
x = parsefloat(packetbuffer+2);
y = parsefloat(packetbuffer+6);
z = parsefloat(packetbuffer+10);
Serial.print(“Gyro ”);
Serial.print(x); Serial.print(’ ‘);
Serial.print(y); Serial.print(’ ‘);
Serial.print(z); Serial.println();
}
// Quaternions
if (packetbuffer[1] == ’Q‘) {
float x, y, z, w;
x = parsefloat(packetbuffer+2);
y = parsefloat(packetbuffer+6);
z = parsefloat(packetbuffer+10);
w = parsefloat(packetbuffer+14);
Serial.print(“Quat ”);
Serial.print(x); Serial.print(’ ‘);
Serial.print(y); Serial.print(’ ‘);
Serial.print(z); Serial.print(’ ‘);
Serial.print(w); Serial.println();
}
}
您还将在名为 packetParser.cpp 的文件中需要以下帮助程序类:
下载:Project Zip 或 packetParser.cpp | 查看上 Github
复制代码
#include
#include
#include
#define PACKET_ACC_LEN (15)
#define PACKET_GYRO_LEN (15)
#define PACKET_MAG_LEN (15)
#define PACKET_QUAT_LEN (19)
#define PACKET_BUTTON_LEN (5)
#define PACKET_COLOR_LEN (6)
#define PACKET_LOCATION_LEN (15)
// READ_BUFSIZE Size of the read buffer for incoming packets
#define READ_BUFSIZE (20)
/* Buffer to hold incoming characters */
uint8_t packetbuffer[READ_BUFSIZE+1];
/**************************************************************************/
/*!
@brief Casts the four bytes at the specified address to a float
*/
/**************************************************************************/
float parsefloat(uint8_t *buffer)
{
float f;
memcpy(&f, buffer, 4);
return f;
}
/**************************************************************************/
/*!
@brief Prints a hexadecimal value in plain characters
@param data Pointer to the byte data
@param numBytes Data length in bytes
*/
/**************************************************************************/
void printHex(const uint8_t * data, const uint32_t numBytes)
{
uint32_t szPos;
for (szPos=0; szPos 《 numBytes; szPos++)
{
Serial.print(F(“0x”));
// Append leading 0 for small values
if (data[szPos] 《= 0xF)
{
Serial.print(F(“0”));
Serial.print(data[szPos] & 0xf, HEX);
}
else
{
Serial.print(data[szPos] & 0xff, HEX);
}
// Add a trailing space if appropriate
if ((numBytes 》 1) && (szPos != numBytes - 1))
{
Serial.print(F(“ ”));
}
}
Serial.println();
}
/**************************************************************************/
/*!
@brief Waits for incoming data and parses it
*/
/**************************************************************************/
uint8_t readPacket(BLEUart *ble_uart, uint16_t timeout)
{
uint16_t origtimeout = timeout, replyidx = 0;
memset(packetbuffer, 0, READ_BUFSIZE);
while (timeout--) {
if (replyidx 》= 20) break;
if ((packetbuffer[1] == ’A‘) && (replyidx == PACKET_ACC_LEN))
break;
if ((packetbuffer[1] == ’G‘) && (replyidx == PACKET_GYRO_LEN))
break;
if ((packetbuffer[1] == ’M‘) && (replyidx == PACKET_MAG_LEN))
break;
if ((packetbuffer[1] == ’Q‘) && (replyidx == PACKET_QUAT_LEN))
break;
if ((packetbuffer[1] == ’B‘) && (replyidx == PACKET_BUTTON_LEN))
break;
if ((packetbuffer[1] == ’C‘) && (replyidx == PACKET_COLOR_LEN))
break;
if ((packetbuffer[1] == ’L‘) && (replyidx == PACKET_LOCATION_LEN))
break;
while (ble_uart-》available()) {
char c = ble_uart-》read();
if (c == ’!‘) {
replyidx = 0;
}
packetbuffer[replyidx] = c;
replyidx++;
timeout = origtimeout;
}
if (timeout == 0) break;
delay(1);
}
packetbuffer[replyidx] = 0; // null term
if (!replyidx) // no data or timeout
return 0;
if (packetbuffer[0] != ’!‘) // doesn’t start with ‘!’ packet beginning
return 0;
// check checksum!
uint8_t xsum = 0;
uint8_t checksum = packetbuffer[replyidx-1];
for (uint8_t i=0; i xsum += packetbuffer[i];
}
xsum = ~xsum;
// Throw an error message if the checksum‘s don’t match
if (xsum != checksum)
{
Serial.print(“Checksum mismatch in packet : ”);
printHex(packetbuffer, replyidx+1);
return 0;
}
// checksum passed!
return replyidx;
}
#include
#include
#include
#define PACKET_ACC_LEN (15)
#define PACKET_GYRO_LEN (15)
#define PACKET_MAG_LEN (15)
#define PACKET_QUAT_LEN (19)
#define PACKET_BUTTON_LEN (5)
#define PACKET_COLOR_LEN (6)
#define PACKET_LOCATION_LEN (15)
// READ_BUFSIZE Size of the read buffer for incoming packets
#define READ_BUFSIZE (20)
/* Buffer to hold incoming characters */
uint8_t packetbuffer[READ_BUFSIZE+1];
/**************************************************************************/
/*!
@brief Casts the four bytes at the specified address to a float
*/
/**************************************************************************/
float parsefloat(uint8_t *buffer)
{
float f;
memcpy(&f, buffer, 4);
return f;
}
/**************************************************************************/
/*!
@brief Prints a hexadecimal value in plain characters
@param data Pointer to the byte data
@param numBytes Data length in bytes
*/
/**************************************************************************/
void printHex(const uint8_t * data, const uint32_t numBytes)
{
uint32_t szPos;
for (szPos=0; szPos 《 numBytes; szPos++)
{
Serial.print(F(“0x”));
// Append leading 0 for small values
if (data[szPos] 《= 0xF)
{
Serial.print(F(“0”));
Serial.print(data[szPos] & 0xf, HEX);
}
else
{
Serial.print(data[szPos] & 0xff, HEX);
}
// Add a trailing space if appropriate
if ((numBytes 》 1) && (szPos != numBytes - 1))
{
Serial.print(F(“ ”));
}
}
Serial.println();
}
/**************************************************************************/
/*!
@brief Waits for incoming data and parses it
*/
/**************************************************************************/
uint8_t readPacket(BLEUart *ble_uart, uint16_t timeout)
{
uint16_t origtimeout = timeout, replyidx = 0;
memset(packetbuffer, 0, READ_BUFSIZE);
while (timeout--) {
if (replyidx 》= 20) break;
if ((packetbuffer[1] == ‘A’) && (replyidx == PACKET_ACC_LEN))
break;
if ((packetbuffer[1] == ‘G’) && (replyidx == PACKET_GYRO_LEN))
break;
if ((packetbuffer[1] == ‘M’) && (replyidx == PACKET_MAG_LEN))
break;
if ((packetbuffer[1] == ‘Q’) && (replyidx == PACKET_QUAT_LEN))
break;
if ((packetbuffer[1] == ‘B’) && (replyidx == PACKET_BUTTON_LEN))
break;
if ((packetbuffer[1] == ‘C’) && (replyidx == PACKET_COLOR_LEN))
break;
if ((packetbuffer[1] == ‘L’) && (replyidx == PACKET_LOCATION_LEN))
break;
while (ble_uart-》available()) {
char c = ble_uart-》read();
if (c == ‘!’) {
replyidx = 0;
}
packetbuffer[replyidx] = c;
replyidx++;
timeout = origtimeout;
}
if (timeout == 0) break;
delay(1);
}
packetbuffer[replyidx] = 0; // null term
if (!replyidx) // no data or timeout
return 0;
if (packetbuffer[0] != ‘!’) // doesn‘t start with ’!‘ packet beginning
return 0;
// check checksum!
uint8_t xsum = 0;
uint8_t checksum = packetbuffer[replyidx-1];
for (uint8_t i=0; i xsum += packetbuffer[i];
}
xsum = ~xsum;
// Throw an error message if the checksum’s don‘t match
if (xsum != checksum)
{
Serial.print(“Checksum mismatch in packet : ”);
printHex(packetbuffer, replyidx+1);
return 0;
}
// checksum passed!
return replyidx;
}
自定义:HRM
BLEService和BLECharacteristic类可用于使用一组基本属性和回调处理程序来实现任何自定义或正式采用的BLE服务特性。 p》
下面的示例显示了如何使用这些类来实现Bluetooth SIG定义的心率监视器服务。
HRM服务定义
UUID :0x180D
特征名称
心率测量
身体传感器位置
心率控制点UUID
0x2A37
0x2A38
0x2A39要求
强制性
可选
有条件的属性
通知
阅读
写入
只有第一个特征是强制性的,但我们也会设置可选的身体传感器位置特征。在此示例中,将不使用心率控制点来简化操作。
实现HRM服务和特征
可以使用以下代码实现核心服务和前两个特征:
首先,定义BLEService和将在您的项目中使用的BLE特性变量:
下载:文件
复制代码
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37
* Body Sensor Location Char: 0x2A38
*/
BLEService hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION); /* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37
* Body Sensor Location Char: 0x2A38
*/
BLEService hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);
然后,您需要用适当的值“填充”这些变量。为简单起见,您可以为放置所有代码的服务定义一个自定义函数,然后只需在“ setup”函数中一次调用此函数:
下载:文件
复制代码
void setupHRM(void)
{
// Configure the Heart Rate Monitor service
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
// Supported Characteristics:
// Name UUID Requirement Properties
// ---------------------------- ------ ----------- ----------
// Heart Rate Measurement 0x2A37 Mandatory Notify
// Body Sensor Location 0x2A38 Optional Read
// Heart Rate Control Point 0x2A39 Conditional Write 《-- Not used here
hrms.begin();
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.。 Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was ’begin()‘ed!
// Configure the Heart Rate Measurement characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Permission = Notify
// Min Len = 1
// Max Len = 8
// B0 = UINT8 - Flag (MANDATORY)
// b5:7 = Reserved
// b4 = RR-Internal (0 = Not present, 1 = Present)
// b3 = Energy expended status (0 = Not present, 1 = Present)
// b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
// b0 = Value format (0 = UINT8, 1 = UINT16)
// B1 = UINT8 - 8-bit heart rate measurement value in BPM
// B2:3 = UINT16 - 16-bit heart rate measurement value in BPM
// B4:5 = UINT16 - Energy expended in joules
// B6:7 = UINT16 - RR Internal (1/1024 second resolution)
hrmc.setProperties(CHR_PROPS_NOTIFY);
hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
hrmc.setFixedLen(2);
hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
hrmc.begin();
uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
hrmc.notify(hrmdata, 2); // Use .notify instead of .write!
// Configure the Body Sensor Location characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Permission = Read
// Min Len = 1
// Max Len = 1
// B0 = UINT8 - Body Sensor Location
// 0 = Other
// 1 = Chest
// 2 = Wrist
// 3 = Finger
// 4 = Hand
// 5 = Ear Lobe
// 6 = Foot
// 7:255 = Reserved
bslc.setProperties(CHR_PROPS_READ);
bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
bslc.setFixedLen(1);
bslc.begin();
bslc.write8(2); // Set the characteristic to ’Wrist‘ (2)
} void setupHRM(void)
{
// Configure the Heart Rate Monitor service
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
// Supported Characteristics:
// Name UUID Requirement Properties
// ---------------------------- ------ ----------- ----------
// Heart Rate Measurement 0x2A37 Mandatory Notify
// Body Sensor Location 0x2A38 Optional Read
// Heart Rate Control Point 0x2A39 Conditional Write 《-- Not used here
hrms.begin();
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.。 Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was ’begin()‘ed!
// Configure the Heart Rate Measurement characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Permission = Notify
// Min Len = 1
// Max Len = 8
// B0 = UINT8 - Flag (MANDATORY)
// b5:7 = Reserved
// b4 = RR-Internal (0 = Not present, 1 = Present)
// b3 = Energy expended status (0 = Not present, 1 = Present)
// b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
// b0 = Value format (0 = UINT8, 1 = UINT16)
// B1 = UINT8 - 8-bit heart rate measurement value in BPM
// B2:3 = UINT16 - 16-bit heart rate measurement value in BPM
// B4:5 = UINT16 - Energy expended in joules
// B6:7 = UINT16 - RR Internal (1/1024 second resolution)
hrmc.setProperties(CHR_PROPS_NOTIFY);
hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
hrmc.setFixedLen(2);
hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
hrmc.begin();
uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
hrmc.notify(hrmdata, 2); // Use .notify instead of .write!
// Configure the Body Sensor Location characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Permission = Read
// Min Len = 1
// Max Len = 1
// B0 = UINT8 - Body Sensor Location
// 0 = Other
// 1 = Chest
// 2 = Wrist
// 3 = Finger
// 4 = Hand
// 5 = Ear Lobe
// 6 = Foot
// 7:255 = Reserved
bslc.setProperties(CHR_PROPS_READ);
bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
bslc.setFixedLen(1);
bslc.begin();
bslc.write8(2); // Set the characteristic to ’Wrist‘ (2)
}
服务+特征设置代码分析
1。首先要做的是在BLEService(上面的 hrms )上调用 .begin()。由于UUID是在草图顶部的对象声明中设置的,因此与BLEService实例通常没有其他关系。
您必须在添加任何BLECharacteristics之前在BLEService上调用.begin()。任何BLECharacteristic都将自动添加到最后一个’begin()‘的BLEService中!
2。接下来,您可以配置心率测量特征(上面的 hrmc )。您为此设置的值将取决于特性定义,但是为方便起见,我们在上面的代码的注释中记录了关键信息。
’hrmc.setProperties(CHR_PROPS_NOTIFY);‘ -设置特性的属性值,该值确定如何访问特性。在这种情况下,Bluetooth SIG将特征定义为通知,这意味着当中央要使用此特征接收数据时,外围设备将接收来自中央的请求(“通知”)。 li》
`hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);`-设置特征的安全性,通常应设置为本示例中使用的值。
`hrmc.setFixedLen(2);`-此告诉蓝牙堆栈特征包含多少字节(通常为1到20之间的值)。在这种情况下,我们将使用两个字节的固定大小,因此我们调用 .setFixedLen 。如果特征的长度可变,则需要通过 .setMaxLen 设置最大大小。
’hrmc.setCccdWriteCallback(cccd_callback);‘-此可选代码设置了将中央更新CCCD记录时将被触发。这是相关的,因为特征是使用NOTIFY属性设置的。当Central设置为“ Notify”位时,它将写入CCCD记录,并且即使在CCCD回调中,您也可以捕获此写操作并打开传感器,例如,通过仅打开传感器来节省功率(然后退回)实际使用或不使用的时间。有关CCCD回调处理程序的实现,请参见本页底部的完整示例代码。
’hrmc.begin();‘设置完所有属性后,必须调用 .begin(),它将特性定义添加到最后一个“ .begin()ed”的BLEService。
3。 (可选)设置特征的初始值,例如以下代码,其中的“ hrmc”填充了正确的值,指示我们提供的是8位心率监测器值,存在人体传感器位置特征,以及将第一个心率值设置为0x04:
请注意,我们使用.notify( ),而不是.write(),因为此特性是使用NOTIFY属性设置的,因此需要以与其他特性稍有不同的方式进行处理。
下载:文件
复制代码
// Set the characteristic to use 8-bit values, with the sensor connected and detected
uint8_t hrmdata[2] = { 0b00000110, 0x40 };
// Use .notify instead of .write!
hrmc.notify(hrmdata, 2); // Set the characteristic to use 8-bit values, with the sensor connected and detected
uint8_t hrmdata[2] = { 0b00000110, 0x40 };
// Use .notify instead of .write!
hrmc.notify(hrmdata, 2);
CCCD回调处理程序具有以下签名:
下载:文件
复制代码
void cccd_callback(uint16_t conn_hdl, BLECharacteristic* chr, uint16_t cccd_value)
{
// Display the raw request packet
Serial.print(“CCCD Updated: ”);
//Serial.printBuffer(request-》data, request-》len);
Serial.print(cccd_value);
Serial.println(“”);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
if (chr-》uuid == htmc.uuid) {
if (chr-》indicateEnabled(conn_hdl)) {
Serial.println(“Temperature Measurement ’Indicate‘ enabled”);
} else {
Serial.println(“Temperature Measurement ’Indicate‘ disabled”);
}
}
} void cccd_callback(uint16_t conn_hdl, BLECharacteristic* chr, uint16_t cccd_value)
{
// Display the raw request packet
Serial.print(“CCCD Updated: ”);
//Serial.printBuffer(request-》data, request-》len);
Serial.print(cccd_value);
Serial.println(“”);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
if (chr-》uuid == htmc.uuid) {
if (chr-》indicateEnabled(conn_hdl)) {
Serial.println(“Temperature Measurement ’Indicate‘ enabled”);
} else {
Serial.println(“Temperature Measurement ’Indicate‘ disabled”);
}
}
}
4。对服务中的任何其他BLE特性重复相同的过程。
完整示例代码
该示例的完整示例代码如下:
下载:Project Zip 或 custom_hrm.ino | 在Github上查看
复制代码
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37
* Body Sensor Location Char: 0x2A38
*/
BLEService hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);
BLEDis bledis; // DIS (Device Information Service) helper class instance
BLEBas blebas; // BAS (Battery Service) helper class instance
uint8_t bps = 0;
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(“Bluefruit52 HRM Example”);
Serial.println(“----------------------- ”);
// Initialise the Bluefruit module
Serial.println(“Initialise the Bluefruit nRF52 module”);
Bluefruit.begin();
// Set the advertised device name (keep it short!)
Serial.println(“Setting Device Name to ’Feather52 HRM‘”);
Bluefruit.setName(“Bluefruit52 HRM”);
// Set the connect/disconnect callback handlers
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
// Configure and Start the Device Information Service
Serial.println(“Configuring the Device Information Service”);
bledis.setManufacturer(“Adafruit Industries”);
bledis.setModel(“Bluefruit Feather52”);
bledis.begin();
// Start the BLE Battery Service and set it to 100%
Serial.println(“Configuring the Battery Service”);
blebas.begin();
blebas.write(100);
// Setup the Heart Rate Monitor service using
// BLEService and BLECharacteristic classes
Serial.println(“Configuring the Heart Rate Monitor Service”);
setupHRM();
// Setup the advertising packet(s)
Serial.println(“Setting up the advertising payload(s)”);
startAdv();
Serial.println(“Ready Player One!!!”);
Serial.println(“ Advertising”);
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include HRM Service UUID
Bluefruit.Advertising.addService(hrms);
// Include Name
Bluefruit.Advertising.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don’t stop advertising after n seconds
}
void setupHRM(void)
{
// Configure the Heart Rate Monitor service
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
// Supported Characteristics:
// Name UUID Requirement Properties
// ---------------------------- ------ ----------- ----------
// Heart Rate Measurement 0x2A37 Mandatory Notify
// Body Sensor Location 0x2A38 Optional Read
// Heart Rate Control Point 0x2A39 Conditional Write 《-- Not used here
hrms.begin();
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.。 Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was ‘begin()’ed!
// Configure the Heart Rate Measurement characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Properties = Notify
// Min Len = 1
// Max Len = 8
// B0 = UINT8 - Flag (MANDATORY)
// b5:7 = Reserved
// b4 = RR-Internal (0 = Not present, 1 = Present)
// b3 = Energy expended status (0 = Not present, 1 = Present)
// b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
// b0 = Value format (0 = UINT8, 1 = UINT16)
// B1 = UINT8 - 8-bit heart rate measurement value in BPM
// B2:3 = UINT16 - 16-bit heart rate measurement value in BPM
// B4:5 = UINT16 - Energy expended in joules
// B6:7 = UINT16 - RR Internal (1/1024 second resolution)
hrmc.setProperties(CHR_PROPS_NOTIFY);
hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
hrmc.setFixedLen(2);
hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
hrmc.begin();
uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
hrmc.write(hrmdata, 2);
// Configure the Body Sensor Location characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Properties = Read
// Min Len = 1
// Max Len = 1
// B0 = UINT8 - Body Sensor Location
// 0 = Other
// 1 = Chest
// 2 = Wrist
// 3 = Finger
// 4 = Hand
// 5 = Ear Lobe
// 6 = Foot
// 7:255 = Reserved
bslc.setProperties(CHR_PROPS_READ);
bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
bslc.setFixedLen(1);
bslc.begin();
bslc.write8(2); // Set the characteristic to ‘Wrist’ (2)
}
void connect_callback(uint16_t conn_handle)
{
// Get the reference to current connection
BLEConnection* connection = Bluefruit.Connection(conn_handle);
char central_name[32] = { 0 };
connection-》getPeerName(central_name, sizeof(central_name));
Serial.print(“Connected to ”);
Serial.println(central_name);
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle connection where this event happens
* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.print(“Disconnected, reason = 0x”); Serial.println(reason, HEX);
Serial.println(“Advertising!”);
}
void cccd_callback(uint16_t conn_hdl, BLECharacteristic* chr, uint16_t cccd_value)
{
// Display the raw request packet
Serial.print(“CCCD Updated: ”);
//Serial.printBuffer(request-》data, request-》len);
Serial.print(cccd_value);
Serial.println(“”);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
if (chr-》uuid == hrmc.uuid) {
if (chr-》notifyEnabled(conn_hdl)) {
Serial.println(“Heart Rate Measurement ‘Notify’ enabled”);
} else {
Serial.println(“Heart Rate Measurement ‘Notify’ disabled”);
}
}
}
void loop()
{
digitalToggle(LED_RED);
if ( Bluefruit.connected() ) {
uint8_t hrmdata[2] = { 0b00000110, bps++ }; // Sensor connected, increment BPS value
// Note: We use .notify instead of .write!
// If it is connected but CCCD is not enabled
// The characteristic‘s value is still updated although notification is not sent
if ( hrmc.notify(hrmdata, sizeof(hrmdata)) ){
Serial.print(“Heart Rate Measurement updated to: ”); Serial.println(bps);
}else{
Serial.println(“ERROR: Notify not set in the CCCD or not connected!”);
}
}
// Only send update once per second
delay(1000);
}
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37
* Body Sensor Location Char: 0x2A38
*/
BLEService hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);
BLEDis bledis; // DIS (Device Information Service) helper class instance
BLEBas blebas; // BAS (Battery Service) helper class instance
uint8_t bps = 0;
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(“Bluefruit52 HRM Example”);
Serial.println(“----------------------- ”);
// Initialise the Bluefruit module
Serial.println(“Initialise the Bluefruit nRF52 module”);
Bluefruit.begin();
// Set the advertised device name (keep it short!)
Serial.println(“Setting Device Name to ’Feather52 HRM‘”);
Bluefruit.setName(“Bluefruit52 HRM”);
// Set the connect/disconnect callback handlers
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
// Configure and Start the Device Information Service
Serial.println(“Configuring the Device Information Service”);
bledis.setManufacturer(“Adafruit Industries”);
bledis.setModel(“Bluefruit Feather52”);
bledis.begin();
// Start the BLE Battery Service and set it to 100%
Serial.println(“Configuring the Battery Service”);
blebas.begin();
blebas.write(100);
// Setup the Heart Rate Monitor service using
// BLEService and BLECharacteristic classes
Serial.println(“Configuring the Heart Rate Monitor Service”);
setupHRM();
// Setup the advertising packet(s)
Serial.println(“Setting up the advertising payload(s)”);
startAdv();
Serial.println(“Ready Player One!!!”);
Serial.println(“ Advertising”);
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include HRM Service UUID
Bluefruit.Advertising.addService(hrms);
// Include Name
Bluefruit.Advertising.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don’t stop advertising after n seconds
}
void setupHRM(void)
{
// Configure the Heart Rate Monitor service
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
// Supported Characteristics:
// Name UUID Requirement Properties
// ---------------------------- ------ ----------- ----------
// Heart Rate Measurement 0x2A37 Mandatory Notify
// Body Sensor Location 0x2A38 Optional Read
// Heart Rate Control Point 0x2A39 Conditional Write 《-- Not used here
hrms.begin();
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.。 Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was ‘begin()’ed!
// Configure the Heart Rate Measurement characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Properties = Notify
// Min Len = 1
// Max Len = 8
// B0 = UINT8 - Flag (MANDATORY)
// b5:7 = Reserved
// b4 = RR-Internal (0 = Not present, 1 = Present)
// b3 = Energy expended status (0 = Not present, 1 = Present)
// b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
// b0 = Value format (0 = UINT8, 1 = UINT16)
// B1 = UINT8 - 8-bit heart rate measurement value in BPM
// B2:3 = UINT16 - 16-bit heart rate measurement value in BPM
// B4:5 = UINT16 - Energy expended in joules
// B6:7 = UINT16 - RR Internal (1/1024 second resolution)
hrmc.setProperties(CHR_PROPS_NOTIFY);
hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
hrmc.setFixedLen(2);
hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
hrmc.begin();
uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
hrmc.write(hrmdata, 2);
// Configure the Body Sensor Location characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Properties = Read
// Min Len = 1
// Max Len = 1
// B0 = UINT8 - Body Sensor Location
// 0 = Other
// 1 = Chest
// 2 = Wrist
// 3 = Finger
// 4 = Hand
// 5 = Ear Lobe
// 6 = Foot
// 7:255 = Reserved
bslc.setProperties(CHR_PROPS_READ);
bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
bslc.setFixedLen(1);
bslc.begin();
bslc.write8(2); // Set the characteristic to ‘Wrist’ (2)
}
void connect_callback(uint16_t conn_handle)
{
// Get the reference to current connection
BLEConnection* connection = Bluefruit.Connection(conn_handle);
char central_name[32] = { 0 };
connection-》getPeerName(central_name, sizeof(central_name));
Serial.print(“Connected to ”);
Serial.println(central_name);
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle connection where this event happens
* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.print(“Disconnected, reason = 0x”); Serial.println(reason, HEX);
Serial.println(“Advertising!”);
}
void cccd_callback(uint16_t conn_hdl, BLECharacteristic* chr, uint16_t cccd_value)
{
// Display the raw request packet
Serial.print(“CCCD Updated: ”);
//Serial.printBuffer(request-》data, request-》len);
Serial.print(cccd_value);
Serial.println(“”);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
if (chr-》uuid == hrmc.uuid) {
if (chr-》notifyEnabled(conn_hdl)) {
Serial.println(“Heart Rate Measurement ‘Notify’ enabled”);
} else {
Serial.println(“Heart Rate Measurement ‘Notify’ disabled”);
}
}
}
void loop()
{
digitalToggle(LED_RED);
if ( Bluefruit.connected() ) {
uint8_t hrmdata[2] = { 0b00000110, bps++ }; // Sensor connected, increment BPS value
// Note: We use .notify instead of .write!
// If it is connected but CCCD is not enabled
// The characteristic‘s value is still updated although notification is not sent
if ( hrmc.notify(hrmdata, sizeof(hrmdata)) ){
Serial.print(“Heart Rate Measurement updated to: ”); Serial.println(bps);
}else{
Serial.println(“ERROR: Notify not set in the CCCD or not connected!”);
}
}
// Only send update once per second
delay(1000);
}
BLE引脚I/O
Firmata是一种通用协议,用于与微控制器通信并控制电路板的引脚,例如设置GPIO输出和输入,PWM输出,模拟读取,等等。..
设置
要运行此演示,您需要使用我们的免费iOS,Android或OS X应用程序在移动设备上打开Bluefruit LE Connect。
加载 StandardFirmataBLE 在Arduino IDE中的示例草图
编译该草图并将其闪存到基于nRF52的Feather
上传完成后,打开串行监视器strong》(工具》串行监视器)
在移动设备上打开 Bluefruit LE Connect 应用程序
连接到适当的目标(可能是’ Bluefruit52 ‘)
连接到应用程序内的 Pin I/O 应用程序
后,请立即连接开关使用Pin I/O模块进行操作,您可以在此处查看本教程https://learn.adafruit.com/bluefruit-le-connect-for-ios/pin-io
完整代码
此代码的最新版本始终在Github上以及位于以下位置的示例文件夹中
以下代码是为了方便起见而提供的,但可能过时了!请参阅上面的链接以获取最新代码。
下载:Project Zip 或 StandardFirmataBLE.ino | 在Github上查看
复制代码
/*
Firmata is a generic protocol for communicating with microcontrollers
from software on a host computer. It is intended to work with
any host computer software package.
To download a host software package, please click on the following link
to open the list of Firmata client libraries in your default browser.
https://github.com/firmata/arduino#firmata-client-libraries
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Last updated October 16th, 2016
*/
// Adafruit nRF52 Boards require Firmata version is at least 2.5.7
#include
#include
#include
#include
#define I2C_WRITE B00000000
#define I2C_READ B00001000
#define I2C_READ_CONTINUOUSLY B00010000
#define I2C_STOP_READING B00011000
#define I2C_READ_WRITE_MODE_MASK B00011000
#define I2C_10BIT_ADDRESS_MODE_MASK B00100000
#define I2C_END_TX_MASK B01000000
#define I2C_STOP_TX 1
#define I2C_RESTART_TX 0
#define I2C_MAX_QUERIES 8
#define I2C_REGISTER_NOT_SPECIFIED -1
// the minimum interval for sampling analog input
#define MINIMUM_SAMPLING_INTERVAL 1
// Adafruit
uint8_t ANALOG_TO_PIN(uint8_t n)
{
switch (n)
{
case 0 : return PIN_A0;
case 1 : return PIN_A1;
case 2 : return PIN_A2;
case 3 : return PIN_A3;
case 4 : return PIN_A4;
case 5 : return PIN_A5;
case 6 : return PIN_A6;
case 7 : return PIN_A7;
}
return 127;
}
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
#ifdef FIRMATA_SERIAL_FEATURE
SerialFirmata serialFeature;
#endif
BLEUart bleuart;
/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
/* digital input ports */
byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence
byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent
/* pins configuration */
byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else
/* timer variables */
unsigned long currentMillis; // store the current value from millis()
unsigned long previousMillis; // for comparison with currentMillis
unsigned int samplingInterval = 19; // how often to run the main loop (in ms)
/* i2c data */
struct i2c_device_info {
byte addr;
int reg;
byte bytes;
byte stopTX;
};
/* for i2c read continuous more */
i2c_device_info query[I2C_MAX_QUERIES];
byte i2cRxData[64];
boolean isI2CEnabled = false;
signed char queryIndex = -1;
// default delay time between i2c read request and Wire.requestFrom()
unsigned int i2cReadDelayTime = 0;
Servo servos[MAX_SERVOS];
byte servoPinMap[TOTAL_PINS];
byte detachedServos[MAX_SERVOS];
byte detachedServoCount = 0;
byte servoCount = 0;
boolean isResetting = false;
// Forward declare a few functions to avoid compiler errors with older versions
// of the Arduino IDE.
void setPinModeCallback(byte, int);
void reportAnalogCallback(byte analogPin, int value);
void sysexCallback(byte, byte, byte*);
/* utility functions */
void wireWrite(byte data)
{
#if ARDUINO 》= 100
Wire.write((byte)data);
#else
Wire.send(data);
#endif
}
byte wireRead(void)
{
#if ARDUINO 》= 100
return Wire.read();
#else
return Wire.receive();
#endif
}
/*==============================================================================
* FUNCTIONS
*============================================================================*/
void attachServo(byte pin, int minPulse, int maxPulse)
{
if (servoCount 《 MAX_SERVOS) {
// reuse indexes of detached servos until all have been reallocated
if (detachedServoCount 》 0) {
servoPinMap[pin] = detachedServos[detachedServoCount - 1];
if (detachedServoCount 》 0) detachedServoCount--;
} else {
servoPinMap[pin] = servoCount;
servoCount++;
}
if (minPulse 》 0 && maxPulse 》 0) {
servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse);
} else {
servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin));
}
} else {
Firmata.sendString(“Max servos attached”);
}
}
void detachServo(byte pin)
{
servos[servoPinMap[pin]].detach();
// if we’re detaching the last servo, decrement the count
// otherwise store the index of the detached servo
if (servoPinMap[pin] == servoCount && servoCount 》 0) {
servoCount--;
} else if (servoCount 》 0) {
// keep track of detached servos because we want to reuse their indexes
// before incrementing the count of attached servos
detachedServoCount++;
detachedServos[detachedServoCount - 1] = servoPinMap[pin];
}
servoPinMap[pin] = 255;
}
void enableI2CPins()
{
byte i;
// is there a faster way to do this? would probaby require importing
// Arduino.h to get SCL and SDA pins
for (i = 0; i 《 TOTAL_PINS; i++) {
if (IS_PIN_I2C(i)) {
// mark pins as i2c so they are ignore in non i2c data requests
setPinModeCallback(i, PIN_MODE_I2C);
}
}
isI2CEnabled = true;
Wire.begin();
}
/* disable the i2c pins so they can be used for other functions */
void disableI2CPins() {
isI2CEnabled = false;
// disable read continuous mode for all devices
queryIndex = -1;
}
void readAndReportData(byte address, int theRegister, byte numBytes, byte stopTX) {
// allow I2C requests that don‘t require a register read
// for example, some devices using an interrupt pin to signify new data available
// do not always require the register read so upon interrupt you call Wire.requestFrom()
if (theRegister != I2C_REGISTER_NOT_SPECIFIED) {
Wire.beginTransmission(address);
wireWrite((byte)theRegister);
Wire.endTransmission(stopTX); // default = true
// do not set a value of 0
if (i2cReadDelayTime 》 0) {
// delay is necessary for some devices such as WiiNunchuck
delayMicroseconds(i2cReadDelayTime);
}
} else {
theRegister = 0; // fill the register with a dummy value
}
Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom
// check to be sure correct number of bytes were returned by slave
if (numBytes 《 Wire.available()) {
Firmata.sendString(“I2C: Too many bytes received”);
} else if (numBytes 》 Wire.available()) {
Firmata.sendString(“I2C: Too few bytes received”);
}
i2cRxData[0] = address;
i2cRxData[1] = theRegister;
for (int i = 0; i 《 numBytes && Wire.available(); i++) {
i2cRxData[2 + i] = wireRead();
}
// send slave address, register and received bytes
Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData);
}
void outputPort(byte portNumber, byte portValue, byte forceSend)
{
// pins not configured as INPUT are cleared to zeros
portValue = portValue & portConfigInputs[portNumber];
// only send if the value is different than previously sent
if (forceSend || previousPINs[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPINs[portNumber] = portValue;
}
}
/* -----------------------------------------------------------------------------
* check all the active digital inputs for change of state, then add any events
* to the Serial output queue using Serial.print() */
void checkDigitalInputs(void)
{
/* Using non-looping code allows constants to be given to readPort()。
* The compiler will apply substantial optimizations if the inputs
* to readPort() are compile-time constants. */
if (TOTAL_PORTS 》 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false);
if (TOTAL_PORTS 》 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false);
if (TOTAL_PORTS 》 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false);
if (TOTAL_PORTS 》 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false);
if (TOTAL_PORTS 》 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false);
if (TOTAL_PORTS 》 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false);
if (TOTAL_PORTS 》 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false);
if (TOTAL_PORTS 》 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false);
if (TOTAL_PORTS 》 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false);
if (TOTAL_PORTS 》 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false);
if (TOTAL_PORTS 》 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false);
if (TOTAL_PORTS 》 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false);
if (TOTAL_PORTS 》 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false);
if (TOTAL_PORTS 》 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false);
if (TOTAL_PORTS 》 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false);
if (TOTAL_PORTS 》 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false);
}
// -----------------------------------------------------------------------------
/* sets the pin mode to the correct state and sets the relevant bits in the
* two bit-arrays that track Digital I/O and PWM status
*/
void setPinModeCallback(byte pin, int mode)
{
if (Firmata.getPinMode(pin) == PIN_MODE_IGNORE)
return;
if (Firmata.getPinMode(pin) == PIN_MODE_I2C && isI2CEnabled && mode != PIN_MODE_I2C) {
// disable i2c so pins can be used for other functions
// the following if statements should reconfigure the pins properly
disableI2CPins();
}
if (IS_PIN_DIGITAL(pin) && mode != PIN_MODE_SERVO) {
if (servoPinMap[pin] 《 MAX_SERVOS && servos[servoPinMap[pin]].attached()) {
detachServo(pin);
}
}
if (IS_PIN_ANALOG(pin)) {
reportAnalogCallback(PIN_TO_ANALOG(pin), mode == PIN_MODE_ANALOG ? 1 : 0); // turn on/off reporting
}
if (IS_PIN_DIGITAL(pin)) {
if (mode == INPUT || mode == PIN_MODE_PULLUP) {
portConfigInputs[pin / 8] |= (1 《《 (pin & 7));
} else {
portConfigInputs[pin / 8] &= ~(1 《《 (pin & 7));
}
}
Firmata.setPinState(pin, 0);
switch (mode) {
case PIN_MODE_ANALOG:
if (IS_PIN_ANALOG(pin)) {
if (IS_PIN_DIGITAL(pin)) {
pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
#if ARDUINO 《= 100
// deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
#endif
}
Firmata.setPinMode(pin, PIN_MODE_ANALOG);
}
break;
case INPUT:
// Adafruit: Input without pull up cause pin state changes randomly --》 lots of transmission data
// if (IS_PIN_DIGITAL(pin)) {
// pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
//#if ARDUINO 《= 100
// // deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6
// digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
//#endif
// Firmata.setPinMode(pin, INPUT);
// }
// break;
case PIN_MODE_PULLUP:
if (IS_PIN_DIGITAL(pin)) {
pinMode(PIN_TO_DIGITAL(pin), INPUT_PULLUP);
Firmata.setPinMode(pin, PIN_MODE_PULLUP);
Firmata.setPinState(pin, 1);
}
break;
case OUTPUT:
if (IS_PIN_DIGITAL(pin)) {
if (Firmata.getPinMode(pin) == PIN_MODE_PWM) {
// Disable PWM if pin mode was previously set to PWM.
digitalWrite(PIN_TO_DIGITAL(pin), LOW);
}
pinMode(PIN_TO_DIGITAL(pin), OUTPUT);
Firmata.setPinMode(pin, OUTPUT);
}
break;
case PIN_MODE_PWM:
if (IS_PIN_PWM(pin)) {
pinMode(PIN_TO_PWM(pin), OUTPUT);
analogWrite(PIN_TO_PWM(pin), 0);
Firmata.setPinMode(pin, PIN_MODE_PWM);
}
break;
case PIN_MODE_SERVO:
if (IS_PIN_DIGITAL(pin)) {
Firmata.setPinMode(pin, PIN_MODE_SERVO);
if (servoPinMap[pin] == 255 || !servos[servoPinMap[pin]].attached()) {
// pass -1 for min and max pulse values to use default values set
// by Servo library
attachServo(pin, -1, -1);
}
}
break;
case PIN_MODE_I2C:
if (IS_PIN_I2C(pin)) {
// mark the pin as i2c
// the user must call I2C_CONFIG to enable I2C for a device
Firmata.setPinMode(pin, PIN_MODE_I2C);
}
break;
case PIN_MODE_SERIAL:
#ifdef FIRMATA_SERIAL_FEATURE
serialFeature.handlePinMode(pin, PIN_MODE_SERIAL);
#endif
break;
default:
Firmata.sendString(“Unknown pin mode”); // TODO: put error msgs in EEPROM
}
// TODO: save status to EEPROM here, if changed
}
/*
* Sets the value of an individual pin. Useful if you want to set a pin value but
* are not tracking the digital port state.
* Can only be used on pins configured as OUTPUT.
* Cannot be used to enable pull-ups on Digital INPUT pins.
*/
void setPinValueCallback(byte pin, int value)
{
if (pin 《 TOTAL_PINS && IS_PIN_DIGITAL(pin)) {
if (Firmata.getPinMode(pin) == OUTPUT) {
Firmata.setPinState(pin, value);
digitalWrite(PIN_TO_DIGITAL(pin), value);
}
}
}
void analogWriteCallback(byte pin, int value)
{
if (pin 《 TOTAL_PINS) {
switch (Firmata.getPinMode(pin)) {
case PIN_MODE_SERVO:
if (IS_PIN_DIGITAL(pin))
servos[servoPinMap[pin]].write(value);
Firmata.setPinState(pin, value);
break;
case PIN_MODE_PWM:
if (IS_PIN_PWM(pin))
analogWrite(PIN_TO_PWM(pin), value);
Firmata.setPinState(pin, value);
break;
}
}
}
void digitalWriteCallback(byte port, int value)
{
byte pin, lastPin, pinValue, mask = 1, pinWriteMask = 0;
if (port 《 TOTAL_PORTS) {
// create a mask of the pins on this port that are writable.
lastPin = port * 8 + 8;
if (lastPin 》 TOTAL_PINS) lastPin = TOTAL_PINS;
for (pin = port * 8; pin 《 lastPin; pin++) {
// do not disturb non-digital pins (eg, Rx & Tx)
if (IS_PIN_DIGITAL(pin)) {
// do not touch pins in PWM, ANALOG, SERVO or other modes
if (Firmata.getPinMode(pin) == OUTPUT || Firmata.getPinMode(pin) == INPUT) {
pinValue = ((byte)value & mask) ? 1 : 0;
if (Firmata.getPinMode(pin) == OUTPUT) {
pinWriteMask |= mask;
} else if (Firmata.getPinMode(pin) == INPUT && pinValue == 1 && Firmata.getPinState(pin) != 1) {
// only handle INPUT here for backwards compatibility
#if ARDUINO 》 100
pinMode(pin, INPUT_PULLUP);
#else
// only write to the INPUT pin to enable pullups if Arduino v1.0.0 or earlier
pinWriteMask |= mask;
#endif
}
Firmata.setPinState(pin, pinValue);
}
}
mask = mask 《《 1;
}
writePort(port, (byte)value, pinWriteMask);
}
}
// -----------------------------------------------------------------------------
/* sets bits in a bit array (int) to toggle the reporting of the analogIns
*/
//void FirmataClass::setAnalogPinReporting(byte pin, byte state) {
//}
void reportAnalogCallback(byte analogPin, int value)
{
if (analogPin 《 TOTAL_ANALOG_PINS) {
if (value == 0) {
analogInputsToReport = analogInputsToReport & ~ (1 《《 analogPin);
} else {
analogInputsToReport = analogInputsToReport | (1 《《 analogPin);
// prevent during system reset or all analog pin values will be reported
// which may report noise for unconnected analog pins
if (!isResetting) {
// Send pin value immediately. This is helpful when connected via
// ethernet, wi-fi or bluetooth so pin states can be known upon
// reconnecting.
Firmata.sendAnalog(analogPin, analogRead( ANALOG_TO_PIN(analogPin) ) );
}
}
}
// TODO: save status to EEPROM here, if changed
}
void reportDigitalCallback(byte port, int value)
{
if (port 《 TOTAL_PORTS) {
reportPINs[port] = (byte)value;
// Send port value immediately. This is helpful when connected via
// ethernet, wi-fi or bluetooth so pin states can be known upon
// reconnecting.
if (value) outputPort(port, readPort(port, portConfigInputs[port]), true);
}
// do not disable analog reporting on these 8 pins, to allow some
// pins used for digital, others analog. Instead, allow both types
// of reporting to be enabled, but check if the pin is configured
// as analog when sampling the analog inputs. Likewise, while
// scanning digital pins, portConfigInputs will mask off values from any
// pins configured as analog
}
/*==============================================================================
* SYSEX-BASED commands
*============================================================================*/
void sysexCallback(byte command, byte argc, byte *argv)
{
byte mode;
byte stopTX;
byte slaveAddress;
byte data;
int slaveRegister;
unsigned int delayTime;
switch (command) {
case I2C_REQUEST:
mode = argv[1] & I2C_READ_WRITE_MODE_MASK;
if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) {
Firmata.sendString(“10-bit addressing not supported”);
return;
}
else {
slaveAddress = argv[0];
}
// need to invert the logic here since 0 will be default for client
// libraries that have not updated to add support for restart tx
if (argv[1] & I2C_END_TX_MASK) {
stopTX = I2C_RESTART_TX;
}
else {
stopTX = I2C_STOP_TX; // default
}
switch (mode) {
case I2C_WRITE:
Wire.beginTransmission(slaveAddress);
for (byte i = 2; i 《 argc; i += 2) {
data = argv[i] + (argv[i + 1] 《《 7);
wireWrite(data);
}
Wire.endTransmission();
delayMicroseconds(70);
break;
case I2C_READ:
if (argc == 6) {
// a slave register is specified
slaveRegister = argv[2] + (argv[3] 《《 7);
data = argv[4] + (argv[5] 《《 7); // bytes to read
}
else {
// a slave register is NOT specified
slaveRegister = I2C_REGISTER_NOT_SPECIFIED;
data = argv[2] + (argv[3] 《《 7); // bytes to read
}
readAndReportData(slaveAddress, (int)slaveRegister, data, stopTX);
break;
case I2C_READ_CONTINUOUSLY:
if ((queryIndex + 1) 》= I2C_MAX_QUERIES) {
// too many queries, just ignore
Firmata.sendString(“too many queries”);
break;
}
if (argc == 6) {
// a slave register is specified
slaveRegister = argv[2] + (argv[3] 《《 7);
data = argv[4] + (argv[5] 《《 7); // bytes to read
}
else {
// a slave register is NOT specified
slaveRegister = (int)I2C_REGISTER_NOT_SPECIFIED;
data = argv[2] + (argv[3] 《《 7); // bytes to read
}
queryIndex++;
query[queryIndex].addr = slaveAddress;
query[queryIndex].reg = slaveRegister;
query[queryIndex].bytes = data;
query[queryIndex].stopTX = stopTX;
break;
case I2C_STOP_READING:
byte queryIndexToSkip;
// if read continuous mode is enabled for only 1 i2c device, disable
// read continuous reporting for that device
if (queryIndex 《= 0) {
queryIndex = -1;
} else {
queryIndexToSkip = 0;
// if read continuous mode is enabled for multiple devices,
// determine which device to stop reading and remove it’s data from
// the array, shifiting other array data to fill the space
for (byte i = 0; i 《 queryIndex + 1; i++) {
if (query[i].addr == slaveAddress) {
queryIndexToSkip = i;
break;
}
}
for (byte i = queryIndexToSkip; i 《 queryIndex + 1; i++) {
if (i 《 I2C_MAX_QUERIES) {
query[i].addr = query[i + 1].addr;
query[i].reg = query[i + 1].reg;
query[i].bytes = query[i + 1].bytes;
query[i].stopTX = query[i + 1].stopTX;
}
}
queryIndex--;
}
break;
default:
break;
}
break;
case I2C_CONFIG:
delayTime = (argv[0] + (argv[1] 《《 7));
if (delayTime 》 0) {
i2cReadDelayTime = delayTime;
}
if (!isI2CEnabled) {
enableI2CPins();
}
break;
case SERVO_CONFIG:
if (argc 》 4) {
// these vars are here for clarity, they‘ll optimized away by the compiler
byte pin = argv[0];
int minPulse = argv[1] + (argv[2] 《《 7);
int maxPulse = argv[3] + (argv[4] 《《 7);
if (IS_PIN_DIGITAL(pin)) {
if (servoPinMap[pin] 《 MAX_SERVOS && servos[servoPinMap[pin]].attached()) {
detachServo(pin);
}
attachServo(pin, minPulse, maxPulse);
setPinModeCallback(pin, PIN_MODE_SERVO);
}
}
break;
case SAMPLING_INTERVAL:
if (argc 》 1) {
samplingInterval = argv[0] + (argv[1] 《《 7);
if (samplingInterval 《 MINIMUM_SAMPLING_INTERVAL) {
samplingInterval = MINIMUM_SAMPLING_INTERVAL;
}
} else {
//Firmata.sendString(“Not enough data”);
}
break;
case EXTENDED_ANALOG:
if (argc 》 1) {
int val = argv[1];
if (argc 》 2) val |= (argv[2] 《《 7);
if (argc 》 3) val |= (argv[3] 《《 14);
analogWriteCallback(argv[0], val);
}
break;
case CAPABILITY_QUERY:
Firmata.write(START_SYSEX);
Firmata.write(CAPABILITY_RESPONSE);
for (byte pin = 0; pin 《 TOTAL_PINS; pin++) {
if (IS_PIN_DIGITAL(pin)) {
Firmata.write((byte)INPUT);
Firmata.write(1);
Firmata.write((byte)PIN_MODE_PULLUP);
Firmata.write(1);
Firmata.write((byte)OUTPUT);
Firmata.write(1);
}
if (IS_PIN_ANALOG(pin)) {
Firmata.write(PIN_MODE_ANALOG);
Firmata.write(10); // 10 = 10-bit resolution
}
if (IS_PIN_PWM(pin)) {
Firmata.write(PIN_MODE_PWM);
Firmata.write(DEFAULT_PWM_RESOLUTION);
}
if (IS_PIN_DIGITAL(pin)) {
Firmata.write(PIN_MODE_SERVO);
Firmata.write(14);
}
if (IS_PIN_I2C(pin)) {
Firmata.write(PIN_MODE_I2C);
Firmata.write(1); // TODO: could assign a number to map to SCL or SDA
}
#ifdef FIRMATA_SERIAL_FEATURE
serialFeature.handleCapability(pin);
#endif
Firmata.write(127);
}
Firmata.write(END_SYSEX);
break;
case PIN_STATE_QUERY:
if (argc 》 0) {
byte pin = argv[0];
Firmata.write(START_SYSEX);
Firmata.write(PIN_STATE_RESPONSE);
Firmata.write(pin);
if (pin 《 TOTAL_PINS) {
Firmata.write(Firmata.getPinMode(pin));
Firmata.write((byte)Firmata.getPinState(pin) & 0x7F);
if (Firmata.getPinState(pin) & 0xFF80) Firmata.write((byte)(Firmata.getPinState(pin) 》》 7) & 0x7F);
if (Firmata.getPinState(pin) & 0xC000) Firmata.write((byte)(Firmata.getPinState(pin) 》》 14) & 0x7F);
}
Firmata.write(END_SYSEX);
}
break;
case ANALOG_MAPPING_QUERY:
Firmata.write(START_SYSEX);
Firmata.write(ANALOG_MAPPING_RESPONSE);
for (byte pin = 0; pin 《 TOTAL_PINS; pin++) {
Firmata.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127);
}
Firmata.write(END_SYSEX);
break;
case SERIAL_MESSAGE:
#ifdef FIRMATA_SERIAL_FEATURE
serialFeature.handleSysex(command, argc, argv);
#endif
break;
}
}
/*==============================================================================
* SETUP()
*============================================================================*/
void systemResetCallback()
{
isResetting = true;
// initialize a defalt state
// TODO: option to load config from EEPROM instead of default
#ifdef FIRMATA_SERIAL_FEATURE
serialFeature.reset();
#endif
if (isI2CEnabled) {
disableI2CPins();
}
for (byte i = 0; i 《 TOTAL_PORTS; i++) {
reportPINs[i] = false; // by default, reporting off
portConfigInputs[i] = 0; // until activated
previousPINs[i] = 0;
}
for (byte i = 0; i 《 TOTAL_PINS; i++) {
// pins with analog capability default to analog input
// otherwise, pins default to digital output
if (IS_PIN_ANALOG(i)) {
// turns off pullup, configures everything
setPinModeCallback(i, PIN_MODE_ANALOG);
} else if (IS_PIN_DIGITAL(i)) {
// sets the output to 0, configures portConfigInputs
setPinModeCallback(i, OUTPUT);
}
servoPinMap[i] = 255;
}
// by default, do not report any analog inputs
analogInputsToReport = 0;
detachedServoCount = 0;
servoCount = 0;
/* send digital inputs to set the initial state on the host computer,
* since once in the loop(), this firmware will only send on change */
/*
TODO: this can never execute, since no pins default to digital input
but it will be needed when/if we support EEPROM stored config
for (byte i=0; i 《 TOTAL_PORTS; i++) {
outputPort(i, readPort(i, portConfigInputs[i]), true);
}
*/
isResetting = false;
}
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(“Bluefruit52 Standard Firmata via BLEUART Example”);
Serial.println(“------------------------------------------------ ”);
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.begin();
Bluefruit.setName(“Bluefruit52”);
Bluefruit.setTxPower(4); // Check bluefruit.h for supported values
// try to go as fast as possible, could be rejected by some central, increase it if needed
// iOS won’t negotitate and will mostly use 30ms
Bluefruit.Periph.setConnInterval(9, 24); // min = 9*1.25=11.25 ms, max = 23*1.25=30ms
// Configure and Start BLE Uart Service
// Firmata use several small write(1) --》 buffering TXD is required to run smoothly
// Enable buffering TXD
bleuart.begin();
bleuart.bufferTXD(true);
Firmata.setFirmwareVersion(FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION);
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
Firmata.attach(SET_DIGITAL_PIN_VALUE, setPinValueCallback);
Firmata.attach(START_SYSEX, sysexCallback);
Firmata.attach(SYSTEM_RESET, systemResetCallback);
// use bleuart as transportation layer
Firmata.begin(bleuart);
// to use a port other than Serial, such as Serial1 on an Arduino Leonardo or Mega,
// Call begin(baud) on the alternate serial port and pass it to Firmata to begin like this:
// Serial1.begin(57600);
// Firmata.begin(Serial1);
// However do not do this if you are using SERIAL_MESSAGE
//Firmata.begin(57600);
//while (!Serial) {
// ; // wait for serial port to connect. Needed for ATmega32u4-based boards and Arduino 101
//}
systemResetCallback(); // reset to default config
// Set up and start advertising
startAdv();
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include bleuart 128-bit uuid
Bluefruit.Advertising.addService(bleuart);
// Secondary Scan Response packet (optional)
// Since there is no room for ‘Name’ in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don‘t stop advertising after n seconds
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
// Skip if not connected and bleuart notification is not enabled
if ( !(Bluefruit.connected() && bleuart.notifyEnabled()) ) return;
byte pin, analogPin;
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* FTDI buffer using Serial.print() */
checkDigitalInputs();
/* STREAMREAD - processing incoming messagse as soon as possible, while still
* checking digital inputs. */
while (Firmata.available())
Firmata.processInput();
// TODO - ensure that Stream buffer doesn’t go over 60 bytes
currentMillis = millis();
if (currentMillis - previousMillis 》 samplingInterval) {
previousMillis += samplingInterval;
/* ANALOGREAD - do all analogReads() at the configured sampling interval */
for (pin = 0; pin 《 TOTAL_PINS; pin++) {
if (IS_PIN_ANALOG(pin) && Firmata.getPinMode(pin) == PIN_MODE_ANALOG) {
analogPin = PIN_TO_ANALOG(pin);
if (analogInputsToReport & (1 《《 analogPin)) {
Firmata.sendAnalog(analogPin, analogRead( ANALOG_TO_PIN(analogPin) ));
}
}
}
// report i2c data for all device with read continuous mode enabled
if (queryIndex 》 -1) {
for (byte i = 0; i 《 queryIndex + 1; i++) {
readAndReportData(query[i].addr, query[i].reg, query[i].bytes, query[i].stopTX);
}
}
}
#ifdef FIRMATA_SERIAL_FEATURE
serialFeature.update();
#endif
// flush TXD since we use bufferTXD()
bleuart.flushTXD();
}
/*
Firmata is a generic protocol for communicating with microcontrollers
from software on a host computer. It is intended to work with
any host computer software package.
To download a host software package, please click on the following link
to open the list of Firmata client libraries in your default browser.
https://github.com/firmata/arduino#firmata-client-libraries
Copyright (C) 2006-2008 Hans-Christoph Steiner. All rights reserved.
Copyright (C) 2010-2011 Paul Stoffregen. All rights reserved.
Copyright (C) 2009 Shigeru Kobayashi. All rights reserved.
Copyright (C) 2009-2016 Jeff Hoefs. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
See file LICENSE.txt for further informations on licensing terms.
Last updated October 16th, 2016
*/
// Adafruit nRF52 Boards require Firmata version is at least 2.5.7
#include
#include
#include
#include
#define I2C_WRITE B00000000
#define I2C_READ B00001000
#define I2C_READ_CONTINUOUSLY B00010000
#define I2C_STOP_READING B00011000
#define I2C_READ_WRITE_MODE_MASK B00011000
#define I2C_10BIT_ADDRESS_MODE_MASK B00100000
#define I2C_END_TX_MASK B01000000
#define I2C_STOP_TX 1
#define I2C_RESTART_TX 0
#define I2C_MAX_QUERIES 8
#define I2C_REGISTER_NOT_SPECIFIED -1
// the minimum interval for sampling analog input
#define MINIMUM_SAMPLING_INTERVAL 1
// Adafruit
uint8_t ANALOG_TO_PIN(uint8_t n)
{
switch (n)
{
case 0 : return PIN_A0;
case 1 : return PIN_A1;
case 2 : return PIN_A2;
case 3 : return PIN_A3;
case 4 : return PIN_A4;
case 5 : return PIN_A5;
case 6 : return PIN_A6;
case 7 : return PIN_A7;
}
return 127;
}
/*==============================================================================
* GLOBAL VARIABLES
*============================================================================*/
#ifdef FIRMATA_SERIAL_FEATURE
SerialFirmata serialFeature;
#endif
BLEUart bleuart;
/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
/* digital input ports */
byte reportPINs[TOTAL_PORTS]; // 1 = report this port, 0 = silence
byte previousPINs[TOTAL_PORTS]; // previous 8 bits sent
/* pins configuration */
byte portConfigInputs[TOTAL_PORTS]; // each bit: 1 = pin in INPUT, 0 = anything else
/* timer variables */
unsigned long currentMillis; // store the current value from millis()
unsigned long previousMillis; // for comparison with currentMillis
unsigned int samplingInterval = 19; // how often to run the main loop (in ms)
/* i2c data */
struct i2c_device_info {
byte addr;
int reg;
byte bytes;
byte stopTX;
};
/* for i2c read continuous more */
i2c_device_info query[I2C_MAX_QUERIES];
byte i2cRxData[64];
boolean isI2CEnabled = false;
signed char queryIndex = -1;
// default delay time between i2c read request and Wire.requestFrom()
unsigned int i2cReadDelayTime = 0;
Servo servos[MAX_SERVOS];
byte servoPinMap[TOTAL_PINS];
byte detachedServos[MAX_SERVOS];
byte detachedServoCount = 0;
byte servoCount = 0;
boolean isResetting = false;
// Forward declare a few functions to avoid compiler errors with older versions
// of the Arduino IDE.
void setPinModeCallback(byte, int);
void reportAnalogCallback(byte analogPin, int value);
void sysexCallback(byte, byte, byte*);
/* utility functions */
void wireWrite(byte data)
{
#if ARDUINO 》= 100
Wire.write((byte)data);
#else
Wire.send(data);
#endif
}
byte wireRead(void)
{
#if ARDUINO 》= 100
return Wire.read();
#else
return Wire.receive();
#endif
}
/*==============================================================================
* FUNCTIONS
*============================================================================*/
void attachServo(byte pin, int minPulse, int maxPulse)
{
if (servoCount 《 MAX_SERVOS) {
// reuse indexes of detached servos until all have been reallocated
if (detachedServoCount 》 0) {
servoPinMap[pin] = detachedServos[detachedServoCount - 1];
if (detachedServoCount 》 0) detachedServoCount--;
} else {
servoPinMap[pin] = servoCount;
servoCount++;
}
if (minPulse 》 0 && maxPulse 》 0) {
servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin), minPulse, maxPulse);
} else {
servos[servoPinMap[pin]].attach(PIN_TO_DIGITAL(pin));
}
} else {
Firmata.sendString(“Max servos attached”);
}
}
void detachServo(byte pin)
{
servos[servoPinMap[pin]].detach();
// if we‘re detaching the last servo, decrement the count
// otherwise store the index of the detached servo
if (servoPinMap[pin] == servoCount && servoCount 》 0) {
servoCount--;
} else if (servoCount 》 0) {
// keep track of detached servos because we want to reuse their indexes
// before incrementing the count of attached servos
detachedServoCount++;
detachedServos[detachedServoCount - 1] = servoPinMap[pin];
}
servoPinMap[pin] = 255;
}
void enableI2CPins()
{
byte i;
// is there a faster way to do this? would probaby require importing
// Arduino.h to get SCL and SDA pins
for (i = 0; i 《 TOTAL_PINS; i++) {
if (IS_PIN_I2C(i)) {
// mark pins as i2c so they are ignore in non i2c data requests
setPinModeCallback(i, PIN_MODE_I2C);
}
}
isI2CEnabled = true;
Wire.begin();
}
/* disable the i2c pins so they can be used for other functions */
void disableI2CPins() {
isI2CEnabled = false;
// disable read continuous mode for all devices
queryIndex = -1;
}
void readAndReportData(byte address, int theRegister, byte numBytes, byte stopTX) {
// allow I2C requests that don’t require a register read
// for example, some devices using an interrupt pin to signify new data available
// do not always require the register read so upon interrupt you call Wire.requestFrom()
if (theRegister != I2C_REGISTER_NOT_SPECIFIED) {
Wire.beginTransmission(address);
wireWrite((byte)theRegister);
Wire.endTransmission(stopTX); // default = true
// do not set a value of 0
if (i2cReadDelayTime 》 0) {
// delay is necessary for some devices such as WiiNunchuck
delayMicroseconds(i2cReadDelayTime);
}
} else {
theRegister = 0; // fill the register with a dummy value
}
Wire.requestFrom(address, numBytes); // all bytes are returned in requestFrom
// check to be sure correct number of bytes were returned by slave
if (numBytes 《 Wire.available()) {
Firmata.sendString(“I2C: Too many bytes received”);
} else if (numBytes 》 Wire.available()) {
Firmata.sendString(“I2C: Too few bytes received”);
}
i2cRxData[0] = address;
i2cRxData[1] = theRegister;
for (int i = 0; i 《 numBytes && Wire.available(); i++) {
i2cRxData[2 + i] = wireRead();
}
// send slave address, register and received bytes
Firmata.sendSysex(SYSEX_I2C_REPLY, numBytes + 2, i2cRxData);
}
void outputPort(byte portNumber, byte portValue, byte forceSend)
{
// pins not configured as INPUT are cleared to zeros
portValue = portValue & portConfigInputs[portNumber];
// only send if the value is different than previously sent
if (forceSend || previousPINs[portNumber] != portValue) {
Firmata.sendDigitalPort(portNumber, portValue);
previousPINs[portNumber] = portValue;
}
}
/* -----------------------------------------------------------------------------
* check all the active digital inputs for change of state, then add any events
* to the Serial output queue using Serial.print() */
void checkDigitalInputs(void)
{
/* Using non-looping code allows constants to be given to readPort()。
* The compiler will apply substantial optimizations if the inputs
* to readPort() are compile-time constants. */
if (TOTAL_PORTS 》 0 && reportPINs[0]) outputPort(0, readPort(0, portConfigInputs[0]), false);
if (TOTAL_PORTS 》 1 && reportPINs[1]) outputPort(1, readPort(1, portConfigInputs[1]), false);
if (TOTAL_PORTS 》 2 && reportPINs[2]) outputPort(2, readPort(2, portConfigInputs[2]), false);
if (TOTAL_PORTS 》 3 && reportPINs[3]) outputPort(3, readPort(3, portConfigInputs[3]), false);
if (TOTAL_PORTS 》 4 && reportPINs[4]) outputPort(4, readPort(4, portConfigInputs[4]), false);
if (TOTAL_PORTS 》 5 && reportPINs[5]) outputPort(5, readPort(5, portConfigInputs[5]), false);
if (TOTAL_PORTS 》 6 && reportPINs[6]) outputPort(6, readPort(6, portConfigInputs[6]), false);
if (TOTAL_PORTS 》 7 && reportPINs[7]) outputPort(7, readPort(7, portConfigInputs[7]), false);
if (TOTAL_PORTS 》 8 && reportPINs[8]) outputPort(8, readPort(8, portConfigInputs[8]), false);
if (TOTAL_PORTS 》 9 && reportPINs[9]) outputPort(9, readPort(9, portConfigInputs[9]), false);
if (TOTAL_PORTS 》 10 && reportPINs[10]) outputPort(10, readPort(10, portConfigInputs[10]), false);
if (TOTAL_PORTS 》 11 && reportPINs[11]) outputPort(11, readPort(11, portConfigInputs[11]), false);
if (TOTAL_PORTS 》 12 && reportPINs[12]) outputPort(12, readPort(12, portConfigInputs[12]), false);
if (TOTAL_PORTS 》 13 && reportPINs[13]) outputPort(13, readPort(13, portConfigInputs[13]), false);
if (TOTAL_PORTS 》 14 && reportPINs[14]) outputPort(14, readPort(14, portConfigInputs[14]), false);
if (TOTAL_PORTS 》 15 && reportPINs[15]) outputPort(15, readPort(15, portConfigInputs[15]), false);
}
// -----------------------------------------------------------------------------
/* sets the pin mode to the correct state and sets the relevant bits in the
* two bit-arrays that track Digital I/O and PWM status
*/
void setPinModeCallback(byte pin, int mode)
{
if (Firmata.getPinMode(pin) == PIN_MODE_IGNORE)
return;
if (Firmata.getPinMode(pin) == PIN_MODE_I2C && isI2CEnabled && mode != PIN_MODE_I2C) {
// disable i2c so pins can be used for other functions
// the following if statements should reconfigure the pins properly
disableI2CPins();
}
if (IS_PIN_DIGITAL(pin) && mode != PIN_MODE_SERVO) {
if (servoPinMap[pin] 《 MAX_SERVOS && servos[servoPinMap[pin]].attached()) {
detachServo(pin);
}
}
if (IS_PIN_ANALOG(pin)) {
reportAnalogCallback(PIN_TO_ANALOG(pin), mode == PIN_MODE_ANALOG ? 1 : 0); // turn on/off reporting
}
if (IS_PIN_DIGITAL(pin)) {
if (mode == INPUT || mode == PIN_MODE_PULLUP) {
portConfigInputs[pin / 8] |= (1 《《 (pin & 7));
} else {
portConfigInputs[pin / 8] &= ~(1 《《 (pin & 7));
}
}
Firmata.setPinState(pin, 0);
switch (mode) {
case PIN_MODE_ANALOG:
if (IS_PIN_ANALOG(pin)) {
if (IS_PIN_DIGITAL(pin)) {
pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
#if ARDUINO 《= 100
// deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6
digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
#endif
}
Firmata.setPinMode(pin, PIN_MODE_ANALOG);
}
break;
case INPUT:
// Adafruit: Input without pull up cause pin state changes randomly --》 lots of transmission data
// if (IS_PIN_DIGITAL(pin)) {
// pinMode(PIN_TO_DIGITAL(pin), INPUT); // disable output driver
//#if ARDUINO 《= 100
// // deprecated since Arduino 1.0.1 - TODO: drop support in Firmata 2.6
// digitalWrite(PIN_TO_DIGITAL(pin), LOW); // disable internal pull-ups
//#endif
// Firmata.setPinMode(pin, INPUT);
// }
// break;
case PIN_MODE_PULLUP:
if (IS_PIN_DIGITAL(pin)) {
pinMode(PIN_TO_DIGITAL(pin), INPUT_PULLUP);
Firmata.setPinMode(pin, PIN_MODE_PULLUP);
Firmata.setPinState(pin, 1);
}
break;
case OUTPUT:
if (IS_PIN_DIGITAL(pin)) {
if (Firmata.getPinMode(pin) == PIN_MODE_PWM) {
// Disable PWM if pin mode was previously set to PWM.
digitalWrite(PIN_TO_DIGITAL(pin), LOW);
}
pinMode(PIN_TO_DIGITAL(pin), OUTPUT);
Firmata.setPinMode(pin, OUTPUT);
}
break;
case PIN_MODE_PWM:
if (IS_PIN_PWM(pin)) {
pinMode(PIN_TO_PWM(pin), OUTPUT);
analogWrite(PIN_TO_PWM(pin), 0);
Firmata.setPinMode(pin, PIN_MODE_PWM);
}
break;
case PIN_MODE_SERVO:
if (IS_PIN_DIGITAL(pin)) {
Firmata.setPinMode(pin, PIN_MODE_SERVO);
if (servoPinMap[pin] == 255 || !servos[servoPinMap[pin]].attached()) {
// pass -1 for min and max pulse values to use default values set
// by Servo library
attachServo(pin, -1, -1);
}
}
break;
case PIN_MODE_I2C:
if (IS_PIN_I2C(pin)) {
// mark the pin as i2c
// the user must call I2C_CONFIG to enable I2C for a device
Firmata.setPinMode(pin, PIN_MODE_I2C);
}
break;
case PIN_MODE_SERIAL:
#ifdef FIRMATA_SERIAL_FEATURE
serialFeature.handlePinMode(pin, PIN_MODE_SERIAL);
#endif
break;
default:
Firmata.sendString(“Unknown pin mode”); // TODO: put error msgs in EEPROM
}
// TODO: save status to EEPROM here, if changed
}
/*
* Sets the value of an individual pin. Useful if you want to set a pin value but
* are not tracking the digital port state.
* Can only be used on pins configured as OUTPUT.
* Cannot be used to enable pull-ups on Digital INPUT pins.
*/
void setPinValueCallback(byte pin, int value)
{
if (pin 《 TOTAL_PINS && IS_PIN_DIGITAL(pin)) {
if (Firmata.getPinMode(pin) == OUTPUT) {
Firmata.setPinState(pin, value);
digitalWrite(PIN_TO_DIGITAL(pin), value);
}
}
}
void analogWriteCallback(byte pin, int value)
{
if (pin 《 TOTAL_PINS) {
switch (Firmata.getPinMode(pin)) {
case PIN_MODE_SERVO:
if (IS_PIN_DIGITAL(pin))
servos[servoPinMap[pin]].write(value);
Firmata.setPinState(pin, value);
break;
case PIN_MODE_PWM:
if (IS_PIN_PWM(pin))
analogWrite(PIN_TO_PWM(pin), value);
Firmata.setPinState(pin, value);
break;
}
}
}
void digitalWriteCallback(byte port, int value)
{
byte pin, lastPin, pinValue, mask = 1, pinWriteMask = 0;
if (port 《 TOTAL_PORTS) {
// create a mask of the pins on this port that are writable.
lastPin = port * 8 + 8;
if (lastPin 》 TOTAL_PINS) lastPin = TOTAL_PINS;
for (pin = port * 8; pin 《 lastPin; pin++) {
// do not disturb non-digital pins (eg, Rx & Tx)
if (IS_PIN_DIGITAL(pin)) {
// do not touch pins in PWM, ANALOG, SERVO or other modes
if (Firmata.getPinMode(pin) == OUTPUT || Firmata.getPinMode(pin) == INPUT) {
pinValue = ((byte)value & mask) ? 1 : 0;
if (Firmata.getPinMode(pin) == OUTPUT) {
pinWriteMask |= mask;
} else if (Firmata.getPinMode(pin) == INPUT && pinValue == 1 && Firmata.getPinState(pin) != 1) {
// only handle INPUT here for backwards compatibility
#if ARDUINO 》 100
pinMode(pin, INPUT_PULLUP);
#else
// only write to the INPUT pin to enable pullups if Arduino v1.0.0 or earlier
pinWriteMask |= mask;
#endif
}
Firmata.setPinState(pin, pinValue);
}
}
mask = mask 《《 1;
}
writePort(port, (byte)value, pinWriteMask);
}
}
// -----------------------------------------------------------------------------
/* sets bits in a bit array (int) to toggle the reporting of the analogIns
*/
//void FirmataClass::setAnalogPinReporting(byte pin, byte state) {
//}
void reportAnalogCallback(byte analogPin, int value)
{
if (analogPin 《 TOTAL_ANALOG_PINS) {
if (value == 0) {
analogInputsToReport = analogInputsToReport & ~ (1 《《 analogPin);
} else {
analogInputsToReport = analogInputsToReport | (1 《《 analogPin);
// prevent during system reset or all analog pin values will be reported
// which may report noise for unconnected analog pins
if (!isResetting) {
// Send pin value immediately. This is helpful when connected via
// ethernet, wi-fi or bluetooth so pin states can be known upon
// reconnecting.
Firmata.sendAnalog(analogPin, analogRead( ANALOG_TO_PIN(analogPin) ) );
}
}
}
// TODO: save status to EEPROM here, if changed
}
void reportDigitalCallback(byte port, int value)
{
if (port 《 TOTAL_PORTS) {
reportPINs[port] = (byte)value;
// Send port value immediately. This is helpful when connected via
// ethernet, wi-fi or bluetooth so pin states can be known upon
// reconnecting.
if (value) outputPort(port, readPort(port, portConfigInputs[port]), true);
}
// do not disable analog reporting on these 8 pins, to allow some
// pins used for digital, others analog. Instead, allow both types
// of reporting to be enabled, but check if the pin is configured
// as analog when sampling the analog inputs. Likewise, while
// scanning digital pins, portConfigInputs will mask off values from any
// pins configured as analog
}
/*==============================================================================
* SYSEX-BASED commands
*============================================================================*/
void sysexCallback(byte command, byte argc, byte *argv)
{
byte mode;
byte stopTX;
byte slaveAddress;
byte data;
int slaveRegister;
unsigned int delayTime;
switch (command) {
case I2C_REQUEST:
mode = argv[1] & I2C_READ_WRITE_MODE_MASK;
if (argv[1] & I2C_10BIT_ADDRESS_MODE_MASK) {
Firmata.sendString(“10-bit addressing not supported”);
return;
}
else {
slaveAddress = argv[0];
}
// need to invert the logic here since 0 will be default for client
// libraries that have not updated to add support for restart tx
if (argv[1] & I2C_END_TX_MASK) {
stopTX = I2C_RESTART_TX;
}
else {
stopTX = I2C_STOP_TX; // default
}
switch (mode) {
case I2C_WRITE:
Wire.beginTransmission(slaveAddress);
for (byte i = 2; i 《 argc; i += 2) {
data = argv[i] + (argv[i + 1] 《《 7);
wireWrite(data);
}
Wire.endTransmission();
delayMicroseconds(70);
break;
case I2C_READ:
if (argc == 6) {
// a slave register is specified
slaveRegister = argv[2] + (argv[3] 《《 7);
data = argv[4] + (argv[5] 《《 7); // bytes to read
}
else {
// a slave register is NOT specified
slaveRegister = I2C_REGISTER_NOT_SPECIFIED;
data = argv[2] + (argv[3] 《《 7); // bytes to read
}
readAndReportData(slaveAddress, (int)slaveRegister, data, stopTX);
break;
case I2C_READ_CONTINUOUSLY:
if ((queryIndex + 1) 》= I2C_MAX_QUERIES) {
// too many queries, just ignore
Firmata.sendString(“too many queries”);
break;
}
if (argc == 6) {
// a slave register is specified
slaveRegister = argv[2] + (argv[3] 《《 7);
data = argv[4] + (argv[5] 《《 7); // bytes to read
}
else {
// a slave register is NOT specified
slaveRegister = (int)I2C_REGISTER_NOT_SPECIFIED;
data = argv[2] + (argv[3] 《《 7); // bytes to read
}
queryIndex++;
query[queryIndex].addr = slaveAddress;
query[queryIndex].reg = slaveRegister;
query[queryIndex].bytes = data;
query[queryIndex].stopTX = stopTX;
break;
case I2C_STOP_READING:
byte queryIndexToSkip;
// if read continuous mode is enabled for only 1 i2c device, disable
// read continuous reporting for that device
if (queryIndex 《= 0) {
queryIndex = -1;
} else {
queryIndexToSkip = 0;
// if read continuous mode is enabled for multiple devices,
// determine which device to stop reading and remove it‘s data from
// the array, shifiting other array data to fill the space
for (byte i = 0; i 《 queryIndex + 1; i++) {
if (query[i].addr == slaveAddress) {
queryIndexToSkip = i;
break;
}
}
for (byte i = queryIndexToSkip; i 《 queryIndex + 1; i++) {
if (i 《 I2C_MAX_QUERIES) {
query[i].addr = query[i + 1].addr;
query[i].reg = query[i + 1].reg;
query[i].bytes = query[i + 1].bytes;
query[i].stopTX = query[i + 1].stopTX;
}
}
queryIndex--;
}
break;
default:
break;
}
break;
case I2C_CONFIG:
delayTime = (argv[0] + (argv[1] 《《 7));
if (delayTime 》 0) {
i2cReadDelayTime = delayTime;
}
if (!isI2CEnabled) {
enableI2CPins();
}
break;
case SERVO_CONFIG:
if (argc 》 4) {
// these vars are here for clarity, they’ll optimized away by the compiler
byte pin = argv[0];
int minPulse = argv[1] + (argv[2] 《《 7);
int maxPulse = argv[3] + (argv[4] 《《 7);
if (IS_PIN_DIGITAL(pin)) {
if (servoPinMap[pin] 《 MAX_SERVOS && servos[servoPinMap[pin]].attached()) {
detachServo(pin);
}
attachServo(pin, minPulse, maxPulse);
setPinModeCallback(pin, PIN_MODE_SERVO);
}
}
break;
case SAMPLING_INTERVAL:
if (argc 》 1) {
samplingInterval = argv[0] + (argv[1] 《《 7);
if (samplingInterval 《 MINIMUM_SAMPLING_INTERVAL) {
samplingInterval = MINIMUM_SAMPLING_INTERVAL;
}
} else {
//Firmata.sendString(“Not enough data”);
}
break;
case EXTENDED_ANALOG:
if (argc 》 1) {
int val = argv[1];
if (argc 》 2) val |= (argv[2] 《《 7);
if (argc 》 3) val |= (argv[3] 《《 14);
analogWriteCallback(argv[0], val);
}
break;
case CAPABILITY_QUERY:
Firmata.write(START_SYSEX);
Firmata.write(CAPABILITY_RESPONSE);
for (byte pin = 0; pin 《 TOTAL_PINS; pin++) {
if (IS_PIN_DIGITAL(pin)) {
Firmata.write((byte)INPUT);
Firmata.write(1);
Firmata.write((byte)PIN_MODE_PULLUP);
Firmata.write(1);
Firmata.write((byte)OUTPUT);
Firmata.write(1);
}
if (IS_PIN_ANALOG(pin)) {
Firmata.write(PIN_MODE_ANALOG);
Firmata.write(10); // 10 = 10-bit resolution
}
if (IS_PIN_PWM(pin)) {
Firmata.write(PIN_MODE_PWM);
Firmata.write(DEFAULT_PWM_RESOLUTION);
}
if (IS_PIN_DIGITAL(pin)) {
Firmata.write(PIN_MODE_SERVO);
Firmata.write(14);
}
if (IS_PIN_I2C(pin)) {
Firmata.write(PIN_MODE_I2C);
Firmata.write(1); // TODO: could assign a number to map to SCL or SDA
}
#ifdef FIRMATA_SERIAL_FEATURE
serialFeature.handleCapability(pin);
#endif
Firmata.write(127);
}
Firmata.write(END_SYSEX);
break;
case PIN_STATE_QUERY:
if (argc 》 0) {
byte pin = argv[0];
Firmata.write(START_SYSEX);
Firmata.write(PIN_STATE_RESPONSE);
Firmata.write(pin);
if (pin 《 TOTAL_PINS) {
Firmata.write(Firmata.getPinMode(pin));
Firmata.write((byte)Firmata.getPinState(pin) & 0x7F);
if (Firmata.getPinState(pin) & 0xFF80) Firmata.write((byte)(Firmata.getPinState(pin) 》》 7) & 0x7F);
if (Firmata.getPinState(pin) & 0xC000) Firmata.write((byte)(Firmata.getPinState(pin) 》》 14) & 0x7F);
}
Firmata.write(END_SYSEX);
}
break;
case ANALOG_MAPPING_QUERY:
Firmata.write(START_SYSEX);
Firmata.write(ANALOG_MAPPING_RESPONSE);
for (byte pin = 0; pin 《 TOTAL_PINS; pin++) {
Firmata.write(IS_PIN_ANALOG(pin) ? PIN_TO_ANALOG(pin) : 127);
}
Firmata.write(END_SYSEX);
break;
case SERIAL_MESSAGE:
#ifdef FIRMATA_SERIAL_FEATURE
serialFeature.handleSysex(command, argc, argv);
#endif
break;
}
}
/*==============================================================================
* SETUP()
*============================================================================*/
void systemResetCallback()
{
isResetting = true;
// initialize a defalt state
// TODO: option to load config from EEPROM instead of default
#ifdef FIRMATA_SERIAL_FEATURE
serialFeature.reset();
#endif
if (isI2CEnabled) {
disableI2CPins();
}
for (byte i = 0; i 《 TOTAL_PORTS; i++) {
reportPINs[i] = false; // by default, reporting off
portConfigInputs[i] = 0; // until activated
previousPINs[i] = 0;
}
for (byte i = 0; i 《 TOTAL_PINS; i++) {
// pins with analog capability default to analog input
// otherwise, pins default to digital output
if (IS_PIN_ANALOG(i)) {
// turns off pullup, configures everything
setPinModeCallback(i, PIN_MODE_ANALOG);
} else if (IS_PIN_DIGITAL(i)) {
// sets the output to 0, configures portConfigInputs
setPinModeCallback(i, OUTPUT);
}
servoPinMap[i] = 255;
}
// by default, do not report any analog inputs
analogInputsToReport = 0;
detachedServoCount = 0;
servoCount = 0;
/* send digital inputs to set the initial state on the host computer,
* since once in the loop(), this firmware will only send on change */
/*
TODO: this can never execute, since no pins default to digital input
but it will be needed when/if we support EEPROM stored config
for (byte i=0; i 《 TOTAL_PORTS; i++) {
outputPort(i, readPort(i, portConfigInputs[i]), true);
}
*/
isResetting = false;
}
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(“Bluefruit52 Standard Firmata via BLEUART Example”);
Serial.println(“------------------------------------------------ ”);
// Config the peripheral connection with maximum bandwidth
// more SRAM required by SoftDevice
// Note: All config***() function must be called before begin()
Bluefruit.configPrphBandwidth(BANDWIDTH_MAX);
Bluefruit.begin();
Bluefruit.setName(“Bluefruit52”);
Bluefruit.setTxPower(4); // Check bluefruit.h for supported values
// try to go as fast as possible, could be rejected by some central, increase it if needed
// iOS won‘t negotitate and will mostly use 30ms
Bluefruit.Periph.setConnInterval(9, 24); // min = 9*1.25=11.25 ms, max = 23*1.25=30ms
// Configure and Start BLE Uart Service
// Firmata use several small write(1) --》 buffering TXD is required to run smoothly
// Enable buffering TXD
bleuart.begin();
bleuart.bufferTXD(true);
Firmata.setFirmwareVersion(FIRMATA_FIRMWARE_MAJOR_VERSION, FIRMATA_FIRMWARE_MINOR_VERSION);
Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
Firmata.attach(SET_PIN_MODE, setPinModeCallback);
Firmata.attach(SET_DIGITAL_PIN_VALUE, setPinValueCallback);
Firmata.attach(START_SYSEX, sysexCallback);
Firmata.attach(SYSTEM_RESET, systemResetCallback);
// use bleuart as transportation layer
Firmata.begin(bleuart);
// to use a port other than Serial, such as Serial1 on an Arduino Leonardo or Mega,
// Call begin(baud) on the alternate serial port and pass it to Firmata to begin like this:
// Serial1.begin(57600);
// Firmata.begin(Serial1);
// However do not do this if you are using SERIAL_MESSAGE
//Firmata.begin(57600);
//while (!Serial) {
// ; // wait for serial port to connect. Needed for ATmega32u4-based boards and Arduino 101
//}
systemResetCallback(); // reset to default config
// Set up and start advertising
startAdv();
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include bleuart 128-bit uuid
Bluefruit.Advertising.addService(bleuart);
// Secondary Scan Response packet (optional)
// Since there is no room for ’Name‘ in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don’t stop advertising after n seconds
}
/*==============================================================================
* LOOP()
*============================================================================*/
void loop()
{
// Skip if not connected and bleuart notification is not enabled
if ( !(Bluefruit.connected() && bleuart.notifyEnabled()) ) return;
byte pin, analogPin;
/* DIGITALREAD - as fast as possible, check for changes and output them to the
* FTDI buffer using Serial.print() */
checkDigitalInputs();
/* STREAMREAD - processing incoming messagse as soon as possible, while still
* checking digital inputs. */
while (Firmata.available())
Firmata.processInput();
// TODO - ensure that Stream buffer doesn‘t go over 60 bytes
currentMillis = millis();
if (currentMillis - previousMillis 》 samplingInterval) {
previousMillis += samplingInterval;
/* ANALOGREAD - do all analogReads() at the configured sampling interval */
for (pin = 0; pin 《 TOTAL_PINS; pin++) {
if (IS_PIN_ANALOG(pin) && Firmata.getPinMode(pin) == PIN_MODE_ANALOG) {
analogPin = PIN_TO_ANALOG(pin);
if (analogInputsToReport & (1 《《 analogPin)) {
Firmata.sendAnalog(analogPin, analogRead( ANALOG_TO_PIN(analogPin) ));
}
}
}
// report i2c data for all device with read continuous mode enabled
if (queryIndex 》 -1) {
for (byte i = 0; i 《 queryIndex + 1; i++) {
readAndReportData(query[i].addr, query[i].reg, query[i].bytes, query[i].stopTX);
}
}
}
#ifdef FIRMATA_SERIAL_FEATURE
serialFeature.update();
#endif
// flush TXD since we use bufferTXD()
bleuart.flushTXD();
}
中央BLEUART
本示例向您展示如何使用Feather nRF52/nRF52840作为中央与其他Bluefruit(nRF52或nRF51)外围设备进行通讯公开bleuart(又名NUS)服务。
客户端服务
由于中央角色访问外围设备上的GATT服务器,因此我们首先需要使用 BLEClientUart helper类声明客户端bleuart实例。如果还使用 BLEClientDis ,我们还可以方便地阅读设备信息。
下载:文件
复制代码
BLEClientDis clientDis;
BLEClientUart clientUart;
BLEClientDis clientDis;
BLEClientUart clientUart;
在我们可以配置客户端服务之前,必须先Bluefruit.begin()至少以1来调用集中模式下支持的并发连接数。由于在这种情况下我们不会将nRF52作为外围设备运行,因此我们将外围设备计数设置为0:
下载:文件
复制代码
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
Bluefruit.begin(0, 1); // Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
Bluefruit.begin(0, 1);
此后,客户端服务)必须通过调用其begin()函数进行初始化,然后您可以从帮助器类中设置要使用的任何回调:
下载:文件
复制代码
// Configure DIS client
clientDis.begin();
// Init BLE Central Uart Serivce
clientUart.begin();
clientUart.setRxCallback(bleuart_rx_callback); // Configure DIS client
clientDis.begin();
// Init BLE Central Uart Serivce
clientUart.begin();
clientUart.setRxCallback(bleuart_rx_callback);
扫描仪
让我们启动广告扫描仪以查找外围设备。
我们’将使用 setRxCallback()连接扫描结果回调。
只要扫描程序发现广告数据,它将被传递到此回调处理程序,我们可以检查在此处发布数据,并且仅连接到宣传bleuart服务的外围设备。
注意:如果外围设备有多个服务,并且bleuart不包含在广告包的UUID列表中,您可以选择使用其他检查,例如,匹配MAC地址,名称检查,使用“其他服务”等。
一旦找到我们希望与之通信的外围设备,请呼叫Bluefruit.Central.connect()与之建立连接:
下载:文件
复制代码
void setup()
{
// Other set up 。..。.
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Don‘t use active scan
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // // 0 = Don’t stop scanning after n seconds
}
/**
* Callback invoked when scanner pick up an advertising data
* @param report Structural advertising data
*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Check if advertising contain BleUart service
if ( Bluefruit.Scanner.checkReportForService(report, clientUart) )
{
Serial.print(“BLE UART service detected. Connecting 。.. ”);
// Connect to device with bleuart service in advertising
Bluefruit.Central.connect(report);
}
} void setup()
{
// Other set up 。..。.
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Don‘t use active scan
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // // 0 = Don’t stop scanning after n seconds
}
/**
* Callback invoked when scanner pick up an advertising data
* @param report Structural advertising data
*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Check if advertising contain BleUart service
if ( Bluefruit.Scanner.checkReportForService(report, clientUart) )
{
Serial.print(“BLE UART service detected. Connecting 。.. ”);
// Connect to device with bleuart service in advertising
Bluefruit.Central.connect(report);
}
}
中心角色
您通常需要设置中央模式设备的 connect回调,当建立连接/与外围设备断开连接。另外,您可以使用connected()轮询连接状态,但是回调有助于显着简化代码:
下载:文件
复制代码
// Callbacks for Central
Bluefruit.Central.setConnectCallback(connect_callback);
Bluefruit.Central.setDisconnectCallback(disconnect_callback); // Callbacks for Central
Bluefruit.Central.setConnectCallback(connect_callback);
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
在connect回调中,我们将尝试通过浏览外围设备的GATT表发现 bleuart服务。这将有助于确定特性的句柄值(例如TXD,RXD等)。这全部由BLEClientUart的.discover()完成。找到服务后,启用TXD特性的CCCD以允许外设发送数据,并且我们准备在设备之间来回发送数据:
下载:文件
复制代码
void connect_callback(uint16_t conn_handle)
{
Serial.println(“Connected”);
Serial.print(“Dicovering DIS 。.. ”);
if ( clientDis.discover(conn_handle) )
{
Serial.println(“Found it”);
char buffer[32+1];
// read and print out Manufacturer
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getManufacturer(buffer, sizeof(buffer)) )
{
Serial.print(“Manufacturer: ”);
Serial.println(buffer);
}
// read and print out Model Number
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getModel(buffer, sizeof(buffer)) )
{
Serial.print(“Model: ”);
Serial.println(buffer);
}
Serial.println();
}
Serial.print(“Discovering BLE Uart Service 。.. ”);
if ( clientUart.discover(conn_handle) )
{
Serial.println(“Found it”);
Serial.println(“Enable TXD‘s notify”);
clientUart.enableTXD();
Serial.println(“Ready to receive from peripheral”);
}else
{
Serial.println(“Found NONE”);
// disconect since we couldn’t find bleuart service
Bluefruit.Central.disconnect(conn_handle);
}
} void connect_callback(uint16_t conn_handle)
{
Serial.println(“Connected”);
Serial.print(“Dicovering DIS 。.. ”);
if ( clientDis.discover(conn_handle) )
{
Serial.println(“Found it”);
char buffer[32+1];
// read and print out Manufacturer
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getManufacturer(buffer, sizeof(buffer)) )
{
Serial.print(“Manufacturer: ”);
Serial.println(buffer);
}
// read and print out Model Number
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getModel(buffer, sizeof(buffer)) )
{
Serial.print(“Model: ”);
Serial.println(buffer);
}
Serial.println();
}
Serial.print(“Discovering BLE Uart Service 。.. ”);
if ( clientUart.discover(conn_handle) )
{
Serial.println(“Found it”);
Serial.println(“Enable TXD‘s notify”);
clientUart.enableTXD();
Serial.println(“Ready to receive from peripheral”);
}else
{
Serial.println(“Found NONE”);
// disconect since we couldn’t find bleuart service
Bluefruit.Central.disconnect(conn_handle);
}
}
完整示例代码
此示例的完整示例代码如下:
下载:Project Zip 或 central_bleuart.ino | 在Github上查看
复制代码
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/*
* This sketch demonstrate the central API()。 A additional bluefruit
* that has bleuart as peripheral is required for the demo.
*/
#include
BLEClientBas clientBas; // battery client
BLEClientDis clientDis; // device information client
BLEClientUart clientUart; // bleuart client
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(“Bluefruit52 Central BLEUART Example”);
Serial.println(“----------------------------------- ”);
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
// SRAM usage required by SoftDevice will increase dramatically with number of connections
Bluefruit.begin(0, 1);
Bluefruit.setName(“Bluefruit52 Central”);
// Configure Battyer client
clientBas.begin();
// Configure DIS client
clientDis.begin();
// Init BLE Central Uart Serivce
clientUart.begin();
clientUart.setRxCallback(bleuart_rx_callback);
// Increase Blink rate to different from PrPh advertising mode
Bluefruit.setConnLedInterval(250);
// Callbacks for Central
Bluefruit.Central.setConnectCallback(connect_callback);
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Don‘t use active scan
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // // 0 = Don’t stop scanning after n seconds
}
/**
* Callback invoked when scanner pick up an advertising data
* @param report Structural advertising data
*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Check if advertising contain BleUart service
if ( Bluefruit.Scanner.checkReportForService(report, clientUart) )
{
Serial.print(“BLE UART service detected. Connecting 。.. ”);
// Connect to device with bleuart service in advertising
Bluefruit.Central.connect(report);
}else
{
// For Softdevice v6: after received a report, scanner will be paused
// We need to call Scanner resume() to continue scanning
Bluefruit.Scanner.resume();
}
}
/**
* Callback invoked when an connection is established
* @param conn_handle
*/
void connect_callback(uint16_t conn_handle)
{
Serial.println(“Connected”);
Serial.print(“Dicovering Device Information 。.. ”);
if ( clientDis.discover(conn_handle) )
{
Serial.println(“Found it”);
char buffer[32+1];
// read and print out Manufacturer
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getManufacturer(buffer, sizeof(buffer)) )
{
Serial.print(“Manufacturer: ”);
Serial.println(buffer);
}
// read and print out Model Number
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getModel(buffer, sizeof(buffer)) )
{
Serial.print(“Model: ”);
Serial.println(buffer);
}
Serial.println();
}else
{
Serial.println(“Found NONE”);
}
Serial.print(“Dicovering Battery 。.. ”);
if ( clientBas.discover(conn_handle) )
{
Serial.println(“Found it”);
Serial.print(“Battery level: ”);
Serial.print(clientBas.read());
Serial.println(“%”);
}else
{
Serial.println(“Found NONE”);
}
Serial.print(“Discovering BLE Uart Service 。.. ”);
if ( clientUart.discover(conn_handle) )
{
Serial.println(“Found it”);
Serial.println(“Enable TXD‘s notify”);
clientUart.enableTXD();
Serial.println(“Ready to receive from peripheral”);
}else
{
Serial.println(“Found NONE”);
// disconnect since we couldn’t find bleuart service
Bluefruit.disconnect(conn_handle);
}
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle
* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.print(“Disconnected, reason = 0x”); Serial.println(reason, HEX);
}
/**
* Callback invoked when uart received data
* @param uart_svc Reference object to the service where the data
* arrived. In this example it is clientUart
*/
void bleuart_rx_callback(BLEClientUart& uart_svc)
{
Serial.print(“[RX]: ”);
while ( uart_svc.available() )
{
Serial.print( (char) uart_svc.read() );
}
Serial.println();
}
void loop()
{
if ( Bluefruit.Central.connected() )
{
// Not discovered yet
if ( clientUart.discovered() )
{
// Discovered means in working state
// Get Serial input and send to Peripheral
if ( Serial.available() )
{
delay(2); // delay a bit for all characters to arrive
char str[20+1] = { 0 };
Serial.readBytes(str, 20);
clientUart.print( str );
}
}
}
}
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/*
* This sketch demonstrate the central API()。 A additional bluefruit
* that has bleuart as peripheral is required for the demo.
*/
#include
BLEClientBas clientBas; // battery client
BLEClientDis clientDis; // device information client
BLEClientUart clientUart; // bleuart client
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(“Bluefruit52 Central BLEUART Example”);
Serial.println(“----------------------------------- ”);
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
// SRAM usage required by SoftDevice will increase dramatically with number of connections
Bluefruit.begin(0, 1);
Bluefruit.setName(“Bluefruit52 Central”);
// Configure Battyer client
clientBas.begin();
// Configure DIS client
clientDis.begin();
// Init BLE Central Uart Serivce
clientUart.begin();
clientUart.setRxCallback(bleuart_rx_callback);
// Increase Blink rate to different from PrPh advertising mode
Bluefruit.setConnLedInterval(250);
// Callbacks for Central
Bluefruit.Central.setConnectCallback(connect_callback);
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Don‘t use active scan
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // // 0 = Don’t stop scanning after n seconds
}
/**
* Callback invoked when scanner pick up an advertising data
* @param report Structural advertising data
*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Check if advertising contain BleUart service
if ( Bluefruit.Scanner.checkReportForService(report, clientUart) )
{
Serial.print(“BLE UART service detected. Connecting 。.. ”);
// Connect to device with bleuart service in advertising
Bluefruit.Central.connect(report);
}else
{
// For Softdevice v6: after received a report, scanner will be paused
// We need to call Scanner resume() to continue scanning
Bluefruit.Scanner.resume();
}
}
/**
* Callback invoked when an connection is established
* @param conn_handle
*/
void connect_callback(uint16_t conn_handle)
{
Serial.println(“Connected”);
Serial.print(“Dicovering Device Information 。.. ”);
if ( clientDis.discover(conn_handle) )
{
Serial.println(“Found it”);
char buffer[32+1];
// read and print out Manufacturer
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getManufacturer(buffer, sizeof(buffer)) )
{
Serial.print(“Manufacturer: ”);
Serial.println(buffer);
}
// read and print out Model Number
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getModel(buffer, sizeof(buffer)) )
{
Serial.print(“Model: ”);
Serial.println(buffer);
}
Serial.println();
}else
{
Serial.println(“Found NONE”);
}
Serial.print(“Dicovering Battery 。.. ”);
if ( clientBas.discover(conn_handle) )
{
Serial.println(“Found it”);
Serial.print(“Battery level: ”);
Serial.print(clientBas.read());
Serial.println(“%”);
}else
{
Serial.println(“Found NONE”);
}
Serial.print(“Discovering BLE Uart Service 。.. ”);
if ( clientUart.discover(conn_handle) )
{
Serial.println(“Found it”);
Serial.println(“Enable TXD‘s notify”);
clientUart.enableTXD();
Serial.println(“Ready to receive from peripheral”);
}else
{
Serial.println(“Found NONE”);
// disconnect since we couldn’t find bleuart service
Bluefruit.disconnect(conn_handle);
}
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle
* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.print(“Disconnected, reason = 0x”); Serial.println(reason, HEX);
}
/**
* Callback invoked when uart received data
* @param uart_svc Reference object to the service where the data
* arrived. In this example it is clientUart
*/
void bleuart_rx_callback(BLEClientUart& uart_svc)
{
Serial.print(“[RX]: ”);
while ( uart_svc.available() )
{
Serial.print( (char) uart_svc.read() );
}
Serial.println();
}
void loop()
{
if ( Bluefruit.Central.connected() )
{
// Not discovered yet
if ( clientUart.discovered() )
{
// Discovered means in working state
// Get Serial input and send to Peripheral
if ( Serial.available() )
{
delay(2); // delay a bit for all characters to arrive
char str[20+1] = { 0 };
Serial.readBytes(str, 20);
clientUart.print( str );
}
}
}
}
双重角色BLEUART
如果您不熟悉Central Role,那就是建议先查看“ Central BLEUART”示例,然后再继续。
此示例演示如何使用Feather nRF52/nRF52840连接到同时使用bleuart(AKA‘NUS’)服务的其他两个Bluefruit或BLE设备,该设备同时在外围设备和中央设备上运行。
此双重角色示例充当BLE桥接器位于中央和外围之间来回转发bleuart消息,如下图所示:
服务器和客户端服务设置
由于Bluefruit设备将同时充当中央和外围设备,因此我们需要声明bleuart h的服务器和客户端实例上级课程:
下载:文件
复制代码
// Peripheral uart service
BLEUart bleuart;
// Central uart client
BLEClientUart clientUart; // Peripheral uart service
BLEUart bleuart;
// Central uart client
BLEClientUart clientUart;
在我们可以配置客户端服务之前,对于外围设备和中央设备的并发连接数,必须使用至少1调用Bluefruit.begin()模式:
下载:文件
复制代码
// Initialize Bluefruit with max concurrent connections as Peripheral = 1, Central = 1
Bluefruit.begin(1, 1); // Initialize Bluefruit with max concurrent connections as Peripheral = 1, Central = 1
Bluefruit.begin(1, 1);
此后,必须通过调用客户端服务的begin()函数以及随后希望连接的任何回调来初始化客户端服务:
下载:文件
复制代码
// Configure and Start BLE Uart Service
bleuart.begin();
bleuart.setRxCallback(prph_bleuart_rx_callback);
// Init BLE Central Uart Serivce
clientUart.begin();
clientUart.setRxCallback(cent_bleuart_rx_callback); // Configure and Start BLE Uart Service
bleuart.begin();
bleuart.setRxCallback(prph_bleuart_rx_callback);
// Init BLE Central Uart Serivce
clientUart.begin();
clientUart.setRxCallback(cent_bleuart_rx_callback);
然后我们准备使用回调将数据从中央转发到外围设备,反之亦然:
下载:文件
复制代码
void cent_bleuart_rx_callback(BLEClientUart& cent_uart)
{
char str[20+1] = { 0 };
cent_uart.read(str, 20);
Serial.print(“[Cent] RX: ”);
Serial.println(str);
if ( bleuart.notifyEnabled() )
{
// Forward data from our peripheral to Mobile
bleuart.print( str );
}else
{
// response with no prph message
clientUart.println(“[Cent] Peripheral role not connected”);
}
}
void prph_bleuart_rx_callback(void)
{
// Forward data from Mobile to our peripheral
char str[20+1] = { 0 };
bleuart.read(str, 20);
Serial.print(“[Prph] RX: ”);
Serial.println(str);
if ( clientUart.discovered() )
{
clientUart.print(str);
}else
{
bleuart.println(“[Prph] Central role not connected”);
}
}
void cent_bleuart_rx_callback(BLEClientUart& cent_uart)
{
char str[20+1] = { 0 };
cent_uart.read(str, 20);
Serial.print(“[Cent] RX: ”);
Serial.println(str);
if ( bleuart.notifyEnabled() )
{
// Forward data from our peripheral to Mobile
bleuart.print( str );
}else
{
// response with no prph message
clientUart.println(“[Cent] Peripheral role not connected”);
}
}
void prph_bleuart_rx_callback(void)
{
// Forward data from Mobile to our peripheral
char str[20+1] = { 0 };
bleuart.read(str, 20);
Serial.print(“[Prph] RX: ”);
Serial.println(str);
if ( clientUart.discovered() )
{
clientUart.print(str);
}else
{
bleuart.println(“[Prph] Central role not connected”);
}
}
》
外围角色
第一件事对于代码的外围部分,要做的是设置 connect回调,当与中心建立/断开连接时会触发该回调。另外,您可以使用connected()轮询连接状态,但是回调有助于显着简化代码:
下载:文件
复制代码
// Callbacks for Peripheral
Bluefruit.setConnectCallback(prph_connect_callback);
Bluefruit.setDisconnectCallback(prph_disconnect_callback); // Callbacks for Peripheral
Bluefruit.setConnectCallback(prph_connect_callback);
Bluefruit.setDisconnectCallback(prph_disconnect_callback);
中心角色
接下来,我们设置中央模式 connect回调,该模式在建立/断开连接时触发使用外围设备:
下载:文件
复制代码
// Callbacks for Central
Bluefruit.Central.setConnectCallback(cent_connect_callback);
Bluefruit.Central.setDisconnectCallback(cent_disconnect_callback); // Callbacks for Central
Bluefruit.Central.setConnectCallback(cent_connect_callback);
Bluefruit.Central.setDisconnectCallback(cent_disconnect_callback);
广告和扫描器
可以同时启动扫描器和广告,以便我们可以发现其他BLE设备并使其被发现。对于扫描器,我们使用的过滤器仅在对等设备的广告数据中找到特定的UUID时才触发回调:
下载:文件
复制代码
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Filter only accept bleuart service
* - Don‘t use active scan
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.filterUuid(bleuart.uuid);
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // 0 = Don’t stop scanning after n seconds
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include bleuart 128-bit uuid
Bluefruit.Advertising.addService(bleuart);
// Secondary Scan Response packet (optional)
// Since there is no room for ‘Name’ in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don‘t stop advertising after n seconds /* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Filter only accept bleuart service
* - Don’t use active scan
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.filterUuid(bleuart.uuid);
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // 0 = Don‘t stop scanning after n seconds
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include bleuart 128-bit uuid
Bluefruit.Advertising.addService(bleuart);
// Secondary Scan Response packet (optional)
// Since there is no room for ’Name‘ in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don’t stop advertising after n seconds
完整示例代码
此示例的完整示例代码如下:
下载:Project Zip 或 dual_bleuart.ino | 在Github上查看
复制代码
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/*
* This sketch demonstrate how to run both Central and Peripheral roles
* at the same time. It will act as a relay between an central (mobile)
* to another peripheral using bleuart service.
*
* Mobile 《--》 DualRole 《--》 peripheral Ble Uart
*/
#include
// OTA DFU service
BLEDfu bledfu;
// Peripheral uart service
BLEUart bleuart;
// Central uart client
BLEClientUart clientUart;
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(“Bluefruit52 Dual Role BLEUART Example”);
Serial.println(“------------------------------------- ”);
// Initialize Bluefruit with max concurrent connections as Peripheral = 1, Central = 1
// SRAM usage required by SoftDevice will increase with number of connections
Bluefruit.begin(1, 1);
Bluefruit.setTxPower(4); // Check bluefruit.h for supported values
Bluefruit.setName(“Bluefruit52 duo”);
// Callbacks for Peripheral
Bluefruit.Periph.setConnectCallback(prph_connect_callback);
Bluefruit.Periph.setDisconnectCallback(prph_disconnect_callback);
// Callbacks for Central
Bluefruit.Central.setConnectCallback(cent_connect_callback);
Bluefruit.Central.setDisconnectCallback(cent_disconnect_callback);
// To be consistent OTA DFU should be added first if it exists
bledfu.begin();
// Configure and Start BLE Uart Service
bleuart.begin();
bleuart.setRxCallback(prph_bleuart_rx_callback);
// Init BLE Central Uart Serivce
clientUart.begin();
clientUart.setRxCallback(cent_bleuart_rx_callback);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Filter only accept bleuart service
* - Don‘t use active scan
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.filterUuid(bleuart.uuid);
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // 0 = Don’t stop scanning after n seconds
// Set up and start advertising
startAdv();
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include bleuart 128-bit uuid
Bluefruit.Advertising.addService(bleuart);
// Secondary Scan Response packet (optional)
// Since there is no room for ‘Name’ in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don‘t stop advertising after n seconds
}
void loop()
{
// do nothing, all the work is done in callback
}
/*------------------------------------------------------------------*/
/* Peripheral
*------------------------------------------------------------------*/
void prph_connect_callback(uint16_t conn_handle)
{
// Get the reference to current connection
BLEConnection* connection = Bluefruit.Connection(conn_handle);
char peer_name[32] = { 0 };
connection-》getPeerName(peer_name, sizeof(peer_name));
Serial.print(“[Prph] Connected to ”);
Serial.println(peer_name);
}
void prph_disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.println();
Serial.println(“[Prph] Disconnected”);
}
void prph_bleuart_rx_callback(uint16_t conn_handle)
{
(void) conn_handle;
// Forward data from Mobile to our peripheral
char str[20+1] = { 0 };
bleuart.read(str, 20);
Serial.print(“[Prph] RX: ”);
Serial.println(str);
if ( clientUart.discovered() )
{
clientUart.print(str);
}else
{
bleuart.println(“[Prph] Central role not connected”);
}
}
/*------------------------------------------------------------------*/
/* Central
*------------------------------------------------------------------*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Since we configure the scanner with filterUuid()
// Scan callback only invoked for device with bleuart service advertised
// Connect to the device with bleuart service in advertising packet
Bluefruit.Central.connect(report);
}
void cent_connect_callback(uint16_t conn_handle)
{
// Get the reference to current connection
BLEConnection* connection = Bluefruit.Connection(conn_handle);
char peer_name[32] = { 0 };
connection-》getPeerName(peer_name, sizeof(peer_name));
Serial.print(“[Cent] Connected to ”);
Serial.println(peer_name);;
if ( clientUart.discover(conn_handle) )
{
// Enable TXD’s notify
clientUart.enableTXD();
}else
{
// disconnect since we couldn‘t find bleuart service
Bluefruit.disconnect(conn_handle);
}
}
void cent_disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.println(“[Cent] Disconnected”);
}
/**
* Callback invoked when uart received data
* @param cent_uart Reference object to the service where the data
* arrived. In this example it is clientUart
*/
void cent_bleuart_rx_callback(BLEClientUart& cent_uart)
{
char str[20+1] = { 0 };
cent_uart.read(str, 20);
Serial.print(“[Cent] RX: ”);
Serial.println(str);
if ( bleuart.notifyEnabled() )
{
// Forward data from our peripheral to Mobile
bleuart.print( str );
}else
{
// response with no prph message
clientUart.println(“[Cent] Peripheral role not connected”);
}
}
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/*
* This sketch demonstrate how to run both Central and Peripheral roles
* at the same time. It will act as a relay between an central (mobile)
* to another peripheral using bleuart service.
*
* Mobile 《--》 DualRole 《--》 peripheral Ble Uart
*/
#include
// OTA DFU service
BLEDfu bledfu;
// Peripheral uart service
BLEUart bleuart;
// Central uart client
BLEClientUart clientUart;
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(“Bluefruit52 Dual Role BLEUART Example”);
Serial.println(“------------------------------------- ”);
// Initialize Bluefruit with max concurrent connections as Peripheral = 1, Central = 1
// SRAM usage required by SoftDevice will increase with number of connections
Bluefruit.begin(1, 1);
Bluefruit.setTxPower(4); // Check bluefruit.h for supported values
Bluefruit.setName(“Bluefruit52 duo”);
// Callbacks for Peripheral
Bluefruit.Periph.setConnectCallback(prph_connect_callback);
Bluefruit.Periph.setDisconnectCallback(prph_disconnect_callback);
// Callbacks for Central
Bluefruit.Central.setConnectCallback(cent_connect_callback);
Bluefruit.Central.setDisconnectCallback(cent_disconnect_callback);
// To be consistent OTA DFU should be added first if it exists
bledfu.begin();
// Configure and Start BLE Uart Service
bleuart.begin();
bleuart.setRxCallback(prph_bleuart_rx_callback);
// Init BLE Central Uart Serivce
clientUart.begin();
clientUart.setRxCallback(cent_bleuart_rx_callback);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Filter only accept bleuart service
* - Don’t use active scan
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.filterUuid(bleuart.uuid);
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // 0 = Don‘t stop scanning after n seconds
// Set up and start advertising
startAdv();
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include bleuart 128-bit uuid
Bluefruit.Advertising.addService(bleuart);
// Secondary Scan Response packet (optional)
// Since there is no room for ’Name‘ in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don’t stop advertising after n seconds
}
void loop()
{
// do nothing, all the work is done in callback
}
/*------------------------------------------------------------------*/
/* Peripheral
*------------------------------------------------------------------*/
void prph_connect_callback(uint16_t conn_handle)
{
// Get the reference to current connection
BLEConnection* connection = Bluefruit.Connection(conn_handle);
char peer_name[32] = { 0 };
connection-》getPeerName(peer_name, sizeof(peer_name));
Serial.print(“[Prph] Connected to ”);
Serial.println(peer_name);
}
void prph_disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.println();
Serial.println(“[Prph] Disconnected”);
}
void prph_bleuart_rx_callback(uint16_t conn_handle)
{
(void) conn_handle;
// Forward data from Mobile to our peripheral
char str[20+1] = { 0 };
bleuart.read(str, 20);
Serial.print(“[Prph] RX: ”);
Serial.println(str);
if ( clientUart.discovered() )
{
clientUart.print(str);
}else
{
bleuart.println(“[Prph] Central role not connected”);
}
}
/*------------------------------------------------------------------*/
/* Central
*------------------------------------------------------------------*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Since we configure the scanner with filterUuid()
// Scan callback only invoked for device with bleuart service advertised
// Connect to the device with bleuart service in advertising packet
Bluefruit.Central.connect(report);
}
void cent_connect_callback(uint16_t conn_handle)
{
// Get the reference to current connection
BLEConnection* connection = Bluefruit.Connection(conn_handle);
char peer_name[32] = { 0 };
connection-》getPeerName(peer_name, sizeof(peer_name));
Serial.print(“[Cent] Connected to ”);
Serial.println(peer_name);;
if ( clientUart.discover(conn_handle) )
{
// Enable TXD‘s notify
clientUart.enableTXD();
}else
{
// disconnect since we couldn’t find bleuart service
Bluefruit.disconnect(conn_handle);
}
}
void cent_disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.println(“[Cent] Disconnected”);
}
/**
* Callback invoked when uart received data
* @param cent_uart Reference object to the service where the data
* arrived. In this example it is clientUart
*/
void cent_bleuart_rx_callback(BLEClientUart& cent_uart)
{
char str[20+1] = { 0 };
cent_uart.read(str, 20);
Serial.print(“[Cent] RX: ”);
Serial.println(str);
if ( bleuart.notifyEnabled() )
{
// Forward data from our peripheral to Mobile
bleuart.print( str );
}else
{
// response with no prph message
clientUart.println(“[Cent] Peripheral role not connected”);
}
}
自定义:中央HRM
BLEClientService和BLEClientCharacteristic类可用于在客户端(通常是中央)使用以下特性实现任何自定义或正式采用的具有特征的BLE服务:一组基本属性和回调处理程序。
以下示例显示了如何使用这些类来实现Bluetooth SIG定义的心率监视器服务。要运行此示例,您将需要一个额外的nRF52运行外围HRM草图
HRM服务定义
UUID :0x180D
只有第一个特征是强制性的,但我们将还实现可选的身体传感器位置特征。在此示例中,将不使用心率控制点来简化操作。
实现HRM服务和特征
可以使用以下代码实现核心服务和前两个特征:
首先,定义BLEService和将在您的项目中使用的BLE特性变量:
下载:文件
复制代码
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37 (Mandatory)
* Body Sensor Location Char: 0x2A38 (Optional)
*/
BLEClientService hrms(UUID16_SVC_HEART_RATE);
BLEClientCharacteristic hrmc(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLEClientCharacteristic bslc(UUID16_CHR_BODY_SENSOR_LOCATION); /* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37 (Mandatory)
* Body Sensor Location Char: 0x2A38 (Optional)
*/
BLEClientService hrms(UUID16_SVC_HEART_RATE);
BLEClientCharacteristic hrmc(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLEClientCharacteristic bslc(UUID16_CHR_BODY_SENSOR_LOCATION);
然后,您需要通过调用它们的begin()来初始化这些变量。
下载:文件
复制代码
// Initialize HRM client
hrms.begin();
// Initialize client characteristics of HRM.
// Note: Client Char will be added to the last service that is begin()ed.
bslc.begin();
// set up callback for receiving measurement
hrmc.setNotifyCallback(hrm_notify_callback);
hrmc.begin(); // Initialize HRM client
hrms.begin();
// Initialize client characteristics of HRM.
// Note: Client Char will be added to the last service that is begin()ed.
bslc.begin();
// set up callback for receiving measurement
hrmc.setNotifyCallback(hrm_notify_callback);
hrmc.begin();
客户服务+特征代码分析
1。首先要做的是在BLEClientService(上面的 hrms )上调用 .begin()。由于UUID是在草图顶部的对象声明中设置的,因此与BLEClientService实例通常没有其他关系。
您必须在添加任何BLEClientCharacteristics之前在BLEClientService上调用.begin()。任何BLEClientCharacteristic都将自动添加到“ begin()”开始的最后一个BLEClientService中!
2。由于心率测量特性( clientMeasurement 以上)是可通知的。您需要为此设置回调
‘hrmc.setNotifyCallback(hrm_notify_callback);’这设置了当我们从外围设备收到通知消息时将触发的回调。这是处理可通知特征的必要条件,因为回调允许我们及时响应消息。对于此示例来说,只是简单地将值打印到序列号上。
‘hrmc.begin();’一旦设置了所有属性,就必须调用 .begin()会将特征定义添加到最后一个‘.begin()ed’的BLEClientService中。
对于不支持通知的特征的注释,例如人体传感器位置,我们可以简单地使用.read()来获取其值。
3。接下来,我们可以开始扫描并连接到宣传HRM服务的外围设备。连接后,我们需要遍历外围GATT表以找出Gatt句柄以引起我们的兴趣。在此示例中,它们是 hrms , hrmc 和 bslc的句柄。此查找感兴趣的服务/特性的过程称为发现。
注意:必须使用Gatt句柄(或仅句柄)来执行所有操作,例如读取,写入,启用通知。要求必须先发现客户机特征,然后才能对其进行任何操作。
在发现其特征之前,应先发现服务。这可以通过调用hrms.discover(conn_handle)来完成。其中conn_handle是连接ID,即我们要发现的外围设备,因为Bluefruit nRF52可以同时连接到多个外围设备。如果找到该服务,则该函数将返回true,否则返回false。
下载:文件
复制代码
// Connect Callback Part 1
void connect_callback(uint16_t conn_handle)
{
Serial.println(“Connected”);
Serial.print(“Discovering HRM Service 。.. ”);
// If HRM is not found, disconnect and return
if ( !hrms.discover(conn_handle) )
{
Serial.println(“Found NONE”);
// disconect since we couldn‘t find HRM service
Bluefruit.Central.disconnect(conn_handle);
return;
}
// Once HRM service is found, we continue to discover its characteristic
Serial.println(“Found it”);
。..。..。..。..。
} // Connect Callback Part 1
void connect_callback(uint16_t conn_handle)
{
Serial.println(“Connected”);
Serial.print(“Discovering HRM Service 。.. ”);
// If HRM is not found, disconnect and return
if ( !hrms.discover(conn_handle) )
{
Serial.println(“Found NONE”);
// disconect since we couldn’t find HRM service
Bluefruit.Central.disconnect(conn_handle);
return;
}
// Once HRM service is found, we continue to discover its characteristic
Serial.println(“Found it”);
。..。..。..。..。
}
4。之后,我们通过调用.discover()继续发现服务中所有感兴趣的特征。如果找到特征,则该函数返回true,否则返回false。您也可以使用.discovered()功能进行检查。服务可以包含更多特征,但我们不需要发现所有特征,只需发现我们想要与之交互的特征即可。
高级:或者,您可以发现所有感兴趣的特征使用Bluefruit.Discovery.discoverCharacteristic()在函数调用中的服务的特征(在示例中未使用)。该API最多可以包含5个特征,如果需要更多特征,也可以使用传递特征数组的变体。该函数将返回找到的特征的数量。
注意:当上述API发现特征时,所有必需的元数据,例如句柄,属性(读,写,通知等。..),cccd句柄将自动更新。然后,只要其属性支持(如操作),就可以在其上使用BLECLientCharacteristicAPI,例如read(),write(),enableNotify()。
=“ i class =” fa fa- cloud-download“》 下载:文件
复制代码
// Connect Callback Part 2
void connect_callback(uint16_t conn_handle)
{
Serial.print(“Discovering Measurement characteristic 。.. ”);
if ( !hrmc.discover() )
{
// Measurement chr is mandatory, if it is not found (valid), then disconnect
Serial.println(“not found !!!”);
Serial.println(“Measurement characteristic is mandatory but not found”);
Bluefruit.Central.disconnect(conn_handle);
return;
}
Serial.println(“Found it”);
// Measurement is found, continue to look for option Body Sensor Location
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Body Sensor Location is optional, print out the location in text if present
Serial.print(“Discovering Body Sensor Location characteristic 。.. ”);
if ( bslc.discover() )
{
Serial.println(“Found it”);
// Body sensor location value is 8 bit
const char* body_str[] = { “Other”, “Chest”, “Wrist”, “Finger”, “Hand”, “Ear Lobe”, “Foot” };
// Read 8-bit BSLC value from peripheral
uint8_t loc_value = bslc.read8();
Serial.print(“Body Location Sensor: ”);
Serial.println(body_str[loc_value]);
}else
{
Serial.println(“Found NONE”);
}
。..。..。..。..。..
} // Connect Callback Part 2
void connect_callback(uint16_t conn_handle)
{
Serial.print(“Discovering Measurement characteristic 。.. ”);
if ( !hrmc.discover() )
{
// Measurement chr is mandatory, if it is not found (valid), then disconnect
Serial.println(“not found !!!”);
Serial.println(“Measurement characteristic is mandatory but not found”);
Bluefruit.Central.disconnect(conn_handle);
return;
}
Serial.println(“Found it”);
// Measurement is found, continue to look for option Body Sensor Location
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Body Sensor Location is optional, print out the location in text if present
Serial.print(“Discovering Body Sensor Location characteristic 。.. ”);
if ( bslc.discover() )
{
Serial.println(“Found it”);
// Body sensor location value is 8 bit
const char* body_str[] = { “Other”, “Chest”, “Wrist”, “Finger”, “Hand”, “Ear Lobe”, “Foot” };
// Read 8-bit BSLC value from peripheral
uint8_t loc_value = bslc.read8();
Serial.print(“Body Location Sensor: ”);
Serial.println(body_str[loc_value]);
}else
{
Serial.println(“Found NONE”);
}
。..。..。..。..。..
}
5。一旦发现hrmc,您应该通过调用hrmc.enableNotify()启用其通知。如果成功(返回true),外围设备现在可以使用通知消息将数据发送给我们。
Download:file
复制代码
// Connect Callback Part 3
void connect_callback(uint16_t conn_handle)
{
。..。..。
// Reaching here means we are ready to go, let‘s enable notification on measurement chr
if ( hrmc.enableNotify() )
{
Serial.println(“Ready to receive HRM Measurement value”);
}else
{
Serial.println(“Couldn’t enable notify for HRM Measurement. Increase DEBUG LEVEL for troubleshooting”);
}
} // Connect Callback Part 3
void connect_callback(uint16_t conn_handle)
{
。..。..。
// Reaching here means we are ready to go, let‘s enable notification on measurement chr
if ( hrmc.enableNotify() )
{
Serial.println(“Ready to receive HRM Measurement value”);
}else
{
Serial.println(“Couldn’t enable notify for HRM Measurement. Increase DEBUG LEVEL for troubleshooting”);
}
}
下载:文件
复制代码
/**
* Hooked callback that triggered when a measurement value is sent from peripheral
* @param chr Pointer to client characteristic that even occurred,
* in this example it should be hrmc
* @param data Pointer to received data
* @param len Length of received data
*/
void hrm_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
{
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Measurement contains of control byte0 and measurement (8 or 16 bit) + optional field
// if byte0‘s bit0 is 0 --》 measurement is 8 bit, otherwise 16 bit.
Serial.print(“HRM Measurement: ”);
if ( data[0] & bit(0) )
{
uint16_t value;
memcpy(&value, data+1, 2);
Serial.println(value);
}
else
{
Serial.println(data[1]);
}
} /**
* Hooked callback that triggered when a measurement value is sent from peripheral
* @param chr Pointer to client characteristic that even occurred,
* in this example it should be hrmc
* @param data Pointer to received data
* @param len Length of received data
*/
void hrm_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
{
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Measurement contains of control byte0 and measurement (8 or 16 bit) + optional field
// if byte0’s bit0 is 0 --》 measurement is 8 bit, otherwise 16 bit.
Serial.print(“HRM Measurement: ”);
if ( data[0] & bit(0) )
{
uint16_t value;
memcpy(&value, data+1, 2);
Serial.println(value);
}
else
{
Serial.println(data[1]);
}
}
完整示例代码
此示例的完整示例代码如下:
下载:Project Zip 或 custom_hrm.ino | 在Github上查看
复制代码
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37
* Body Sensor Location Char: 0x2A38
*/
BLEService hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);
BLEDis bledis; // DIS (Device Information Service) helper class instance
BLEBas blebas; // BAS (Battery Service) helper class instance
uint8_t bps = 0;
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(“Bluefruit52 HRM Example”);
Serial.println(“----------------------- ”);
// Initialise the Bluefruit module
Serial.println(“Initialise the Bluefruit nRF52 module”);
Bluefruit.begin();
// Set the advertised device name (keep it short!)
Serial.println(“Setting Device Name to ‘Feather52 HRM’”);
Bluefruit.setName(“Bluefruit52 HRM”);
// Set the connect/disconnect callback handlers
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
// Configure and Start the Device Information Service
Serial.println(“Configuring the Device Information Service”);
bledis.setManufacturer(“Adafruit Industries”);
bledis.setModel(“Bluefruit Feather52”);
bledis.begin();
// Start the BLE Battery Service and set it to 100%
Serial.println(“Configuring the Battery Service”);
blebas.begin();
blebas.write(100);
// Setup the Heart Rate Monitor service using
// BLEService and BLECharacteristic classes
Serial.println(“Configuring the Heart Rate Monitor Service”);
setupHRM();
// Setup the advertising packet(s)
Serial.println(“Setting up the advertising payload(s)”);
startAdv();
Serial.println(“Ready Player One!!!”);
Serial.println(“ Advertising”);
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include HRM Service UUID
Bluefruit.Advertising.addService(hrms);
// Include Name
Bluefruit.Advertising.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don‘t stop advertising after n seconds
}
void setupHRM(void)
{
// Configure the Heart Rate Monitor service
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
// Supported Characteristics:
// Name UUID Requirement Properties
// ---------------------------- ------ ----------- ----------
// Heart Rate Measurement 0x2A37 Mandatory Notify
// Body Sensor Location 0x2A38 Optional Read
// Heart Rate Control Point 0x2A39 Conditional Write 《-- Not used here
hrms.begin();
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.。 Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was ’begin()‘ed!
// Configure the Heart Rate Measurement characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Properties = Notify
// Min Len = 1
// Max Len = 8
// B0 = UINT8 - Flag (MANDATORY)
// b5:7 = Reserved
// b4 = RR-Internal (0 = Not present, 1 = Present)
// b3 = Energy expended status (0 = Not present, 1 = Present)
// b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
// b0 = Value format (0 = UINT8, 1 = UINT16)
// B1 = UINT8 - 8-bit heart rate measurement value in BPM
// B2:3 = UINT16 - 16-bit heart rate measurement value in BPM
// B4:5 = UINT16 - Energy expended in joules
// B6:7 = UINT16 - RR Internal (1/1024 second resolution)
hrmc.setProperties(CHR_PROPS_NOTIFY);
hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
hrmc.setFixedLen(2);
hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
hrmc.begin();
uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
hrmc.write(hrmdata, 2);
// Configure the Body Sensor Location characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Properties = Read
// Min Len = 1
// Max Len = 1
// B0 = UINT8 - Body Sensor Location
// 0 = Other
// 1 = Chest
// 2 = Wrist
// 3 = Finger
// 4 = Hand
// 5 = Ear Lobe
// 6 = Foot
// 7:255 = Reserved
bslc.setProperties(CHR_PROPS_READ);
bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
bslc.setFixedLen(1);
bslc.begin();
bslc.write8(2); // Set the characteristic to ’Wrist‘ (2)
}
void connect_callback(uint16_t conn_handle)
{
// Get the reference to current connection
BLEConnection* connection = Bluefruit.Connection(conn_handle);
char central_name[32] = { 0 };
connection-》getPeerName(central_name, sizeof(central_name));
Serial.print(“Connected to ”);
Serial.println(central_name);
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle connection where this event happens
* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.print(“Disconnected, reason = 0x”); Serial.println(reason, HEX);
Serial.println(“Advertising!”);
}
void cccd_callback(uint16_t conn_hdl, BLECharacteristic* chr, uint16_t cccd_value)
{
// Display the raw request packet
Serial.print(“CCCD Updated: ”);
//Serial.printBuffer(request-》data, request-》len);
Serial.print(cccd_value);
Serial.println(“”);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
if (chr-》uuid == hrmc.uuid) {
if (chr-》notifyEnabled(conn_hdl)) {
Serial.println(“Heart Rate Measurement ’Notify‘ enabled”);
} else {
Serial.println(“Heart Rate Measurement ’Notify‘ disabled”);
}
}
}
void loop()
{
digitalToggle(LED_RED);
if ( Bluefruit.connected() ) {
uint8_t hrmdata[2] = { 0b00000110, bps++ }; // Sensor connected, increment BPS value
// Note: We use .notify instead of .write!
// If it is connected but CCCD is not enabled
// The characteristic’s value is still updated although notification is not sent
if ( hrmc.notify(hrmdata, sizeof(hrmdata)) ){
Serial.print(“Heart Rate Measurement updated to: ”); Serial.println(bps);
}else{
Serial.println(“ERROR: Notify not set in the CCCD or not connected!”);
}
}
// Only send update once per second
delay(1000);
}
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37
* Body Sensor Location Char: 0x2A38
*/
BLEService hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);
BLEDis bledis; // DIS (Device Information Service) helper class instance
BLEBas blebas; // BAS (Battery Service) helper class instance
uint8_t bps = 0;
void setup()
{
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
Serial.println(“Bluefruit52 HRM Example”);
Serial.println(“----------------------- ”);
// Initialise the Bluefruit module
Serial.println(“Initialise the Bluefruit nRF52 module”);
Bluefruit.begin();
// Set the advertised device name (keep it short!)
Serial.println(“Setting Device Name to ‘Feather52 HRM’”);
Bluefruit.setName(“Bluefruit52 HRM”);
// Set the connect/disconnect callback handlers
Bluefruit.Periph.setConnectCallback(connect_callback);
Bluefruit.Periph.setDisconnectCallback(disconnect_callback);
// Configure and Start the Device Information Service
Serial.println(“Configuring the Device Information Service”);
bledis.setManufacturer(“Adafruit Industries”);
bledis.setModel(“Bluefruit Feather52”);
bledis.begin();
// Start the BLE Battery Service and set it to 100%
Serial.println(“Configuring the Battery Service”);
blebas.begin();
blebas.write(100);
// Setup the Heart Rate Monitor service using
// BLEService and BLECharacteristic classes
Serial.println(“Configuring the Heart Rate Monitor Service”);
setupHRM();
// Setup the advertising packet(s)
Serial.println(“Setting up the advertising payload(s)”);
startAdv();
Serial.println(“Ready Player One!!!”);
Serial.println(“ Advertising”);
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include HRM Service UUID
Bluefruit.Advertising.addService(hrms);
// Include Name
Bluefruit.Advertising.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don‘t stop advertising after n seconds
}
void setupHRM(void)
{
// Configure the Heart Rate Monitor service
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
// Supported Characteristics:
// Name UUID Requirement Properties
// ---------------------------- ------ ----------- ----------
// Heart Rate Measurement 0x2A37 Mandatory Notify
// Body Sensor Location 0x2A38 Optional Read
// Heart Rate Control Point 0x2A39 Conditional Write 《-- Not used here
hrms.begin();
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.。 Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was ’begin()‘ed!
// Configure the Heart Rate Measurement characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Properties = Notify
// Min Len = 1
// Max Len = 8
// B0 = UINT8 - Flag (MANDATORY)
// b5:7 = Reserved
// b4 = RR-Internal (0 = Not present, 1 = Present)
// b3 = Energy expended status (0 = Not present, 1 = Present)
// b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
// b0 = Value format (0 = UINT8, 1 = UINT16)
// B1 = UINT8 - 8-bit heart rate measurement value in BPM
// B2:3 = UINT16 - 16-bit heart rate measurement value in BPM
// B4:5 = UINT16 - Energy expended in joules
// B6:7 = UINT16 - RR Internal (1/1024 second resolution)
hrmc.setProperties(CHR_PROPS_NOTIFY);
hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
hrmc.setFixedLen(2);
hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
hrmc.begin();
uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
hrmc.write(hrmdata, 2);
// Configure the Body Sensor Location characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Properties = Read
// Min Len = 1
// Max Len = 1
// B0 = UINT8 - Body Sensor Location
// 0 = Other
// 1 = Chest
// 2 = Wrist
// 3 = Finger
// 4 = Hand
// 5 = Ear Lobe
// 6 = Foot
// 7:255 = Reserved
bslc.setProperties(CHR_PROPS_READ);
bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
bslc.setFixedLen(1);
bslc.begin();
bslc.write8(2); // Set the characteristic to ’Wrist‘ (2)
}
void connect_callback(uint16_t conn_handle)
{
// Get the reference to current connection
BLEConnection* connection = Bluefruit.Connection(conn_handle);
char central_name[32] = { 0 };
connection-》getPeerName(central_name, sizeof(central_name));
Serial.print(“Connected to ”);
Serial.println(central_name);
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle connection where this event happens
* @param reason is a BLE_HCI_STATUS_CODE which can be found in ble_hci.h
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.print(“Disconnected, reason = 0x”); Serial.println(reason, HEX);
Serial.println(“Advertising!”);
}
void cccd_callback(uint16_t conn_hdl, BLECharacteristic* chr, uint16_t cccd_value)
{
// Display the raw request packet
Serial.print(“CCCD Updated: ”);
//Serial.printBuffer(request-》data, request-》len);
Serial.print(cccd_value);
Serial.println(“”);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
if (chr-》uuid == hrmc.uuid) {
if (chr-》notifyEnabled(conn_hdl)) {
Serial.println(“Heart Rate Measurement ’Notify‘ enabled”);
} else {
Serial.println(“Heart Rate Measurement ’Notify‘ disabled”);
}
}
}
void loop()
{
digitalToggle(LED_RED);
if ( Bluefruit.connected() ) {
uint8_t hrmdata[2] = { 0b00000110, bps++ }; // Sensor connected, increment BPS value
// Note: We use .notify instead of .write!
// If it is connected but CCCD is not enabled
// The characteristic’s value is still updated although notification is not sent
if ( hrmc.notify(hrmdata, sizeof(hrmdata)) ){
Serial.print(“Heart Rate Measurement updated to: ”); Serial.println(bps);
}else{
Serial.println(“ERROR: Notify not set in the CCCD or not connected!”);
}
}
// Only send update once per second
delay(1000);
}
Arduino Bluefruit nRF52 API
Adafruit nRF52内核定义了许多自定义类,旨在简化在项目中使用BLE的工作。
下面列出了关键类,并在本学习指南的其他地方对其进行了详细研究:
AdafruitBluefruit 是Adafruit Bluefruit nRF52 API的主要入口点。此类提供了许多基本功能和类,例如广告,设备上定义的GATT服务和特性列表以及连接状态。
BLEService 是包装器类BLE GATT服务记录,可用于定义自定义服务定义,或充当任何服务帮助程序类的基类。
BLECharacteristic 是BLE的包装器类GATT特征记录,可用于定义自定义特征,或充当任何特征辅助类的基类。
BLEDis 是DIS或“设备”的辅助类信息服务”。
BLEUart 是NUS或“北欧UART服务”的帮助类。
BLEBeacon 是帮助程序类,使用广告包将nRF52配置为信标,以发送正确格式的信标数据。
BLEMidi 是与M一起使用的帮助程序类通过BLE的IDI数据。
BLEHidAdafruit 是在BLE上模拟HID鼠标或键盘的助手类。
每个辅助类的详细信息在本学习指南中都可以找到。
AdafruitBluefruit
Adafruit的nRF52 BSP代码库仍在根据客户反馈进行积极开发和测试。因此,此处的课程文档可能不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
此基类是Adafruit Bluefruit nRF52 API的主要入口点,它公开了用于配置设备的大多数帮助程序类和功能。
API
AdafruitBluefruit具有以下公共API:
下载:文件
复制代码
// Constructor
AdafruitBluefruit(void);
/*------------------------------------------------------------------*/
/* Lower Level Classes (Bluefruit.Advertising.*, etc.)
*------------------------------------------------------------------*/
BLEGap Gap;
BLEGatt Gatt;
BLEAdvertising Advertising;
BLEAdvertisingData ScanResponse;
BLEScanner Scanner;
BLECentral Central;
BLEDiscovery Discovery;
/*------------------------------------------------------------------*/
/* SoftDevice Configure Functions, must call before begin()。
* These function affect the SRAM consumed by SoftDevice.
*------------------------------------------------------------------*/
void configServiceChanged (bool changed);
void configUuid128Count (uint8_t uuid128_max);
void configAttrTableSize (uint32_t attr_table_size);
// Config Bandwidth for connections
void configPrphConn (uint16_t mtu_max, uint8_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize);
void configCentralConn (uint16_t mtu_max, uint8_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize);
// Convenient function to config connection
void configPrphBandwidth (uint8_t bw);
void configCentralBandwidth(uint8_t bw);
err_t begin(uint8_t prph_count = 1, uint8_t central_count = 0);
/*------------------------------------------------------------------*/
/* General Functions
*------------------------------------------------------------------*/
void setName (const char* str);
uint8_t getName (char* name, uint16_t bufsize);
bool setTxPower (int8_t power);
int8_t getTxPower (void);
bool setApperance (uint16_t appear);
uint16_t getApperance (void);
void autoConnLed (bool enabled);
void setConnLedInterval (uint32_t ms);
/*------------------------------------------------------------------*/
/* GAP, Connections and Bonding
*------------------------------------------------------------------*/
bool connected (void);
bool disconnect (void);
bool setConnInterval (uint16_t min, uint16_t max);
bool setConnIntervalMS (uint16_t min_ms, uint16_t max_ms);
uint16_t connHandle (void);
bool connPaired (void);
uint16_t connInterval (void);
bool requestPairing (void);
void clearBonds (void);
ble_gap_addr_t getPeerAddr (void);
uint8_t getPeerAddr (uint8_t addr[6]);
void printInfo(void);
/*------------------------------------------------------------------*/
/* Callbacks
*------------------------------------------------------------------*/
void setConnectCallback ( BLEGap::connect_callback_t fp);
void setDisconnectCallback( BLEGap::disconnect_callback_t fp); // Constructor
AdafruitBluefruit(void);
/*------------------------------------------------------------------*/
/* Lower Level Classes (Bluefruit.Advertising.*, etc.)
*------------------------------------------------------------------*/
BLEGap Gap;
BLEGatt Gatt;
BLEAdvertising Advertising;
BLEAdvertisingData ScanResponse;
BLEScanner Scanner;
BLECentral Central;
BLEDiscovery Discovery;
/*------------------------------------------------------------------*/
/* SoftDevice Configure Functions, must call before begin()。
* These function affect the SRAM consumed by SoftDevice.
*------------------------------------------------------------------*/
void configServiceChanged (bool changed);
void configUuid128Count (uint8_t uuid128_max);
void configAttrTableSize (uint32_t attr_table_size);
// Config Bandwidth for connections
void configPrphConn (uint16_t mtu_max, uint8_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize);
void configCentralConn (uint16_t mtu_max, uint8_t event_len, uint8_t hvn_qsize, uint8_t wrcmd_qsize);
// Convenient function to config connection
void configPrphBandwidth (uint8_t bw);
void configCentralBandwidth(uint8_t bw);
err_t begin(uint8_t prph_count = 1, uint8_t central_count = 0);
/*------------------------------------------------------------------*/
/* General Functions
*------------------------------------------------------------------*/
void setName (const char* str);
uint8_t getName (char* name, uint16_t bufsize);
bool setTxPower (int8_t power);
int8_t getTxPower (void);
bool setApperance (uint16_t appear);
uint16_t getApperance (void);
void autoConnLed (bool enabled);
void setConnLedInterval (uint32_t ms);
/*------------------------------------------------------------------*/
/* GAP, Connections and Bonding
*------------------------------------------------------------------*/
bool connected (void);
bool disconnect (void);
bool setConnInterval (uint16_t min, uint16_t max);
bool setConnIntervalMS (uint16_t min_ms, uint16_t max_ms);
uint16_t connHandle (void);
bool connPaired (void);
uint16_t connInterval (void);
bool requestPairing (void);
void clearBonds (void);
ble_gap_addr_t getPeerAddr (void);
uint8_t getPeerAddr (uint8_t addr[6]);
void printInfo(void);
/*------------------------------------------------------------------*/
/* Callbacks
*------------------------------------------------------------------*/
void setConnectCallback ( BLEGap::connect_callback_t fp);
void setDisconnectCallback( BLEGap::disconnect_callback_t fp);
这些功能通常可通过“ Bluefruit。* ”使用。例如,要检查草图中的连接状态,可以运行“ if (Bluefruit.connected()) { 。.. }”。
示例
有关如何与父级 Bluefruit 类一起使用的示例,请参见示例本指南后面的部分。最好在实际使用案例的背景下检查此类。
您还可以通过Github在线浏览最新的示例代码:
浏览最新的示例Github上的代码
BLEGap
此页面正在进行中,因为API会随着我们迁移到S132v5(nRF52832)和S140(nRF52840),并添加了更好的中央模式支持。
此Bluefruit的GAP API可通过Bluefruit.Gap.***访问并具有以下公共功能:
下载:文件
复制代码
typedef void (*connect_callback_t ) (uint16_t conn_handle);
typedef void (*disconnect_callback_t ) (uint16_t conn_handle, uint8_t reason);
uint8_t getAddr (uint8_t mac[6]);
bool setAddr (uint8_t mac[6], uint8_t type);
bool connected (uint16_t conn_handle);
uint8_t getRole (uint16_t conn_handle);
uint8_t getPeerAddr (uint16_t conn_handle, uint8_t addr[6]);
ble_gap_addr_t getPeerAddr (uint16_t conn_handle);
uint16_t getPeerName (uint16_t conn_handle, char* buf, uint16_t bufsize);
uint16_t getMTU (uint16_t conn_handle);
uint16_t getMaxMtuByConnCfg (uint8_t conn_cfg);
uint16_t getMaxMtu (uint8_t conn_handle);
typedef void (*connect_callback_t ) (uint16_t conn_handle);
typedef void (*disconnect_callback_t ) (uint16_t conn_handle, uint8_t reason);
uint8_t getAddr (uint8_t mac[6]);
bool setAddr (uint8_t mac[6], uint8_t type);
bool connected (uint16_t conn_handle);
uint8_t getRole (uint16_t conn_handle);
uint8_t getPeerAddr (uint16_t conn_handle, uint8_t addr[6]);
ble_gap_addr_t getPeerAddr (uint16_t conn_handle);
uint16_t getPeerName (uint16_t conn_handle, char* buf, uint16_t bufsize);
uint16_t getMTU (uint16_t conn_handle);
uint16_t getMaxMtuByConnCfg (uint8_t conn_cfg);
uint16_t getMaxMtu (uint8_t conn_handle);
BLEAdvertising
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
“广告”是使您的低功耗蓝牙设备对聆听范围内的其他设备可见。无线电会发出特殊的格式化程序广告数据包,其中包含诸如设备名称,是否可以连接到设备(或仅广播)等信息。
您还可以在广告数据包中包含自定义数据,这对信标的工作方式至关重要。
BLEAdvertisingData和BLEAdvertising 类提供了许多帮助程序功能,以使创建格式良好的广告包以及使用扫描响应选项,这是一个可选的辅助广告数据包,可由中央设备请求。 (这会为您提供另外27个字节的广告数据,但不会像主广告数据包那样自动发送出去。)
这两个广告数据包可通过父AdafruitBluefruit类访问,调用“ Bluefruit.Advertising.*”和“ Bluefruit.ScanResponse.*”。
有关使用这些帮助器类的示例,请参阅本指南后面的任何示例,因为所有设备将在启动过程中做广告。
API
BLEAdvertisingData 类具有以下公共API:
下载:文件
复制代码
/*------------- Adv Data -------------*/
bool addData(uint8_t type, const void* data, uint8_t len);
bool addFlags(uint8_t flags);
bool addTxPower(void);
bool addName(void);
bool addAppearance(uint16_t appearance);
bool addManufacturerData(const void* data, uint8_t count);
/*------------- UUID -------------*/
bool addUuid(BLEUuid bleuuid);
bool addUuid(BLEUuid bleuuid1, BLEUuid bleuuid2);
bool addUuid(BLEUuid bleuuid1, BLEUuid bleuuid2, BLEUuid bleuuid3);
bool addUuid(BLEUuid bleuuid1, BLEUuid bleuuid2, BLEUuid bleuuid3, BLEUuid bleuuid4);
bool addUuid(BLEUuid bleuuid[], uint8_t count);
/*------------- Service -------------*/
bool addService(BLEService& service);
bool addService(BLEService& service1, BLEService& service2);
bool addService(BLEService& service1, BLEService& service2, BLEService& service3);
bool addService(BLEService& service1, BLEService& service2, BLEService& service3, BLEService& service4);
/*------------- Client Service -------------*/
bool addService(BLEClientService& service);
// Functions to work with the raw advertising packet
uint8_t count(void);
uint8_t* getData(void);
bool setData(const uint8_t* data, uint8_t count);
void clearData(void);
bool setData(Advertisable& adv_able) { return adv_able.setAdv(*this); } /*------------- Adv Data -------------*/
bool addData(uint8_t type, const void* data, uint8_t len);
bool addFlags(uint8_t flags);
bool addTxPower(void);
bool addName(void);
bool addAppearance(uint16_t appearance);
bool addManufacturerData(const void* data, uint8_t count);
/*------------- UUID -------------*/
bool addUuid(BLEUuid bleuuid);
bool addUuid(BLEUuid bleuuid1, BLEUuid bleuuid2);
bool addUuid(BLEUuid bleuuid1, BLEUuid bleuuid2, BLEUuid bleuuid3);
bool addUuid(BLEUuid bleuuid1, BLEUuid bleuuid2, BLEUuid bleuuid3, BLEUuid bleuuid4);
bool addUuid(BLEUuid bleuuid[], uint8_t count);
/*------------- Service -------------*/
bool addService(BLEService& service);
bool addService(BLEService& service1, BLEService& service2);
bool addService(BLEService& service1, BLEService& service2, BLEService& service3);
bool addService(BLEService& service1, BLEService& service2, BLEService& service3, BLEService& service4);
/*------------- Client Service -------------*/
bool addService(BLEClientService& service);
// Functions to work with the raw advertising packet
uint8_t count(void);
uint8_t* getData(void);
bool setData(const uint8_t* data, uint8_t count);
void clearData(void);
bool setData(Advertisable& adv_able) { return adv_able.setAdv(*this); }
除了BLEAdvertisingData中的API外, BLEAdvertising 类还具有指示广告行为的功能,例如慢/快超时,adv间隔和回调等。..
下载:文件
复制代码
typedef void (*stop_callback_t) (void);
typedef void (*slow_callback_t) (void);
void setType(uint8_t adv_type);
void setFastTimeout(uint16_t sec);
void setSlowCallback(slow_callback_t fp);
void setStopCallback(stop_callback_t fp);
void setInterval (uint16_t fast, uint16_t slow);
void setIntervalMS(uint16_t fast, uint16_t slow);
uint16_t getInterval(void);
bool setBeacon(BLEBeacon& beacon);
bool setBeacon(EddyStoneUrl& eddy_url);
bool isRunning(void);
void restartOnDisconnect(bool enable);
bool start(uint16_t timeout = 0);
bool stop (void);
typedef void (*stop_callback_t) (void);
typedef void (*slow_callback_t) (void);
void setType(uint8_t adv_type);
void setFastTimeout(uint16_t sec);
void setSlowCallback(slow_callback_t fp);
void setStopCallback(stop_callback_t fp);
void setInterval (uint16_t fast, uint16_t slow);
void setIntervalMS(uint16_t fast, uint16_t slow);
uint16_t getInterval(void);
bool setBeacon(BLEBeacon& beacon);
bool setBeacon(EddyStoneUrl& eddy_url);
bool isRunning(void);
void restartOnDisconnect(bool enable);
bool start(uint16_t timeout = 0);
bool stop (void);
相关信息
通用访问配置文件:此页面包含为“数据”类型字段分配的数字的正式列表。通过提供有效的“数据”类型将数据插入广告包,还可以选择在数据包中插入与所选值相对应的正确格式的有效载荷。
示例
有关实际示例代码,请参见后面的示例部分在本指南中。下面的代码段仅用于说明目的,但是广告应在实际用例的背景下进行检查,因为它会因一种设置而异!
下载:文件
复制代码
void setup(void)
{
// Other startup code here
// 。..
// Set up Advertising Packet
setupAdv();
// Start Advertising
Bluefruit.Advertising.start();
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include bleuart 128-bit uuid
Bluefruit.Advertising.addService(bleuart);
// Secondary Scan Response packet (optional)
// Since there is no room for ‘Name’ in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don‘t stop advertising after n seconds
} void setup(void)
{
// Other startup code here
// 。..
// Set up Advertising Packet
setupAdv();
// Start Advertising
Bluefruit.Advertising.start();
}
void startAdv(void)
{
// Advertising packet
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include bleuart 128-bit uuid
Bluefruit.Advertising.addService(bleuart);
// Secondary Scan Response packet (optional)
// Since there is no room for ’Name‘ in Advertising packet
Bluefruit.ScanResponse.addName();
/* Start Advertising
* - Enable auto advertising if disconnected
* - Interval: fast mode = 20 ms, slow mode = 152.5 ms
* - Timeout for fast mode is 30 seconds
* - Start(timeout) with timeout = 0 will advertise forever (until connected)
*
* For recommended advertising interval
* https://developer.apple.com/library/content/qa/qa1931/_index.html
*/
Bluefruit.Advertising.restartOnDisconnect(true);
Bluefruit.Advertising.setInterval(32, 244); // in unit of 0.625 ms
Bluefruit.Advertising.setFastTimeout(30); // number of seconds in fast mode
Bluefruit.Advertising.start(0); // 0 = Don’t stop advertising after n seconds
}
BLEScanner
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
该文档基于BSP 0.7.0及更高版本。在使用下面的代码之前,请确保您具有最新版本。
BLEScanner类用于中央模式,并有助于扫描范围内的BLE外围设备并解析外围设备发出的广告数据。
通常通过Bluefruit类(在启动时实例化)访问BLEScanner类,如下所示:
下载:文件
复制代码
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Filter for devices with a min RSSI of -80 dBm
* - Interval = 100 ms, window = 50 ms
* - Use active scan (requests the optional scan response packet)
* - Start(0) = will scan forever since no timeout is given
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.filterRssi(-80); // Only invoke callback when RSSI 》= -80 dBm
Bluefruit.Scanner.setInterval(160, 80); // in units of 0.625 ms
Bluefruit.Scanner.useActiveScan(true); // Request scan response data
Bluefruit.Scanner.start(0); // 0 = Don‘t stop scanning after n seconds /* Start Central Scanning
* - Enable auto scan if disconnected
* - Filter for devices with a min RSSI of -80 dBm
* - Interval = 100 ms, window = 50 ms
* - Use active scan (requests the optional scan response packet)
* - Start(0) = will scan forever since no timeout is given
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.filterRssi(-80); // Only invoke callback when RSSI 》= -80 dBm
Bluefruit.Scanner.setInterval(160, 80); // in units of 0.625 ms
Bluefruit.Scanner.useActiveScan(true); // Request scan response data
Bluefruit.Scanner.start(0); // 0 = Don’t stop scanning after n seconds
API
BLEScanner具有以下公共API:
下载:文件
复制代码
typedef void (*rx_callback_t) (ble_gap_evt_adv_report_t*);
typedef void (*stop_callback_t) (void);
BLEScanner(void);
ble_gap_scan_params_t* getParams(void);
bool isRunning(void);
void useActiveScan(bool enable);
void setInterval(uint16_t interval, uint16_t window);
void setIntervalMS(uint16_t interval, uint16_t window);
void restartOnDisconnect(bool enable);
void filterRssi(int8_t min_rssi);
void filterMSD(uint16_t manuf_id);
void filterUuid(BLEUuid ble_uuid);
void filterUuid(BLEUuid ble_uuid1, BLEUuid ble_uuid2);
void filterUuid(BLEUuid ble_uuid1, BLEUuid ble_uuid2, BLEUuid ble_uuid3);
void filterUuid(BLEUuid ble_uuid1, BLEUuid ble_uuid2, BLEUuid ble_uuid3, BLEUuid ble_uuid4);
void filterUuid(BLEUuid ble_uuid[], uint8_t count);
void clearFilters(void);
bool start(uint16_t timeout = 0);
bool stop(void);
/*------------- Callbacks -------------*/
void setRxCallback(rx_callback_t fp);
void setStopCallback(stop_callback_t fp);
/*------------- Data Parser -------------*/
uint8_t parseReportByType(const uint8_t* scandata, uint8_t scanlen, uint8_t type, uint8_t* buf, uint8_t bufsize = 0);
uint8_t parseReportByType(const ble_gap_evt_adv_report_t* report, uint8_t type, uint8_t* buf, uint8_t bufsize = 0);
bool checkReportForUuid(const ble_gap_evt_adv_report_t* report, BLEUuid ble_uuid);
bool checkReportForService(const ble_gap_evt_adv_report_t* report, BLEClientService svc);
bool checkReportForService(const ble_gap_evt_adv_report_t* report, BLEService svc); typedef void (*rx_callback_t) (ble_gap_evt_adv_report_t*);
typedef void (*stop_callback_t) (void);
BLEScanner(void);
ble_gap_scan_params_t* getParams(void);
bool isRunning(void);
void useActiveScan(bool enable);
void setInterval(uint16_t interval, uint16_t window);
void setIntervalMS(uint16_t interval, uint16_t window);
void restartOnDisconnect(bool enable);
void filterRssi(int8_t min_rssi);
void filterMSD(uint16_t manuf_id);
void filterUuid(BLEUuid ble_uuid);
void filterUuid(BLEUuid ble_uuid1, BLEUuid ble_uuid2);
void filterUuid(BLEUuid ble_uuid1, BLEUuid ble_uuid2, BLEUuid ble_uuid3);
void filterUuid(BLEUuid ble_uuid1, BLEUuid ble_uuid2, BLEUuid ble_uuid3, BLEUuid ble_uuid4);
void filterUuid(BLEUuid ble_uuid[], uint8_t count);
void clearFilters(void);
bool start(uint16_t timeout = 0);
bool stop(void);
/*------------- Callbacks -------------*/
void setRxCallback(rx_callback_t fp);
void setStopCallback(stop_callback_t fp);
/*------------- Data Parser -------------*/
uint8_t parseReportByType(const uint8_t* scandata, uint8_t scanlen, uint8_t type, uint8_t* buf, uint8_t bufsize = 0);
uint8_t parseReportByType(const ble_gap_evt_adv_report_t* report, uint8_t type, uint8_t* buf, uint8_t bufsize = 0);
bool checkReportForUuid(const ble_gap_evt_adv_report_t* report, BLEUuid ble_uuid);
bool checkReportForService(const ble_gap_evt_adv_report_t* report, BLEClientService svc);
bool checkReportForService(const ble_gap_evt_adv_report_t* report, BLEService svc);
setRxCallback(rx_callback_t fp)
每当检测到有效的广告包(基于BLEScanner类中应用的任何可选过滤器)时,回调函数(请参见rx_callback_t)将被调用。
回调函数具有以下签名:
注意:ble_gap_evt_adv_report_t是Nordic nRF52 SD的一部分,在ble_gap.h中定义。
下载:文件
复制代码
void scan_callback(ble_gap_evt_adv_report_t* report)
{
/* Display the timestamp and device address */
if (report-》scan_rsp)
{
/* This is a Scan Response packet */
Serial.printf(“[SR%10d] Packet received from ”, millis());
}
else
{
/* This is a normal advertising packet */
Serial.printf(“[ADV%9d] Packet received from ”, millis());
}
Serial.printBuffer(report-》peer_addr.addr, 6, ‘:’);
Serial.print(“ ”);
/* Raw buffer contents */
Serial.printf(“%14s %d bytes ”, “PAYLOAD”, report-》dlen);
if (report-》dlen)
{
Serial.printf(“%15s”, “ ”);
Serial.printBuffer(report-》data, report-》dlen, ‘-’);
Serial.println();
}
/* RSSI value */
Serial.printf(“%14s %d dBm ”, “RSSI”, report-》rssi);
/* Adv Type */
Serial.printf(“%14s ”, “ADV TYPE”);
switch (report-》type)
{
case BLE_GAP_ADV_TYPE_ADV_IND:
Serial.printf(“Connectable undirected ”);
break;
case BLE_GAP_ADV_TYPE_ADV_DIRECT_IND:
Serial.printf(“Connectable directed ”);
break;
case BLE_GAP_ADV_TYPE_ADV_SCAN_IND:
Serial.printf(“Scannable undirected ”);
break;
case BLE_GAP_ADV_TYPE_ADV_NONCONN_IND:
Serial.printf(“Non-connectable undirected ”);
break;
}
/* Check for BLE UART UUID */
if ( Bluefruit.Scanner.checkReportForUuid(report, BLEUART_UUID_SERVICE) )
{
Serial.printf(“%14s %s ”, “BLE UART”, “UUID Found!”);
}
/* Check for DIS UUID */
if ( Bluefruit.Scanner.checkReportForUuid(report, UUID16_SVC_DEVICE_INFORMATION) )
{
Serial.printf(“%14s %s ”, “DIS”, “UUID Found!”);
}
Serial.println();
} void scan_callback(ble_gap_evt_adv_report_t* report)
{
/* Display the timestamp and device address */
if (report-》scan_rsp)
{
/* This is a Scan Response packet */
Serial.printf(“[SR%10d] Packet received from ”, millis());
}
else
{
/* This is a normal advertising packet */
Serial.printf(“[ADV%9d] Packet received from ”, millis());
}
Serial.printBuffer(report-》peer_addr.addr, 6, ‘:’);
Serial.print(“ ”);
/* Raw buffer contents */
Serial.printf(“%14s %d bytes ”, “PAYLOAD”, report-》dlen);
if (report-》dlen)
{
Serial.printf(“%15s”, “ ”);
Serial.printBuffer(report-》data, report-》dlen, ‘-’);
Serial.println();
}
/* RSSI value */
Serial.printf(“%14s %d dBm ”, “RSSI”, report-》rssi);
/* Adv Type */
Serial.printf(“%14s ”, “ADV TYPE”);
switch (report-》type)
{
case BLE_GAP_ADV_TYPE_ADV_IND:
Serial.printf(“Connectable undirected ”);
break;
case BLE_GAP_ADV_TYPE_ADV_DIRECT_IND:
Serial.printf(“Connectable directed ”);
break;
case BLE_GAP_ADV_TYPE_ADV_SCAN_IND:
Serial.printf(“Scannable undirected ”);
break;
case BLE_GAP_ADV_TYPE_ADV_NONCONN_IND:
Serial.printf(“Non-connectable undirected ”);
break;
}
/* Check for BLE UART UUID */
if ( Bluefruit.Scanner.checkReportForUuid(report, BLEUART_UUID_SERVICE) )
{
Serial.printf(“%14s %s ”, “BLE UART”, “UUID Found!”);
}
/* Check for DIS UUID */
if ( Bluefruit.Scanner.checkReportForUuid(report, UUID16_SVC_DEVICE_INFORMATION) )
{
Serial.printf(“%14s %s ”, “DIS”, “UUID Found!”);
}
Serial.println();
}
void useActiveScan ( bool enable);
启用“ Active Scan”将enable参数设置为1将导致设备请求可选的扫描响应广告包,这是第二个31字节的广告包,可用于传输其他信息。
默认情况下,禁用主动扫描,因此,除非调用此函数并将其设置为1,然后再调用Bluefruit.Scanner.start(0),否则BLEScanner不会接收任何扫描响应数据包。
void filterRssi(int8_t min_rssi);
void filterMSD(uint16_t manuf_id);
void filterUuid(BLEUuid ble_uuid);
void filterUuid(BLEUuid ble_uuid1,BLEUuid ble_uuid2);
void filterUuid(BLEUuid ble_uuid1,BLEUuid ble_uuid2,BLEUuid3 ;; filterUuid(BLEUuid ble_uuid1,BLEUuid ble_uuid2,BLEUuid ble_uuid3,BLEUuid ble_uuid4);
void filterUuid(BLEUuid ble_uuid [],uint8_t count);
过滤器可以应用于BLEScanner来缩小范围数据发送到回调处理程序,并使您的处理广告包更加容易。
从BSP 0.7.0开始,存在以下三个过滤器:
filterRssi(int8_t min_rssi):将广告结果过滤到至少具有指定RSSI值的设备,这使您可以忽略距离太远或信号太弱的设备。数字越大,信号越强,因此-90是非常弱的信号,而-60是更强的信号。
filterUuid(BLEUuid ble_uuid):过滤广告结果到向广告自己宣传具有指定的服务UUID。如果输入了多个UUID,则将使用布尔OR逻辑对其进行过滤,这意味着出现的任何单个UUID都将被视为匹配项。
void filterMSD(uint16_t manuf_id):筛选器将结果发布给包含制造商特定数据的设备数据类型,以及使用指定的Bluetooth客户ID(manuf_id)的用户)。例如,这对于过滤同时使用MSD字段的iBeacon与Eddystone设备可能非常有用,或者查找与您自己的CID匹配的自定义MSD数据。
通过.filterUuid之一添加多个UUID时(。..)函数,它们的UUID将使用布尔“或”逻辑进行过滤,这意味着在广告数据包中检测到任何指定的UUID时都会触发回调。
void clearFilters(void);
此功能清除并使用上面的函数设置过滤值。
bool start(uint16_t timeout = 0);
bool stop(void);
.start和.stop函数可用于开始和停止扫描,应在设置所有主要参数(定时,滤波器等)后调用。
.start函数有一个名为timeout的参数,该参数设置扫描广告包的秒数。将此设置为“ 0”(默认值)将导致设备永远扫描。
在调用.start之前,请确保已设置BLEScanner参数的所有过滤器。
void restartOnDisconnect ( bool enable);
设置此项与外围设备断开连接后,如果将功能设置为“ 1”,将导致扫描过程再次开始。默认行为是在断开连接时自动重新启动扫描。
示例
有关在中央模式下使用几乎所有BLEScanner和广告API的示例,请参阅中央示例文件夹中的central_scan_advanced.ino。
central_scan_advanced .ino on Github
此示例仅在BSP 0.7.0及更高版本中可用!
BLEService
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
定义自定义BLE Gatt服务时将使用此基类,例如构成此处所述的Adafruit Bluefruit nRF52 API的各种服务帮助程序类。
除非您要实现自定义GATT服务和特征,您通常不会直接使用此基类,而是会实例化并调用Bluefruit nRF52 API中包含的更高级别的帮助程序服务或特性。
基本用法
使用BLEService类通常只需要两项操作:
您需要在构造函数中使用适当的16位或128位UUID声明并实例化该类:
下载:文件
复制代码
BLEService myService = BLEService(0x1234); BLEService myService = BLEService(0x1234);
然后您需要在实例上调用 .begin()方法,然后再向其中添加任何BLECharacteristics(通过BLECharacteristic的相应.begin()函数调用):
下载:文件
复制代码
myService.begin(); myService.begin();
操作顺序(重要!)
使用BLEService和BLECharacteristic时要考虑的一个非常重要的事情是,任何BLECharacteristic将自动添加到调用了.begin()函数的最后一个BLEService中。因此,您必须先调用yourService.begin(),然后再添加任何特征!
请参见本页底部的示例,以获取有关其实际工作方式的具体示例。
API
BLEService具有以下总体类结构:
此文档可能会因为错误已修复,并且开发了API。您应该始终向Github仓库咨询最新的代码发布和类定义!
i》下载:文件
复制代码
BLEUuid uuid;
static BLEService* lastService;
BLEService(void);
BLEService(uint16_t uuid16);
BLEService(uint8_t const uuid128[]);
void setUuid(uint16_t uuid16);
void setUuid(uint8_t const uuid128[]);
virtual err_t begin(void); BLEUuid uuid;
static BLEService* lastService;
BLEService(void);
BLEService(uint16_t uuid16);
BLEService(uint8_t const uuid128[]);
void setUuid(uint16_t uuid16);
void setUuid(uint8_t const uuid128[]);
virtual err_t begin(void);
示例
以下示例声明了HRM(心率监视器)服务,并为其分配了一些特征:
请注意,此示例代码不完整。对于完整的示例,请打开nRF52 BSP的“ custom_hrm”示例!以下代码仅用于说明目的。
下载:文件
复制代码
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37
* Body Sensor Location Char: 0x2A38
*/
BLEService hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);
void setupHRM(void)
{
// Configure the Heart Rate Monitor service
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
// Supported Characteristics:
// Name UUID Requirement Properties
// ---------------------------- ------ ----------- ----------
// Heart Rate Measurement 0x2A37 Mandatory Notify
// Body Sensor Location 0x2A38 Optional Read
// Heart Rate Control Point 0x2A39 Conditional Write 《-- Not used here
hrms.begin();
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.。 Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was ‘begin()’ed!
// Configure the Heart Rate Measurement characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Permission = Notify
// Min Len = 1
// Max Len = 8
// B0 = UINT8 - Flag (MANDATORY)
// b5:7 = Reserved
// b4 = RR-Internal (0 = Not present, 1 = Present)
// b3 = Energy expended status (0 = Not present, 1 = Present)
// b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
// b0 = Value format (0 = UINT8, 1 = UINT16)
// B1 = UINT8 - 8-bit heart rate measurement value in BPM
// B2:3 = UINT16 - 16-bit heart rate measurement value in BPM
// B4:5 = UINT16 - Energy expended in joules
// B6:7 = UINT16 - RR Internal (1/1024 second resolution)
hrmc.setProperties(CHR_PROPS_NOTIFY);
hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
hrmc.setFixedLen(2);
hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
hrmc.begin();
uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
hrmc.notify(hrmdata, 2); // Use .notify instead of .write!
// Configure the Body Sensor Location characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Permission = Read
// Min Len = 1
// Max Len = 1
// B0 = UINT8 - Body Sensor Location
// 0 = Other
// 1 = Chest
// 2 = Wrist
// 3 = Finger
// 4 = Hand
// 5 = Ear Lobe
// 6 = Foot
// 7:255 = Reserved
bslc.setProperties(CHR_PROPS_READ);
bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
bslc.setFixedLen(1);
bslc.begin();
bslc.write8(2); // Set the characteristic to ‘Wrist’ (2)
}
void cccd_callback(BLECharacteristic& chr, uint16_t cccd_value)
{
// Display the raw request packet
Serial.print(“CCCD Updated: ”);
//Serial.printBuffer(request-》data, request-》len);
Serial.print(cccd_value);
Serial.println(“”);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
if (chr.uuid == hrmc.uuid) {
if (chr.notifyEnabled()) {
Serial.println(“Heart Rate Measurement ‘Notify’ enabled”);
} else {
Serial.println(“Heart Rate Measurement ‘Notify’ disabled”);
}
}
} /* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37
* Body Sensor Location Char: 0x2A38
*/
BLEService hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);
void setupHRM(void)
{
// Configure the Heart Rate Monitor service
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
// Supported Characteristics:
// Name UUID Requirement Properties
// ---------------------------- ------ ----------- ----------
// Heart Rate Measurement 0x2A37 Mandatory Notify
// Body Sensor Location 0x2A38 Optional Read
// Heart Rate Control Point 0x2A39 Conditional Write 《-- Not used here
hrms.begin();
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.。 Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was ‘begin()’ed!
// Configure the Heart Rate Measurement characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Permission = Notify
// Min Len = 1
// Max Len = 8
// B0 = UINT8 - Flag (MANDATORY)
// b5:7 = Reserved
// b4 = RR-Internal (0 = Not present, 1 = Present)
// b3 = Energy expended status (0 = Not present, 1 = Present)
// b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
// b0 = Value format (0 = UINT8, 1 = UINT16)
// B1 = UINT8 - 8-bit heart rate measurement value in BPM
// B2:3 = UINT16 - 16-bit heart rate measurement value in BPM
// B4:5 = UINT16 - Energy expended in joules
// B6:7 = UINT16 - RR Internal (1/1024 second resolution)
hrmc.setProperties(CHR_PROPS_NOTIFY);
hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
hrmc.setFixedLen(2);
hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
hrmc.begin();
uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
hrmc.notify(hrmdata, 2); // Use .notify instead of .write!
// Configure the Body Sensor Location characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Permission = Read
// Min Len = 1
// Max Len = 1
// B0 = UINT8 - Body Sensor Location
// 0 = Other
// 1 = Chest
// 2 = Wrist
// 3 = Finger
// 4 = Hand
// 5 = Ear Lobe
// 6 = Foot
// 7:255 = Reserved
bslc.setProperties(CHR_PROPS_READ);
bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
bslc.setFixedLen(1);
bslc.begin();
bslc.write8(2); // Set the characteristic to ‘Wrist’ (2)
}
void cccd_callback(BLECharacteristic& chr, uint16_t cccd_value)
{
// Display the raw request packet
Serial.print(“CCCD Updated: ”);
//Serial.printBuffer(request-》data, request-》len);
Serial.print(cccd_value);
Serial.println(“”);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
if (chr.uuid == hrmc.uuid) {
if (chr.notifyEnabled()) {
Serial.println(“Heart Rate Measurement ‘Notify’ enabled”);
} else {
Serial.println(“Heart Rate Measurement ‘Notify’ disabled”);
}
}
}
BLECharacteristic
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
此基类在定义自定义BLE GATT特征时使用,并用于吞吐Adafruit Bluefruit nRF52 API和帮助器类。
除非您要实现自定义GATT服务和特征,否则通常不会直接使用此基类,并实例化并调用Bluefruit nRF52 API中包含的更高级别的帮助程序服务或特性。
基本用法
使用BLECharacteristic类的主要步骤有两个。
首先,您需要声明并使用16位或128位UUID实例化BLECharacteristic类:
下载:文件
复制代码
BLECharacteristic myChar = BLECharacteristic(0xABCD);
BLECharacteristic myChar = BLECharacteristic(0xABCD);
然后您需要设置相关属性特性的特征,至少具有以下值:
下载:文件
复制代码
myChar.setProperties(CHR_PROPS_READ);
myChar.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
myChar.setFixedLen(1); // Alternatively .setMaxLen(uint16_t len)
myChar.begin(); myChar.setProperties(CHR_PROPS_READ);
myChar.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
myChar.setFixedLen(1); // Alternatively .setMaxLen(uint16_t len)
myChar.begin();
.setProperties 可以设置为以下一项或多项以下宏,它们对应于特征定义的八位“属性”字段中的一位:
CHR_PROPS_BROADCAST = bit(0),
CHR_PROPS_READ = bit( 1),
CHR_PROPS_WRITE_WO_RESP = bit(2),
CHR_PROPS_WRITE = bit(3),
CHR_PROPS_NOTIFY = bit(4),
CHR_PROPS_INDICATE = bit(5)
.setPermission 设置特征的安全级别,其中第一个值设置读取权限,第二个值设置写入权限,两个字段都可以具有以下值之一:
SECMODE_NO_ACCESS = 0x00,
SECMODE_OPEN = 0x11,
SECMODE_ENC_NO_MITM = 0x21 ,
SECMODE_ENC_WITH_MITM = 0x31,
SECMODE_SIGNED_NO_MITM = 0x12,
SECMODE_SIGNED_WITH_MITM = 0x22
.setFixedLen()表示此特征有多少字节。对于使用“通知”或“指示”的特征,此值可以从1..20开始,其他特征类型可以从1..512设置,并且》 20字节的值将跨多个20字节数据包发送。如果特征具有变量len,则将 .setMaxLen()值设置为该值将保留的最大值(最多20个字节)。
.begin( )将导致将此特性添加到最后一个被称为 .begin()方法的 BLEService 中。
操作顺序(重要!)
使用BLEService和BLECharacteristic时要考虑的一个非常重要的事情是,任何BLECharacteristic都会自动添加到调用了.begin()函数的最后一个BLEService中。因此,您必须先调用yourService.begin(),然后再添加任何特征!
请参见本页底部的示例,以获取有关其实际工作方式的具体示例。
API
BLECharacteristic具有以下总体类结构:
此文档可能会因为错误已修复,并且开发了API。您应该始终向Github仓库咨询最新的代码发布和类定义!
i》下载:文件
复制代码
/*--------- Callback Signatures ----------*/
typedef void (*read_authorize_cb_t) (BLECharacteristic& chr, ble_gatts_evt_read_t * request);
typedef void (*write_authorize_cb_t) (BLECharacteristic& chr, ble_gatts_evt_write_t* request);
typedef void (*write_cb_t) (BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset);
typedef void (*write_cccd_cb_t) (BLECharacteristic& chr, uint16_t value);
BLEUuid uuid;
// Constructors
BLECharacteristic(void);
BLECharacteristic(BLEUuid bleuuid);
// Destructor
virtual ~BLECharacteristic();
BLEService& parentService(void);
void setTempMemory(void);
/*------------- Configure -------------*/
void setUuid(BLEUuid bleuuid);
void setProperties(uint8_t prop);
void setPermission(BleSecurityMode read_perm, BleSecurityMode write_perm);
void setMaxLen(uint16_t max_len);
void setFixedLen(uint16_t fixed_len);
/*------------- Descriptors -------------*/
void setUserDescriptor(const char* descriptor); // aka user descriptor
void setReportRefDescriptor(uint8_t id, uint8_t type); // TODO refactor to use addDescriptor()
void setPresentationFormatDescriptor(uint8_t type, int8_t exponent, uint16_t unit, uint8_t name_space = 1, uint16_t descritpor = 0);
/*------------- Callbacks -------------*/
void setWriteCallback (write_cb_t fp);
void setCccdWriteCallback (write_cccd_cb_t fp);
void setReadAuthorizeCallback(read_authorize_cb_t fp);
void setWriteAuthorizeCallbak(write_authorize_cb_t fp);
virtual err_t begin(void);
// Add Descriptor function must be called right after begin()
err_t addDescriptor(BLEUuid bleuuid, void const * content, uint16_t len, BleSecurityMode read_perm = SECMODE_OPEN, BleSecurityMode write_perm = SECMODE_NO_ACCESS);
ble_gatts_char_handles_t handles(void);
/*------------- Write -------------*/
uint16_t write(const void* data, uint16_t len);
uint16_t write(const char* str);
uint16_t write8 (uint8_t num);
uint16_t write16 (uint16_t num);
uint16_t write32 (uint32_t num);
uint16_t write32 (int num);
/*------------- Read -------------*/
uint16_t read(void* buffer, uint16_t bufsize);
uint8_t read8 (void);
uint16_t read16(void);
uint32_t read32(void);
/*------------- Notify -------------*/
uint16_t getCccd(void);
bool notifyEnabled(void);
bool notify(const void* data, uint16_t len);
bool notify(const char* str);
bool notify8 (uint8_t num);
bool notify16 (uint16_t num);
bool notify32 (uint32_t num);
bool notify32 (int num);
/*------------- Indicate -------------*/
bool indicateEnabled(void);
bool indicate(const void* data, uint16_t len);
bool indicate(const char* str);
bool indicate8 (uint8_t num);
bool indicate16 (uint16_t num);
bool indicate32 (uint32_t num);
bool indicate32 (int num);
/*--------- Callback Signatures ----------*/
typedef void (*read_authorize_cb_t) (BLECharacteristic& chr, ble_gatts_evt_read_t * request);
typedef void (*write_authorize_cb_t) (BLECharacteristic& chr, ble_gatts_evt_write_t* request);
typedef void (*write_cb_t) (BLECharacteristic& chr, uint8_t* data, uint16_t len, uint16_t offset);
typedef void (*write_cccd_cb_t) (BLECharacteristic& chr, uint16_t value);
BLEUuid uuid;
// Constructors
BLECharacteristic(void);
BLECharacteristic(BLEUuid bleuuid);
// Destructor
virtual ~BLECharacteristic();
BLEService& parentService(void);
void setTempMemory(void);
/*------------- Configure -------------*/
void setUuid(BLEUuid bleuuid);
void setProperties(uint8_t prop);
void setPermission(BleSecurityMode read_perm, BleSecurityMode write_perm);
void setMaxLen(uint16_t max_len);
void setFixedLen(uint16_t fixed_len);
/*------------- Descriptors -------------*/
void setUserDescriptor(const char* descriptor); // aka user descriptor
void setReportRefDescriptor(uint8_t id, uint8_t type); // TODO refactor to use addDescriptor()
void setPresentationFormatDescriptor(uint8_t type, int8_t exponent, uint16_t unit, uint8_t name_space = 1, uint16_t descritpor = 0);
/*------------- Callbacks -------------*/
void setWriteCallback (write_cb_t fp);
void setCccdWriteCallback (write_cccd_cb_t fp);
void setReadAuthorizeCallback(read_authorize_cb_t fp);
void setWriteAuthorizeCallbak(write_authorize_cb_t fp);
virtual err_t begin(void);
// Add Descriptor function must be called right after begin()
err_t addDescriptor(BLEUuid bleuuid, void const * content, uint16_t len, BleSecurityMode read_perm = SECMODE_OPEN, BleSecurityMode write_perm = SECMODE_NO_ACCESS);
ble_gatts_char_handles_t handles(void);
/*------------- Write -------------*/
uint16_t write(const void* data, uint16_t len);
uint16_t write(const char* str);
uint16_t write8 (uint8_t num);
uint16_t write16 (uint16_t num);
uint16_t write32 (uint32_t num);
uint16_t write32 (int num);
/*------------- Read -------------*/
uint16_t read(void* buffer, uint16_t bufsize);
uint8_t read8 (void);
uint16_t read16(void);
uint32_t read32(void);
/*------------- Notify -------------*/
uint16_t getCccd(void);
bool notifyEnabled(void);
bool notify(const void* data, uint16_t len);
bool notify(const char* str);
bool notify8 (uint8_t num);
bool notify16 (uint16_t num);
bool notify32 (uint32_t num);
bool notify32 (int num);
/*------------- Indicate -------------*/
bool indicateEnabled(void);
bool indicate(const void* data, uint16_t len);
bool indicate(const char* str);
bool indicate8 (uint8_t num);
bool indicate16 (uint16_t num);
bool indicate32 (uint32_t num);
bool indicate32 (int num);
示例
以下示例配置心率监视器(HRM)服务的实例及其相关特征:
请注意,此示例代码不完整。对于完整的示例,请打开nRF52 BSP的“ custom_hrm”示例!以下代码仅用于说明目的。
下载:文件
复制代码
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37
* Body Sensor Location Char: 0x2A38
*/
BLEService hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);
void setupHRM(void)
{
// Configure the Heart Rate Monitor service
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
// Supported Characteristics:
// Name UUID Requirement Properties
// ---------------------------- ------ ----------- ----------
// Heart Rate Measurement 0x2A37 Mandatory Notify
// Body Sensor Location 0x2A38 Optional Read
// Heart Rate Control Point 0x2A39 Conditional Write 《-- Not used here
hrms.begin();
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.。 Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was ‘begin()’ed!
// Configure the Heart Rate Measurement characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Permission = Notify
// Min Len = 1
// Max Len = 8
// B0 = UINT8 - Flag (MANDATORY)
// b5:7 = Reserved
// b4 = RR-Internal (0 = Not present, 1 = Present)
// b3 = Energy expended status (0 = Not present, 1 = Present)
// b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
// b0 = Value format (0 = UINT8, 1 = UINT16)
// B1 = UINT8 - 8-bit heart rate measurement value in BPM
// B2:3 = UINT16 - 16-bit heart rate measurement value in BPM
// B4:5 = UINT16 - Energy expended in joules
// B6:7 = UINT16 - RR Internal (1/1024 second resolution)
hrmc.setProperties(CHR_PROPS_NOTIFY);
hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
hrmc.setFixedLen(2);
hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
hrmc.begin();
uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
hrmc.notify(hrmdata, 2); // Use .notify instead of .write!
// Configure the Body Sensor Location characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Permission = Read
// Min Len = 1
// Max Len = 1
// B0 = UINT8 - Body Sensor Location
// 0 = Other
// 1 = Chest
// 2 = Wrist
// 3 = Finger
// 4 = Hand
// 5 = Ear Lobe
// 6 = Foot
// 7:255 = Reserved
bslc.setProperties(CHR_PROPS_READ);
bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
bslc.setFixedLen(1);
bslc.begin();
bslc.write8(2); // Set the characteristic to ‘Wrist’ (2)
}
void cccd_callback(BLECharacteristic& chr, uint16_t cccd_value)
{
// Display the raw request packet
Serial.print(“CCCD Updated: ”);
//Serial.printBuffer(request-》data, request-》len);
Serial.print(cccd_value);
Serial.println(“”);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
if (chr.uuid == hrmc.uuid) {
if (chr.notifyEnabled()) {
Serial.println(“Heart Rate Measurement ‘Notify’ enabled”);
} else {
Serial.println(“Heart Rate Measurement ‘Notify’ disabled”);
}
}
} /* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37
* Body Sensor Location Char: 0x2A38
*/
BLEService hrms = BLEService(UUID16_SVC_HEART_RATE);
BLECharacteristic hrmc = BLECharacteristic(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLECharacteristic bslc = BLECharacteristic(UUID16_CHR_BODY_SENSOR_LOCATION);
void setupHRM(void)
{
// Configure the Heart Rate Monitor service
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.service.heart_rate.xml
// Supported Characteristics:
// Name UUID Requirement Properties
// ---------------------------- ------ ----------- ----------
// Heart Rate Measurement 0x2A37 Mandatory Notify
// Body Sensor Location 0x2A38 Optional Read
// Heart Rate Control Point 0x2A39 Conditional Write 《-- Not used here
hrms.begin();
// Note: You must call .begin() on the BLEService before calling .begin() on
// any characteristic(s) within that service definition.。 Calling .begin() on
// a BLECharacteristic will cause it to be added to the last BLEService that
// was ‘begin()’ed!
// Configure the Heart Rate Measurement characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Permission = Notify
// Min Len = 1
// Max Len = 8
// B0 = UINT8 - Flag (MANDATORY)
// b5:7 = Reserved
// b4 = RR-Internal (0 = Not present, 1 = Present)
// b3 = Energy expended status (0 = Not present, 1 = Present)
// b1:2 = Sensor contact status (0+1 = Not supported, 2 = Supported but contact not detected, 3 = Supported and detected)
// b0 = Value format (0 = UINT8, 1 = UINT16)
// B1 = UINT8 - 8-bit heart rate measurement value in BPM
// B2:3 = UINT16 - 16-bit heart rate measurement value in BPM
// B4:5 = UINT16 - Energy expended in joules
// B6:7 = UINT16 - RR Internal (1/1024 second resolution)
hrmc.setProperties(CHR_PROPS_NOTIFY);
hrmc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
hrmc.setFixedLen(2);
hrmc.setCccdWriteCallback(cccd_callback); // Optionally capture CCCD updates
hrmc.begin();
uint8_t hrmdata[2] = { 0b00000110, 0x40 }; // Set the characteristic to use 8-bit values, with the sensor connected and detected
hrmc.notify(hrmdata, 2); // Use .notify instead of .write!
// Configure the Body Sensor Location characteristic
// See: https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Permission = Read
// Min Len = 1
// Max Len = 1
// B0 = UINT8 - Body Sensor Location
// 0 = Other
// 1 = Chest
// 2 = Wrist
// 3 = Finger
// 4 = Hand
// 5 = Ear Lobe
// 6 = Foot
// 7:255 = Reserved
bslc.setProperties(CHR_PROPS_READ);
bslc.setPermission(SECMODE_OPEN, SECMODE_NO_ACCESS);
bslc.setFixedLen(1);
bslc.begin();
bslc.write8(2); // Set the characteristic to ‘Wrist’ (2)
}
void cccd_callback(BLECharacteristic& chr, uint16_t cccd_value)
{
// Display the raw request packet
Serial.print(“CCCD Updated: ”);
//Serial.printBuffer(request-》data, request-》len);
Serial.print(cccd_value);
Serial.println(“”);
// Check the characteristic this CCCD update is associated with in case
// this handler is used for multiple CCCD records.
if (chr.uuid == hrmc.uuid) {
if (chr.notifyEnabled()) {
Serial.println(“Heart Rate Measurement ‘Notify’ enabled”);
} else {
Serial.println(“Heart Rate Measurement ‘Notify’ disabled”);
}
}
}
BLEClientService
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
定义自定义BLE Gatt客户端时使用此基类。
除非您要实现自定义GATT客户端服务和特征,否则通常不会直接使用此基类,而是会实例化并调用a Bluefruit nRF52 API中包含了更高级别的帮助服务或特性。
基本用法
使用BLEClientService类通常只需要三元操作:
1。)您需要使用适当的16-声明和实例化该类。构造函数中的1位或128位UUID:
下载:文件
复制代码
BLEClientService myService = BLEService(0x1234); BLEClientService myService = BLEService(0x1234);
2。)然后,您需要在计算机上调用 .begin()方法之前的实例向其添加任何BLEClientCharacteristics(通过BLEClientCharacteristic的相应 .begin()函数调用):
下载:文件
复制代码
myService.begin(); myService.begin();
3)连接时,例如在connect回调中,您应该调用 .discover()来发现服务
下载:文件
复制代码
myService.discover(); myService.discover();
API
BLEClientService具有以下总体类结构:
由于修正了错误,并且开发了API,因此本文档可能已过时。您应该始终向Github仓库咨询最新的代码发布和类定义!
i》下载:文件
复制代码
BLEUuid uuid;
// Constructors
BLEClientService(void);
BLEClientService(BLEUuid bleuuid);
virtual bool begin(void);
virtual bool discover (uint16_t conn_handle);
bool discovered(void);
uint16_t connHandle(void);
void setHandleRange(ble_gattc_handle_range_t handle_range);
ble_gattc_handle_range_t getHandleRange(void); BLEUuid uuid;
// Constructors
BLEClientService(void);
BLEClientService(BLEUuid bleuuid);
virtual bool begin(void);
virtual bool discover (uint16_t conn_handle);
bool discovered(void);
uint16_t connHandle(void);
void setHandleRange(ble_gattc_handle_range_t handle_range);
ble_gattc_handle_range_t getHandleRange(void);
示例
以下示例声明了HRM(心率监视器)服务,并为其分配了一些特征:
下载:文件
复制代码
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This sketch show how to use BLEClientService and BLEClientCharacteristic
* to implement a custom client that is used to talk with Gatt server on
* peripheral.
*
* Note: you will need another feather52 running peripheral/custom_HRM sketch
* to test with.
*/
#include
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37 (Mandatory)
* Body Sensor Location Char: 0x2A38 (Optional)
*/
BLEClientService hrms(UUID16_SVC_HEART_RATE);
BLEClientCharacteristic hrmc(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLEClientCharacteristic bslc(UUID16_CHR_BODY_SENSOR_LOCATION);
void setup()
{
Serial.begin(115200);
Serial.println(“Bluefruit52 Central Custom HRM Example”);
Serial.println(“-------------------------------------- ”);
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
// SRAM usage required by SoftDevice will increase dramatically with number of connections
Bluefruit.begin(0, 1);
Bluefruit.setName(“Bluefruit52 Central”);
// Initialize HRM client
hrms.begin();
// Initialize client characteristics of HRM.
// Note: Client Char will be added to the last service that is begin()ed.
bslc.begin();
// set up callback for receiving measurement
hrmc.setNotifyCallback(hrm_notify_callback);
hrmc.begin();
// Increase Blink rate to different from PrPh advertising mode
Bluefruit.setConnLedInterval(250);
// Callbacks for Central
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
Bluefruit.Central.setConnectCallback(connect_callback);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Don‘t use active scan
* - Filter only accept HRM service
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.filterUuid(hrms.uuid);
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // // 0 = Don’t stop scanning after n seconds
}
void loop()
{
// do nothing
}
/**
* Callback invoked when scanner pick up an advertising data
* @param report Structural advertising data
*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Connect to device with HRM service in advertising
Bluefruit.Central.connect(report);
}
/**
* Callback invoked when an connection is established
* @param conn_handle
*/
void connect_callback(uint16_t conn_handle)
{
Serial.println(“Connected”);
Serial.print(“Discovering HRM Service 。.. ”);
// If HRM is not found, disconnect and return
if ( !hrms.discover(conn_handle) )
{
Serial.println(“Found NONE”);
// disconect since we couldn‘t find HRM service
Bluefruit.Central.disconnect(conn_handle);
return;
}
// Once HRM service is found, we continue to discover its characteristic
Serial.println(“Found it”);
Serial.print(“Discovering Measurement characteristic 。.. ”);
if ( !hrmc.discover() )
{
// Measurement chr is mandatory, if it is not found (valid), then disconnect
Serial.println(“not found !!!”);
Serial.println(“Measurement characteristic is mandatory but not found”);
Bluefruit.Central.disconnect(conn_handle);
return;
}
Serial.println(“Found it”);
// Measurement is found, continue to look for option Body Sensor Location
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Body Sensor Location is optional, print out the location in text if present
Serial.print(“Discovering Body Sensor Location characteristic 。.. ”);
if ( bslc.discover() )
{
Serial.println(“Found it”);
// Body sensor location value is 8 bit
const char* body_str[] = { “Other”, “Chest”, “Wrist”, “Finger”, “Hand”, “Ear Lobe”, “Foot” };
// Read 8-bit BSLC value from peripheral
uint8_t loc_value = bslc.read8();
Serial.print(“Body Location Sensor: ”);
Serial.println(body_str[loc_value]);
}else
{
Serial.println(“Found NONE”);
}
// Reaching here means we are ready to go, let’s enable notification on measurement chr
if ( hrmc.enableNotify() )
{
Serial.println(“Ready to receive HRM Measurement value”);
}else
{
Serial.println(“Couldn‘t enable notify for HRM Measurement. Increase DEBUG LEVEL for troubleshooting”);
}
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle
* @param reason
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.println(“Disconnected”);
}
/**
* Hooked callback that triggered when a measurement value is sent from peripheral
* @param chr Pointer client characteristic that even occurred,
* in this example it should be hrmc
* @param data Pointer to received data
* @param len Length of received data
*/
void hrm_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
{
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Measurement contains of control byte0 and measurement (8 or 16 bit) + optional field
// if byte0’s bit0 is 0 --》 measurement is 8 bit, otherwise 16 bit.
Serial.print(“HRM Measurement: ”);
if ( data[0] & bit(0) )
{
uint16_t value;
memcpy(&value, data+1, 2);
Serial.println(value);
}
else
{
Serial.println(data[1]);
}
}
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This sketch show how to use BLEClientService and BLEClientCharacteristic
* to implement a custom client that is used to talk with Gatt server on
* peripheral.
*
* Note: you will need another feather52 running peripheral/custom_HRM sketch
* to test with.
*/
#include
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37 (Mandatory)
* Body Sensor Location Char: 0x2A38 (Optional)
*/
BLEClientService hrms(UUID16_SVC_HEART_RATE);
BLEClientCharacteristic hrmc(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLEClientCharacteristic bslc(UUID16_CHR_BODY_SENSOR_LOCATION);
void setup()
{
Serial.begin(115200);
Serial.println(“Bluefruit52 Central Custom HRM Example”);
Serial.println(“-------------------------------------- ”);
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
// SRAM usage required by SoftDevice will increase dramatically with number of connections
Bluefruit.begin(0, 1);
Bluefruit.setName(“Bluefruit52 Central”);
// Initialize HRM client
hrms.begin();
// Initialize client characteristics of HRM.
// Note: Client Char will be added to the last service that is begin()ed.
bslc.begin();
// set up callback for receiving measurement
hrmc.setNotifyCallback(hrm_notify_callback);
hrmc.begin();
// Increase Blink rate to different from PrPh advertising mode
Bluefruit.setConnLedInterval(250);
// Callbacks for Central
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
Bluefruit.Central.setConnectCallback(connect_callback);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Don‘t use active scan
* - Filter only accept HRM service
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.filterUuid(hrms.uuid);
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // // 0 = Don’t stop scanning after n seconds
}
void loop()
{
// do nothing
}
/**
* Callback invoked when scanner pick up an advertising data
* @param report Structural advertising data
*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Connect to device with HRM service in advertising
Bluefruit.Central.connect(report);
}
/**
* Callback invoked when an connection is established
* @param conn_handle
*/
void connect_callback(uint16_t conn_handle)
{
Serial.println(“Connected”);
Serial.print(“Discovering HRM Service 。.. ”);
// If HRM is not found, disconnect and return
if ( !hrms.discover(conn_handle) )
{
Serial.println(“Found NONE”);
// disconect since we couldn‘t find HRM service
Bluefruit.Central.disconnect(conn_handle);
return;
}
// Once HRM service is found, we continue to discover its characteristic
Serial.println(“Found it”);
Serial.print(“Discovering Measurement characteristic 。.. ”);
if ( !hrmc.discover() )
{
// Measurement chr is mandatory, if it is not found (valid), then disconnect
Serial.println(“not found !!!”);
Serial.println(“Measurement characteristic is mandatory but not found”);
Bluefruit.Central.disconnect(conn_handle);
return;
}
Serial.println(“Found it”);
// Measurement is found, continue to look for option Body Sensor Location
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Body Sensor Location is optional, print out the location in text if present
Serial.print(“Discovering Body Sensor Location characteristic 。.. ”);
if ( bslc.discover() )
{
Serial.println(“Found it”);
// Body sensor location value is 8 bit
const char* body_str[] = { “Other”, “Chest”, “Wrist”, “Finger”, “Hand”, “Ear Lobe”, “Foot” };
// Read 8-bit BSLC value from peripheral
uint8_t loc_value = bslc.read8();
Serial.print(“Body Location Sensor: ”);
Serial.println(body_str[loc_value]);
}else
{
Serial.println(“Found NONE”);
}
// Reaching here means we are ready to go, let’s enable notification on measurement chr
if ( hrmc.enableNotify() )
{
Serial.println(“Ready to receive HRM Measurement value”);
}else
{
Serial.println(“Couldn‘t enable notify for HRM Measurement. Increase DEBUG LEVEL for troubleshooting”);
}
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle
* @param reason
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.println(“Disconnected”);
}
/**
* Hooked callback that triggered when a measurement value is sent from peripheral
* @param chr Pointer client characteristic that even occurred,
* in this example it should be hrmc
* @param data Pointer to received data
* @param len Length of received data
*/
void hrm_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
{
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Measurement contains of control byte0 and measurement (8 or 16 bit) + optional field
// if byte0’s bit0 is 0 --》 measurement is 8 bit, otherwise 16 bit.
Serial.print(“HRM Measurement: ”);
if ( data[0] & bit(0) )
{
uint16_t value;
memcpy(&value, data+1, 2);
Serial.println(value);
}
else
{
Serial.println(data[1]);
}
}
BLEClientCharacteristic
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
在为BLE GATT特性定义自定义客户端时使用此基类,并且在整个Adafruit Bluefruit nRF52 API和帮助程序类中使用此基类。
除非您为GATT服务和特性实现自定义客户端,否则通常不会直接使用此基类,而是会实例化并调用Bluefruit nRF52 API中包含的更高级别的帮助程序服务或特性。
基本用法
使用BLECharacteristic类的主要步骤有三个。
1。 )首先,您需要使用16位或128位UUID声明并实例化BLECharacteristic类:
下载:文件
复制代码
BLEClientCharacteristic myChar = BLEClientCharacteristic(0xABCD); BLEClientCharacteristic myChar = BLEClientCharacteristic(0xABCD);
2。)然后如果特征支持通知或指示,则需要为其设置相关的回调。
下载:文件
复制代码
myChar.setNotifyCallback(notify_callback);
myChar.begin(); myChar.setNotifyCallback(notify_callback);
myChar.begin();
.setNotifyCallback 当我们从外围设备收到通知消息时将触发该消息。这是处理可通知特征的必要条件,因为回调使我们能够及时响应消息
.begin()将导致该特征被添加到最后一个调用了它的 .begin()方法的BLEClientService 。
3)发现通过调用.discover()必须执行任何操作,例如.read()、。 write()、。 enableNotify()。
下载:文件
复制代码
if ( myChar.discover() )
{
uint32_t value = myChar.read32();
} if ( myChar.discover() )
{
uint32_t value = myChar.read32();
}
API
BLEClientCharacteristic具有以下总体类结构:
由于修正了错误,并且开发了API,因此本文档可能已过时。您应该始终向Github仓库咨询最新的代码发布和类定义!
i》下载:文件
复制代码
/*--------- Callback Signatures ----------*/
typedef void (*notify_cb_t ) (BLEClientCharacteristic* chr, uint8_t* data, uint16_t len);
typedef void (*indicate_cb_t) (BLEClientCharacteristic* chr, uint8_t* data, uint16_t len);
BLEUuid uuid;
// Constructors
BLEClientCharacteristic(void);
BLEClientCharacteristic(BLEUuid bleuuid);
// Destructor
virtual ~BLEClientCharacteristic();
void begin(BLEClientService* parent_svc = NULL);
bool discover(void);
bool discovered(void);
uint16_t connHandle(void);
uint16_t valueHandle(void);
uint8_t properties(void);
BLEClientService& parentService(void);
/*------------- Read -------------*/
uint16_t read(void* buffer, uint16_t bufsize);
uint8_t read8 (void);
uint16_t read16(void);
uint32_t read32(void);
/*------------- Write without Response-------------*/
uint16_t write (const void* data, uint16_t len);
uint16_t write8 (uint8_t value);
uint16_t write16 (uint16_t value);
uint16_t write32 (uint32_t value);
/*------------- Write with Response-------------*/
uint16_t write_resp(const void* data, uint16_t len);
uint16_t write8_resp (uint8_t value);
uint16_t write16_resp (uint16_t value);
uint16_t write32_resp (uint32_t value);
/*------------- Notify -------------*/
bool writeCCCD (uint16_t value);
bool enableNotify (void);
bool disableNotify (void);
bool enableIndicate (void);
bool disableIndicate (void);
/*------------- Callbacks -------------*/
void setNotifyCallback(notify_cb_t fp, bool useAdaCallback = true);
void setIndicateCallback(indicate_cb_t fp, bool useAdaCallback = true); /*--------- Callback Signatures ----------*/
typedef void (*notify_cb_t ) (BLEClientCharacteristic* chr, uint8_t* data, uint16_t len);
typedef void (*indicate_cb_t) (BLEClientCharacteristic* chr, uint8_t* data, uint16_t len);
BLEUuid uuid;
// Constructors
BLEClientCharacteristic(void);
BLEClientCharacteristic(BLEUuid bleuuid);
// Destructor
virtual ~BLEClientCharacteristic();
void begin(BLEClientService* parent_svc = NULL);
bool discover(void);
bool discovered(void);
uint16_t connHandle(void);
uint16_t valueHandle(void);
uint8_t properties(void);
BLEClientService& parentService(void);
/*------------- Read -------------*/
uint16_t read(void* buffer, uint16_t bufsize);
uint8_t read8 (void);
uint16_t read16(void);
uint32_t read32(void);
/*------------- Write without Response-------------*/
uint16_t write (const void* data, uint16_t len);
uint16_t write8 (uint8_t value);
uint16_t write16 (uint16_t value);
uint16_t write32 (uint32_t value);
/*------------- Write with Response-------------*/
uint16_t write_resp(const void* data, uint16_t len);
uint16_t write8_resp (uint8_t value);
uint16_t write16_resp (uint16_t value);
uint16_t write32_resp (uint32_t value);
/*------------- Notify -------------*/
bool writeCCCD (uint16_t value);
bool enableNotify (void);
bool disableNotify (void);
bool enableIndicate (void);
bool disableIndicate (void);
/*------------- Callbacks -------------*/
void setNotifyCallback(notify_cb_t fp, bool useAdaCallback = true);
void setIndicateCallback(indicate_cb_t fp, bool useAdaCallback = true);
示例
以下示例配置心率监视器(HRM)服务的实例及其相关特征:
下载:文件
复制代码
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This sketch show how to use BLEClientService and BLEClientCharacteristic
* to implement a custom client that is used to talk with Gatt server on
* peripheral.
*
* Note: you will need another feather52 running peripheral/custom_HRM sketch
* to test with.
*/
#include
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37 (Mandatory)
* Body Sensor Location Char: 0x2A38 (Optional)
*/
BLEClientService hrms(UUID16_SVC_HEART_RATE);
BLEClientCharacteristic hrmc(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLEClientCharacteristic bslc(UUID16_CHR_BODY_SENSOR_LOCATION);
void setup()
{
Serial.begin(115200);
Serial.println(“Bluefruit52 Central Custom HRM Example”);
Serial.println(“-------------------------------------- ”);
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
// SRAM usage required by SoftDevice will increase dramatically with number of connections
Bluefruit.begin(0, 1);
Bluefruit.setName(“Bluefruit52 Central”);
// Initialize HRM client
hrms.begin();
// Initialize client characteristics of HRM.
// Note: Client Char will be added to the last service that is begin()ed.
bslc.begin();
// set up callback for receiving measurement
hrmc.setNotifyCallback(hrm_notify_callback);
hrmc.begin();
// Increase Blink rate to different from PrPh advertising mode
Bluefruit.setConnLedInterval(250);
// Callbacks for Central
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
Bluefruit.Central.setConnectCallback(connect_callback);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Don‘t use active scan
* - Filter only accept HRM service
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.filterUuid(hrms.uuid);
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // // 0 = Don’t stop scanning after n seconds
}
void loop()
{
// do nothing
}
/**
* Callback invoked when scanner pick up an advertising data
* @param report Structural advertising data
*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Connect to device with HRM service in advertising
Bluefruit.Central.connect(report);
}
/**
* Callback invoked when an connection is established
* @param conn_handle
*/
void connect_callback(uint16_t conn_handle)
{
Serial.println(“Connected”);
Serial.print(“Discovering HRM Service 。.. ”);
// If HRM is not found, disconnect and return
if ( !hrms.discover(conn_handle) )
{
Serial.println(“Found NONE”);
// disconect since we couldn‘t find HRM service
Bluefruit.Central.disconnect(conn_handle);
return;
}
// Once HRM service is found, we continue to discover its characteristic
Serial.println(“Found it”);
Serial.print(“Discovering Measurement characteristic 。.. ”);
if ( !hrmc.discover() )
{
// Measurement chr is mandatory, if it is not found (valid), then disconnect
Serial.println(“not found !!!”);
Serial.println(“Measurement characteristic is mandatory but not found”);
Bluefruit.Central.disconnect(conn_handle);
return;
}
Serial.println(“Found it”);
// Measurement is found, continue to look for option Body Sensor Location
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Body Sensor Location is optional, print out the location in text if present
Serial.print(“Discovering Body Sensor Location characteristic 。.. ”);
if ( bslc.discover() )
{
Serial.println(“Found it”);
// Body sensor location value is 8 bit
const char* body_str[] = { “Other”, “Chest”, “Wrist”, “Finger”, “Hand”, “Ear Lobe”, “Foot” };
// Read 8-bit BSLC value from peripheral
uint8_t loc_value = bslc.read8();
Serial.print(“Body Location Sensor: ”);
Serial.println(body_str[loc_value]);
}else
{
Serial.println(“Found NONE”);
}
// Reaching here means we are ready to go, let’s enable notification on measurement chr
if ( hrmc.enableNotify() )
{
Serial.println(“Ready to receive HRM Measurement value”);
}else
{
Serial.println(“Couldn‘t enable notify for HRM Measurement. Increase DEBUG LEVEL for troubleshooting”);
}
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle
* @param reason
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.println(“Disconnected”);
}
/**
* Hooked callback that triggered when a measurement value is sent from peripheral
* @param chr Pointer client characteristic that even occurred,
* in this example it should be hrmc
* @param data Pointer to received data
* @param len Length of received data
*/
void hrm_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
{
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Measurement contains of control byte0 and measurement (8 or 16 bit) + optional field
// if byte0’s bit0 is 0 --》 measurement is 8 bit, otherwise 16 bit.
Serial.print(“HRM Measurement: ”);
if ( data[0] & bit(0) )
{
uint16_t value;
memcpy(&value, data+1, 2);
Serial.println(value);
}
else
{
Serial.println(data[1]);
}
}
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
/* This sketch show how to use BLEClientService and BLEClientCharacteristic
* to implement a custom client that is used to talk with Gatt server on
* peripheral.
*
* Note: you will need another feather52 running peripheral/custom_HRM sketch
* to test with.
*/
#include
/* HRM Service Definitions
* Heart Rate Monitor Service: 0x180D
* Heart Rate Measurement Char: 0x2A37 (Mandatory)
* Body Sensor Location Char: 0x2A38 (Optional)
*/
BLEClientService hrms(UUID16_SVC_HEART_RATE);
BLEClientCharacteristic hrmc(UUID16_CHR_HEART_RATE_MEASUREMENT);
BLEClientCharacteristic bslc(UUID16_CHR_BODY_SENSOR_LOCATION);
void setup()
{
Serial.begin(115200);
Serial.println(“Bluefruit52 Central Custom HRM Example”);
Serial.println(“-------------------------------------- ”);
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
// SRAM usage required by SoftDevice will increase dramatically with number of connections
Bluefruit.begin(0, 1);
Bluefruit.setName(“Bluefruit52 Central”);
// Initialize HRM client
hrms.begin();
// Initialize client characteristics of HRM.
// Note: Client Char will be added to the last service that is begin()ed.
bslc.begin();
// set up callback for receiving measurement
hrmc.setNotifyCallback(hrm_notify_callback);
hrmc.begin();
// Increase Blink rate to different from PrPh advertising mode
Bluefruit.setConnLedInterval(250);
// Callbacks for Central
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
Bluefruit.Central.setConnectCallback(connect_callback);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Don‘t use active scan
* - Filter only accept HRM service
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.filterUuid(hrms.uuid);
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // // 0 = Don’t stop scanning after n seconds
}
void loop()
{
// do nothing
}
/**
* Callback invoked when scanner pick up an advertising data
* @param report Structural advertising data
*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Connect to device with HRM service in advertising
Bluefruit.Central.connect(report);
}
/**
* Callback invoked when an connection is established
* @param conn_handle
*/
void connect_callback(uint16_t conn_handle)
{
Serial.println(“Connected”);
Serial.print(“Discovering HRM Service 。.. ”);
// If HRM is not found, disconnect and return
if ( !hrms.discover(conn_handle) )
{
Serial.println(“Found NONE”);
// disconect since we couldn‘t find HRM service
Bluefruit.Central.disconnect(conn_handle);
return;
}
// Once HRM service is found, we continue to discover its characteristic
Serial.println(“Found it”);
Serial.print(“Discovering Measurement characteristic 。.. ”);
if ( !hrmc.discover() )
{
// Measurement chr is mandatory, if it is not found (valid), then disconnect
Serial.println(“not found !!!”);
Serial.println(“Measurement characteristic is mandatory but not found”);
Bluefruit.Central.disconnect(conn_handle);
return;
}
Serial.println(“Found it”);
// Measurement is found, continue to look for option Body Sensor Location
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.body_sensor_location.xml
// Body Sensor Location is optional, print out the location in text if present
Serial.print(“Discovering Body Sensor Location characteristic 。.. ”);
if ( bslc.discover() )
{
Serial.println(“Found it”);
// Body sensor location value is 8 bit
const char* body_str[] = { “Other”, “Chest”, “Wrist”, “Finger”, “Hand”, “Ear Lobe”, “Foot” };
// Read 8-bit BSLC value from peripheral
uint8_t loc_value = bslc.read8();
Serial.print(“Body Location Sensor: ”);
Serial.println(body_str[loc_value]);
}else
{
Serial.println(“Found NONE”);
}
// Reaching here means we are ready to go, let’s enable notification on measurement chr
if ( hrmc.enableNotify() )
{
Serial.println(“Ready to receive HRM Measurement value”);
}else
{
Serial.println(“Couldn‘t enable notify for HRM Measurement. Increase DEBUG LEVEL for troubleshooting”);
}
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle
* @param reason
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.println(“Disconnected”);
}
/**
* Hooked callback that triggered when a measurement value is sent from peripheral
* @param chr Pointer client characteristic that even occurred,
* in this example it should be hrmc
* @param data Pointer to received data
* @param len Length of received data
*/
void hrm_notify_callback(BLEClientCharacteristic* chr, uint8_t* data, uint16_t len)
{
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.heart_rate_measurement.xml
// Measurement contains of control byte0 and measurement (8 or 16 bit) + optional field
// if byte0’s bit0 is 0 --》 measurement is 8 bit, otherwise 16 bit.
Serial.print(“HRM Measurement: ”);
if ( data[0] & bit(0) )
{
uint16_t value;
memcpy(&value, data+1, 2);
Serial.println(value);
}
else
{
Serial.println(data[1]);
}
}
BLEDiscovery
此页面正在进行中,因为API的更改如下:我们迁移到S132v5(nRF52832)和S140(nRF52840),并添加了更好的中央模式支持。 (托管在BLE外设上)更容易。为了发现服务,必须使用BLEClientService的discover() API,如下所示:
API
下载:文件
复制代码
BLEDiscovery(void); // Constructor
void begin(void);
bool begun(void);
void setHandleRange(ble_gattc_handle_range_t handle_range);
ble_gattc_handle_range_t getHandleRange(void);
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic* chr[], uint8_t count);
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1);
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2);
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2, BLEClientCharacteristic& chr3);
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2, BLEClientCharacteristic& chr3, BLEClientCharacteristic& chr4);
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2, BLEClientCharacteristic& chr3, BLEClientCharacteristic& chr4, BLEClientCharacteristic& chr5); BLEDiscovery(void); // Constructor
void begin(void);
bool begun(void);
void setHandleRange(ble_gattc_handle_range_t handle_range);
ble_gattc_handle_range_t getHandleRange(void);
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic* chr[], uint8_t count);
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1);
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2);
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2, BLEClientCharacteristic& chr3);
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2, BLEClientCharacteristic& chr3, BLEClientCharacteristic& chr4);
uint8_t discoverCharacteristic(uint16_t conn_handle, BLEClientCharacteristic& chr1, BLEClientCharacteristic& chr2, BLEClientCharacteristic& chr3, BLEClientCharacteristic& chr4, BLEClientCharacteristic& chr5);
有关如何使用此API的具体示例,请参见BSP的示例中的“ Central”文件夹。
BLEDis
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
该帮助程序类充当Bluetooth设备信息服务(0x180A)的包装。这项正式的GATT服务使您能够以通用方式发布有关设备的基本信息。
Bluefruit BLEDis帮助器类具有以下特征:
型号字符串(0x2A24 ),通过 .setModel(const char *)
序列号字符串(0x2A25),私有
固件修订字符串(0x2A26),私有
硬件修订字符串(0x2A27),通过 .setHardwareRev(const char *)
软件修订字符串(0x2A28),通过 .setSoftwareRev( const char *)
制造商名称字符串(0x2A29),通过 .setManufacturer(const char *)
The 序列号字符串是私有的,并填充有在制造过程中使用nRF52832 SoC编程的唯一设备ID。
固件修订字符串也是私有的并填充以下字段(以帮助我们跟踪问题并提供更好的feedb支持论坛中的确认):
软件设备名称(Sxxx)
软件设备版本(xxx)
引导程序版本(xxx)
注意:“软设备”和“引导加载程序”字段是分开的用一个逗号表示,这意味着最终输出将类似于以下字符串:‘S132 2.0.1,0.5.0’
其余特征都是public和可以使用适当的帮助器函数设置为一个值(长度最多20个字符),但是它们具有以下默认值(对于nRF52832):
型号数字字符串:Bluefruit Feather 52
硬件修订版字符串:NULL
软件修订版字符串:nRF52 BSP版本号
制造商名称字符串:Adafruit Industries
将公用值设置为NULL将阻止该特性出现在DIS服务中。
API
在BLEDis类中定义了以下函数和构造函数:
下载:文件
复制代码
BLEDis(void);
void setModel(const char* model);
void setHardwareRev(const char* hw_rev);
void setSoftwareRev(const char* sw_rev);
void setManufacturer(const char* manufacturer);
err_t begin(void); BLEDis(void);
void setModel(const char* model);
void setHardwareRev(const char* hw_rev);
void setSoftwareRev(const char* sw_rev);
void setManufacturer(const char* manufacturer);
err_t begin(void);
通过上面的 .set *()函数设置各个特征值,并在设置所有值后调用 .begin()函数可将服务添加到设备的内部GATT注册表中。
示例
以下裸露的示例演示如何使用用户可配置的值字符串设置设备信息服务:
下载:文件
复制代码
#include
BLEDis bledis;
void setup()
{
Serial.begin(115200);
Serial.println(“Bluefruit52 DIS Example”);
Bluefruit.begin();
Bluefruit.setName(“Bluefruit52”);
// Configure and Start Device Information Service
bledis.setManufacturer(“Adafruit Industries”);
bledis.setModel(“Bluefruit Feather52”);
bledis.begin();
// Set up Advertising Packet
setupAdv();
// Start Advertising
Bluefruit.Advertising.start();
}
void setupAdv(void)
{
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// There isn‘t enough room in the advertising packet for the
// name so we’ll place it on the secondary Scan Response packet
Bluefruit.ScanResponse.addName();
}
void loop()
{
} #include
BLEDis bledis;
void setup()
{
Serial.begin(115200);
Serial.println(“Bluefruit52 DIS Example”);
Bluefruit.begin();
Bluefruit.setName(“Bluefruit52”);
// Configure and Start Device Information Service
bledis.setManufacturer(“Adafruit Industries”);
bledis.setModel(“Bluefruit Feather52”);
bledis.begin();
// Set up Advertising Packet
setupAdv();
// Start Advertising
Bluefruit.Advertising.start();
}
void setupAdv(void)
{
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// There isn‘t enough room in the advertising packet for the
// name so we’ll place it on the secondary Scan Response packet
Bluefruit.ScanResponse.addName();
}
void loop()
{
}
输出
如果您检查使用iOS,Android或OS上的Bluefruit LE Connect应用程序的设备X,您应该会看到类似于以下输出的内容:
BLEUart
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
BLEUart是NUS(北欧UART服务)的包装类,NUS是北欧半导体定义的专有服务,我们将其用作Bluefruit模块与我们的移动和桌面Bluefruit LE Connect应用程序之间的基准传输机制。您可以使用它轻松地在外围设备和中央设备之间双向发送ASCII或二进制数据。
API
BLEUart具有以下公共API:
下载:文件
复制代码
// RX Callback signature (fires when data was written by the central)
typedef void (*rx_callback_t) (void);
// Constructor
BLEUart(uint16_t fifo_depth = BLE_UART_DEFAULT_FIFO_DEPTH);
virtual err_t begin(void);
bool notifyEnabled(void);
void setRxCallback( rx_callback_t fp);
// Stream API
virtual int read ( void );
virtual int read ( uint8_t * buf, size_t size );
virtual size_t write ( uint8_t b );
virtual size_t write ( const uint8_t *content, size_t len );
virtual int available ( void );
virtual int peek ( void );
virtual void flush ( void );
// Pull in write(str) and write(buf, size) from Print
using Print::write; // RX Callback signature (fires when data was written by the central)
typedef void (*rx_callback_t) (void);
// Constructor
BLEUart(uint16_t fifo_depth = BLE_UART_DEFAULT_FIFO_DEPTH);
virtual err_t begin(void);
bool notifyEnabled(void);
void setRxCallback( rx_callback_t fp);
// Stream API
virtual int read ( void );
virtual int read ( uint8_t * buf, size_t size );
virtual size_t write ( uint8_t b );
virtual size_t write ( const uint8_t *content, size_t len );
virtual int available ( void );
virtual int peek ( void );
virtual void flush ( void );
// Pull in write(str) and write(buf, size) from Print
using Print::write;
示例
下面的示例演示如何使用BLEUart帮助器类。
该示例可能已过时,您应该始终在nRF52 BSP中查阅最新的示例代码!
下载:文件
复制代码
#include
BLEDis bledis;
BLEUart bleuart;
BLEBas blebas;
#define STATUS_LED (17)
#define BLINKY_MS (2000)
uint32_t blinkyms;
void setup()
{
Serial.begin(115200);
Serial.println(“Bluefruit52 BLEUART Example”);
// Setup LED pins and reset blinky counter
pinMode(STATUS_LED, OUTPUT);
blinkyms = millis();
// Setup the BLE LED to be enabled on CONNECT
// Note: This is actually the default behaviour, but provided
// here in case you want to control this manually via PIN 19
Bluefruit.autoConnLed(true);
Bluefruit.begin();
Bluefruit.setName(“Bluefruit52”);
Bluefruit.setConnectCallback(connect_callback);
Bluefruit.setDisconnectCallback(disconnect_callback);
// Configure and Start Device Information Service
bledis.setManufacturer(“Adafruit Industries”);
bledis.setModel(“Bluefruit Feather52”);
bledis.begin();
// Configure and Start BLE Uart Service
bleuart.begin();
// Start BLE Battery Service
blebas.begin();
blebas.update(100);
// Set up Advertising Packet
setupAdv();
// Start Advertising
Bluefruit.Advertising.start();
}
void setupAdv(void)
{
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include bleuart 128-bit uuid
Bluefruit.Advertising.addService(bleuart);
// There is no room for Name in Advertising packet
// Use Scan response for Name
Bluefruit.ScanResponse.addName();
}
void loop()
{
// Blinky!
if (blinkyms+BLINKY_MS 《 millis()) {
blinkyms = millis();
digitalToggle(STATUS_LED);
}
// Forward from Serial to BLEUART
if (Serial.available())
{
// Delay to get enough input data since we have a
// limited amount of space in the transmit buffer
delay(2);
uint8_t buf[64];
int count = Serial.readBytes(buf, sizeof(buf));
bleuart.write( buf, count );
}
// Forward from BLEUART to Serial
if ( bleuart.available() )
{
uint8_t ch;
ch = (uint8_t) bleuart.read();
Serial.write(ch);
}
}
void connect_callback(void)
{
Serial.println(“Connected”);
}
void disconnect_callback(uint8_t reason)
{
(void) reason;
Serial.println();
Serial.println(“Disconnected”);
Serial.println(“Bluefruit will start advertising again”);
} #include
BLEDis bledis;
BLEUart bleuart;
BLEBas blebas;
#define STATUS_LED (17)
#define BLINKY_MS (2000)
uint32_t blinkyms;
void setup()
{
Serial.begin(115200);
Serial.println(“Bluefruit52 BLEUART Example”);
// Setup LED pins and reset blinky counter
pinMode(STATUS_LED, OUTPUT);
blinkyms = millis();
// Setup the BLE LED to be enabled on CONNECT
// Note: This is actually the default behaviour, but provided
// here in case you want to control this manually via PIN 19
Bluefruit.autoConnLed(true);
Bluefruit.begin();
Bluefruit.setName(“Bluefruit52”);
Bluefruit.setConnectCallback(connect_callback);
Bluefruit.setDisconnectCallback(disconnect_callback);
// Configure and Start Device Information Service
bledis.setManufacturer(“Adafruit Industries”);
bledis.setModel(“Bluefruit Feather52”);
bledis.begin();
// Configure and Start BLE Uart Service
bleuart.begin();
// Start BLE Battery Service
blebas.begin();
blebas.update(100);
// Set up Advertising Packet
setupAdv();
// Start Advertising
Bluefruit.Advertising.start();
}
void setupAdv(void)
{
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
Bluefruit.Advertising.addTxPower();
// Include bleuart 128-bit uuid
Bluefruit.Advertising.addService(bleuart);
// There is no room for Name in Advertising packet
// Use Scan response for Name
Bluefruit.ScanResponse.addName();
}
void loop()
{
// Blinky!
if (blinkyms+BLINKY_MS 《 millis()) {
blinkyms = millis();
digitalToggle(STATUS_LED);
}
// Forward from Serial to BLEUART
if (Serial.available())
{
// Delay to get enough input data since we have a
// limited amount of space in the transmit buffer
delay(2);
uint8_t buf[64];
int count = Serial.readBytes(buf, sizeof(buf));
bleuart.write( buf, count );
}
// Forward from BLEUART to Serial
if ( bleuart.available() )
{
uint8_t ch;
ch = (uint8_t) bleuart.read();
Serial.write(ch);
}
}
void connect_callback(void)
{
Serial.println(“Connected”);
}
void disconnect_callback(uint8_t reason)
{
(void) reason;
Serial.println();
Serial.println(“Disconnected”);
Serial.println(“Bluefruit will start advertising again”);
}
BLEClientUart
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
BLEClientUart是NUS或“北欧UART服务”(又称为“ BLE UART”)的客户端的包装器类。仅当您的Bluefruit nRF52板充当与其他公开BLEUart服务的BLE外设的中央通信时,才需要此操作。
API
BLEClientUart具有以下公共API:
下载:文件
复制代码
// Callback Signatures
typedef void (*rx_callback_t) (BLEClientUart& svc);
BLEClientUart(uint16_t fifo_depth = BLE_UART_DEFAULT_FIFO_DEPTH);
virtual bool begin(void);
virtual bool discover(uint16_t conn_handle);
void setRxCallback( rx_callback_t fp);
bool enableTXD(void);
bool disableTXD(void);
// Stream API
virtual int read ( void );
virtual int read ( uint8_t * buf, size_t size );
int read ( char * buf, size_t size ) { return read( (uint8_t*) buf, size); }
virtual size_t write ( uint8_t b );
virtual size_t write ( const uint8_t *content, size_t len );
virtual int available ( void );
virtual int peek ( void );
virtual void flush ( void ); // Callback Signatures
typedef void (*rx_callback_t) (BLEClientUart& svc);
BLEClientUart(uint16_t fifo_depth = BLE_UART_DEFAULT_FIFO_DEPTH);
virtual bool begin(void);
virtual bool discover(uint16_t conn_handle);
void setRxCallback( rx_callback_t fp);
bool enableTXD(void);
bool disableTXD(void);
// Stream API
virtual int read ( void );
virtual int read ( uint8_t * buf, size_t size );
int read ( char * buf, size_t size ) { return read( (uint8_t*) buf, size); }
virtual size_t write ( uint8_t b );
virtual size_t write ( const uint8_t *content, size_t len );
virtual int available ( void );
virtual int peek ( void );
virtual void flush ( void );
示例
下面的示例演示如何使用BLEClientUart帮助器类。
下载:文件
复制代码
#include
BLEClientDis clientDis;
BLEClientUart clientUart;
void setup()
{
Serial.begin(115200);
Serial.println(“Bluefruit52 Central BLEUART Example”);
Serial.println(“----------------------------------- ”);
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
// SRAM usage required by SoftDevice will increase dramatically with number of connections
Bluefruit.begin(0, 1);
Bluefruit.setName(“Bluefruit52 Central”);
// Configure DIS client
clientDis.begin();
// Init BLE Central Uart Serivce
clientUart.begin();
clientUart.setRxCallback(bleuart_rx_callback);
// Increase Blink rate to different from PrPh advertising mode
Bluefruit.setConnLedInterval(250);
// Callbacks for Central
Bluefruit.Central.setConnectCallback(connect_callback);
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Don‘t use active scan
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // // 0 = Don’t stop scanning after n seconds
}
/**
* Callback invoked when scanner pick up an advertising data
* @param report Structural advertising data
*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Check if advertising contain BleUart service
if ( Bluefruit.Scanner.checkReportForService(report, clientUart) )
{
Serial.print(“BLE UART service detected. Connecting 。.. ”);
// Connect to device with bleuart service in advertising
Bluefruit.Central.connect(report);
}
}
/**
* Callback invoked when an connection is established
* @param conn_handle
*/
void connect_callback(uint16_t conn_handle)
{
Serial.println(“Connected”);
Serial.print(“Dicovering DIS 。.. ”);
if ( clientDis.discover(conn_handle) )
{
Serial.println(“Found it”);
char buffer[32+1];
// read and print out Manufacturer
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getManufacturer(buffer, sizeof(buffer)) )
{
Serial.print(“Manufacturer: ”);
Serial.println(buffer);
}
// read and print out Model Number
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getModel(buffer, sizeof(buffer)) )
{
Serial.print(“Model: ”);
Serial.println(buffer);
}
Serial.println();
}
Serial.print(“Discovering BLE Uart Service 。.. ”);
if ( clientUart.discover(conn_handle) )
{
Serial.println(“Found it”);
Serial.println(“Enable TXD‘s notify”);
clientUart.enableTXD();
Serial.println(“Ready to receive from peripheral”);
}else
{
Serial.println(“Found NONE”);
// disconect since we couldn’t find bleuart service
Bluefruit.Central.disconnect(conn_handle);
}
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle
* @param reason
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.println(“Disconnected”);
}
/**
* Callback invoked when uart received data
* @param uart_svc Reference object to the service where the data
* arrived. In this example it is clientUart
*/
void bleuart_rx_callback(BLEClientUart& uart_svc)
{
Serial.print(“[RX]: ”);
while ( uart_svc.available() )
{
Serial.print( (char) uart_svc.read() );
}
Serial.println();
}
void loop()
{
if ( Bluefruit.Central.connected() )
{
// Not discovered yet
if ( clientUart.discovered() )
{
// Discovered means in working state
// Get Serial input and send to Peripheral
if ( Serial.available() )
{
delay(2); // delay a bit for all characters to arrive
char str[20+1] = { 0 };
Serial.readBytes(str, 20);
clientUart.print( str );
}
}
}
}
#include
BLEClientDis clientDis;
BLEClientUart clientUart;
void setup()
{
Serial.begin(115200);
Serial.println(“Bluefruit52 Central BLEUART Example”);
Serial.println(“----------------------------------- ”);
// Initialize Bluefruit with maximum connections as Peripheral = 0, Central = 1
// SRAM usage required by SoftDevice will increase dramatically with number of connections
Bluefruit.begin(0, 1);
Bluefruit.setName(“Bluefruit52 Central”);
// Configure DIS client
clientDis.begin();
// Init BLE Central Uart Serivce
clientUart.begin();
clientUart.setRxCallback(bleuart_rx_callback);
// Increase Blink rate to different from PrPh advertising mode
Bluefruit.setConnLedInterval(250);
// Callbacks for Central
Bluefruit.Central.setConnectCallback(connect_callback);
Bluefruit.Central.setDisconnectCallback(disconnect_callback);
/* Start Central Scanning
* - Enable auto scan if disconnected
* - Interval = 100 ms, window = 80 ms
* - Don‘t use active scan
* - Start(timeout) with timeout = 0 will scan forever (until connected)
*/
Bluefruit.Scanner.setRxCallback(scan_callback);
Bluefruit.Scanner.restartOnDisconnect(true);
Bluefruit.Scanner.setInterval(160, 80); // in unit of 0.625 ms
Bluefruit.Scanner.useActiveScan(false);
Bluefruit.Scanner.start(0); // // 0 = Don’t stop scanning after n seconds
}
/**
* Callback invoked when scanner pick up an advertising data
* @param report Structural advertising data
*/
void scan_callback(ble_gap_evt_adv_report_t* report)
{
// Check if advertising contain BleUart service
if ( Bluefruit.Scanner.checkReportForService(report, clientUart) )
{
Serial.print(“BLE UART service detected. Connecting 。.. ”);
// Connect to device with bleuart service in advertising
Bluefruit.Central.connect(report);
}
}
/**
* Callback invoked when an connection is established
* @param conn_handle
*/
void connect_callback(uint16_t conn_handle)
{
Serial.println(“Connected”);
Serial.print(“Dicovering DIS 。.. ”);
if ( clientDis.discover(conn_handle) )
{
Serial.println(“Found it”);
char buffer[32+1];
// read and print out Manufacturer
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getManufacturer(buffer, sizeof(buffer)) )
{
Serial.print(“Manufacturer: ”);
Serial.println(buffer);
}
// read and print out Model Number
memset(buffer, 0, sizeof(buffer));
if ( clientDis.getModel(buffer, sizeof(buffer)) )
{
Serial.print(“Model: ”);
Serial.println(buffer);
}
Serial.println();
}
Serial.print(“Discovering BLE Uart Service 。.. ”);
if ( clientUart.discover(conn_handle) )
{
Serial.println(“Found it”);
Serial.println(“Enable TXD‘s notify”);
clientUart.enableTXD();
Serial.println(“Ready to receive from peripheral”);
}else
{
Serial.println(“Found NONE”);
// disconect since we couldn’t find bleuart service
Bluefruit.Central.disconnect(conn_handle);
}
}
/**
* Callback invoked when a connection is dropped
* @param conn_handle
* @param reason
*/
void disconnect_callback(uint16_t conn_handle, uint8_t reason)
{
(void) conn_handle;
(void) reason;
Serial.println(“Disconnected”);
}
/**
* Callback invoked when uart received data
* @param uart_svc Reference object to the service where the data
* arrived. In this example it is clientUart
*/
void bleuart_rx_callback(BLEClientUart& uart_svc)
{
Serial.print(“[RX]: ”);
while ( uart_svc.available() )
{
Serial.print( (char) uart_svc.read() );
}
Serial.println();
}
void loop()
{
if ( Bluefruit.Central.connected() )
{
// Not discovered yet
if ( clientUart.discovered() )
{
// Discovered means in working state
// Get Serial input and send to Peripheral
if ( Serial.available() )
{
delay(2); // delay a bit for all characters to arrive
char str[20+1] = { 0 };
Serial.readBytes(str, 20);
clientUart.print( str );
}
}
}
}
BLEBeacon
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
BLEBeacon帮助程序类使您可以轻松地将nRF52配置为“信标”,它使用广告包将特定格式的数据块发送到侦听范围内的任何设备。
以下值必须设置才能生成有效的“信标”包:
制造商ID :一个16位值(已在Bluetooth SIG中注册!),用于标识
主要:一个16位的“主要”数字,用于区分信标节点。
次要:A 16位的“次要”数字,用于区分信标节点。
RSSI @ 1M :一个带符号的8位值(int8_t),指示距离RSSI 1m处的RSSI测量值节点,用于估计与信标本身的距离。
这些值可以是s
API
BLEBeacon具有以下公共API:
下载:文件
复制代码
// Constructors
BLEBeacon(void);
BLEBeacon(uint8_t const uuid128[16]);
BLEBeacon(uint8_t const uuid128[16], uint16_t major, uint16_t minor, int8_t rssi);
// Set the beacon payload values
void setManufacturer(uint16_t manfacturer);
void setUuid(uint8_t const uuid128[16]);
void setMajorMinor(uint16_t major, uint16_t minor);
void setRssiAt1m(int8_t rssi);
// Start advertising
bool start(void);
bool start(BLEAdvertising& adv); // Constructors
BLEBeacon(void);
BLEBeacon(uint8_t const uuid128[16]);
BLEBeacon(uint8_t const uuid128[16], uint16_t major, uint16_t minor, int8_t rssi);
// Set the beacon payload values
void setManufacturer(uint16_t manfacturer);
void setUuid(uint8_t const uuid128[16]);
void setMajorMinor(uint16_t major, uint16_t minor);
void setRssiAt1m(int8_t rssi);
// Start advertising
bool start(void);
bool start(BLEAdvertising& adv);
除了这些功能外,BLEAdvertising类(可通过“ Bluefruit.Advertising。*”访问)公开以下功能,将信标有效负载分配给广告有效负载:
下载:文件
复制代码
bool setBeacon(BLEBeacon& beacon); bool setBeacon(BLEBeacon& beacon);
请参见以下示例,以了解具体用法
示例
以下示例将nRF52配置为发布“信标”有效负载:
下载:文件
复制代码
#include
// Beacon uses the Manufacturer Specific Data field in the advertising
// packet, which means you must provide a valid Manufacturer ID. Update
// the field below to an appropriate value. For a list of valid IDs see:
// https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
// 0x004C is Apple (for example)
#define MANUFACTURER_ID 0x004C
// AirLocate UUID: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0
uint8_t beaconUuid[16] =
{
0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2,
0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0,
};
// A valid Beacon packet consists of the following information:
// UUID, Major, Minor, RSSI @ 1M
BLEBeacon beacon(beaconUuid, 0x0001, 0x0000, -54);
void setup()
{
Serial.begin(115200);
Serial.println(“Bluefruit52 Beacon Example”);
Bluefruit.begin();
Bluefruit.setName(“Bluefruit52”);
// Manufacturer ID is required for Manufacturer Specific Data
beacon.setManufacturer(MANUFACTURER_ID);
// Setup the advertising packet
setupAdv();
// Start advertising
Bluefruit.Advertising.start();
}
void setupAdv(void)
{
// Set the beacon payload using the BLEBeacon class populated
// earlier in this example
Bluefruit.Advertising.setBeacon(beacon);
// char* adv = Bluefruit.Advertising.getData();
// There is no room left for ‘Name’ in the advertising packet
// Use the optinal secondary Scan Response packet for ‘Name’ instead
Bluefruit.ScanResponse.addName();
}
void loop()
{
// Toggle both LEDs every second
digitalToggle(LED_BUILTIN);
delay(1000);
} #include
// Beacon uses the Manufacturer Specific Data field in the advertising
// packet, which means you must provide a valid Manufacturer ID. Update
// the field below to an appropriate value. For a list of valid IDs see:
// https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
// 0x004C is Apple (for example)
#define MANUFACTURER_ID 0x004C
// AirLocate UUID: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0
uint8_t beaconUuid[16] =
{
0xE2, 0xC5, 0x6D, 0xB5, 0xDF, 0xFB, 0x48, 0xD2,
0xB0, 0x60, 0xD0, 0xF5, 0xA7, 0x10, 0x96, 0xE0,
};
// A valid Beacon packet consists of the following information:
// UUID, Major, Minor, RSSI @ 1M
BLEBeacon beacon(beaconUuid, 0x0001, 0x0000, -54);
void setup()
{
Serial.begin(115200);
Serial.println(“Bluefruit52 Beacon Example”);
Bluefruit.begin();
Bluefruit.setName(“Bluefruit52”);
// Manufacturer ID is required for Manufacturer Specific Data
beacon.setManufacturer(MANUFACTURER_ID);
// Setup the advertising packet
setupAdv();
// Start advertising
Bluefruit.Advertising.start();
}
void setupAdv(void)
{
// Set the beacon payload using the BLEBeacon class populated
// earlier in this example
Bluefruit.Advertising.setBeacon(beacon);
// char* adv = Bluefruit.Advertising.getData();
// There is no room left for ‘Name’ in the advertising packet
// Use the optinal secondary Scan Response packet for ‘Name’ instead
Bluefruit.ScanResponse.addName();
}
void loop()
{
// Toggle both LEDs every second
digitalToggle(LED_BUILTIN);
delay(1000);
}
测试
如果使用nRF Beacons应用程序进行测试(iOS或Android) ),您可以将应用程序配置为查找您提供的UUID,制造商ID,主要和次要值,并且应该可以看到信标,如以下两个屏幕截图所示:
确保UUID,Major和Minor值匹配,否则应用程序将无法检测到您的信标节点!
BLEMidi
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
BLEMidi是一个帮助程序类,它添加了对使用Bluetooth over蓝牙LE规范的MIDI消息发送和接收MIDI的支持。 BLEMidi支持完整的标准MIDI协议(包括SysEx消息),它还可以用作Arduino MIDI库的硬件接口。
API
BLEMidi具有以下公共API。
下载:文件
复制代码
// Constructor
BLEMidi(uint16_t fifo_depth = 128);
err_t begin (void);
bool notifyEnabled (void);
// Stream API for Arduino MIDI Library Interface
int read (void);
size_t write (uint8_t b);
int available (void);
int peek (void);
void flush (void);
size_t write (const char *str);
size_t write (const uint8_t *buffer, size_t size); // Constructor
BLEMidi(uint16_t fifo_depth = 128);
err_t begin (void);
bool notifyEnabled (void);
// Stream API for Arduino MIDI Library Interface
int read (void);
size_t write (uint8_t b);
int available (void);
int peek (void);
void flush (void);
size_t write (const char *str);
size_t write (const uint8_t *buffer, size_t size);
安装Arduino MIDI库
BLEMidi与Arduino MIDI库结合使用最容易。在继续示例代码之前,您将需要安装 4.3.0或更高版本。
下一步,从主题下拉列表中选择 Communication ,然后输入 MIDI库进入搜索框。单击安装按钮,以安装4.3.0或更高版本的 MIDI库。
示例
blemidi 示例演示了如何将BLEMidi帮助器类与 Arduino MIDI库一起使用。该示例发送一个循环的琶音,并将打开的所有MIDI音符打印和音符消息打印到Arduino串行监视器。
此示例可能已过时,您应始终参考以下示例中的最新示例代码Bluefruit52示例文件夹!
下载:文件
复制代码
/*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include
#include
BLEDis bledis;
BLEMidi blemidi;
// Create a new instance of the Arduino MIDI Library,
// and attach BluefruitLE MIDI as the transport.
MIDI_CREATE_BLE_INSTANCE(blemidi);
// Variable that holds the current position in the sequence.
int position = 0;
// Store example melody as an array of note values
byte note_sequence[] = {
74,78,81,86,90,93,98,102,57,61,66,69,73,78,81,85,88,92,97,100,97,92,88,85,81,78,
74,69,66,62,57,62,66,69,74,78,81,86,90,93,97,102,97,93,90,85,81,78,73,68,64,61,
56,61,64,68,74,78,81,86,90,93,98,102
};
void setup()
{
Serial.begin(115200);
Serial.println(“Adafruit Bluefruit52 MIDI over Bluetooth LE Example”);
Bluefruit.begin();
Bluefruit.setName(“Bluefruit52 MIDI”);
// Setup the on board blue LED to be enabled on CONNECT
Bluefruit.autoConnLed(true);
// Configure and Start Device Information Service
bledis.setManufacturer(“Adafruit Industries”);
bledis.setModel(“Bluefruit Feather52”);
bledis.begin();
// Initialize MIDI, and listen to all MIDI channels
// This will also call blemidi service‘s begin()
MIDI.begin(MIDI_CHANNEL_OMNI);
// Attach the handleNoteOn function to the MIDI Library. It will
// be called whenever the Bluefruit receives MIDI Note On messages.
MIDI.setHandleNoteOn(handleNoteOn);
// Do the same for MIDI Note Off messages.
MIDI.setHandleNoteOff(handleNoteOff);
// Set General Discoverable Mode flag
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
// Advertise TX Power
Bluefruit.Advertising.addTxPower();
// Advertise BLE MIDI Service
Bluefruit.Advertising.addService(blemidi);
// Advertise device name in the Scan Response
Bluefruit.ScanResponse.addName();
// Start Advertising
Bluefruit.Advertising.start();
// Start MIDI read loop
Scheduler.startLoop(midiRead);
}
void handleNoteOn(byte channel, byte pitch, byte velocity)
{
// Log when a note is pressed.
Serial.printf(“Note on: channel = %d, pitch = %d, velocity - %d”, channel, pitch, velocity);
Serial.println();
}
void handleNoteOff(byte channel, byte pitch, byte velocity)
{
// Log when a note is released.
Serial.printf(“Note off: channel = %d, pitch = %d, velocity - %d”, channel, pitch, velocity);
Serial.println();
}
void loop()
{
// Don’t continue if we aren‘t connected.
if (! Bluefruit.connected()) {
return;
}
// Don’t continue if the connected device isn‘t ready to receive messages.
if (! blemidi.notifyEnabled()) {
return;
}
// Setup variables for the current and previous
// positions in the note sequence.
int current = position;
int previous = position - 1;
// If we currently are at position 0, set the
// previous position to the last note in the sequence.
if (previous 《 0) {
previous = sizeof(note_sequence) - 1;
}
// Send Note On for current position at full velocity (127) on channel 1.
MIDI.sendNoteOn(note_sequence[current], 127, 1);
// Send Note Off for previous note.
MIDI.sendNoteOff(note_sequence[previous], 0, 1);
// Increment position
position++;
// If we are at the end of the sequence, start over.
if (position 》= sizeof(note_sequence)) {
position = 0;
}
delay(286);
}
void midiRead()
{
// Don’t continue if we aren‘t connected.
if (! Bluefruit.connected()) {
return;
}
// Don’t continue if the connected device isn‘t ready to receive messages.
if (! blemidi.notifyEnabled()) {
return;
}
// read any new MIDI messages
MIDI.read();
} /*********************************************************************
This is an example for our nRF52 based Bluefruit LE modules
Pick one up today in the adafruit shop!
Adafruit invests time and resources providing this open source code,
please support Adafruit and open-source hardware by purchasing
products from Adafruit!
MIT license, check LICENSE for more information
All text above, and the splash screen below must be included in
any redistribution
*********************************************************************/
#include
#include
BLEDis bledis;
BLEMidi blemidi;
// Create a new instance of the Arduino MIDI Library,
// and attach BluefruitLE MIDI as the transport.
MIDI_CREATE_BLE_INSTANCE(blemidi);
// Variable that holds the current position in the sequence.
int position = 0;
// Store example melody as an array of note values
byte note_sequence[] = {
74,78,81,86,90,93,98,102,57,61,66,69,73,78,81,85,88,92,97,100,97,92,88,85,81,78,
74,69,66,62,57,62,66,69,74,78,81,86,90,93,97,102,97,93,90,85,81,78,73,68,64,61,
56,61,64,68,74,78,81,86,90,93,98,102
};
void setup()
{
Serial.begin(115200);
Serial.println(“Adafruit Bluefruit52 MIDI over Bluetooth LE Example”);
Bluefruit.begin();
Bluefruit.setName(“Bluefruit52 MIDI”);
// Setup the on board blue LED to be enabled on CONNECT
Bluefruit.autoConnLed(true);
// Configure and Start Device Information Service
bledis.setManufacturer(“Adafruit Industries”);
bledis.setModel(“Bluefruit Feather52”);
bledis.begin();
// Initialize MIDI, and listen to all MIDI channels
// This will also call blemidi service’s begin()
MIDI.begin(MIDI_CHANNEL_OMNI);
// Attach the handleNoteOn function to the MIDI Library. It will
// be called whenever the Bluefruit receives MIDI Note On messages.
MIDI.setHandleNoteOn(handleNoteOn);
// Do the same for MIDI Note Off messages.
MIDI.setHandleNoteOff(handleNoteOff);
// Set General Discoverable Mode flag
Bluefruit.Advertising.addFlags(BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE);
// Advertise TX Power
Bluefruit.Advertising.addTxPower();
// Advertise BLE MIDI Service
Bluefruit.Advertising.addService(blemidi);
// Advertise device name in the Scan Response
Bluefruit.ScanResponse.addName();
// Start Advertising
Bluefruit.Advertising.start();
// Start MIDI read loop
Scheduler.startLoop(midiRead);
}
void handleNoteOn(byte channel, byte pitch, byte velocity)
{
// Log when a note is pressed.
Serial.printf(“Note on: channel = %d, pitch = %d, velocity - %d”, channel, pitch, velocity);
Serial.println();
}
void handleNoteOff(byte channel, byte pitch, byte velocity)
{
// Log when a note is released.
Serial.printf(“Note off: channel = %d, pitch = %d, velocity - %d”, channel, pitch, velocity);
Serial.println();
}
void loop()
{
// Don‘t continue if we aren’t connected.
if (! Bluefruit.connected()) {
return;
}
// Don‘t continue if the connected device isn’t ready to receive messages.
if (! blemidi.notifyEnabled()) {
return;
}
// Setup variables for the current and previous
// positions in the note sequence.
int current = position;
int previous = position - 1;
// If we currently are at position 0, set the
// previous position to the last note in the sequence.
if (previous 《 0) {
previous = sizeof(note_sequence) - 1;
}
// Send Note On for current position at full velocity (127) on channel 1.
MIDI.sendNoteOn(note_sequence[current], 127, 1);
// Send Note Off for previous note.
MIDI.sendNoteOff(note_sequence[previous], 0, 1);
// Increment position
position++;
// If we are at the end of the sequence, start over.
if (position 》= sizeof(note_sequence)) {
position = 0;
}
delay(286);
}
void midiRead()
{
// Don‘t continue if we aren’t connected.
if (! Bluefruit.connected()) {
return;
}
// Don‘t continue if the connected device isn’t ready to receive messages.
if (! blemidi.notifyEnabled()) {
return;
}
// read any new MIDI messages
MIDI.read();
}
用法
您需要在所选平台上进行一些设置,才能连接到启用BLE MIDI的Bluefruit52。
单击下面的平台查看设备的BLE MIDI设置说明:
macOS(OS X)
iOS
Android
Windows
将Bluefruit52连接到您的软件合成器后,琶音将自动播放。下面的视频显示了在iOS上连接到Moog的Animoog的Bluefruit52。
注意:视频中使用的板是预发布的原型。生产板是标准的Adafruit Black。
BLEHidAdafruit
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
BLEHidAdafruit允许您使用HID(人机接口设备)配置文件模拟鼠标或键盘,该配置文件是Bluetooth Low Energy标准的一部分。
大多数支持Bluetooth Low Energy且运行最新的现代移动设备一旦您将Bluefruit nRF52/nRF52840 Feather配对并运行适当的草图,系统通常会立即支持蓝牙低功耗鼠标和键盘。
API
BLEHidAdafruit帮助器类具有以下公共API:
下载:文件
复制代码
// Constructor
BLEHidAdafruit(void);
// Call this once to start the HID service
virtual err_t begin(void);
// Keyboard
err_t keyboardReport(hid_keyboard_report_t* report);
err_t keyboardReport(uint8_t modifier, uint8_t keycode[6]);
err_t keyboardReport(uint8_t modifier, uint8_t keycode0, uint8_t keycode1=0, uint8_t keycode2=0, uint8_t keycode3=0, uint8_t keycode4=0, uint8_t keycode5=0);
err_t keyPress(char ch);
err_t keyRelease(void);
err_t keySequence(const char* str, int interal=5);
// Consumer Media Keys
err_t consumerReport(uint16_t usage_code);
err_t consumerKeyPress(uint16_t usage_code);
err_t consumerKeyRelease(void);
// Mouse
err_t mouseReport(hid_mouse_report_t* report);
err_t mouseReport(uint8_t buttons, int8_t x, int8_t y, int8_t wheel=0, int8_t pan=0);
err_t mouseButtonPress(uint8_t buttons);
err_t mouseButtonRelease(void);
err_t mouseMove(int8_t x, int8_t y);
err_t mouseScroll(int8_t scroll);
err_t mousePan(int8_t pan); // Constructor
BLEHidAdafruit(void);
// Call this once to start the HID service
virtual err_t begin(void);
// Keyboard
err_t keyboardReport(hid_keyboard_report_t* report);
err_t keyboardReport(uint8_t modifier, uint8_t keycode[6]);
err_t keyboardReport(uint8_t modifier, uint8_t keycode0, uint8_t keycode1=0, uint8_t keycode2=0, uint8_t keycode3=0, uint8_t keycode4=0, uint8_t keycode5=0);
err_t keyPress(char ch);
err_t keyRelease(void);
err_t keySequence(const char* str, int interal=5);
// Consumer Media Keys
err_t consumerReport(uint16_t usage_code);
err_t consumerKeyPress(uint16_t usage_code);
err_t consumerKeyRelease(void);
// Mouse
err_t mouseReport(hid_mouse_report_t* report);
err_t mouseReport(uint8_t buttons, int8_t x, int8_t y, int8_t wheel=0, int8_t pan=0);
err_t mouseButtonPress(uint8_t buttons);
err_t mouseButtonRelease(void);
err_t mouseMove(int8_t x, int8_t y);
err_t mouseScroll(int8_t scroll);
err_t mousePan(int8_t pan);
示例草图
有许多示例草图显示h由于要使用BLEHidAdafruit类。您可以通过以下链接在Github上浏览最新的源代码:
hid_keyboard:此示例将模拟一个HID键盘,等待数据通过nRF52的串行端口(通过USB串行)到达,并通过无线方式将该数据发送到绑定的Central设备。
hid_mouse:此示例将模拟HID鼠标。要使用它,请运行草图并打开“串行监视器”,然后输入适当的字符以移动鼠标或触发/释放鼠标按钮。
绑定HID设备
要使用HID鼠标或键盘,您首先需要绑定这两个设备。绑定过程包括以下步骤:
两个设备将正常相互连接
在两个设备之间交换一组安全密钥,并将其存储在非安全密钥中。两侧的易失性存储器。这是为了确保双方有足够的信心与自己认为用于将来连接的设备进行对话,并通过设备之间的空中通信进行加密(以便人们可以“嗅探”您的键盘数据等)。
在nRF52端,此密钥数据将使用内部文件系统存储在为此目的而保留的闪存部分中。
涉及这些安全密钥的存储过程作为绑定,并允许绑定的设备在将来进行安全通信而无需用户干预。
要取消绑定协议,您只需通过clearbondssketch删除nRF52上的密钥,或删除您的计算机移动设备上的绑定数据。
如果遇到任何绑定问题,请尝试运行clearbonds草图以从本地非易失性存储器中删除旧的绑定数据!
设置您的Bluefruit设备进行绑定
要绑定设备,请在nRF52上运行适当的HID草图以模拟HID鼠标或HID键盘。如果您使用的是HID鼠标示例,则可能需要打开串行监视器才能使用它。
在此示例中,我们将运行 hid_keyboard 示例草图,并对其进行闪烁到nRF52,应该会给您以下结果:
打开串行监视器将为您提供以下输出(尽管可能会有所不同,具体取决于您选择的调试级别):
在iOS上绑定
要绑定到iOS设备,请确保草图正在运行(如上所述),然后进入设置应用,然后选择蓝牙。
您应该会在此页面底部看到名为 Bluefruit52 的设备(这可能会有所不同取决于您使用的草图的版本!):
点击设备,您将收到如下配对请求:
点击对按钮,设备将配对并绑定,并且将来会自动相互连接。
如果一切顺利,您将在我的设备中看到该设备列表,如下所示:
测试HID键盘和绑定
要测试HID键盘草图和绑定过程,请打开 Serial Monitor (或您喜欢的终端仿真器),输入一些文本,如果您正在使用Serial Monitor,请单击发送 》按钮。这样会将一些文本无线发送到应用程序中焦点所在的任何文本框或文本控件。
文本将出现在您的移动应用或绑定设备中。
如果字符与您的字符不完全匹配发送,请确保检查您的键盘语言设置,因为您可能正在将数据发送到具有不同键盘设置的设备!
BLEAncs
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
BLEAncs是帮助程序类,使您可以从Apple通知中心服务从iPhone或iPad等设备接收通知。
,它可用于接收警报,例如来电或未接电话,电子邮件或大多数显示在移动设备屏幕上的警报。
API
由于BLEAncs类正在进行中,因此BLEAncs帮助程序类的最新公共API应该在这里查看。
ANCS OLED示例
ancs_oled 示例使用Adafruit FeatherWing OLED显示任何传入的警报。
草图要求
要使用此示例草图,必须在系统上安装以下库:
Adafruit_GFX(Github源)
Adafruit_SSD1306(Github源)
Bluefruit nRF52 BSP的0.6.0或更高版本
加载Sket ch
可以通过外围设备》 ancs_oled 下的示例菜单加载ancs_oled草图:
加载草图后,您可以构建固件,然后通过上传按钮或菜单选项:
请确保Adafruit_SSD1306.h文件已启用“ SSD1306_128_32”宏。在设置了“ SSD1306_128_64”的情况下运行草图将导致损坏的数据出现在OLED显示屏上。
一旦草图在nRF52 Feather上运行,您可以继续进行操作一次配对过程,如下所述。
配对到您的移动设备
在开始接收通知之前,您需要将nRF52 Feather和移动设备“配对”。
配对过程会导致一组密钥被锁定。在两个设备上进行了交换和存储,以便双方都知道它正在与其最初绑定的同一设备进行通信,并防止中间的任何设备窃听潜在的敏感数据。
一次性配对该过程如下所述,并假设您已经在nRF52设备上运行ancs_oled草图。
1。在设置应用中,转到蓝牙:
2。滚动到“我的设备”列表的底部,然后单击其他设备下的 Bluefruit52 :
3。出现配对对话框时,单击配对按钮:
4。等待配对过程完成,此时 Bluefruit52 应出现在我的处于已连接状态的设备列表:
一旦两个设备配对,它们将自动重新连接只要它们在有效范围内并启用了蓝牙无线电,便可以互相访问。
等待警报
这时,移动设备生成的任何警报将与通知类别和日期一起显示在OLED显示屏上:
某些警报(例如传入呼叫)也可以与之相关联的动作,利用显示屏左侧的三个按钮来决定要采取的动作。
在ancs_oled示例中,我们有一个特殊的部分您可以按相应按钮接受或拒绝呼叫的来电代码:
下载:file
复制代码
// Check buttons
uint32_t presedButtons = readPressedButtons();
if ( myNotifs[activeIndex].ntf.categoryID == ANCS_CAT_INCOMING_CALL )
{
/* Incoming call event
* - Button A to accept call
* - Button C to decline call
*/
if ( presedButtons & bit(BUTTON_A) )
{
bleancs.actPositive(myNotifs[activeIndex].ntf.uid);
}
if ( presedButtons & bit(BUTTON_C) )
{
bleancs.actNegative(myNotifs[activeIndex].ntf.uid);
}
}
// Check buttons
uint32_t presedButtons = readPressedButtons();
if ( myNotifs[activeIndex].ntf.categoryID == ANCS_CAT_INCOMING_CALL )
{
/* Incoming call event
* - Button A to accept call
* - Button C to decline call
*/
if ( presedButtons & bit(BUTTON_A) )
{
bleancs.actPositive(myNotifs[activeIndex].ntf.uid);
}
if ( presedButtons & bit(BUTTON_C) )
{
bleancs.actNegative(myNotifs[activeIndex].ntf.uid);
}
}
BLEClientCts
Bluefruit nRF52 BSP代码库正在根据客户反馈进行积极开发和测试。因此,此处的课程文档不完整,您应该向Github存储库咨询最新的代码和API开发:https://goo.gl/LdEx62
BLEClientCts是一个实现当前采用的当前时间服务的帮助程序类,它使您能够从iPhone或iPad等设备接收时间。
API
下载:文件
复制代码
// Callback Signatures
typedef void (*adjust_callback_t) (uint8_t reason);
BLEClientCts(void);
virtual bool begin(void);
virtual bool discover(uint16_t conn_handle);
bool getCurrentTime(void);
bool getLocalTimeInfo(void);
bool enableAdjust(void);
void setAdjustCallback(adjust_callback_t fp);
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.current_time.xml
struct ATTR_PACKED {
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t weekday;
uint8_t subsecond;
uint8_t adjust_reason;
} Time;
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.local_time_information.xml
struct ATTR_PACKED {
int8_t timezone;
uint8_t dst_offset;
}LocalInfo; // Callback Signatures
typedef void (*adjust_callback_t) (uint8_t reason);
BLEClientCts(void);
virtual bool begin(void);
virtual bool discover(uint16_t conn_handle);
bool getCurrentTime(void);
bool getLocalTimeInfo(void);
bool enableAdjust(void);
void setAdjustCallback(adjust_callback_t fp);
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.current_time.xml
struct ATTR_PACKED {
uint16_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t weekday;
uint8_t subsecond;
uint8_t adjust_reason;
} Time;
// https://www.bluetooth.com/specifications/gatt/viewer?attributeXmlFile=org.bluetooth.characteristic.local_time_information.xml
struct ATTR_PACKED {
int8_t timezone;
uint8_t dst_offset;
}LocalInfo;
客户端CTS OLED示例
client_cts_oled 示例使用Adafruit FeatherWing OLED显示接收时间。
草图要求
为了使用此示例,请绘制以下库必须在您的系统上安装:
Adafruit_GFX(Github源)
Adafruit_SSD1306(Github源)
加载草图
client_cts_oled草图可以通过外围设备》 client_cts_oled 下的示例菜单加载:
加载了草图后,您可以构建固件,然后通过上传按钮或菜单选项将其刷新到设备中一旦草图在nRF52 Feather上运行,您就可以进行一次性配对过程,如下所述。
确保Adafruit_SSD1306.h文件已启用“ SSD1306_128_32”宏。运行设置为‘SSD1306_128_64’的草图将导致损坏的数据出现在OLED显示屏上。
配对到您的移动设备
在开始接收通知之前,您需要将nRF52 Feather和移动设备“配对”。
配对过程使一组密钥被交换并存储在两个设备上,以便每一侧都知道它正在与其最初绑定的同一设备通信,并防止中间的任何设备窃听潜在的敏感信息数据。
一次性配对过程如下所述,并假设您已经在nRF52设备上运行ancs_oled草图。
1。在设置应用中,转到蓝牙:
2。滚动到“我的设备”列表的底部,然后单击其他设备下的 Bluefruit52 :
3。出现配对对话框时,单击配对按钮:
4。等待配对过程完成,此时 Bluefruit52 应出现在我的处于已连接状态的设备列表:
一旦两个设备配对,它们将自动重新连接只要它们在有效范围内并启用了蓝牙无线电,便可以互相访问。
等待时间数据
这时,将读取来自移动设备的时间数据并将其显示在OLED上。出于演示目的,草图将每秒从移动设备读取一次时间数据。但是,实际上,nRF52应该具有一个内部计时器来跟踪秒,并且仅在数小时或数天后才能与移动设备进行读取/同步,这类似于IP设备从NTP服务器获取时间的方式。
BLECentral
此页面正在进行中,因为API随着我们迁移到S132v5(nRF52832)和S140(nRF52840),并添加了更好的中央模式支持。
可以通过Bluefruit.Central.*访问中央模式API,并且具有以下公共功能:
下载:文件
复制代码
void begin(void);
/*------------------------------------------------------------------*/
/* GAP
*------------------------------------------------------------------*/
bool setConnInterval(uint16_t min, uint16_t max);
bool setConnIntervalMS (uint16_t min_ms, uint16_t max_ms);
bool connect(const ble_gap_evt_adv_report_t* adv_report);
bool connect(const ble_gap_addr_t *peer_addr);
bool disconnect(uint16_t conn_handle);
bool connected (uint16_t conn_handle); // If connected to a specific peripheral
bool connected (void); // If connected to any peripherals
/*------------- Callbacks -------------*/
void setConnectCallback ( BLEGap::connect_callback_t fp);
void setDisconnectCallback( BLEGap::disconnect_callback_t fp); void begin(void);
/*------------------------------------------------------------------*/
/* GAP
*------------------------------------------------------------------*/
bool setConnInterval(uint16_t min, uint16_t max);
bool setConnIntervalMS (uint16_t min_ms, uint16_t max_ms);
bool connect(const ble_gap_evt_adv_report_t* adv_report);
bool connect(const ble_gap_addr_t *peer_addr);
bool disconnect(uint16_t conn_handle);
bool connected (uint16_t conn_handle); // If connected to a specific peripheral
bool connected (void); // If connected to any peripherals
/*------------- Callbacks -------------*/
void setConnectCallback ( BLEGap::connect_callback_t fp);
void setDisconnectCallback( BLEGap::disconnect_callback_t fp);
有关如何使用中央模式API的示例,请参见中央示例文件夹。
nRF52 ADC
nRF52系列包括一个可调的“逐次逼近型ADC”,可以将其配置为以高达14位分辨率(0..16383)转换数据。 ,并且参考电压可以在内部最高调整为3.6V。
ADC的默认值为 10位分辨率(0..1023),其中 3.6V参考电压,表示从ADC返回的每个数字= 3600mV/1024 = 3.515625mV 。
模拟参考电压
内部参考电压为0.6V,具有可变增益设置,可以调节通过 analogReference(。..)函数,提供以下值之一:
AR_INTERNAL (0.6V Ref * 6 = 0 。.3.6V)《-默认
AR_INTERNAL_3_0 (0.6V Ref * 5 = 0..3.0V)
AR_INTERNAL_2_4 (0.6V参考* 4 = 0..2.4V)
AR_INTERNAL_1 _8 (0.6V参考* 3 = 0..1.8V)
AR_INTERNAL_1_2 (0.6V Ref * 2 = 0..1.6V)
AR_VDD4 (VDD/4 REF * 4 = 0..VDD)
例如:
下载:文件
复制代码
// Set the analog reference to 3.0V (default = 3.6V)
analogReference(AR_INTERNAL_3_0); // Set the analog reference to 3.0V (default = 3.6V)
analogReference(AR_INTERNAL_3_0);
模拟分辨率
ADC分辨率可以设置为8 ,10,12或14位,使用 analogReadResolution(。..)函数,默认值为10位:
下载:文件
复制代码
// Set the resolution to 12-bit (0..4095)
analogReadResolution(12); // Can be 8, 10, 12 or 14 // Set the resolution to 12-bit (0..4095)
analogReadResolution(12); // Can be 8, 10, 12 or 14
默认ADC示例(10位,3.6V参考)
原始来源该代码包含在nRF52 BSP中,可以在此处在线查看。
下载:项目邮编或 adc.ino | 在Github上查看
复制代码
int adcin = A5;
int adcvalue = 0;
float mv_per_lsb = 3600.0F/1024.0F; // 10-bit ADC with 3.6V input range
void setup() {
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
}
void loop() {
// Get a fresh ADC value
adcvalue = analogRead(adcin);
// Display the results
Serial.print(adcvalue);
Serial.print(“ [”);
Serial.print((float)adcvalue * mv_per_lsb);
Serial.println(“ mV]”);
delay(100);
}
int adcin = A5;
int adcvalue = 0;
float mv_per_lsb = 3600.0F/1024.0F; // 10-bit ADC with 3.6V input range
void setup() {
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
}
void loop() {
// Get a fresh ADC value
adcvalue = analogRead(adcin);
// Display the results
Serial.print(adcvalue);
Serial.print(“ [”);
Serial.print((float)adcvalue * mv_per_lsb);
Serial.println(“ mV]”);
delay(100);
}
高级示例(12位,3.0V参考)
此代码的原始源包含在nRF52 BSP,可以在此处在线查看。
下载:Project Zip 或adc_vbat.ino | 在Github上查看
复制代码
#include
#if defined ARDUINO_NRF52840_CIRCUITPLAY
#define PIN_VBAT A8 // this is just a mock read, we‘ll use the light sensor, so we can run the test
#endif
uint32_t vbat_pin = PIN_VBAT; // A7 for feather nRF52832, A6 for nRF52840
#define VBAT_MV_PER_LSB (0.73242188F) // 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096
#ifdef NRF52840_XXAA // if this is for nrf52840
#define VBAT_DIVIDER (0.5F) // 150K + 150K voltage divider on VBAT
#define VBAT_DIVIDER_COMP (2.0F) // Compensation factor for the VBAT divider
#else
#define VBAT_DIVIDER (0.71275837F) // 2M + 0.806M voltage divider on VBAT = (2M / (0.806M + 2M))
#define VBAT_DIVIDER_COMP (1.403F) // Compensation factor for the VBAT divider
#endif
#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB)
float readVBAT(void) {
float raw;
// Set the analog reference to 3.0V (default = 3.6V)
analogReference(AR_INTERNAL_3_0);
// Set the resolution to 12-bit (0..4095)
analogReadResolution(12); // Can be 8, 10, 12 or 14
// Let the ADC settle
delay(1);
// Get the raw 12-bit, 0..3000mV ADC value
raw = analogRead(vbat_pin);
// Set the ADC back to the default settings
analogReference(AR_DEFAULT);
analogReadResolution(10);
// Convert the raw value to compensated mv, taking the resistor-
// divider into account (providing the actual LIPO voltage)
// ADC range is 0..3000mV and resolution is 12-bit (0..4095)
return raw * REAL_VBAT_MV_PER_LSB;
}
uint8_t mvToPercent(float mvolts) {
if(mvolts《3300)
return 0;
if(mvolts 《3600) {
mvolts -= 3300;
return mvolts/30;
}
mvolts -= 3600;
return 10 + (mvolts * 0.15F ); // thats mvolts /6.66666666
}
void setup() {
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
// Get a single ADC sample and throw it away
readVBAT();
}
void loop() {
// Get a raw ADC reading
float vbat_mv = readVBAT();
// Convert from raw mv to percentage (based on LIPO chemistry)
uint8_t vbat_per = mvToPercent(vbat_mv);
// Display the results
Serial.print(“LIPO = ”);
Serial.print(vbat_mv);
Serial.print(“ mV (”);
Serial.print(vbat_per);
Serial.println(“%)”);
delay(1000);
} #include
#if defined ARDUINO_NRF52840_CIRCUITPLAY
#define PIN_VBAT A8 // this is just a mock read, we’ll use the light sensor, so we can run the test
#endif
uint32_t vbat_pin = PIN_VBAT; // A7 for feather nRF52832, A6 for nRF52840
#define VBAT_MV_PER_LSB (0.73242188F) // 3.0V ADC range and 12-bit ADC resolution = 3000mV/4096
#ifdef NRF52840_XXAA // if this is for nrf52840
#define VBAT_DIVIDER (0.5F) // 150K + 150K voltage divider on VBAT
#define VBAT_DIVIDER_COMP (2.0F) // Compensation factor for the VBAT divider
#else
#define VBAT_DIVIDER (0.71275837F) // 2M + 0.806M voltage divider on VBAT = (2M / (0.806M + 2M))
#define VBAT_DIVIDER_COMP (1.403F) // Compensation factor for the VBAT divider
#endif
#define REAL_VBAT_MV_PER_LSB (VBAT_DIVIDER_COMP * VBAT_MV_PER_LSB)
float readVBAT(void) {
float raw;
// Set the analog reference to 3.0V (default = 3.6V)
analogReference(AR_INTERNAL_3_0);
// Set the resolution to 12-bit (0..4095)
analogReadResolution(12); // Can be 8, 10, 12 or 14
// Let the ADC settle
delay(1);
// Get the raw 12-bit, 0..3000mV ADC value
raw = analogRead(vbat_pin);
// Set the ADC back to the default settings
analogReference(AR_DEFAULT);
analogReadResolution(10);
// Convert the raw value to compensated mv, taking the resistor-
// divider into account (providing the actual LIPO voltage)
// ADC range is 0..3000mV and resolution is 12-bit (0..4095)
return raw * REAL_VBAT_MV_PER_LSB;
}
uint8_t mvToPercent(float mvolts) {
if(mvolts《3300)
return 0;
if(mvolts 《3600) {
mvolts -= 3300;
return mvolts/30;
}
mvolts -= 3600;
return 10 + (mvolts * 0.15F ); // thats mvolts /6.66666666
}
void setup() {
Serial.begin(115200);
while ( !Serial ) delay(10); // for nrf52840 with native usb
// Get a single ADC sample and throw it away
readVBAT();
}
void loop() {
// Get a raw ADC reading
float vbat_mv = readVBAT();
// Convert from raw mv to percentage (based on LIPO chemistry)
uint8_t vbat_per = mvToPercent(vbat_mv);
// Display the results
Serial.print(“LIPO = ”);
Serial.print(vbat_mv);
Serial.print(“ mV (”);
Serial.print(vbat_per);
Serial.println(“%)”);
delay(1000);
}
内存映射
此页面适用于BSP 0.8.0及更高版本,引入了引导加载程序v5.1.0和S132 v5.xx对于早期版本(BSP版本《0.8.0),请参阅本页底部的引导加载程序v0.5.0和S132 v2.xx。
BSP发行版和Bootloader版本
内存使用量取决于Softdevice和/或Bootloader的版本(单/双银行)。以下是BSP版本随附的Bootloader和Softdevice版本
- 0.9.x :nRF52832 S132 v6.1.1单库和nRF52840 S140 v6.1.1单库
- 0.8.x :nRF52832 S132 v2.0.1双库和S132 v5.1.0双库
- 0.7.x及更高版本:nRF52832 S132 v2.0.1双库
从BSP 0.9.x开始,只有单库
div》闪存
nRF52832具有512 KB闪存,nRF52840具有1024 KB闪存。 Flash的布局如下:
SoftDevice :闪存的此部分包含 Soft Device ,它是Nordic的黑匣子蓝牙低功耗堆栈。
应用程序:此闪存部分存储您在Arduino IDE中编译的用户草图。
保留:此闪存部分保留为空以启用双库安全的固件更新。每当您尝试更新应用程序时,新的应用程序数据将首先被写入空闲内存部分,并在与当前应用程序代码交换出来之前经过验证。这是为了确保DFU成功完成,并且整个映像都安全地存储在设备上,以避免丢失应用程序代码。 单个存储区引导加载程序
用户数据不使用该区域:此闪存的28KB部分保留用于配置设置。它使用称为Little File System的开源文件系统,该文件系统是ARM Mbed OpenSource的一部分,用于存储绑定数据。例如,当您将nRF52与另一个中央设备绑定时。
DFU Bootloader :闪存的这一部分存储了将由前面所述的MBR执行的实际Bootloader代码。
SRAM布局
nRF52832具有64KB的SRAM可用,尽管堆栈和堆的存储位置如下所述,实际的内存使用量取决于您的项目:
软件设备: 由Linker脚本专门分配给SoftDevice的SRAM数量。大小可能会有所变化,并且会因版本而异。对于BSP版本 0.8.0 ,它是 12.5 KB 。但是,SoftDevice所需的实际内存取决于Bluefruit的configNNN()API确定的运行时配置。
草图BSS:草图使用的静态和全局数据。
堆内存:最大的内存区域,用于分配实时操作系统(RTOS)线程堆栈,malloc()等。大小基于变量如下所示,是 Y = 64-12.5-X-2(KB),其中12.5KB取决于所使用的软设备。
堆栈内存:由非RTOS线程代码使用,主要用于中断服务例程(ISR)和SoftDevice API调用。当前大小为 2 KB 。
影响SoftDevice SRAM使用率的功能
Bluefruit nRF52 configNNN ()函数设置SoftDevice的行为,从而确定SRAM的总使用量。 必须在begin()之前调用这些函数。
configUuid128Count ():定义SoftDevice支持的UUID128条目的数量,例如Bleuart,BleMidi或其他服务和特征。默认值为10。
configAttrTableSize():属性表的总大小,服务和特色。如果您的应用程序需要很多特性,则可能需要增加它。 默认值为2048个字节。
configPrphConn(),configPrphBandwidth():这些函数设置用于确定外围设备连接带宽的参数。configPrphBandwidth( )是使用适当参数调用configPrphConn()的便捷助手。
configCentralConn(),configCentralBandwidth():这些函数设置用于确定中央模式连接带宽的参数。 configCentralBandwidth()是使用适当参数调用configCentralConn()的便捷帮助程序。
begin(): Bluefruit nRF52的 begin()函数也会影响带宽,因为它需要2个(可选)参数。第一个是外围链路(到移动电话,计算机等)的并发连接数,第二个是中央链路(到BLE附件或在外围模式下运行的另一个feather52)的并发连接数。SoftDevice v5.x的最大并发连接数为20 。
如果遇到出现错误消息“ SoftDevice需要比链接程序提供的SRAM更多的错误”,请尝试更改系统配置-例如。较低的带宽,较少的连接或较小的属性表大小。另一个高级选项是修改链接描述文件,但是在进行更改时应该格外小心。
下载:文件
复制代码
[CFG] SoftDevice config requires more SRAM than provided by the linker.
App Ram Start must be at least 0x20004180 (provided 0x20003200)。
Please update linker file or re-config SoftDevice. [CFG] SoftDevice config requires more SRAM than provided by the linker.
App Ram Start must be at least 0x20004180 (provided 0x20003200)。
Please update linker file or re-config SoftDevice.
软件资源
为帮助您使Bluefruit LE模块与其他Central设备进行通信,我们为大多数支持主要平台的平台汇集了许多开源工具低功耗蓝牙。
Bluefruit LE Client Apps和库
Adafruit已将以下移动或桌面应用和库组合在一起,以使您的Bluefruit LE模块与移动设备或笔记本电脑进行通讯变得尽可能容易,并尽可能提供完整的源代码:/p》
Bluefruit LE Connect(Android/Java)
从Android 4.3开始,已向Android添加了蓝牙低功耗支持(尽管从4.4开始才非常稳定),并且我们已经将Bluefruit LE Connect发布到Play商店。
Github上也提供了适用于Android的Bluefruit LE Connect的完整代码,可帮助您g et从您自己的Android应用开始。您需要使用最新版本的Android Studio才能使用此项目。
Bluefruit LE Connect(iOS/Swift )
苹果公司很早就采用低功耗蓝牙技术,并且苹果的应用程序商店中也有iOS版的Bluefruit LE Connectapp。
Bluefruit LE的完整快速源代码Github也提供适用于iOS的Connect。您需要XCode并访问Apple的developerper程序才能使用此项目:
版本1.x源代码:https://github.com/adafruit/Bluefruit_LE_Connect
版本2.x源代码:https://github.com/adafruit/Bluefruit_LE_Connect_v2
该应用程序的2.x版是一个完整的重写,在单个代码库中包含iOS,OS X GUI和OS X命令行工具。
OS X(Swift)的Bluefruit LE Connect
此OS X桌面应用程序基于相同的V2.x代码库作为iOS应用程序,并允许您从笔记本电脑或Mac的便利性访问BLE UART,基本的Pin I/O和OTA DFU固件更新。
这是在本地记录传感器数据并进行记录的理想选择将其导出为CSV,JSON或XML文件以在另一个应用程序中进行解析,并使用nat
Github上也提供了完整的源代码。
用于OS X的Bluefruit LE命令行更新程序(快速)
此实验性命令行工具不受支持,仅作为概念证明提供,但可以用于允许从命令行为Bluefruit设备更新固件。
此实用程序通过检查Bluefruit设备上的固件版本(通过设备信息服务)来执行类似于GUI应用程序的自动固件更新,并将其与在线可用的固件版本进行比较,并在适当的情况下在后台下载文件。
只需通过DMG文件安装预编译的工具,然后将其放置在系统路径中的某个位置,或在本地运行该文件通过“ 。/bluefruit”查看帮助菜单:
下载:文件
复制代码
$ 。/bluefruit
bluefruit v0.3
Usage:
bluefruit
[options.。.]
Commands:
Scan peripherals: scan
Automatic update: update [--enable-beta] [--uuid ]
Custom firmware: dfu --hex [--init ] [--uuid ]
Show this screen: --help
Show version: --version
Options:
--uuid If present the peripheral with that uuid is used. If not present a list of peripherals is displayed
--enable-beta If not present only stable versions are used
Short syntax:
-u = --uuid, -b = --enable-beta, -h = --hex, -i = --init, -v = --version, -? = --help $ 。/bluefruit
bluefruit v0.3
Usage:
bluefruit
[options.。.]
Commands:
Scan peripherals: scan
Automatic update: update [--enable-beta] [--uuid ]
Custom firmware: dfu --hex [--init ] [--uuid ]
Show this screen: --help
Show version: --version
Options:
--uuid If present the peripheral with that uuid is used. If not present a list of peripherals is displayed
--enable-beta If not present only stable versions are used
Short syntax:
-u = --uuid, -b = --enable-beta, -h = --hex, -i = --init, -v = --version, -? = --help
不推荐使用:Bluefruit Buddy(OS X )
此本地OS X应用程序是概念验证的基本应用程序,可让您使用最新的Macbook或iMac连接到Bluefruit LE模块。您可以获取有关模块的基本信息,并使用UART服务发送和接收数据。
该应用程序的完整源代码可在github存储库中的Adafruit_BluefruitLE_OSX中找到。
ABLE(跨平台/节点+电子)
ABLE(Adafruit Bluefruit LE Desktop)是一个跨平台的桌面应用程序基于Sandeep Misty的贵族库和Github的Electronproject(由Atom使用)。
它可在OS X,Windows 7+和精选Linux上运行(Ubuntu已在本地测试)。 Windows 7支持特别有趣,因为Windows 7没有对低功耗蓝牙的本机支持,但是贵族库直接与受支持的蓝牙4.0 USB dongle对话,在系统上模拟BLE(尽管目前仍处于BETA早期,并且断开了连接并采取了
此应用程序可让您收集传感器数据或执行移动Bluefruit LE Connect应用程序所提供的许多相同功能,但只能在台式机上进行。
该应用程序仍处于测试阶段,但除了易于使用的预编译二进制文件之外,还提供了完整的源代码。
div》 Bluefruit LE Python包装器
作为概念证明,我们在使Python与OS X上的本机Bluetooth API以及某些Linux目标上的最新版Bluez一起工作方面做了一些努力。/p》
当前有一些示例草图显示了h由于获取了BLE UART数据以及来自设备信息服务(DIS)的一些基本详细信息。
这不是一个积极支持的项目,而只是一个实验,但是如果您有最新的Macbook或Raspberry Pi并了解Python,您可能想在我们的github帐户中查看Adafruit_Python_BluefruitLE。
调试工具
如果您的冒险意识变得更好,并且您的Bluefruit LE模块陷入了困境,那么以下工具可能会有用
这些调试工具纯粹是为高级用户提供的便利出于设备恢复目的,不建议您这样做,除非您对可能的开发板感到满意。使用它们需要您自担风险。
AdaLink(Python)
此命令行工具是基于python的包装程序,可使用Segger J-Link或STLink/V2对ARM MCU进行编程。您可以使用它重新刷新Bluefruit LE模块
在Github上Adafruit_Adalinkrepo主文件上的readme.md文件中提供了有关如何使用该工具的详细信息。
使用AdaLink完全重新编程Bluefruit LE模块将需要四个文件,并且看起来像这样(使用JLink):
下载:文件
复制代码
adalink nrf51822 --programmer jlink --wipe
--program-hex “Adafruit_BluefruitLE_Firmware/softdevice/s110_nrf51_8.0.0_softdevice.hex”
--program-hex “Adafruit_BluefruitLE_Firmware/bootloader/bootloader_0002.hex”
--program-hex “Adafruit_BluefruitLE_Firmware/0.6.7/blefriend32/blefriend32_s110_xxac_0_6_7_150917_blefriend32.hex”
--program-hex “Adafruit_BluefruitLE_Firmware/0.6.7/blefriend32/blefriend32_s110_xxac_0_6_7_150917_blefriend32_signature.hex” adalink nrf51822 --programmer jlink --wipe
--program-hex “Adafruit_BluefruitLE_Firmware/softdevice/s110_nrf51_8.0.0_softdevice.hex”
--program-hex “Adafruit_BluefruitLE_Firmware/bootloader/bootloader_0002.hex”
--program-hex “Adafruit_BluefruitLE_Firmware/0.6.7/blefriend32/blefriend32_s110_xxac_0_6_7_150917_blefriend32.hex”
--program-hex “Adafruit_BluefruitLE_Firmware/0.6.7/blefriend32/blefriend32_s110_xxac_0_6_7_150917_blefriend32_signature.hex”
您也可以使用AdaLink工具获取有关模块的一些基本信息,例如通过--info命令当前正在编程哪个SoftDevice或IC版本(16KB SRAM或32KB SRAM):
下载:文件
复制代码
$ adalink nrf51822 -p jlink --info
Hardware ID : QFACA10 (32KB)
Segger ID : nRF51822_xxAC
SD Version : S110 8.0.0
Device Addr : **:**:**:**:**:**
Device ID : **************** $ adalink nrf51822 -p jlink --info
Hardware ID : QFACA10 (32KB)
Segger ID : nRF51822_xxAC
SD Version : S110 8.0.0
Device Addr : **:**:**:**:**:**
Device ID : ****************
Adafruit nRF51822 Flasher(Python)
Adafruit的nRF51822 Flasheris是我们在生产中使用的内部Python工具来烧写电路板当他们通过测试程序并下线时,或者只是在调试时针对不同的固件版本进行测试。
它依赖于表面下的AdaLink或OpenOCD(请参见上文),但是您可以使用此命令行
该工具目前支持使用特定的SoftDevice,Bootloader和Bluefruit固件组合来刷新nRF51822。
该工具目前支持通过AdaLink使用Segger J-Link或STLink/V2,或者在Raspberry Pi上使用GPIO可以访问传统的ARM SWD调试器。 (该存储库中包含用于RPi的OpenOCD的预构建版本,因为在原始RPi上从头开始构建它需要很长时间。)
由于该工具的纯粹是一个内部项目,但将其公开,以防万一它可以帮助冒险的客户自行清理木板。
下载:文件
复制代码
$ python flash.py --jtag=jlink --board=blefriend32 --softdevice=8.0.0 --bootloader=2 --firmware=0.6.7
jtag : jlink
softdevice : 8.0.0
bootloader : 2
board : blefriend32
firmware : 0.6.7
Writing Softdevice + DFU bootloader + Application to flash memory
adalink -v nrf51822 --programmer jlink --wipe --program-hex “Adafruit_BluefruitLE_Firmware/softdevice/s110_nrf51_8.0.0_softdevice.hex” --program-hex “Adafruit_BluefruitLE_Firmware/bootloader/bootloader_0002.hex” --program-hex “Adafruit_BluefruitLE_Firmware/0.6.7/blefriend32/blefriend32_s110_xxac_0_6_7_150917_blefriend32.hex” --program-hex “Adafruit_BluefruitLE_Firmware/0.6.7/blefriend32/blefriend32_s110_xxac_0_6_7_150917_blefriend32_signature.hex”
。.. $ python flash.py --jtag=jlink --board=blefriend32 --softdevice=8.0.0 --bootloader=2 --firmware=0.6.7
jtag : jlink
softdevice : 8.0.0
bootloader : 2
board : blefriend32
firmware : 0.6.7
Writing Softdevice + DFU bootloader + Application to flash memory
adalink -v nrf51822 --programmer jlink --wipe --program-hex “Adafruit_BluefruitLE_Firmware/softdevice/s110_nrf51_8.0.0_softdevice.hex” --program-hex “Adafruit_BluefruitLE_Firmware/bootloader/bootloader_0002.hex” --program-hex “Adafruit_BluefruitLE_Firmware/0.6.7/blefriend32/blefriend32_s110_xxac_0_6_7_150917_blefriend32.hex” --program-hex “Adafruit_BluefruitLE_Firmware/0.6.7/blefriend32/blefriend32_s110_xxac_0_6_7_150917_blefriend32_signature.hex”
。..
下载
使用Bluefruit nRF52 Feather可以使用以下资源:
Adafruit_nRF52_Arduino:此设备的核心代码(托管)
nRF52示例草图:从Github上的核心存储库浏览示例代码
nRF52832产品规格v1.4:nRF52832 SoC的关键技术文档
GitHub上的EagleCAD PCB文件
羽毛Bluefruit NRF52引脚图
MyNewt Bootloader
这是要在MyNewt支持下进行编程-Bootloader无法与Arduino一起使用!
feather52_boot_bleuart.bin
您可以使用adalink和jlink通过SWD将其编程为地址0,例如:
adalink -v nrf52832 -p jlink -b feather52_boot_bleuart.bin 0
(别忘了您可能必须擦除芯片)
模块详细信息
Bluefruit nRF52 Feather使用Raytac的MDBT42Q模块。有关该模块的详细信息,包括FCC和其他认证,请参见以下文档:
MDBT42Q-Version_B.pdf
示意图
示意图(2018年3月16日之前)
常见问题解答
注意:有关与 BSP 有关的常见问题解答,请参见专用的BSP常见问题解答列表。
nRF51和nRF52 Bluefruit板之间有什么区别?我应该使用哪一个?
这两个电路板系列采用了截然不同的设计
所有基于nRF51的模块都基于AT命令集(通过UART或SPI),并且需要两个MCU来运行:托管AT命令解析器的nRF51,以及发送AT的外部MCU
nRF52板直接在nRF52上运行代码,本机执行并直接调用Nordic S132 SoftDevice(它们的专有蓝牙低功耗堆栈)。由于没有中间AT层或传输层,因此可以实现更高效的代码,并且由于仅涉及单个设备,因此还可以降低总体功耗。
nRF52通常会为您提供更好的性能,但对于在需要使用具有nRF52所不具备的功能(例如USB)的MCU的情况下,基于nRF51的板仍然是首选的解决方案。
我可以在nRF52上运行nRF51 Bluefruit草图吗?
否。这两个电路板系列根本不同,并且具有完全独立的API和编程模型。如果要从nRF51迁移到nRF52,则需要重新设计草图以使用较新的API,从而使您能够构建在nRF52832 MCU上本地运行的代码。
我可以使用nRF52作为连接其他BLE外设的中央吗?
S132软设备和nRF52832硬件支持中央模式,所以是的,这是可能的。 ,但是,在此早期开发阶段,只有裸骨在Adafruit nRF52代码库中支持中央模式,只需测试HW和S132并确保正确配置了所有内容。提供了一个侦听传入广告包,将包内容和元数据打印到串行监视器的示例。我们希望将来会添加更多的中央模式示例,但是优先考虑了Peripheral API和初始版本的示例。
Arduino草图如何在nRF52上执行?我可以进行硬实时处理(位撞击NeoPixels,软件序列等)吗?
为了与低级蓝牙低功耗堆栈同时在nRF52上运行Arduino代码,Bluefruit nRF52 Feather使用FreeRTOS作为任务调度程序。调度程序将自动在任务之间切换,并在给定时刻将时钟周期分配给优先级最高的任务。此过程通常对您透明,但是如果您有严格的实时要求,则可能会产生影响。当启用无线电并主动将其用于低功耗蓝牙时,nRF52不能保证满足硬定时要求。即使在没有FreeRTOS的情况下,这在nRF52上也是不可能的,因为SoftDevice(Nordic的专有二进制blob堆栈)的优先级高于任何用户代码,包括对中断处理程序的控制。
我可以使用GDB调试nRF52吗?
您可以,是的,但它需要Segger J-Link(无论如何,这是我们已经针对其进行测试的,存在其他选项),并且它是高级操作。
假设您已安装了Segger J-Link驱动程序,则可以从命令行按以下方式启动Segger的GDB服务器(此处使用OSX/Linux) ):
$ JLinkGDBServer -device nrf52832_xxaa -if swd -speed auto
然后打开一个新的终端窗口,确保您可以从命令行访问gcc-arm-none-eabi-gdb,然后输入以下命令:
$ 。/arm-none-eabi-gdb something.ino.elf
`something.ino.elf`是构建草图时生成的.elf文件的名称。您可以通过在Arduino IDE首选项中启用“在:[x]编译期间显示详细输出”来找到此功能。您可以在没有.elf文件的情况下运行GDB,但是指向.elf文件将为您提供所有元数据,如在特定地址显示实际源代码等。
cc = 1》提示符,输入以下命令以连接到Segger GDB服务器(由于HW不一定是本地的,因此请相应地更新IP地址!):
(gdb)
如果一切顺利,您应该看到设备已停止的当前代码行(通常,在启动Segger GDB服务器后,nRF52上的执行就会停止)。
可以发送GDB调试命令,这本身就是一个教程!但是,作为速成课程,
要继续执行,请键入“ (gdb) target remote 127.0.0.1:2331”,然后键入“ monitor go”
要停止执行(阅读例如,注册值。),键入‘continue’
要显示当前堆栈跟踪(暂停时),请输入‘monitor halt’
以获取有关当前堆栈帧(通常是当前正在执行的功能)的信息,请尝试以下操作:
bt:显示有关当前堆栈帧的信息
info frame:显示有关传递到堆栈框架中的参数的信息
info args:显示堆栈框架中的局部变量
info locals:转储核心ARM寄存器值,这些值对于调试特定故障条件很有用
是否存在除GDB之外的其他任何跨平台或免费调试选项?
如果您有Segger J-Link,也可以使用Segger的OZone调试ger GUI与设备进行交互,但是请检查许可条款,因为根据使用的J-Link模块有使用限制,这是必需的。
您将需要通过SWD将nRF52连接到J-Link。 PCB底部的SWCLK引脚和SWCLK引脚,或者可以通过SWD接头进行细间距焊接。
您可以在板上可用焊盘上的标准2x5 SWD接头上进行焊接,或者可以将导线焊接到PCB底部的SWD和SWCLK焊盘上,并使用SWD电缆分接板,或者仅通过其他方法将电缆直接连接到J-Link。
您还需要将JLink上的 VTRef 引脚连接到Feather上的 3.3V ,以使J-Link知道目标具有的电压电平,并通过连接GND共享公共GND。
开始调试之前,您需要获取.elf文件,其中包含草图的所有调试信息。您可以通过在 Arduino偏好设置对话框中启用在编译期间显示详细输出来找到此文件。构建草图时,需要查看日志输出,并找到.elf文件,该文件类似于以下内容(根据所使用的操作系统而有所不同):info registers
在OZone新建项目向导中,当系统提示您在OZone中选择目标设备时,选择 nRF52832_xxAA ,然后确保已将调试器的目标接口设置为 SWD ,最后指向上面的.elf文件:
下一步选择附加或通过菜单系统进入运行程序选项,这将导致调试器通过SWD连接到nRF52:
这时,您可以单击 PAUSE 图标停止程序执行,然后分析变量,或在程序执行的适当位置设置断点,并在调试时进行调试
div》
单击文本编辑器的左侧将在下图中的第69行上设置一个断点,例如,然后选择 Debug》 Reset》 Reset&Run 》从菜单或图标中将导致主板复位,您应该在设置的断点处停止:
您可以尝试通过 View 菜单项添加一些其他调试窗口和选项,例如调用堆栈,它将向您显示到达当前断点之前被调用的所有函数:
我可以进行两个Bluefruit nRF52的对话吗?
是的,通过在外围设备模式下运行一块板,在中央模式下运行一块板,中央将与外围板建立连接,并且您可以使用BLE UART或定制服务。请参阅以下Central BLE UART示例以帮助您入门:https://github.com/adafruit/Adafruit_nRF52_Arduino/tree/master/libraries/Bluefruit52Lib/examples/Central
在Linux上,即使指定路径中存在“ arm-none-eabi-g ++”,我也得到了“ arm-none-eabi-g ++:没有这样的文件或目录”。我该怎么办?
这可能是由于32-位和64位版本的编译器,libc和IDE。编译器使用32位二进制文件,因此您还需要在系统上安装32位版本的libc(详细信息)。尝试从命令行运行以下命令来解决此问题:
/var/folders/86/hb2vp14n5_5_yvdz_z8w9x_c0000gn/T/arduino_build_118496/ancs_oled.ino.elf
,当Arduino无法将草图上传到Feather时该怎么办?
如果出现此错误:
sudo dpkg --add-architecture i386
sudo apt-get update
sudo apt-get install libc6:i386
这可能是由 bootloader 版本与您的羽毛和安装的BSP不匹配引起的。由于闪存布局(更多详细信息)和Softdevice API(与引导加载程序捆绑在一起)的差异,使用选定的引导加载程序构建的草图只能上传到具有相同版本的电路板上。简而言之,您需要在羽毛上升级/刻录引导程序以匹配,请按照上述步骤进行更新。Bootloader指南
只需更新一次羽毛即可
羽毛/Metro nRF52832和nRF52840是否支持BLE Mesh?
它们都支持BLE Mesh,但我们不提供用于Mesh的Arduino库。您需要基于北欧的sdk网格编写代码。
无法使用macOS上传素描/更新引导程序
Timed out waiting for acknowledgement from device.
Failed to upgrade target. Error is: No data received on serial port. Not able to proceed.
Traceback (most recent call last):
File “nordicsemi\__main__.py”, line 294, in serial
File “nordicsemi\dfu\dfu.py”, line 235, in dfu_send_images
File “nordicsemi\dfu\dfu.py”, line 203, in _dfu_send_image
File “nordicsemi\dfu\dfu_transport_serial.py”, line 155, in send_init_packet
File “nordicsemi\dfu\dfu_transport_serial.py”, line 243, in send_packet
File “nordicsemi\dfu\dfu_transport_serial.py”, line 282, in get_ack_nr
nordicsemi.exceptions.NordicSemiException: No data received on serial port. Not able to proceed.
Arduino: 1.8.8 (Mac OS X), Board: “Adafruit Bluefruit nRF52832 Feather, 0.2.9 (s132 6.1.1), Level 0 (Release)”
[1716] Error loading Python lib ‘/var/folders/gw/b0cg4zm508qf_rf2m655gd3m0000gn/T/_MEIE6ec69/Python’: dlopen: dlopen(/var/folders/gw/b0cg4zm508qf_rf2m655gd3m0000gn/T/_MEIE6ec69/Python, 10): Symbol not found: _futimens
Referenced from: /var/folders/gw/b0cg4zm508qf_rf2m655gd3m0000gn/T/_MEIE6ec69/Python (which was built for Mac OS X 10.13)
Expected in: /usr/lib/libSystem.B.dylib
in /var/folders/gw/b0cg4zm508qf_rf2m655gd3m0000gn/T/_MEIE6ec69/Python
exit status 255
是可能是由于预建的adafruit-nrfutil无法在Mac上运行。如果您的Mac早于MacOS,则二进制文件是在MacOS 10.13上生成的。请更新您的macOS,或者您可以在这里按照此仓库的自述文件https://github.com/adafruit/Adafruit_nRF52_nrfutil手动安装它(首先尝试使用pip3,如果不起作用则从源代码安装)。然后使用已安装的二进制文件替换BSP中的二进制文件。
责任编辑:wv
-
芯片
+关注
关注
450文章
49493浏览量
416469 -
编程
+关注
关注
88文章
3513浏览量
93169
发布评论请先 登录
相关推荐
集成电路学习指南:科目选择与未来发展全景解读
![集成电路<b class='flag-5'>学习指南</b>:科目选择与未来发展全景解读](https://file1.elecfans.com/web2/M00/F6/F1/wKgZomaDW_WAIH5aAAB16_fw0XE154.png)
NRF52832蓝牙芯片详细介绍及应用方案
![<b class='flag-5'>NRF</b>52832蓝牙芯片详细介绍及应用方案](https://file1.elecfans.com/web2/M00/E4/C6/wKgaomY9ujqALicKAACCy2hV45k822.png)
nRF52833及蓝牙5系列NRF52832低功耗蓝牙芯片对比
![<b class='flag-5'>nRF</b>52833及蓝牙5系列<b class='flag-5'>NRF</b>52832低功耗蓝牙芯片对比](https://file1.elecfans.com/web2/M00/E2/A4/wKgaomY51gCAOz7aAAKLkjb_XBE152.png)
芯片封装工程师必备知识和学习指南
![芯片封装工程师必备知识和<b class='flag-5'>学习指南</b>](https://file1.elecfans.com/web2/M00/DA/EF/wKgZomYrFm-ANqhUAAAxnNXTxMc468.png)
MDMFC52L1H6-MINAS A6系列通用型使用说明书 (安全注意指南) 松下
![MDMFC<b class='flag-5'>52</b>L1H6-MINAS A6系列通用型使用说明书 (安全注意<b class='flag-5'>指南</b>) 松下](https://file1.elecfans.com/web2/M00/A9/42/wKgaomUwmYKAXjWEAAAsCdY_muE289.png)
MDMFC52L1H5-MINAS A6系列通用型使用说明书 (安全注意指南) 松下
![MDMFC<b class='flag-5'>52</b>L1H5-MINAS A6系列通用型使用说明书 (安全注意<b class='flag-5'>指南</b>) 松下](https://file1.elecfans.com/web2/M00/A9/42/wKgaomUwmYKAXjWEAAAsCdY_muE289.png)
MDMFC52L1G6-MINAS A6系列通用型使用说明书 (安全注意指南) 松下
![MDMFC<b class='flag-5'>52</b>L1G6-MINAS A6系列通用型使用说明书 (安全注意<b class='flag-5'>指南</b>) 松下](https://file1.elecfans.com/web2/M00/A9/42/wKgaomUwmYKAXjWEAAAsCdY_muE289.png)
MDMFC52L1G5-MINAS A6系列通用型使用说明书 (安全注意指南) 松下
![MDMFC<b class='flag-5'>52</b>L1G5-MINAS A6系列通用型使用说明书 (安全注意<b class='flag-5'>指南</b>) 松下](https://file1.elecfans.com/web2/M00/A9/42/wKgaomUwmYKAXjWEAAAsCdY_muE289.png)
MDMFC52L1D5-MINAS A6系列通用型使用说明书 (安全注意指南) 松下
![MDMFC<b class='flag-5'>52</b>L1D5-MINAS A6系列通用型使用说明书 (安全注意<b class='flag-5'>指南</b>) 松下](https://file1.elecfans.com/web2/M00/A9/42/wKgaomUwmYKAXjWEAAAsCdY_muE289.png)
MDMFC52L1C6-MINAS A6系列通用型使用说明书 (安全注意指南) 松下
![MDMFC<b class='flag-5'>52</b>L1C6-MINAS A6系列通用型使用说明书 (安全注意<b class='flag-5'>指南</b>) 松下](https://file1.elecfans.com/web2/M00/A9/42/wKgaomUwmYKAXjWEAAAsCdY_muE289.png)
MDMFC52L1C5-MINAS A6系列通用型使用说明书 (安全注意指南) 松下
![MDMFC<b class='flag-5'>52</b>L1C5-MINAS A6系列通用型使用说明书 (安全注意<b class='flag-5'>指南</b>) 松下](https://file1.elecfans.com/web2/M00/A9/42/wKgaomUwmYKAXjWEAAAsCdY_muE289.png)
评论