欢迎光临
我们一直在努力

【Bluetooth-SIG】【CoreV6.2】【Vol3 Part G】【GATT】【42】【Characteristic Value Write Overview:Client 如何写入 Ch】

第 42 篇:Characteristic Value Write Overview:Client 如何写入 Characteristic Value

1. 本篇学习范围

本文继续学习:

  • Bluetooth Core Specification v6.2
  • Vol 3:Host
  • Part G:Generic Attribute Profile,GATT
  • Section 4.9:Characteristic Value Write
  • Section 4.9.1:Write Without Response
  • Section 4.9.2:Signed Write Without Response
  • Section 4.9.3:Write Characteristic Value
  • Section 4.9.4:Write Long Characteristic Value
  • Section 4.9.5:Characteristic Value Reliable Writes

上一篇我们学习了 Characteristic Value Read,也就是:

Client 如何从 Server 读取 Characteristic Value

这一篇进入相反方向:

Client 如何把新的 Characteristic Value 写到 Server

规范 4.9 说明,Characteristic Value Write procedure 用于向 Server 写 Characteristic Value,并定义了 5 个子过程:Write Without Response、Signed Write Without Response、Write Characteristic Value、Write Long Characteristic Value、Characteristic Value Reliable Writes。

一句话先剧透:

写 Characteristic Value 就是 Client 改 Server 上某个 Value:可以“塞纸条不等回执”,也可以“要求店员回执”,还可以“大订单分段提交”,甚至可以“店员复述订单,确认无误后再执行”。

土味版:

读 Value 是拿奶茶;写 Value 是改订单。
Write Without Response 是把纸条塞给店员就走;
Write Request 是等店员说“收到”;
Write Long 是长订单分几页写;
Reliable Writes 是店员先复述订单,你确认没错后再下厨房。
蓝牙写入不玄学,玄学通常来自:没看 Properties、写错 handle、MTU 算错、把无响应写当成可靠写。


2. 本篇核心结论

先记住 16 个结论:

  • Characteristic Value Write 用于 Client 向 Server 写 Characteristic Value。
  • 写 Value 必须使用 Characteristic Value Handle,不是 Characteristic Declaration Handle。
  • Write Without Response 使用 ATT_WRITE_CMD,没有 ATT_WRITE_RSP。
  • Write Without Response 单包最多写 ATT_MTU – 3 octets 的 Value。
  • Signed Write Without Response 使用 ATT_SIGNED_WRITE_CMD,用于未加密 ATT bearer 上的签名写。
  • Signed Write Without Response 单包最多写 ATT_MTU – 15 octets。
  • Signed Write Without Response 不用于 BR/EDR,也不用于已加密的 LE Security Mode 1 Level 2/3 链路。
  • Write Characteristic Value 使用 ATT_WRITE_REQ / ATT_WRITE_RSP。
  • Write Characteristic Value 有 ATT 层响应,成功返回 ATT_WRITE_RSP,失败返回 ATT_ERROR_RSP。
  • 普通 Write Characteristic Value 不能写 long attribute。
  • Write Long Characteristic Value 使用 ATT_PREPARE_WRITE_REQ + ATT_EXECUTE_WRITE_REQ。
  • Write Long 第一段 offset 通常为 0x0000,后续 offset 是下一个尚未写入的 octet。
  • Prepare Write 的单段 Value 最大是 ATT_MTU – 5。
  • Execute Write 的 flags 为 0x01 表示执行,为 0x00 表示取消。
  • Reliable Writes 也是 Prepare + Execute,但 Client 要检查 Server echo 回来的值,确认无误后再 Execute。
  • Properties 表示能力,Permissions / Security 决定真正能不能写。
  • 图解:

    #mermaid-svg-kd6mtCUDwzvN8uaf{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-kd6mtCUDwzvN8uaf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-kd6mtCUDwzvN8uaf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-kd6mtCUDwzvN8uaf .error-icon{fill:#552222;}#mermaid-svg-kd6mtCUDwzvN8uaf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-kd6mtCUDwzvN8uaf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-kd6mtCUDwzvN8uaf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-kd6mtCUDwzvN8uaf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-kd6mtCUDwzvN8uaf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-kd6mtCUDwzvN8uaf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-kd6mtCUDwzvN8uaf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-kd6mtCUDwzvN8uaf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-kd6mtCUDwzvN8uaf .marker.cross{stroke:#333333;}#mermaid-svg-kd6mtCUDwzvN8uaf svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-kd6mtCUDwzvN8uaf p{margin:0;}#mermaid-svg-kd6mtCUDwzvN8uaf .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-kd6mtCUDwzvN8uaf .cluster-label text{fill:#333;}#mermaid-svg-kd6mtCUDwzvN8uaf .cluster-label span{color:#333;}#mermaid-svg-kd6mtCUDwzvN8uaf .cluster-label span p{background-color:transparent;}#mermaid-svg-kd6mtCUDwzvN8uaf .label text,#mermaid-svg-kd6mtCUDwzvN8uaf span{fill:#333;color:#333;}#mermaid-svg-kd6mtCUDwzvN8uaf .node rect,#mermaid-svg-kd6mtCUDwzvN8uaf .node circle,#mermaid-svg-kd6mtCUDwzvN8uaf .node ellipse,#mermaid-svg-kd6mtCUDwzvN8uaf .node polygon,#mermaid-svg-kd6mtCUDwzvN8uaf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-kd6mtCUDwzvN8uaf .rough-node .label text,#mermaid-svg-kd6mtCUDwzvN8uaf .node .label text,#mermaid-svg-kd6mtCUDwzvN8uaf .image-shape .label,#mermaid-svg-kd6mtCUDwzvN8uaf .icon-shape .label{text-anchor:middle;}#mermaid-svg-kd6mtCUDwzvN8uaf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-kd6mtCUDwzvN8uaf .rough-node .label,#mermaid-svg-kd6mtCUDwzvN8uaf .node .label,#mermaid-svg-kd6mtCUDwzvN8uaf .image-shape .label,#mermaid-svg-kd6mtCUDwzvN8uaf .icon-shape .label{text-align:center;}#mermaid-svg-kd6mtCUDwzvN8uaf .node.clickable{cursor:pointer;}#mermaid-svg-kd6mtCUDwzvN8uaf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-kd6mtCUDwzvN8uaf .arrowheadPath{fill:#333333;}#mermaid-svg-kd6mtCUDwzvN8uaf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-kd6mtCUDwzvN8uaf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-kd6mtCUDwzvN8uaf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kd6mtCUDwzvN8uaf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-kd6mtCUDwzvN8uaf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kd6mtCUDwzvN8uaf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-kd6mtCUDwzvN8uaf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-kd6mtCUDwzvN8uaf .cluster text{fill:#333;}#mermaid-svg-kd6mtCUDwzvN8uaf .cluster span{color:#333;}#mermaid-svg-kd6mtCUDwzvN8uaf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-kd6mtCUDwzvN8uaf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-kd6mtCUDwzvN8uaf rect.text{fill:none;stroke-width:0;}#mermaid-svg-kd6mtCUDwzvN8uaf .icon-shape,#mermaid-svg-kd6mtCUDwzvN8uaf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-kd6mtCUDwzvN8uaf .icon-shape p,#mermaid-svg-kd6mtCUDwzvN8uaf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-kd6mtCUDwzvN8uaf .icon-shape .label rect,#mermaid-svg-kd6mtCUDwzvN8uaf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-kd6mtCUDwzvN8uaf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-kd6mtCUDwzvN8uaf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-kd6mtCUDwzvN8uaf :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Characteristic Value Write

    Write Without ResponseATT_WRITE_CMD

    Signed Write Without ResponseATT_SIGNED_WRITE_CMD

    Write Characteristic ValueATT_WRITE_REQ/RSP

    Write Long Characteristic ValuePrepare + Execute

    Reliable WritesPrepare echo check + Execute

    快,但无 ATT 回执

    未加密 bearer 上签名写

    有 ATT 回执

    长 Value 分段写

    写前双向校验

    一句话:

    写 Value 的核心差异 = 是否要响应、是否要签名、是否超过单包、是否要可靠校验。


    3. 先分清:写 Value,不是写 Declaration,也不是写 Descriptor

    写 Characteristic Value 时,目标 handle 必须是:

    Characteristic Value Handle

    不是:

    Characteristic Declaration Handle

    也不是:

    Descriptor Handle

    典型结构:

    HandleAttribute Type说明
    0x0203 0x2803 Characteristic Declaration
    0x0204 Characteristic UUID Characteristic Value
    0x0205 0x2902 CCCD Descriptor

    写业务 Value:

    ATT_WRITE_REQ(handle = 0x0204, value = …)

    写 CCCD:

    ATT_WRITE_REQ(handle = 0x0205, value = 01 00)

    图解:

    #mermaid-svg-U9ShAOibkgJZBGOB{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-U9ShAOibkgJZBGOB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-U9ShAOibkgJZBGOB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-U9ShAOibkgJZBGOB .error-icon{fill:#552222;}#mermaid-svg-U9ShAOibkgJZBGOB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-U9ShAOibkgJZBGOB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-U9ShAOibkgJZBGOB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-U9ShAOibkgJZBGOB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-U9ShAOibkgJZBGOB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-U9ShAOibkgJZBGOB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-U9ShAOibkgJZBGOB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-U9ShAOibkgJZBGOB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-U9ShAOibkgJZBGOB .marker.cross{stroke:#333333;}#mermaid-svg-U9ShAOibkgJZBGOB svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-U9ShAOibkgJZBGOB p{margin:0;}#mermaid-svg-U9ShAOibkgJZBGOB .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-U9ShAOibkgJZBGOB .cluster-label text{fill:#333;}#mermaid-svg-U9ShAOibkgJZBGOB .cluster-label span{color:#333;}#mermaid-svg-U9ShAOibkgJZBGOB .cluster-label span p{background-color:transparent;}#mermaid-svg-U9ShAOibkgJZBGOB .label text,#mermaid-svg-U9ShAOibkgJZBGOB span{fill:#333;color:#333;}#mermaid-svg-U9ShAOibkgJZBGOB .node rect,#mermaid-svg-U9ShAOibkgJZBGOB .node circle,#mermaid-svg-U9ShAOibkgJZBGOB .node ellipse,#mermaid-svg-U9ShAOibkgJZBGOB .node polygon,#mermaid-svg-U9ShAOibkgJZBGOB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-U9ShAOibkgJZBGOB .rough-node .label text,#mermaid-svg-U9ShAOibkgJZBGOB .node .label text,#mermaid-svg-U9ShAOibkgJZBGOB .image-shape .label,#mermaid-svg-U9ShAOibkgJZBGOB .icon-shape .label{text-anchor:middle;}#mermaid-svg-U9ShAOibkgJZBGOB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-U9ShAOibkgJZBGOB .rough-node .label,#mermaid-svg-U9ShAOibkgJZBGOB .node .label,#mermaid-svg-U9ShAOibkgJZBGOB .image-shape .label,#mermaid-svg-U9ShAOibkgJZBGOB .icon-shape .label{text-align:center;}#mermaid-svg-U9ShAOibkgJZBGOB .node.clickable{cursor:pointer;}#mermaid-svg-U9ShAOibkgJZBGOB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-U9ShAOibkgJZBGOB .arrowheadPath{fill:#333333;}#mermaid-svg-U9ShAOibkgJZBGOB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-U9ShAOibkgJZBGOB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-U9ShAOibkgJZBGOB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U9ShAOibkgJZBGOB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-U9ShAOibkgJZBGOB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U9ShAOibkgJZBGOB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-U9ShAOibkgJZBGOB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-U9ShAOibkgJZBGOB .cluster text{fill:#333;}#mermaid-svg-U9ShAOibkgJZBGOB .cluster span{color:#333;}#mermaid-svg-U9ShAOibkgJZBGOB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-U9ShAOibkgJZBGOB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-U9ShAOibkgJZBGOB rect.text{fill:none;stroke-width:0;}#mermaid-svg-U9ShAOibkgJZBGOB .icon-shape,#mermaid-svg-U9ShAOibkgJZBGOB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-U9ShAOibkgJZBGOB .icon-shape p,#mermaid-svg-U9ShAOibkgJZBGOB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-U9ShAOibkgJZBGOB .icon-shape .label rect,#mermaid-svg-U9ShAOibkgJZBGOB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-U9ShAOibkgJZBGOB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-U9ShAOibkgJZBGOB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-U9ShAOibkgJZBGOB :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    0x0203Declaration不要写这里

    0x0204Characteristic Value写业务值

    0x0205CCCD Descriptor写通知配置

    Write Characteristic Value

    Write Characteristic Descriptor

    通俗讲:

    Declaration 是菜单,Value 是奶茶,Descriptor 是杯身按钮。
    写 Value 是改奶茶内容;写 CCCD 是按关注按钮。
    你把“开启通知”写到 Value 上,就像把关注按钮贴进奶茶里,店员会很困惑,协议栈也会很诚实地失败。


    4. 五种写法总览

    写法ATT PDU有无 ATT 响应单包限制典型用途
    Write Without Response ATT_WRITE_CMD 无 ATT_MTU – 3 高频控制、低延迟写
    Signed Write Without Response ATT_SIGNED_WRITE_CMD 无 ATT_MTU – 15 未加密连接上的签名写
    Write Characteristic Value ATT_WRITE_REQ/RSP 有 ATT_MTU – 3 普通配置、控制点
    Write Long Characteristic Value ATT_PREPARE_WRITE_REQ/RSP + ATT_EXECUTE_WRITE_REQ/RSP 有 分段,Prepare 单段 ATT_MTU – 5 长字符串、长配置
    Reliable Writes ATT_PREPARE_WRITE_REQ/RSP + ATT_EXECUTE_WRITE_REQ/RSP 有,并检查 echo 分段 要确认写入内容无误的场景

    Table 4.2 也把这些 GATT 写入子过程映射到底层 ATT opcode:无响应写对应 ATT_WRITE_CMD,签名无响应写对应 ATT_SIGNED_WRITE_CMD,普通写对应 ATT_WRITE_REQ/RSP/ERROR,长写和可靠写都对应 Prepare / Execute 系列 PDU。

    图解:

    #mermaid-svg-7kYsbJRPiYgFPZls{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7kYsbJRPiYgFPZls .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7kYsbJRPiYgFPZls .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7kYsbJRPiYgFPZls .error-icon{fill:#552222;}#mermaid-svg-7kYsbJRPiYgFPZls .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7kYsbJRPiYgFPZls .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7kYsbJRPiYgFPZls .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7kYsbJRPiYgFPZls .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7kYsbJRPiYgFPZls .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7kYsbJRPiYgFPZls .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7kYsbJRPiYgFPZls .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7kYsbJRPiYgFPZls .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7kYsbJRPiYgFPZls .marker.cross{stroke:#333333;}#mermaid-svg-7kYsbJRPiYgFPZls svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7kYsbJRPiYgFPZls p{margin:0;}#mermaid-svg-7kYsbJRPiYgFPZls .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-7kYsbJRPiYgFPZls .cluster-label text{fill:#333;}#mermaid-svg-7kYsbJRPiYgFPZls .cluster-label span{color:#333;}#mermaid-svg-7kYsbJRPiYgFPZls .cluster-label span p{background-color:transparent;}#mermaid-svg-7kYsbJRPiYgFPZls .label text,#mermaid-svg-7kYsbJRPiYgFPZls span{fill:#333;color:#333;}#mermaid-svg-7kYsbJRPiYgFPZls .node rect,#mermaid-svg-7kYsbJRPiYgFPZls .node circle,#mermaid-svg-7kYsbJRPiYgFPZls .node ellipse,#mermaid-svg-7kYsbJRPiYgFPZls .node polygon,#mermaid-svg-7kYsbJRPiYgFPZls .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7kYsbJRPiYgFPZls .rough-node .label text,#mermaid-svg-7kYsbJRPiYgFPZls .node .label text,#mermaid-svg-7kYsbJRPiYgFPZls .image-shape .label,#mermaid-svg-7kYsbJRPiYgFPZls .icon-shape .label{text-anchor:middle;}#mermaid-svg-7kYsbJRPiYgFPZls .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7kYsbJRPiYgFPZls .rough-node .label,#mermaid-svg-7kYsbJRPiYgFPZls .node .label,#mermaid-svg-7kYsbJRPiYgFPZls .image-shape .label,#mermaid-svg-7kYsbJRPiYgFPZls .icon-shape .label{text-align:center;}#mermaid-svg-7kYsbJRPiYgFPZls .node.clickable{cursor:pointer;}#mermaid-svg-7kYsbJRPiYgFPZls .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7kYsbJRPiYgFPZls .arrowheadPath{fill:#333333;}#mermaid-svg-7kYsbJRPiYgFPZls .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7kYsbJRPiYgFPZls .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7kYsbJRPiYgFPZls .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7kYsbJRPiYgFPZls .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7kYsbJRPiYgFPZls .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7kYsbJRPiYgFPZls .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7kYsbJRPiYgFPZls .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7kYsbJRPiYgFPZls .cluster text{fill:#333;}#mermaid-svg-7kYsbJRPiYgFPZls .cluster span{color:#333;}#mermaid-svg-7kYsbJRPiYgFPZls div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7kYsbJRPiYgFPZls .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7kYsbJRPiYgFPZls rect.text{fill:none;stroke-width:0;}#mermaid-svg-7kYsbJRPiYgFPZls .icon-shape,#mermaid-svg-7kYsbJRPiYgFPZls .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7kYsbJRPiYgFPZls .icon-shape p,#mermaid-svg-7kYsbJRPiYgFPZls .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7kYsbJRPiYgFPZls .icon-shape .label rect,#mermaid-svg-7kYsbJRPiYgFPZls .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7kYsbJRPiYgFPZls .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7kYsbJRPiYgFPZls .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7kYsbJRPiYgFPZls :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    No

    No

    Yes

    Yes

    No

    Yes

    No

    Yes

    Client wants to write Characteristic Value

    Need ATT acknowledgment?

    Need signed data on unencrypted bearer?

    Write Without Response

    Signed Write Without Response

    Value longer than ATT_MTU – 3?

    Write Characteristic Value

    Need echo verification?

    Write Long

    Reliable Writes

    土味讲:

    小纸条、签名纸条、挂号信、长订单、复述订单。
    五种写法不是为了吓人,是为了解决不同场景。
    你发一个灯光开关,用无响应写可能很爽;你发一个 OTA 配置,还无响应裸奔,那就是拿蓝牙当许愿池。


    5. Write Without Response:无响应写

    Write Without Response 用于 Client 已经知道 Characteristic Value Handle,而且不需要 Server 确认写入成功的场景。规范规定它使用 ATT_WRITE_CMD,Attribute Handle 设置为 Characteristic Value Handle,Attribute Value 设置为新的 Characteristic Value;它只写单包前 ATT_MTU – 3 octets,不能用于 long characteristic。

    流程:

    GATT ServerGATT ClientGATT ServerGATT Client#mermaid-svg-H8ZHptyRdtzwi96R{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-H8ZHptyRdtzwi96R .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-H8ZHptyRdtzwi96R .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-H8ZHptyRdtzwi96R .error-icon{fill:#552222;}#mermaid-svg-H8ZHptyRdtzwi96R .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-H8ZHptyRdtzwi96R .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-H8ZHptyRdtzwi96R .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-H8ZHptyRdtzwi96R .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-H8ZHptyRdtzwi96R .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-H8ZHptyRdtzwi96R .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-H8ZHptyRdtzwi96R .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-H8ZHptyRdtzwi96R .marker{fill:#333333;stroke:#333333;}#mermaid-svg-H8ZHptyRdtzwi96R .marker.cross{stroke:#333333;}#mermaid-svg-H8ZHptyRdtzwi96R svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-H8ZHptyRdtzwi96R p{margin:0;}#mermaid-svg-H8ZHptyRdtzwi96R .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-H8ZHptyRdtzwi96R text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-H8ZHptyRdtzwi96R .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-H8ZHptyRdtzwi96R .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-H8ZHptyRdtzwi96R .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-H8ZHptyRdtzwi96R .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-H8ZHptyRdtzwi96R #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-H8ZHptyRdtzwi96R .sequenceNumber{fill:white;}#mermaid-svg-H8ZHptyRdtzwi96R #sequencenumber{fill:#333;}#mermaid-svg-H8ZHptyRdtzwi96R #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-H8ZHptyRdtzwi96R .messageText{fill:#333;stroke:none;}#mermaid-svg-H8ZHptyRdtzwi96R .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-H8ZHptyRdtzwi96R .labelText,#mermaid-svg-H8ZHptyRdtzwi96R .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-H8ZHptyRdtzwi96R .loopText,#mermaid-svg-H8ZHptyRdtzwi96R .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-H8ZHptyRdtzwi96R .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-H8ZHptyRdtzwi96R .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-H8ZHptyRdtzwi96R .noteText,#mermaid-svg-H8ZHptyRdtzwi96R .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-H8ZHptyRdtzwi96R .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-H8ZHptyRdtzwi96R .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-H8ZHptyRdtzwi96R .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-H8ZHptyRdtzwi96R .actorPopupMenu{position:absolute;}#mermaid-svg-H8ZHptyRdtzwi96R .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-H8ZHptyRdtzwi96R .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-H8ZHptyRdtzwi96R .actor-man circle,#mermaid-svg-H8ZHptyRdtzwi96R line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-H8ZHptyRdtzwi96R :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}No ATT_WRITE_RSPNo ATT_ERROR_RSPATT_WRITE_CMDhandle = Value Handlevalue = new value

    关键点:

    没有 ATT 层响应。

    ATT 规范也明确说,ATT_WRITE_CMD 不会触发 ATT_ERROR_RSP 或 ATT_WRITE_RSP;如果 Server 因任何原因不能写,该 command 会被忽略。

    通俗讲:

    无响应写就是“纸条塞门缝”。
    你塞了,店员不回“收到”。
    店员可能处理,也可能因为纸条太长、内容非法、权限不对而直接扔掉。
    快是真的快,但你别拿它当挂号信。


    6. Write Without Response 的长度限制:ATT_MTU – 3

    ATT_WRITE_CMD 的格式里有:

    Opcode: 1 octet
    Attribute Handle: 2 octets
    Attribute Value: 0 to ATT_MTU – 3

    所以 Value payload 最大是:

    ATT_MTU – 3

    ATT 规范中的 ATT_WRITE_CMD 表也明确把 Attribute Value 长度定义为 0 to (ATT_MTU – 3)。

    表格:

    ATT_MTUWrite Without Response 最大 Value
    23 20 bytes
    100 97 bytes
    185 182 bytes
    247 244 bytes
    512 509 bytes

    图解:

    #mermaid-svg-mIpi6kjf5l79TzIr{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mIpi6kjf5l79TzIr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mIpi6kjf5l79TzIr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mIpi6kjf5l79TzIr .error-icon{fill:#552222;}#mermaid-svg-mIpi6kjf5l79TzIr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mIpi6kjf5l79TzIr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mIpi6kjf5l79TzIr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mIpi6kjf5l79TzIr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mIpi6kjf5l79TzIr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mIpi6kjf5l79TzIr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mIpi6kjf5l79TzIr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mIpi6kjf5l79TzIr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mIpi6kjf5l79TzIr .marker.cross{stroke:#333333;}#mermaid-svg-mIpi6kjf5l79TzIr svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mIpi6kjf5l79TzIr p{margin:0;}#mermaid-svg-mIpi6kjf5l79TzIr .packetByte{font-size:10px;}#mermaid-svg-mIpi6kjf5l79TzIr .packetByte.start{fill:black;}#mermaid-svg-mIpi6kjf5l79TzIr .packetByte.end{fill:black;}#mermaid-svg-mIpi6kjf5l79TzIr .packetLabel{fill:black;font-size:12px;}#mermaid-svg-mIpi6kjf5l79TzIr .packetTitle{fill:black;font-size:14px;}#mermaid-svg-mIpi6kjf5l79TzIr .packetBlock{stroke:black;stroke-width:1;fill:#efefef;}#mermaid-svg-mIpi6kjf5l79TzIr :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}Opcode – 1 octet07Attribute Handle – 2 octets823Attribute Value – up to ATT_MTU – 32431Attribute Value – up to ATT_MTU – 33263ATT_WRITE_CMD

    土味讲:

    写包也有盘沿:Opcode 1 字节,Handle 2 字节。
    MTU 23 时,真正能装的 Value 是 20 字节。
    早期 BLE 里“20 字节一包”的祖传梗,就是这么来的。


    7. Write Without Response 适合什么?

    适合:

    场景原因
    高频控制 减少 request/response 往返
    实时性优先 不等 Server 回执
    上层自己有确认机制 ATT 层不负责确认
    可丢少量数据 例如连续传感器控制流
    写入短值 单包内完成

    不适合:

    场景原因
    关键配置 没有 ATT 回执
    长数据 不能写 long characteristic
    必须知道失败原因 Server 不回 error
    需要严格顺序确认 缺少每次写入确认
    安全策略复杂 失败可能被静默忽略

    图解:

    #mermaid-svg-dsxzQbExBbDoAbPH{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dsxzQbExBbDoAbPH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dsxzQbExBbDoAbPH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dsxzQbExBbDoAbPH .error-icon{fill:#552222;}#mermaid-svg-dsxzQbExBbDoAbPH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dsxzQbExBbDoAbPH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dsxzQbExBbDoAbPH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dsxzQbExBbDoAbPH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dsxzQbExBbDoAbPH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dsxzQbExBbDoAbPH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dsxzQbExBbDoAbPH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dsxzQbExBbDoAbPH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dsxzQbExBbDoAbPH .marker.cross{stroke:#333333;}#mermaid-svg-dsxzQbExBbDoAbPH svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dsxzQbExBbDoAbPH p{margin:0;}#mermaid-svg-dsxzQbExBbDoAbPH .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-dsxzQbExBbDoAbPH .cluster-label text{fill:#333;}#mermaid-svg-dsxzQbExBbDoAbPH .cluster-label span{color:#333;}#mermaid-svg-dsxzQbExBbDoAbPH .cluster-label span p{background-color:transparent;}#mermaid-svg-dsxzQbExBbDoAbPH .label text,#mermaid-svg-dsxzQbExBbDoAbPH span{fill:#333;color:#333;}#mermaid-svg-dsxzQbExBbDoAbPH .node rect,#mermaid-svg-dsxzQbExBbDoAbPH .node circle,#mermaid-svg-dsxzQbExBbDoAbPH .node ellipse,#mermaid-svg-dsxzQbExBbDoAbPH .node polygon,#mermaid-svg-dsxzQbExBbDoAbPH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dsxzQbExBbDoAbPH .rough-node .label text,#mermaid-svg-dsxzQbExBbDoAbPH .node .label text,#mermaid-svg-dsxzQbExBbDoAbPH .image-shape .label,#mermaid-svg-dsxzQbExBbDoAbPH .icon-shape .label{text-anchor:middle;}#mermaid-svg-dsxzQbExBbDoAbPH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dsxzQbExBbDoAbPH .rough-node .label,#mermaid-svg-dsxzQbExBbDoAbPH .node .label,#mermaid-svg-dsxzQbExBbDoAbPH .image-shape .label,#mermaid-svg-dsxzQbExBbDoAbPH .icon-shape .label{text-align:center;}#mermaid-svg-dsxzQbExBbDoAbPH .node.clickable{cursor:pointer;}#mermaid-svg-dsxzQbExBbDoAbPH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dsxzQbExBbDoAbPH .arrowheadPath{fill:#333333;}#mermaid-svg-dsxzQbExBbDoAbPH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dsxzQbExBbDoAbPH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dsxzQbExBbDoAbPH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dsxzQbExBbDoAbPH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dsxzQbExBbDoAbPH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dsxzQbExBbDoAbPH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dsxzQbExBbDoAbPH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dsxzQbExBbDoAbPH .cluster text{fill:#333;}#mermaid-svg-dsxzQbExBbDoAbPH .cluster span{color:#333;}#mermaid-svg-dsxzQbExBbDoAbPH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dsxzQbExBbDoAbPH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dsxzQbExBbDoAbPH rect.text{fill:none;stroke-width:0;}#mermaid-svg-dsxzQbExBbDoAbPH .icon-shape,#mermaid-svg-dsxzQbExBbDoAbPH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dsxzQbExBbDoAbPH .icon-shape p,#mermaid-svg-dsxzQbExBbDoAbPH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dsxzQbExBbDoAbPH .icon-shape .label rect,#mermaid-svg-dsxzQbExBbDoAbPH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dsxzQbExBbDoAbPH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dsxzQbExBbDoAbPH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dsxzQbExBbDoAbPH :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Write Without Response

    Good for fast short writes

    Bad for critical reliable writes

    LED / motor / streaming control

    Device configuration / OTA / account data

    通俗讲:

    你控制灯光亮度,从 1 写到 100,丢一两个中间值问题不大。
    你写设备绑定配置,丢一次可能直接售后。
    无响应写像喊口令,适合“快”;不适合“必须确认”。


    8. Signed Write Without Response:签名无响应写

    Signed Write Without Response 用于 Client 已知 Characteristic Value Handle、ATT bearer 未加密的场景;它只能在 Characteristic Properties 的 authenticated bit 启用,并且 Client 和 Server 共享 bond 时使用。规范还规定它只写单包前 ATT_MTU – 15 octets,不能用于 long attribute。

    它使用:

    ATT_SIGNED_WRITE_CMD

    流程:

    GATT ServerGATT ClientGATT ServerGATT Client#mermaid-svg-EY2Hgk6NAH1FujOy{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EY2Hgk6NAH1FujOy .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EY2Hgk6NAH1FujOy .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EY2Hgk6NAH1FujOy .error-icon{fill:#552222;}#mermaid-svg-EY2Hgk6NAH1FujOy .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EY2Hgk6NAH1FujOy .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EY2Hgk6NAH1FujOy .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EY2Hgk6NAH1FujOy .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EY2Hgk6NAH1FujOy .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EY2Hgk6NAH1FujOy .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EY2Hgk6NAH1FujOy .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EY2Hgk6NAH1FujOy .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EY2Hgk6NAH1FujOy .marker.cross{stroke:#333333;}#mermaid-svg-EY2Hgk6NAH1FujOy svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EY2Hgk6NAH1FujOy p{margin:0;}#mermaid-svg-EY2Hgk6NAH1FujOy .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-EY2Hgk6NAH1FujOy text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-EY2Hgk6NAH1FujOy .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-EY2Hgk6NAH1FujOy .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-EY2Hgk6NAH1FujOy .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-EY2Hgk6NAH1FujOy .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-EY2Hgk6NAH1FujOy #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-EY2Hgk6NAH1FujOy .sequenceNumber{fill:white;}#mermaid-svg-EY2Hgk6NAH1FujOy #sequencenumber{fill:#333;}#mermaid-svg-EY2Hgk6NAH1FujOy #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-EY2Hgk6NAH1FujOy .messageText{fill:#333;stroke:none;}#mermaid-svg-EY2Hgk6NAH1FujOy .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-EY2Hgk6NAH1FujOy .labelText,#mermaid-svg-EY2Hgk6NAH1FujOy .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-EY2Hgk6NAH1FujOy .loopText,#mermaid-svg-EY2Hgk6NAH1FujOy .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-EY2Hgk6NAH1FujOy .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-EY2Hgk6NAH1FujOy .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-EY2Hgk6NAH1FujOy .noteText,#mermaid-svg-EY2Hgk6NAH1FujOy .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-EY2Hgk6NAH1FujOy .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-EY2Hgk6NAH1FujOy .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-EY2Hgk6NAH1FujOy .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-EY2Hgk6NAH1FujOy .actorPopupMenu{position:absolute;}#mermaid-svg-EY2Hgk6NAH1FujOy .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-EY2Hgk6NAH1FujOy .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-EY2Hgk6NAH1FujOy .actor-man circle,#mermaid-svg-EY2Hgk6NAH1FujOy line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-EY2Hgk6NAH1FujOy :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}No responseServer verifies signatureATT_SIGNED_WRITE_CMDhandle = Value Handlevalue + authentication signature

    为什么是 ATT_MTU – 15?

    因为 PDU 里除了:

    Opcode 1 byte
    Handle 2 bytes

    还要放:

    Authentication Signature 12 bytes

    所以剩余 Value 空间是:

    ATT_MTU – 1 – 2 – 12 = ATT_MTU – 15

    土味讲:

    签名无响应写就是“纸条不等回执,但纸条上盖了章”。
    盖章占空间,所以能写的正文比普通无响应写更少。
    一张纸既要写内容又要盖 12 字节大章,正文自然少一截。


    9. Signed Write 的几个限制

    规范给了几个非常明确的限制:

    限制说明
    ATT bearer 未加密 已加密时应使用普通 Write Without Response
    Characteristic Properties authenticated bit 启用 不是所有 Characteristic 都能签名写
    Client / Server 有 bond 签名依赖共享安全材料
    不用于 BR/EDR BR/EDR ATT bearer 因 Security Mode 4 总是加密
    不能写 long attribute 单包限制 ATT_MTU – 15

    如果连接已经使用 LE Security Mode 1 Level 2 或 Level 3 加密,规范要求使用 Write Without Response,而不是 Signed Write Without Response;在 BR/EDR 上该子过程也不使用。

    签名数据的底层安全机制来自 GAP/SM:数据签名使用 CSRK,签名由 counter 和 MAC 组成;接收方用存储的 CSRK 和收到的 counter 重新计算 MAC,如果不匹配就忽略该 Data PDU。

    通俗讲:

    已经在加密包厢里说话,就不用每张纸条再盖章。
    没在包厢,但双方以前认识、有签名钥匙,才用签名纸条。
    BR/EDR 这边本来就走加密包厢,所以不玩这套。


    10. Signed Write 失败为什么没有 Error?

    因为它是 command,不是 request。

    规范说,如果要写的 authenticated Characteristic Value 太长、按 profile 定义是非法值,或者签名不能认证 Client,那么写不会成功,并且 Server 不生成 error。

    这点和普通 ATT_WRITE_REQ 不同。

    写法失败是否回 Error
    ATT_WRITE_REQ 可以回 ATT_ERROR_RSP
    ATT_WRITE_CMD 不回
    ATT_SIGNED_WRITE_CMD 不回

    通俗讲:

    签名无响应写仍然是“无响应”。
    盖了章,不代表店员会给你回执。
    章错了、内容错了、纸条太长,Server 可能直接忽略。
    想知道失败原因,请用有响应写,别问纸条为什么不说话。


    11. Write Characteristic Value:普通有响应写

    Write Characteristic Value 用于 Client 已知 Characteristic Value Handle,并且希望 Server 给出 ATT 层响应的场景。规范规定它使用 ATT_WRITE_REQ,Attribute Handle 设置为 Characteristic Value Handle,Attribute Value 设置为新的 Characteristic Value;成功时 Server 返回 ATT_WRITE_RSP。

    流程:

    GATT ServerGATT ClientGATT ServerGATT Client#mermaid-svg-NdHmpXHfEZHBtcEn{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NdHmpXHfEZHBtcEn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NdHmpXHfEZHBtcEn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NdHmpXHfEZHBtcEn .error-icon{fill:#552222;}#mermaid-svg-NdHmpXHfEZHBtcEn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NdHmpXHfEZHBtcEn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NdHmpXHfEZHBtcEn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NdHmpXHfEZHBtcEn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NdHmpXHfEZHBtcEn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NdHmpXHfEZHBtcEn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NdHmpXHfEZHBtcEn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NdHmpXHfEZHBtcEn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NdHmpXHfEZHBtcEn .marker.cross{stroke:#333333;}#mermaid-svg-NdHmpXHfEZHBtcEn svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NdHmpXHfEZHBtcEn p{margin:0;}#mermaid-svg-NdHmpXHfEZHBtcEn .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NdHmpXHfEZHBtcEn text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-NdHmpXHfEZHBtcEn .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-NdHmpXHfEZHBtcEn .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-NdHmpXHfEZHBtcEn .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-NdHmpXHfEZHBtcEn .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-NdHmpXHfEZHBtcEn #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-NdHmpXHfEZHBtcEn .sequenceNumber{fill:white;}#mermaid-svg-NdHmpXHfEZHBtcEn #sequencenumber{fill:#333;}#mermaid-svg-NdHmpXHfEZHBtcEn #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-NdHmpXHfEZHBtcEn .messageText{fill:#333;stroke:none;}#mermaid-svg-NdHmpXHfEZHBtcEn .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NdHmpXHfEZHBtcEn .labelText,#mermaid-svg-NdHmpXHfEZHBtcEn .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-NdHmpXHfEZHBtcEn .loopText,#mermaid-svg-NdHmpXHfEZHBtcEn .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-NdHmpXHfEZHBtcEn .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-NdHmpXHfEZHBtcEn .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-NdHmpXHfEZHBtcEn .noteText,#mermaid-svg-NdHmpXHfEZHBtcEn .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-NdHmpXHfEZHBtcEn .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NdHmpXHfEZHBtcEn .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NdHmpXHfEZHBtcEn .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NdHmpXHfEZHBtcEn .actorPopupMenu{position:absolute;}#mermaid-svg-NdHmpXHfEZHBtcEn .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-NdHmpXHfEZHBtcEn .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NdHmpXHfEZHBtcEn .actor-man circle,#mermaid-svg-NdHmpXHfEZHBtcEn line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-NdHmpXHfEZHBtcEn :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}ATT_WRITE_REQhandle = Value Handlevalue = new valueATT_WRITE_RSP

    这是最常见、最稳妥的短值写法。

    适合:

    • 配置参数;
    • 控制点;
    • 模式切换;
    • 用户设置;
    • 需要知道是否成功的写入。

    通俗讲:

    普通有响应写像挂号信。
    Client 写:“把设备名改成 My Device。”
    Server 回:“收到,写好了。”
    速度比塞纸条慢一点,但你心里踏实。


    12. 普通写的长度限制:ATT_MTU – 3

    Write Characteristic Value 只写前:

    ATT_MTU – 3

    个 octets。规范明确说,这个子过程不能用于写 long attribute,长 Attribute 应该使用 Write Long Characteristic Value。

    图解:

    #mermaid-svg-jeePImzFWC671NZv{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jeePImzFWC671NZv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jeePImzFWC671NZv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jeePImzFWC671NZv .error-icon{fill:#552222;}#mermaid-svg-jeePImzFWC671NZv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jeePImzFWC671NZv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jeePImzFWC671NZv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jeePImzFWC671NZv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jeePImzFWC671NZv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jeePImzFWC671NZv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jeePImzFWC671NZv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jeePImzFWC671NZv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jeePImzFWC671NZv .marker.cross{stroke:#333333;}#mermaid-svg-jeePImzFWC671NZv svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jeePImzFWC671NZv p{margin:0;}#mermaid-svg-jeePImzFWC671NZv .packetByte{font-size:10px;}#mermaid-svg-jeePImzFWC671NZv .packetByte.start{fill:black;}#mermaid-svg-jeePImzFWC671NZv .packetByte.end{fill:black;}#mermaid-svg-jeePImzFWC671NZv .packetLabel{fill:black;font-size:12px;}#mermaid-svg-jeePImzFWC671NZv .packetTitle{fill:black;font-size:14px;}#mermaid-svg-jeePImzFWC671NZv .packetBlock{stroke:black;stroke-width:1;fill:#efefef;}#mermaid-svg-jeePImzFWC671NZv :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}Opcode – 1 octet07Attribute Handle – 2 octets823Attribute Value – up to ATT_MTU – 32431Attribute Value – up to ATT_MTU – 33263ATT_WRITE_REQ

    和无响应写一样,原因也是:

    Opcode 1 byte + Handle 2 bytes

    表格:

    ATT_MTU普通 Write 最大 Value
    23 20 bytes
    100 97 bytes
    185 182 bytes
    247 244 bytes
    512 509 bytes

    通俗讲:

    有响应写和无响应写,单包 Value 空间都是 MTU – 3。
    差别不在盘子大小,差别在店员会不会回你“收到”。


    13. 普通写的错误处理

    如果写入失败,Server 返回 ATT_ERROR_RSP。

    规范列出的失败原因包括:认证不足、授权不足、加密 key size 太短、写入值太长、Characteristic Value 不允许写;如果 Value 按 profile 定义非法,Server 返回 Application Error 0x80–0x9F。

    常见错误表:

    场景可能错误
    Value 不允许写 Write Not Permitted
    未满足认证 Insufficient Authentication
    未满足授权 Insufficient Authorization
    加密 key size 太短 Encryption Key Size Too Short
    未加密但要求加密 Insufficient Encryption
    Value 太长 Invalid Attribute Value Length
    Profile 认为值非法 Application Error 0x80–0x9F
    Handle 无效 Invalid Handle

    图解:

    #mermaid-svg-8FeO2knUiSqOFjuU{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8FeO2knUiSqOFjuU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8FeO2knUiSqOFjuU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8FeO2knUiSqOFjuU .error-icon{fill:#552222;}#mermaid-svg-8FeO2knUiSqOFjuU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8FeO2knUiSqOFjuU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8FeO2knUiSqOFjuU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8FeO2knUiSqOFjuU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8FeO2knUiSqOFjuU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8FeO2knUiSqOFjuU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8FeO2knUiSqOFjuU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8FeO2knUiSqOFjuU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8FeO2knUiSqOFjuU .marker.cross{stroke:#333333;}#mermaid-svg-8FeO2knUiSqOFjuU svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8FeO2knUiSqOFjuU p{margin:0;}#mermaid-svg-8FeO2knUiSqOFjuU .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-8FeO2knUiSqOFjuU .cluster-label text{fill:#333;}#mermaid-svg-8FeO2knUiSqOFjuU .cluster-label span{color:#333;}#mermaid-svg-8FeO2knUiSqOFjuU .cluster-label span p{background-color:transparent;}#mermaid-svg-8FeO2knUiSqOFjuU .label text,#mermaid-svg-8FeO2knUiSqOFjuU span{fill:#333;color:#333;}#mermaid-svg-8FeO2knUiSqOFjuU .node rect,#mermaid-svg-8FeO2knUiSqOFjuU .node circle,#mermaid-svg-8FeO2knUiSqOFjuU .node ellipse,#mermaid-svg-8FeO2knUiSqOFjuU .node polygon,#mermaid-svg-8FeO2knUiSqOFjuU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8FeO2knUiSqOFjuU .rough-node .label text,#mermaid-svg-8FeO2knUiSqOFjuU .node .label text,#mermaid-svg-8FeO2knUiSqOFjuU .image-shape .label,#mermaid-svg-8FeO2knUiSqOFjuU .icon-shape .label{text-anchor:middle;}#mermaid-svg-8FeO2knUiSqOFjuU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8FeO2knUiSqOFjuU .rough-node .label,#mermaid-svg-8FeO2knUiSqOFjuU .node .label,#mermaid-svg-8FeO2knUiSqOFjuU .image-shape .label,#mermaid-svg-8FeO2knUiSqOFjuU .icon-shape .label{text-align:center;}#mermaid-svg-8FeO2knUiSqOFjuU .node.clickable{cursor:pointer;}#mermaid-svg-8FeO2knUiSqOFjuU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8FeO2knUiSqOFjuU .arrowheadPath{fill:#333333;}#mermaid-svg-8FeO2knUiSqOFjuU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8FeO2knUiSqOFjuU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8FeO2knUiSqOFjuU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8FeO2knUiSqOFjuU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8FeO2knUiSqOFjuU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8FeO2knUiSqOFjuU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8FeO2knUiSqOFjuU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8FeO2knUiSqOFjuU .cluster text{fill:#333;}#mermaid-svg-8FeO2knUiSqOFjuU .cluster span{color:#333;}#mermaid-svg-8FeO2knUiSqOFjuU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8FeO2knUiSqOFjuU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8FeO2knUiSqOFjuU rect.text{fill:none;stroke-width:0;}#mermaid-svg-8FeO2knUiSqOFjuU .icon-shape,#mermaid-svg-8FeO2knUiSqOFjuU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8FeO2knUiSqOFjuU .icon-shape p,#mermaid-svg-8FeO2knUiSqOFjuU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8FeO2knUiSqOFjuU .icon-shape .label rect,#mermaid-svg-8FeO2knUiSqOFjuU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8FeO2knUiSqOFjuU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8FeO2knUiSqOFjuU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8FeO2knUiSqOFjuU :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    No

    Yes

    No

    Yes

    No

    Yes

    No

    Yes

    No

    Yes

    ATT_WRITE_REQ

    Handle valid?

    ATT_ERROR_RSPInvalid Handle

    Write permitted?

    ATT_ERROR_RSPWrite Not Permitted

    Security OK?

    ATT_ERROR_RSPAuth/Authz/Encryption error

    Length OK?

    ATT_ERROR_RSPInvalid Attribute Value Length

    Profile value valid?

    ATT_ERROR_RSPApplication Error

    ATT_WRITE_RSP

    土味讲:

    有响应写的好处就是:失败会说原因。
    是没权限、没加密、值太长、值非法,Server 可以明明白白告诉你。
    比无响应写那种“纸条扔出去后石沉大海”靠谱得多。


    14. Write Long Characteristic Value:长值分段写

    Write Long Characteristic Value 用于 Client 已知 Characteristic Value Handle,但要写的 Value 长度超过单个 ATT_WRITE_REQ 能承载的大小。规范规定它使用 ATT_PREPARE_WRITE_REQ 和 ATT_EXECUTE_WRITE_REQ;每个 Prepare 带 Attribute Handle、Value Offset 和 Part Attribute Value;第一段 offset 通常为 0x0000,后续 offset 是下一个尚未写入的 octet,所有片段传完后用 Execute Write 写入完整值。

    流程:

    GATT ServerGATT ClientGATT ServerGATT Client#mermaid-svg-HUmNeWxHvc3JRKO5{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HUmNeWxHvc3JRKO5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HUmNeWxHvc3JRKO5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HUmNeWxHvc3JRKO5 .error-icon{fill:#552222;}#mermaid-svg-HUmNeWxHvc3JRKO5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HUmNeWxHvc3JRKO5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HUmNeWxHvc3JRKO5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HUmNeWxHvc3JRKO5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HUmNeWxHvc3JRKO5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HUmNeWxHvc3JRKO5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HUmNeWxHvc3JRKO5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HUmNeWxHvc3JRKO5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HUmNeWxHvc3JRKO5 .marker.cross{stroke:#333333;}#mermaid-svg-HUmNeWxHvc3JRKO5 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HUmNeWxHvc3JRKO5 p{margin:0;}#mermaid-svg-HUmNeWxHvc3JRKO5 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HUmNeWxHvc3JRKO5 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-HUmNeWxHvc3JRKO5 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-HUmNeWxHvc3JRKO5 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-HUmNeWxHvc3JRKO5 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-HUmNeWxHvc3JRKO5 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-HUmNeWxHvc3JRKO5 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-HUmNeWxHvc3JRKO5 .sequenceNumber{fill:white;}#mermaid-svg-HUmNeWxHvc3JRKO5 #sequencenumber{fill:#333;}#mermaid-svg-HUmNeWxHvc3JRKO5 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-HUmNeWxHvc3JRKO5 .messageText{fill:#333;stroke:none;}#mermaid-svg-HUmNeWxHvc3JRKO5 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HUmNeWxHvc3JRKO5 .labelText,#mermaid-svg-HUmNeWxHvc3JRKO5 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-HUmNeWxHvc3JRKO5 .loopText,#mermaid-svg-HUmNeWxHvc3JRKO5 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-HUmNeWxHvc3JRKO5 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-HUmNeWxHvc3JRKO5 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-HUmNeWxHvc3JRKO5 .noteText,#mermaid-svg-HUmNeWxHvc3JRKO5 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-HUmNeWxHvc3JRKO5 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HUmNeWxHvc3JRKO5 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HUmNeWxHvc3JRKO5 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-HUmNeWxHvc3JRKO5 .actorPopupMenu{position:absolute;}#mermaid-svg-HUmNeWxHvc3JRKO5 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-HUmNeWxHvc3JRKO5 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-HUmNeWxHvc3JRKO5 .actor-man circle,#mermaid-svg-HUmNeWxHvc3JRKO5 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-HUmNeWxHvc3JRKO5 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}ATT_PREPARE_WRITE_REQhandle, offset=0x0000, part1ATT_PREPARE_WRITE_RSPecho handle, offset, part1ATT_PREPARE_WRITE_REQhandle, offset=next, part2ATT_PREPARE_WRITE_RSPecho handle, offset, part2ATT_EXECUTE_WRITE_REQflags = 0x01ATT_EXECUTE_WRITE_RSP

    通俗讲:

    长写像大订单。
    一张纸写不下,就分几页。
    Prepare 是把每页先交给店员排队,Execute 是最后说:“确认,下单。”
    没有 Execute,前面只是排队,不是真正上菜。


    15. Prepare Write 的单段长度:ATT_MTU – 5

    Prepare Write PDU 里有:

    Opcode: 1 octet
    Attribute Handle: 2 octets
    Value Offset: 2 octets
    Part Attribute Value: 0 to ATT_MTU – 5

    ATT 规范中 ATT_PREPARE_WRITE_RSP 的 Part Attribute Value 长度也定义为 0 to (ATT_MTU – 5),并且响应中的 handle、offset、part value 应与对应请求相同。

    表格:

    ATT_MTUPrepare Write 单段最大 Part Value
    23 18 bytes
    100 95 bytes
    185 180 bytes
    247 242 bytes
    512 507 bytes

    图解:

    #mermaid-svg-jv4Xmmn2lf9mgS4q{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jv4Xmmn2lf9mgS4q .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jv4Xmmn2lf9mgS4q .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jv4Xmmn2lf9mgS4q .error-icon{fill:#552222;}#mermaid-svg-jv4Xmmn2lf9mgS4q .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jv4Xmmn2lf9mgS4q .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jv4Xmmn2lf9mgS4q .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jv4Xmmn2lf9mgS4q .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jv4Xmmn2lf9mgS4q .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jv4Xmmn2lf9mgS4q .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jv4Xmmn2lf9mgS4q .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jv4Xmmn2lf9mgS4q .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jv4Xmmn2lf9mgS4q .marker.cross{stroke:#333333;}#mermaid-svg-jv4Xmmn2lf9mgS4q svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jv4Xmmn2lf9mgS4q p{margin:0;}#mermaid-svg-jv4Xmmn2lf9mgS4q .packetByte{font-size:10px;}#mermaid-svg-jv4Xmmn2lf9mgS4q .packetByte.start{fill:black;}#mermaid-svg-jv4Xmmn2lf9mgS4q .packetByte.end{fill:black;}#mermaid-svg-jv4Xmmn2lf9mgS4q .packetLabel{fill:black;font-size:12px;}#mermaid-svg-jv4Xmmn2lf9mgS4q .packetTitle{fill:black;font-size:14px;}#mermaid-svg-jv4Xmmn2lf9mgS4q .packetBlock{stroke:black;stroke-width:1;fill:#efefef;}#mermaid-svg-jv4Xmmn2lf9mgS4q :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}Opcode – 1 octet07Attribute Handle – 2 octets823Value Offset – 2 octets2431Value Offset – 2 octets3239Part Attribute Value – up to ATT_MTU – 54063Part Attribute Value – up to ATT_MTU – 56479ATT_PREPARE_WRITE_REQ

    土味讲:

    Prepare 比普通 Write 又多了 offset,所以盘沿更宽。
    普通写扣 3 字节,Prepare 写扣 5 字节。
    大订单分页时,每页还要写页码,页码也占纸。


    16. Execute Write:执行还是取消

    ATT_EXECUTE_WRITE_REQ 用于请求 Server 写入或取消当前 Client prepare queue 中的所有 prepared values。ATT 规范规定它应作为 atomic operation 处理;Flags 为 0x01 表示立即写入所有 pending prepared values,为 0x00 表示取消并丢弃所有 pending prepared values。

    Flags含义
    0x00 Cancel all prepared writes
    0x01 Immediately write all pending prepared values

    图解:

    #mermaid-svg-GOLdttpkrFzFFEOt{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GOLdttpkrFzFFEOt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GOLdttpkrFzFFEOt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GOLdttpkrFzFFEOt .error-icon{fill:#552222;}#mermaid-svg-GOLdttpkrFzFFEOt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GOLdttpkrFzFFEOt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GOLdttpkrFzFFEOt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GOLdttpkrFzFFEOt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GOLdttpkrFzFFEOt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GOLdttpkrFzFFEOt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GOLdttpkrFzFFEOt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GOLdttpkrFzFFEOt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GOLdttpkrFzFFEOt .marker.cross{stroke:#333333;}#mermaid-svg-GOLdttpkrFzFFEOt svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GOLdttpkrFzFFEOt p{margin:0;}#mermaid-svg-GOLdttpkrFzFFEOt .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-GOLdttpkrFzFFEOt .cluster-label text{fill:#333;}#mermaid-svg-GOLdttpkrFzFFEOt .cluster-label span{color:#333;}#mermaid-svg-GOLdttpkrFzFFEOt .cluster-label span p{background-color:transparent;}#mermaid-svg-GOLdttpkrFzFFEOt .label text,#mermaid-svg-GOLdttpkrFzFFEOt span{fill:#333;color:#333;}#mermaid-svg-GOLdttpkrFzFFEOt .node rect,#mermaid-svg-GOLdttpkrFzFFEOt .node circle,#mermaid-svg-GOLdttpkrFzFFEOt .node ellipse,#mermaid-svg-GOLdttpkrFzFFEOt .node polygon,#mermaid-svg-GOLdttpkrFzFFEOt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GOLdttpkrFzFFEOt .rough-node .label text,#mermaid-svg-GOLdttpkrFzFFEOt .node .label text,#mermaid-svg-GOLdttpkrFzFFEOt .image-shape .label,#mermaid-svg-GOLdttpkrFzFFEOt .icon-shape .label{text-anchor:middle;}#mermaid-svg-GOLdttpkrFzFFEOt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-GOLdttpkrFzFFEOt .rough-node .label,#mermaid-svg-GOLdttpkrFzFFEOt .node .label,#mermaid-svg-GOLdttpkrFzFFEOt .image-shape .label,#mermaid-svg-GOLdttpkrFzFFEOt .icon-shape .label{text-align:center;}#mermaid-svg-GOLdttpkrFzFFEOt .node.clickable{cursor:pointer;}#mermaid-svg-GOLdttpkrFzFFEOt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-GOLdttpkrFzFFEOt .arrowheadPath{fill:#333333;}#mermaid-svg-GOLdttpkrFzFFEOt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-GOLdttpkrFzFFEOt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-GOLdttpkrFzFFEOt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GOLdttpkrFzFFEOt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-GOLdttpkrFzFFEOt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GOLdttpkrFzFFEOt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-GOLdttpkrFzFFEOt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GOLdttpkrFzFFEOt .cluster text{fill:#333;}#mermaid-svg-GOLdttpkrFzFFEOt .cluster span{color:#333;}#mermaid-svg-GOLdttpkrFzFFEOt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-GOLdttpkrFzFFEOt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GOLdttpkrFzFFEOt rect.text{fill:none;stroke-width:0;}#mermaid-svg-GOLdttpkrFzFFEOt .icon-shape,#mermaid-svg-GOLdttpkrFzFFEOt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-GOLdttpkrFzFFEOt .icon-shape p,#mermaid-svg-GOLdttpkrFzFFEOt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-GOLdttpkrFzFFEOt .icon-shape .label rect,#mermaid-svg-GOLdttpkrFzFFEOt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-GOLdttpkrFzFFEOt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GOLdttpkrFzFFEOt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GOLdttpkrFzFFEOt :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    0x00

    0x01

    ATT_EXECUTE_WRITE_REQ

    Flags

    Discard prepared writes

    Write all pending prepared values

    ATT_EXECUTE_WRITE_RSP

    原子性很关键:

    要么这一批 prepared values 一起执行,要么取消。
    这让长写更像一次完整事务。

    土味讲:

    Prepare 是把订单页放到柜台,Execute 是最后拍板。
    0x01:下单!
    0x00:算了,撤单!
    店员不能写一半说“另一半明天再说”,那叫蓝牙分期付款,规范不鼓励。


    17. Write Long 的例子

    规范 Figure 4.16 给了 Write Long Characteristic Value 的例子:Client 分三次 ATT_PREPARE_WRITE_REQ 写入 "My Special Device "、"Next To The Kitche"、"n Door",offset 分别是 0x0000、0x0012、0x0024,最后用 ATT_EXECUTE_WRITE_REQ(0x01) 执行。

    图解:

    ServerClientServerClient#mermaid-svg-THFRbTvnotOgyHKI{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-THFRbTvnotOgyHKI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-THFRbTvnotOgyHKI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-THFRbTvnotOgyHKI .error-icon{fill:#552222;}#mermaid-svg-THFRbTvnotOgyHKI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-THFRbTvnotOgyHKI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-THFRbTvnotOgyHKI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-THFRbTvnotOgyHKI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-THFRbTvnotOgyHKI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-THFRbTvnotOgyHKI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-THFRbTvnotOgyHKI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-THFRbTvnotOgyHKI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-THFRbTvnotOgyHKI .marker.cross{stroke:#333333;}#mermaid-svg-THFRbTvnotOgyHKI svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-THFRbTvnotOgyHKI p{margin:0;}#mermaid-svg-THFRbTvnotOgyHKI .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-THFRbTvnotOgyHKI text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-THFRbTvnotOgyHKI .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-THFRbTvnotOgyHKI .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-THFRbTvnotOgyHKI .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-THFRbTvnotOgyHKI .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-THFRbTvnotOgyHKI #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-THFRbTvnotOgyHKI .sequenceNumber{fill:white;}#mermaid-svg-THFRbTvnotOgyHKI #sequencenumber{fill:#333;}#mermaid-svg-THFRbTvnotOgyHKI #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-THFRbTvnotOgyHKI .messageText{fill:#333;stroke:none;}#mermaid-svg-THFRbTvnotOgyHKI .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-THFRbTvnotOgyHKI .labelText,#mermaid-svg-THFRbTvnotOgyHKI .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-THFRbTvnotOgyHKI .loopText,#mermaid-svg-THFRbTvnotOgyHKI .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-THFRbTvnotOgyHKI .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-THFRbTvnotOgyHKI .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-THFRbTvnotOgyHKI .noteText,#mermaid-svg-THFRbTvnotOgyHKI .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-THFRbTvnotOgyHKI .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-THFRbTvnotOgyHKI .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-THFRbTvnotOgyHKI .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-THFRbTvnotOgyHKI .actorPopupMenu{position:absolute;}#mermaid-svg-THFRbTvnotOgyHKI .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-THFRbTvnotOgyHKI .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-THFRbTvnotOgyHKI .actor-man circle,#mermaid-svg-THFRbTvnotOgyHKI line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-THFRbTvnotOgyHKI :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}PREPARE_WRITE_REQoffset 0x0000"My Special Device "PREPARE_WRITE_RSPechoPREPARE_WRITE_REQoffset 0x0012"Next To The Kitche"PREPARE_WRITE_RSPechoPREPARE_WRITE_REQoffset 0x0024"n Door"PREPARE_WRITE_RSPechoEXECUTE_WRITE_REQflags = 0x01EXECUTE_WRITE_RSP

    通俗讲:

    设备名太长,一张纸写不下。
    Client 分三页递给 Server。
    最后说:“确认执行。”
    Server 才把完整名字真正写进去。
    没有最后那句 Execute,前面只是草稿箱。


    18. Write Long 的错误处理

    Write Long 中,Prepare 阶段可能失败:认证不足、授权不足、加密 key size 太短、prepared value 太长、Value 不允许写、profile 认为值非法等,都可以返回 ATT_ERROR_RSP。

    ATT 层还规定,如果 prepare queue 空间不足,返回 Prepare Queue Full (0x09);Server 在收到 ATT_EXECUTE_WRITE_REQ 之前不得改变 attribute value。

    Execute 阶段也可能失败:比如 prepared Attribute Value 超过最大有效长度,返回 Invalid Attribute Value Length (0x0D);Value Offset 大于当前 attribute value 长度,返回 Invalid Offset (0x07);数据库变化则可能返回 Database Out Of Sync (0x12)。

    图解:

    #mermaid-svg-uqvD1wyGpHilIZ1r{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uqvD1wyGpHilIZ1r .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uqvD1wyGpHilIZ1r .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uqvD1wyGpHilIZ1r .error-icon{fill:#552222;}#mermaid-svg-uqvD1wyGpHilIZ1r .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uqvD1wyGpHilIZ1r .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uqvD1wyGpHilIZ1r .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uqvD1wyGpHilIZ1r .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uqvD1wyGpHilIZ1r .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uqvD1wyGpHilIZ1r .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uqvD1wyGpHilIZ1r .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uqvD1wyGpHilIZ1r .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uqvD1wyGpHilIZ1r .marker.cross{stroke:#333333;}#mermaid-svg-uqvD1wyGpHilIZ1r svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uqvD1wyGpHilIZ1r p{margin:0;}#mermaid-svg-uqvD1wyGpHilIZ1r .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-uqvD1wyGpHilIZ1r .cluster-label text{fill:#333;}#mermaid-svg-uqvD1wyGpHilIZ1r .cluster-label span{color:#333;}#mermaid-svg-uqvD1wyGpHilIZ1r .cluster-label span p{background-color:transparent;}#mermaid-svg-uqvD1wyGpHilIZ1r .label text,#mermaid-svg-uqvD1wyGpHilIZ1r span{fill:#333;color:#333;}#mermaid-svg-uqvD1wyGpHilIZ1r .node rect,#mermaid-svg-uqvD1wyGpHilIZ1r .node circle,#mermaid-svg-uqvD1wyGpHilIZ1r .node ellipse,#mermaid-svg-uqvD1wyGpHilIZ1r .node polygon,#mermaid-svg-uqvD1wyGpHilIZ1r .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uqvD1wyGpHilIZ1r .rough-node .label text,#mermaid-svg-uqvD1wyGpHilIZ1r .node .label text,#mermaid-svg-uqvD1wyGpHilIZ1r .image-shape .label,#mermaid-svg-uqvD1wyGpHilIZ1r .icon-shape .label{text-anchor:middle;}#mermaid-svg-uqvD1wyGpHilIZ1r .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uqvD1wyGpHilIZ1r .rough-node .label,#mermaid-svg-uqvD1wyGpHilIZ1r .node .label,#mermaid-svg-uqvD1wyGpHilIZ1r .image-shape .label,#mermaid-svg-uqvD1wyGpHilIZ1r .icon-shape .label{text-align:center;}#mermaid-svg-uqvD1wyGpHilIZ1r .node.clickable{cursor:pointer;}#mermaid-svg-uqvD1wyGpHilIZ1r .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uqvD1wyGpHilIZ1r .arrowheadPath{fill:#333333;}#mermaid-svg-uqvD1wyGpHilIZ1r .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uqvD1wyGpHilIZ1r .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uqvD1wyGpHilIZ1r .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uqvD1wyGpHilIZ1r .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uqvD1wyGpHilIZ1r .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uqvD1wyGpHilIZ1r .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uqvD1wyGpHilIZ1r .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uqvD1wyGpHilIZ1r .cluster text{fill:#333;}#mermaid-svg-uqvD1wyGpHilIZ1r .cluster span{color:#333;}#mermaid-svg-uqvD1wyGpHilIZ1r div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uqvD1wyGpHilIZ1r .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uqvD1wyGpHilIZ1r rect.text{fill:none;stroke-width:0;}#mermaid-svg-uqvD1wyGpHilIZ1r .icon-shape,#mermaid-svg-uqvD1wyGpHilIZ1r .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uqvD1wyGpHilIZ1r .icon-shape p,#mermaid-svg-uqvD1wyGpHilIZ1r .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uqvD1wyGpHilIZ1r .icon-shape .label rect,#mermaid-svg-uqvD1wyGpHilIZ1r .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uqvD1wyGpHilIZ1r .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uqvD1wyGpHilIZ1r .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uqvD1wyGpHilIZ1r :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    No

    Yes

    No

    Yes

    No

    Yes

    ATT_PREPARE_WRITE_REQ

    Queue space?

    Prepare Queue Full

    Permission/Security OK?

    ATT_ERROR_RSP

    Queued, value not changed yet

    ATT_EXECUTE_WRITE_REQ

    Offsets/length/app valid?

    ATT_ERROR_RSPqueue cleared

    Write atomically

    通俗讲:

    长写有两道关:
    第一关是“草稿能不能先放进队列”;
    第二关是“最终能不能执行”。
    草稿通过不代表订单已经做了,Execute 通过才算真正下厨房。


    19. Reliable Writes:可靠写

    Characteristic Value Reliable Writes 也是 Prepare + Execute,但目的更强调:

    确认 Server 收到的值就是 Client 想写的值

    规范说,该子过程用于 Client 已知 Characteristic Value Handle,并且需要在写入前确认正确 Characteristic Value 将被写入;它把要写的值双向传输,在第一阶段 Client 传给 Server,Server 校验,Client 再通过 Server response 检查每个 Characteristic Value 是否被 Server 正确接收;完成后第二阶段执行所有 prepared writes。

    流程:

    GATT ServerGATT ClientGATT ServerGATT Client#mermaid-svg-yac52E5XBayh59dT{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-yac52E5XBayh59dT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yac52E5XBayh59dT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yac52E5XBayh59dT .error-icon{fill:#552222;}#mermaid-svg-yac52E5XBayh59dT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yac52E5XBayh59dT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yac52E5XBayh59dT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yac52E5XBayh59dT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yac52E5XBayh59dT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yac52E5XBayh59dT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yac52E5XBayh59dT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yac52E5XBayh59dT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yac52E5XBayh59dT .marker.cross{stroke:#333333;}#mermaid-svg-yac52E5XBayh59dT svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yac52E5XBayh59dT p{margin:0;}#mermaid-svg-yac52E5XBayh59dT .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-yac52E5XBayh59dT text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-yac52E5XBayh59dT .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-yac52E5XBayh59dT .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-yac52E5XBayh59dT .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-yac52E5XBayh59dT .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-yac52E5XBayh59dT #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-yac52E5XBayh59dT .sequenceNumber{fill:white;}#mermaid-svg-yac52E5XBayh59dT #sequencenumber{fill:#333;}#mermaid-svg-yac52E5XBayh59dT #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-yac52E5XBayh59dT .messageText{fill:#333;stroke:none;}#mermaid-svg-yac52E5XBayh59dT .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-yac52E5XBayh59dT .labelText,#mermaid-svg-yac52E5XBayh59dT .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-yac52E5XBayh59dT .loopText,#mermaid-svg-yac52E5XBayh59dT .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-yac52E5XBayh59dT .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-yac52E5XBayh59dT .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-yac52E5XBayh59dT .noteText,#mermaid-svg-yac52E5XBayh59dT .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-yac52E5XBayh59dT .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-yac52E5XBayh59dT .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-yac52E5XBayh59dT .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-yac52E5XBayh59dT .actorPopupMenu{position:absolute;}#mermaid-svg-yac52E5XBayh59dT .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-yac52E5XBayh59dT .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-yac52E5XBayh59dT .actor-man circle,#mermaid-svg-yac52E5XBayh59dT line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-yac52E5XBayh59dT :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}Client checks echoClient checks echo againATT_PREPARE_WRITE_REQhandle, offset, valueATT_PREPARE_WRITE_RSPecho handle, offset, valueATT_PREPARE_WRITE_REQnext handle/offset/valueATT_PREPARE_WRITE_RSPechoATT_EXECUTE_WRITE_REQflags = 0x01ATT_EXECUTE_WRITE_RSP

    通俗讲:

    Reliable Writes 像点大单时店员复述:
    “您要的是 My Special Device,对吧?”
    Client 检查:“对,没听错。”
    全部确认后才 Execute。
    不是不信 Server,是蓝牙工程师怕十六进制听岔。


    20. Reliable Writes 和 Write Long 的区别

    它们底层都用 Prepare + Execute,但语义重点不同。

    项目Write LongReliable Writes
    主要目的 写入超过单个 ATT_WRITE_REQ 的长 Value 写前确认 Server 收到的值正确
    是否要求 Client 校验 echo 不需要验证 Prepare Response 中的值 需要检查每个 Prepare Response
    是否可写多个值 通常用于一个长 Value 可被高层用于按顺序写多个值
    关注点 分段和最终执行 正确性确认和执行
    底层 PDU Prepare + Execute Prepare + Execute

    规范在 Write Long 中特别备注:该子过程不需要验证 ATT_PREPARE_WRITE_RSP 里的值;而 Reliable Writes 明确要求 Client 检查每个 Characteristic Value,以确认 Server 正确接收。

    图解:

    #mermaid-svg-M6ArBOQi8jp2uQFR{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-M6ArBOQi8jp2uQFR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-M6ArBOQi8jp2uQFR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-M6ArBOQi8jp2uQFR .error-icon{fill:#552222;}#mermaid-svg-M6ArBOQi8jp2uQFR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-M6ArBOQi8jp2uQFR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-M6ArBOQi8jp2uQFR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-M6ArBOQi8jp2uQFR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-M6ArBOQi8jp2uQFR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-M6ArBOQi8jp2uQFR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-M6ArBOQi8jp2uQFR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-M6ArBOQi8jp2uQFR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-M6ArBOQi8jp2uQFR .marker.cross{stroke:#333333;}#mermaid-svg-M6ArBOQi8jp2uQFR svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-M6ArBOQi8jp2uQFR p{margin:0;}#mermaid-svg-M6ArBOQi8jp2uQFR .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-M6ArBOQi8jp2uQFR .cluster-label text{fill:#333;}#mermaid-svg-M6ArBOQi8jp2uQFR .cluster-label span{color:#333;}#mermaid-svg-M6ArBOQi8jp2uQFR .cluster-label span p{background-color:transparent;}#mermaid-svg-M6ArBOQi8jp2uQFR .label text,#mermaid-svg-M6ArBOQi8jp2uQFR span{fill:#333;color:#333;}#mermaid-svg-M6ArBOQi8jp2uQFR .node rect,#mermaid-svg-M6ArBOQi8jp2uQFR .node circle,#mermaid-svg-M6ArBOQi8jp2uQFR .node ellipse,#mermaid-svg-M6ArBOQi8jp2uQFR .node polygon,#mermaid-svg-M6ArBOQi8jp2uQFR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-M6ArBOQi8jp2uQFR .rough-node .label text,#mermaid-svg-M6ArBOQi8jp2uQFR .node .label text,#mermaid-svg-M6ArBOQi8jp2uQFR .image-shape .label,#mermaid-svg-M6ArBOQi8jp2uQFR .icon-shape .label{text-anchor:middle;}#mermaid-svg-M6ArBOQi8jp2uQFR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-M6ArBOQi8jp2uQFR .rough-node .label,#mermaid-svg-M6ArBOQi8jp2uQFR .node .label,#mermaid-svg-M6ArBOQi8jp2uQFR .image-shape .label,#mermaid-svg-M6ArBOQi8jp2uQFR .icon-shape .label{text-align:center;}#mermaid-svg-M6ArBOQi8jp2uQFR .node.clickable{cursor:pointer;}#mermaid-svg-M6ArBOQi8jp2uQFR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-M6ArBOQi8jp2uQFR .arrowheadPath{fill:#333333;}#mermaid-svg-M6ArBOQi8jp2uQFR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-M6ArBOQi8jp2uQFR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-M6ArBOQi8jp2uQFR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M6ArBOQi8jp2uQFR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-M6ArBOQi8jp2uQFR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M6ArBOQi8jp2uQFR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-M6ArBOQi8jp2uQFR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-M6ArBOQi8jp2uQFR .cluster text{fill:#333;}#mermaid-svg-M6ArBOQi8jp2uQFR .cluster span{color:#333;}#mermaid-svg-M6ArBOQi8jp2uQFR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-M6ArBOQi8jp2uQFR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-M6ArBOQi8jp2uQFR rect.text{fill:none;stroke-width:0;}#mermaid-svg-M6ArBOQi8jp2uQFR .icon-shape,#mermaid-svg-M6ArBOQi8jp2uQFR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M6ArBOQi8jp2uQFR .icon-shape p,#mermaid-svg-M6ArBOQi8jp2uQFR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-M6ArBOQi8jp2uQFR .icon-shape .label rect,#mermaid-svg-M6ArBOQi8jp2uQFR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M6ArBOQi8jp2uQFR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-M6ArBOQi8jp2uQFR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-M6ArBOQi8jp2uQFR :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Prepare + Execute

    Write Longfocus: long value segmentation

    Reliable Writesfocus: echo verification

    No need to verify Prepare RSP values

    Client verifies echoed values

    土味讲:

    Write Long 是“订单太长,分几页写”。
    Reliable Writes 是“订单不能错,店员复述一遍”。
    两者都拿 Prepare/Execute 这套工具,但一个解决长度,一个解决确认。


    21. Write Properties 和 Permissions 的关系

    Characteristic Properties 里和写有关的 bit 包括:

    Property对应写法
    Write Without Response 允许无响应写
    Authenticated Signed Writes 允许签名无响应写
    Write 允许有响应写
    Extended Properties 可能进一步支持 Reliable Write 等扩展

    但要记住:

    Properties 是能力菜单;
    Permissions / Security 是门禁。

    ATT 安全章节也提醒:能读不代表能写,能写不代表能读,每个 attribute 都可以有不同 security requirements;Server 访问 attribute 时应先检查 authentication 和 authorization。

    图解:

    #mermaid-svg-rHVkN1vXmIcANu4m{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-rHVkN1vXmIcANu4m .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-rHVkN1vXmIcANu4m .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-rHVkN1vXmIcANu4m .error-icon{fill:#552222;}#mermaid-svg-rHVkN1vXmIcANu4m .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rHVkN1vXmIcANu4m .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-rHVkN1vXmIcANu4m .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rHVkN1vXmIcANu4m .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rHVkN1vXmIcANu4m .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-rHVkN1vXmIcANu4m .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rHVkN1vXmIcANu4m .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rHVkN1vXmIcANu4m .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rHVkN1vXmIcANu4m .marker.cross{stroke:#333333;}#mermaid-svg-rHVkN1vXmIcANu4m svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rHVkN1vXmIcANu4m p{margin:0;}#mermaid-svg-rHVkN1vXmIcANu4m .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-rHVkN1vXmIcANu4m .cluster-label text{fill:#333;}#mermaid-svg-rHVkN1vXmIcANu4m .cluster-label span{color:#333;}#mermaid-svg-rHVkN1vXmIcANu4m .cluster-label span p{background-color:transparent;}#mermaid-svg-rHVkN1vXmIcANu4m .label text,#mermaid-svg-rHVkN1vXmIcANu4m span{fill:#333;color:#333;}#mermaid-svg-rHVkN1vXmIcANu4m .node rect,#mermaid-svg-rHVkN1vXmIcANu4m .node circle,#mermaid-svg-rHVkN1vXmIcANu4m .node ellipse,#mermaid-svg-rHVkN1vXmIcANu4m .node polygon,#mermaid-svg-rHVkN1vXmIcANu4m .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rHVkN1vXmIcANu4m .rough-node .label text,#mermaid-svg-rHVkN1vXmIcANu4m .node .label text,#mermaid-svg-rHVkN1vXmIcANu4m .image-shape .label,#mermaid-svg-rHVkN1vXmIcANu4m .icon-shape .label{text-anchor:middle;}#mermaid-svg-rHVkN1vXmIcANu4m .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-rHVkN1vXmIcANu4m .rough-node .label,#mermaid-svg-rHVkN1vXmIcANu4m .node .label,#mermaid-svg-rHVkN1vXmIcANu4m .image-shape .label,#mermaid-svg-rHVkN1vXmIcANu4m .icon-shape .label{text-align:center;}#mermaid-svg-rHVkN1vXmIcANu4m .node.clickable{cursor:pointer;}#mermaid-svg-rHVkN1vXmIcANu4m .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-rHVkN1vXmIcANu4m .arrowheadPath{fill:#333333;}#mermaid-svg-rHVkN1vXmIcANu4m .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rHVkN1vXmIcANu4m .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rHVkN1vXmIcANu4m .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rHVkN1vXmIcANu4m .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-rHVkN1vXmIcANu4m .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rHVkN1vXmIcANu4m .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-rHVkN1vXmIcANu4m .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rHVkN1vXmIcANu4m .cluster text{fill:#333;}#mermaid-svg-rHVkN1vXmIcANu4m .cluster span{color:#333;}#mermaid-svg-rHVkN1vXmIcANu4m div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rHVkN1vXmIcANu4m .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-rHVkN1vXmIcANu4m rect.text{fill:none;stroke-width:0;}#mermaid-svg-rHVkN1vXmIcANu4m .icon-shape,#mermaid-svg-rHVkN1vXmIcANu4m .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-rHVkN1vXmIcANu4m .icon-shape p,#mermaid-svg-rHVkN1vXmIcANu4m .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-rHVkN1vXmIcANu4m .icon-shape .label rect,#mermaid-svg-rHVkN1vXmIcANu4m .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-rHVkN1vXmIcANu4m .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-rHVkN1vXmIcANu4m .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-rHVkN1vXmIcANu4m :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Yes

    No

    Characteristic Properties

    Capability menu

    Attribute Permissions / Security

    Access gate

    Client chooses write procedure

    Write allowed now?

    Write succeeds

    Error for requestIgnore for command

    通俗讲:

    Properties 像菜单写“支持改甜度”。
    Permissions 像店员问“你是不是这杯的主人”。
    菜单支持,不代表任何路人都能改。
    蓝牙不是自助餐,不能见到 Value 就改。


    22. Write Value 和 Write Descriptor 的区别

    写 Characteristic Value 和写 Descriptor 可能都使用 ATT_WRITE_REQ。

    区别还是:

    handle 指向谁

    操作Handle
    Write Characteristic Value Characteristic Value Handle
    Write Characteristic Descriptor Descriptor Handle

    例如:

    HandleType说明
    0x0204 Temperature UUID Temperature Value
    0x0205 0x2902 CCCD Descriptor

    写温度配置值:

    ATT_WRITE_REQ(handle = 0x0204, value = …)

    开启通知:

    ATT_WRITE_REQ(handle = 0x0205, value = 01 00)

    Descriptor Write 章节也规定,写 descriptor 时 ATT_WRITE_REQ 的 Attribute Handle 设置为 characteristic descriptor handle,成功时返回 ATT_WRITE_RSP。

    图解:

    #mermaid-svg-ypwVShUw3p0V6Wqc{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ypwVShUw3p0V6Wqc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ypwVShUw3p0V6Wqc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ypwVShUw3p0V6Wqc .error-icon{fill:#552222;}#mermaid-svg-ypwVShUw3p0V6Wqc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ypwVShUw3p0V6Wqc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ypwVShUw3p0V6Wqc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ypwVShUw3p0V6Wqc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ypwVShUw3p0V6Wqc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ypwVShUw3p0V6Wqc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ypwVShUw3p0V6Wqc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ypwVShUw3p0V6Wqc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ypwVShUw3p0V6Wqc .marker.cross{stroke:#333333;}#mermaid-svg-ypwVShUw3p0V6Wqc svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ypwVShUw3p0V6Wqc p{margin:0;}#mermaid-svg-ypwVShUw3p0V6Wqc .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ypwVShUw3p0V6Wqc .cluster-label text{fill:#333;}#mermaid-svg-ypwVShUw3p0V6Wqc .cluster-label span{color:#333;}#mermaid-svg-ypwVShUw3p0V6Wqc .cluster-label span p{background-color:transparent;}#mermaid-svg-ypwVShUw3p0V6Wqc .label text,#mermaid-svg-ypwVShUw3p0V6Wqc span{fill:#333;color:#333;}#mermaid-svg-ypwVShUw3p0V6Wqc .node rect,#mermaid-svg-ypwVShUw3p0V6Wqc .node circle,#mermaid-svg-ypwVShUw3p0V6Wqc .node ellipse,#mermaid-svg-ypwVShUw3p0V6Wqc .node polygon,#mermaid-svg-ypwVShUw3p0V6Wqc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ypwVShUw3p0V6Wqc .rough-node .label text,#mermaid-svg-ypwVShUw3p0V6Wqc .node .label text,#mermaid-svg-ypwVShUw3p0V6Wqc .image-shape .label,#mermaid-svg-ypwVShUw3p0V6Wqc .icon-shape .label{text-anchor:middle;}#mermaid-svg-ypwVShUw3p0V6Wqc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ypwVShUw3p0V6Wqc .rough-node .label,#mermaid-svg-ypwVShUw3p0V6Wqc .node .label,#mermaid-svg-ypwVShUw3p0V6Wqc .image-shape .label,#mermaid-svg-ypwVShUw3p0V6Wqc .icon-shape .label{text-align:center;}#mermaid-svg-ypwVShUw3p0V6Wqc .node.clickable{cursor:pointer;}#mermaid-svg-ypwVShUw3p0V6Wqc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ypwVShUw3p0V6Wqc .arrowheadPath{fill:#333333;}#mermaid-svg-ypwVShUw3p0V6Wqc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ypwVShUw3p0V6Wqc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ypwVShUw3p0V6Wqc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ypwVShUw3p0V6Wqc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ypwVShUw3p0V6Wqc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ypwVShUw3p0V6Wqc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ypwVShUw3p0V6Wqc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ypwVShUw3p0V6Wqc .cluster text{fill:#333;}#mermaid-svg-ypwVShUw3p0V6Wqc .cluster span{color:#333;}#mermaid-svg-ypwVShUw3p0V6Wqc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ypwVShUw3p0V6Wqc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ypwVShUw3p0V6Wqc rect.text{fill:none;stroke-width:0;}#mermaid-svg-ypwVShUw3p0V6Wqc .icon-shape,#mermaid-svg-ypwVShUw3p0V6Wqc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ypwVShUw3p0V6Wqc .icon-shape p,#mermaid-svg-ypwVShUw3p0V6Wqc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ypwVShUw3p0V6Wqc .icon-shape .label rect,#mermaid-svg-ypwVShUw3p0V6Wqc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ypwVShUw3p0V6Wqc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ypwVShUw3p0V6Wqc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ypwVShUw3p0V6Wqc :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    ATT_WRITE_REQ

    handle = Value Handle

    handle = Descriptor Handle

    Write Characteristic Value

    Write Characteristic Descriptor

    土味讲:

    同样是写,写 Value 是改奶茶,写 CCCD 是按关注按钮。
    PDU 长得像,业务语义完全不同。
    你把“关注”写进奶茶里,奶茶不会开始推送,最多开始变苦。


    23. Android 开发视角

    Android 里常见写法包括:

    writeCharacteristic(…)
    writeDescriptor(…)

    从协议角度看:

    Android 语义可能对应
    Write With Response ATT_WRITE_REQ / ATT_WRITE_RSP
    Write Without Response ATT_WRITE_CMD
    Signed Write ATT_SIGNED_WRITE_CMD,较少直接使用
    Long Write 底层可能使用 Prepare / Execute,或由应用分片
    Descriptor Write ATT_WRITE_REQ,handle 指向 descriptor

    Android 开发重点:

    坑说明
    写错 handle Declaration / Value / Descriptor 要分清
    只看 UUID 不看 Properties 可能对不支持写的 Characteristic 写
    无响应写当可靠写 没有 ATT 回执,失败可能静默
    忘记 MTU – 3 单包写入长度算错
    开通知写错对象 应写 CCCD descriptor,不是 Characteristic Value
    不处理 write callback / error 有响应写失败原因会丢失
    高频写不做流控 Host / Controller buffer 可能被打爆

    图解:

    #mermaid-svg-FRnDK4KXjfEGZEnD{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FRnDK4KXjfEGZEnD .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FRnDK4KXjfEGZEnD .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FRnDK4KXjfEGZEnD .error-icon{fill:#552222;}#mermaid-svg-FRnDK4KXjfEGZEnD .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FRnDK4KXjfEGZEnD .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FRnDK4KXjfEGZEnD .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FRnDK4KXjfEGZEnD .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FRnDK4KXjfEGZEnD .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FRnDK4KXjfEGZEnD .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FRnDK4KXjfEGZEnD .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FRnDK4KXjfEGZEnD .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FRnDK4KXjfEGZEnD .marker.cross{stroke:#333333;}#mermaid-svg-FRnDK4KXjfEGZEnD svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FRnDK4KXjfEGZEnD p{margin:0;}#mermaid-svg-FRnDK4KXjfEGZEnD .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-FRnDK4KXjfEGZEnD .cluster-label text{fill:#333;}#mermaid-svg-FRnDK4KXjfEGZEnD .cluster-label span{color:#333;}#mermaid-svg-FRnDK4KXjfEGZEnD .cluster-label span p{background-color:transparent;}#mermaid-svg-FRnDK4KXjfEGZEnD .label text,#mermaid-svg-FRnDK4KXjfEGZEnD span{fill:#333;color:#333;}#mermaid-svg-FRnDK4KXjfEGZEnD .node rect,#mermaid-svg-FRnDK4KXjfEGZEnD .node circle,#mermaid-svg-FRnDK4KXjfEGZEnD .node ellipse,#mermaid-svg-FRnDK4KXjfEGZEnD .node polygon,#mermaid-svg-FRnDK4KXjfEGZEnD .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FRnDK4KXjfEGZEnD .rough-node .label text,#mermaid-svg-FRnDK4KXjfEGZEnD .node .label text,#mermaid-svg-FRnDK4KXjfEGZEnD .image-shape .label,#mermaid-svg-FRnDK4KXjfEGZEnD .icon-shape .label{text-anchor:middle;}#mermaid-svg-FRnDK4KXjfEGZEnD .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FRnDK4KXjfEGZEnD .rough-node .label,#mermaid-svg-FRnDK4KXjfEGZEnD .node .label,#mermaid-svg-FRnDK4KXjfEGZEnD .image-shape .label,#mermaid-svg-FRnDK4KXjfEGZEnD .icon-shape .label{text-align:center;}#mermaid-svg-FRnDK4KXjfEGZEnD .node.clickable{cursor:pointer;}#mermaid-svg-FRnDK4KXjfEGZEnD .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FRnDK4KXjfEGZEnD .arrowheadPath{fill:#333333;}#mermaid-svg-FRnDK4KXjfEGZEnD .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FRnDK4KXjfEGZEnD .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FRnDK4KXjfEGZEnD .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FRnDK4KXjfEGZEnD .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FRnDK4KXjfEGZEnD .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FRnDK4KXjfEGZEnD .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FRnDK4KXjfEGZEnD .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FRnDK4KXjfEGZEnD .cluster text{fill:#333;}#mermaid-svg-FRnDK4KXjfEGZEnD .cluster span{color:#333;}#mermaid-svg-FRnDK4KXjfEGZEnD div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FRnDK4KXjfEGZEnD .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FRnDK4KXjfEGZEnD rect.text{fill:none;stroke-width:0;}#mermaid-svg-FRnDK4KXjfEGZEnD .icon-shape,#mermaid-svg-FRnDK4KXjfEGZEnD .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FRnDK4KXjfEGZEnD .icon-shape p,#mermaid-svg-FRnDK4KXjfEGZEnD .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FRnDK4KXjfEGZEnD .icon-shape .label rect,#mermaid-svg-FRnDK4KXjfEGZEnD .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FRnDK4KXjfEGZEnD .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FRnDK4KXjfEGZEnD .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FRnDK4KXjfEGZEnD :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    Android App

    Check Characteristic Properties

    Choose Write Type

    BluetoothGatt write

    ATT_WRITE_REQ or ATT_WRITE_CMD

    Callback or no ATT response

    通俗讲:

    Android 写 Characteristic 看起来是一个 API,底层有两种脾气:
    有响应写会回你“成了/炸了”;
    无响应写像快递放门口。
    App 自己要知道这单是挂号信还是普通传单。


    24. iOS CoreBluetooth 视角

    iOS 里典型写法是:

    writeValue(_:for:type:)

    其中 type 常见有:

    .withResponse
    .withoutResponse

    协议对应:

    iOS 写类型GATT/ATT 语义
    .withResponse ATT_WRITE_REQ / ATT_WRITE_RSP
    .withoutResponse ATT_WRITE_CMD
    writeValue(_:for: CBDescriptor) Descriptor Write

    iOS 开发重点:

    点说明
    .withoutResponse 不等于一定成功 无 ATT 层回执
    .withResponse 适合配置类写入 可收到错误反馈
    长数据要按平台能力分片 不能盲塞超长 payload
    写 Descriptor 和写 Characteristic 区分 API handle 语义不同
    通知订阅通常用 setNotifyValue 封装 底层仍涉及 CCCD

    图解:

    #mermaid-svg-m9t0bEmfJRsyDwwa{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-m9t0bEmfJRsyDwwa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-m9t0bEmfJRsyDwwa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-m9t0bEmfJRsyDwwa .error-icon{fill:#552222;}#mermaid-svg-m9t0bEmfJRsyDwwa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-m9t0bEmfJRsyDwwa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-m9t0bEmfJRsyDwwa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-m9t0bEmfJRsyDwwa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-m9t0bEmfJRsyDwwa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-m9t0bEmfJRsyDwwa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-m9t0bEmfJRsyDwwa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-m9t0bEmfJRsyDwwa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-m9t0bEmfJRsyDwwa .marker.cross{stroke:#333333;}#mermaid-svg-m9t0bEmfJRsyDwwa svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-m9t0bEmfJRsyDwwa p{margin:0;}#mermaid-svg-m9t0bEmfJRsyDwwa .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-m9t0bEmfJRsyDwwa .cluster-label text{fill:#333;}#mermaid-svg-m9t0bEmfJRsyDwwa .cluster-label span{color:#333;}#mermaid-svg-m9t0bEmfJRsyDwwa .cluster-label span p{background-color:transparent;}#mermaid-svg-m9t0bEmfJRsyDwwa .label text,#mermaid-svg-m9t0bEmfJRsyDwwa span{fill:#333;color:#333;}#mermaid-svg-m9t0bEmfJRsyDwwa .node rect,#mermaid-svg-m9t0bEmfJRsyDwwa .node circle,#mermaid-svg-m9t0bEmfJRsyDwwa .node ellipse,#mermaid-svg-m9t0bEmfJRsyDwwa .node polygon,#mermaid-svg-m9t0bEmfJRsyDwwa .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-m9t0bEmfJRsyDwwa .rough-node .label text,#mermaid-svg-m9t0bEmfJRsyDwwa .node .label text,#mermaid-svg-m9t0bEmfJRsyDwwa .image-shape .label,#mermaid-svg-m9t0bEmfJRsyDwwa .icon-shape .label{text-anchor:middle;}#mermaid-svg-m9t0bEmfJRsyDwwa .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-m9t0bEmfJRsyDwwa .rough-node .label,#mermaid-svg-m9t0bEmfJRsyDwwa .node .label,#mermaid-svg-m9t0bEmfJRsyDwwa .image-shape .label,#mermaid-svg-m9t0bEmfJRsyDwwa .icon-shape .label{text-align:center;}#mermaid-svg-m9t0bEmfJRsyDwwa .node.clickable{cursor:pointer;}#mermaid-svg-m9t0bEmfJRsyDwwa .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-m9t0bEmfJRsyDwwa .arrowheadPath{fill:#333333;}#mermaid-svg-m9t0bEmfJRsyDwwa .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-m9t0bEmfJRsyDwwa .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-m9t0bEmfJRsyDwwa .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-m9t0bEmfJRsyDwwa .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-m9t0bEmfJRsyDwwa .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-m9t0bEmfJRsyDwwa .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-m9t0bEmfJRsyDwwa .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-m9t0bEmfJRsyDwwa .cluster text{fill:#333;}#mermaid-svg-m9t0bEmfJRsyDwwa .cluster span{color:#333;}#mermaid-svg-m9t0bEmfJRsyDwwa div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-m9t0bEmfJRsyDwwa .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-m9t0bEmfJRsyDwwa rect.text{fill:none;stroke-width:0;}#mermaid-svg-m9t0bEmfJRsyDwwa .icon-shape,#mermaid-svg-m9t0bEmfJRsyDwwa .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-m9t0bEmfJRsyDwwa .icon-shape p,#mermaid-svg-m9t0bEmfJRsyDwwa .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-m9t0bEmfJRsyDwwa .icon-shape .label rect,#mermaid-svg-m9t0bEmfJRsyDwwa .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-m9t0bEmfJRsyDwwa .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-m9t0bEmfJRsyDwwa .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-m9t0bEmfJRsyDwwa :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    iOS App

    withResponse / withoutResponse

    CoreBluetooth

    ATT_WRITE_REQ

    ATT_WRITE_CMD

    通俗讲:

    iOS 服务员比较体面,会把很多 handle 细节藏起来。
    但 .withResponse 和 .withoutResponse 的协议差别非常硬:
    一个要回执,一个不等回执。
    你不能点了“无需回执”,又投诉“怎么没人回我”。


    25. 嵌入式 GATT Server 视角

    Server 端实现写入时,要重点检查:

    检查项要求
    Handle 是否有效 无效返回 Invalid Handle
    是否是 Value Handle 写 Value 和写 Descriptor 要分开
    Properties 是否支持对应写法 Write / Write Without Response / Signed Write
    Permissions 是否允许写 Write Not Permitted / Security error
    是否需要加密/认证/授权 按安全要求处理
    Value 长度是否合法 太长返回或忽略,取决于 PDU
    Value 内容是否合法 profile 定义非法时返回 Application Error 或忽略
    无响应写失败是否静默 ATT_WRITE_CMD 不回 error
    Prepare queue 是否足够 不够返回 Prepare Queue Full
    Execute 是否原子执行 Prepare 后真正写入发生在 Execute
    Reliable Write 是否 echo 正确 Prepare Response 要正确回显
    多 bearer 场景 prepared write 顺序与 bearer 注意事项

    ATT 规范要求 Server 在访问 attribute 时先检查 authentication 和 authorization,这个顺序有安全意义。

    通俗讲:

    Server 写入实现不是收到包就 memcpy。
    你要先看门牌、看权限、看安全、看长度、看值是否合法。
    无响应写失败可以静默,但别把静默当偷懒许可证。
    嵌入式端偷一次懒,App 端抓包抓到天亮。


    26. 抓包视角:怎么看写入?

    抓包时按 PDU 识别写法。

    PDU写法
    ATT_WRITE_CMD Write Without Response
    ATT_SIGNED_WRITE_CMD Signed Write Without Response
    ATT_WRITE_REQ / ATT_WRITE_RSP Write Characteristic Value 或 Write Descriptor
    ATT_PREPARE_WRITE_REQ / RSP Write Long 或 Reliable Write 第一阶段
    ATT_EXECUTE_WRITE_REQ / RSP Write Long 或 Reliable Write 第二阶段

    26.1 看是不是有响应

    ATT_WRITE_CMD -> 没有响应
    ATT_WRITE_REQ -> 应有 ATT_WRITE_RSP 或 ATT_ERROR_RSP

    26.2 看 handle 指向谁

    handle = Characteristic Value Handle -> 写 Value
    handle = Descriptor Handle -> 写 Descriptor

    26.3 看 payload 长度

    WRITE_CMD / WRITE_REQ Value <= ATT_MTU – 3
    SIGNED_WRITE_CMD Value <= ATT_MTU – 15
    PREPARE_WRITE part <= ATT_MTU – 5

    26.4 看长写是否 Execute

    只有 Prepare,没有 Execute -> 没有真正提交
    Execute flags 0x01 -> 提交
    Execute flags 0x00 -> 取消

    抓包流程图:

    #mermaid-svg-78j3QI1xnv226Y0M{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-78j3QI1xnv226Y0M .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-78j3QI1xnv226Y0M .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-78j3QI1xnv226Y0M .error-icon{fill:#552222;}#mermaid-svg-78j3QI1xnv226Y0M .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-78j3QI1xnv226Y0M .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-78j3QI1xnv226Y0M .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-78j3QI1xnv226Y0M .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-78j3QI1xnv226Y0M .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-78j3QI1xnv226Y0M .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-78j3QI1xnv226Y0M .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-78j3QI1xnv226Y0M .marker{fill:#333333;stroke:#333333;}#mermaid-svg-78j3QI1xnv226Y0M .marker.cross{stroke:#333333;}#mermaid-svg-78j3QI1xnv226Y0M svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-78j3QI1xnv226Y0M p{margin:0;}#mermaid-svg-78j3QI1xnv226Y0M .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-78j3QI1xnv226Y0M .cluster-label text{fill:#333;}#mermaid-svg-78j3QI1xnv226Y0M .cluster-label span{color:#333;}#mermaid-svg-78j3QI1xnv226Y0M .cluster-label span p{background-color:transparent;}#mermaid-svg-78j3QI1xnv226Y0M .label text,#mermaid-svg-78j3QI1xnv226Y0M span{fill:#333;color:#333;}#mermaid-svg-78j3QI1xnv226Y0M .node rect,#mermaid-svg-78j3QI1xnv226Y0M .node circle,#mermaid-svg-78j3QI1xnv226Y0M .node ellipse,#mermaid-svg-78j3QI1xnv226Y0M .node polygon,#mermaid-svg-78j3QI1xnv226Y0M .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-78j3QI1xnv226Y0M .rough-node .label text,#mermaid-svg-78j3QI1xnv226Y0M .node .label text,#mermaid-svg-78j3QI1xnv226Y0M .image-shape .label,#mermaid-svg-78j3QI1xnv226Y0M .icon-shape .label{text-anchor:middle;}#mermaid-svg-78j3QI1xnv226Y0M .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-78j3QI1xnv226Y0M .rough-node .label,#mermaid-svg-78j3QI1xnv226Y0M .node .label,#mermaid-svg-78j3QI1xnv226Y0M .image-shape .label,#mermaid-svg-78j3QI1xnv226Y0M .icon-shape .label{text-align:center;}#mermaid-svg-78j3QI1xnv226Y0M .node.clickable{cursor:pointer;}#mermaid-svg-78j3QI1xnv226Y0M .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-78j3QI1xnv226Y0M .arrowheadPath{fill:#333333;}#mermaid-svg-78j3QI1xnv226Y0M .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-78j3QI1xnv226Y0M .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-78j3QI1xnv226Y0M .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-78j3QI1xnv226Y0M .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-78j3QI1xnv226Y0M .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-78j3QI1xnv226Y0M .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-78j3QI1xnv226Y0M .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-78j3QI1xnv226Y0M .cluster text{fill:#333;}#mermaid-svg-78j3QI1xnv226Y0M .cluster span{color:#333;}#mermaid-svg-78j3QI1xnv226Y0M div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-78j3QI1xnv226Y0M .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-78j3QI1xnv226Y0M rect.text{fill:none;stroke-width:0;}#mermaid-svg-78j3QI1xnv226Y0M .icon-shape,#mermaid-svg-78j3QI1xnv226Y0M .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-78j3QI1xnv226Y0M .icon-shape p,#mermaid-svg-78j3QI1xnv226Y0M .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-78j3QI1xnv226Y0M .icon-shape .label rect,#mermaid-svg-78j3QI1xnv226Y0M .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-78j3QI1xnv226Y0M .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-78j3QI1xnv226Y0M .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-78j3QI1xnv226Y0M :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

    ATT_WRITE_CMD

    ATT_SIGNED_WRITE_CMD

    ATT_WRITE_REQ

    ATT_PREPARE_WRITE_REQ

    ATT_EXECUTE_WRITE_REQ

    0x01

    0x00

    ATT PDU

    PDU type

    Write Without Response

    Signed Write

    Write with Response

    Prepare phase

    Execute phase

    No ATT response expected

    Expect ATT_WRITE_RSP or ERROR

    Expect PREPARE_WRITE_RSP echo

    flags

    Commit

    Cancel

    抓包口诀:

    52 是 Write Command,不等回执;
    12 是 Write Request,要看 13 或 Error;
    D2 是 Signed Write,签名占 12 字节;
    16/17 是 Prepare,18/19 是 Execute;
    写业务看 Value Handle,开通知看 CCCD Handle。


    27. 五种写法对比总结表

    写法是否可靠知道结果是否适合长值是否有错误响应Value 空间
    Write Without Response 不可靠 不适合 无 ATT_MTU – 3
    Signed Write Without Response 不可靠,但有签名认证 不适合 无 ATT_MTU – 15
    Write Characteristic Value 可知道成功/失败 不适合 long 有 ATT_MTU – 3
    Write Long Characteristic Value 可知道提交结果 适合 有 Prepare 每段 ATT_MTU – 5
    Reliable Writes 可验证 Server 收到的值 适合复杂/多个值 有 Prepare 每段 ATT_MTU – 5

    通俗总结:

    要快:无响应写。
    要签名:签名无响应写。
    要回执:普通写。
    要写长:长写。
    要确保 Server 听对了:可靠写。
    选错写法,轻则多几个包,重则数据写丢还没人告诉你。


    28. 设计建议

    28.1 控制命令优先用有响应写

    例如重启设备、保存配置、绑定账号、切换工作模式,建议使用有响应写或更可靠的业务 ACK。

    28.2 高频流式数据可以考虑无响应写

    例如连续控制、短包流式传输,可以使用 Write Without Response,但要有流控策略。

    28.3 长数据不要硬塞普通 Write

    超过 ATT_MTU – 3 的数据应分片或使用 Write Long。

    28.4 写 CCCD 属于写 Descriptor

    开启 Notification / Indication 是写 CCCD,不是写 Characteristic Value。

    28.5 需要确认内容无误时使用 Reliable Writes

    尤其是多个值要按顺序一次性写入,或者写入内容必须经过 echo 校验时。

    28.6 Server 不要虚假声明 Properties

    不支持的写法不要在 Properties 里声明。

    28.7 App 不要把无响应写当可靠传输

    无响应写失败可能没有任何 ATT 层反馈。


    29. 常见误区

    29.1 误区一:Write Without Response 也会返回错误

    不对。

    ATT_WRITE_CMD 不返回 ATT_WRITE_RSP,也不返回 ATT_ERROR_RSP;Server 不能写时会忽略该 command。

    29.2 误区二:普通 Write 可以写任意长度

    不对。

    普通 Write 只能写前 ATT_MTU – 3 octets,不能写 long attribute。

    29.3 误区三:Signed Write 比普通无响应写只是多了安全,长度不变

    不对。

    Signed Write 需要 12-byte authentication signature,所以 Value 空间是 ATT_MTU – 15。

    29.4 误区四:已加密 LE 链路还应该用 Signed Write

    不对。

    已处于 LE Security Mode 1 Level 2 或 Level 3 加密时,应使用 Write Without Response,而不是 Signed Write Without Response。

    29.5 误区五:BR/EDR 上也用 Signed Write

    不对。

    BR/EDR 的 ATT bearer 因 Security Mode 4 总是加密,所以这个子过程不用于 BR/EDR。

    29.6 误区六:Prepare Write 成功就表示值已经写入

    不对。

    Server 在收到 ATT_EXECUTE_WRITE_REQ 之前不得改变 attribute value;Prepare 只是进入 prepare queue。

    29.7 误区七:Write Long 和 Reliable Writes 完全一样

    不对。

    二者底层都用 Prepare + Execute,但 Write Long 解决长度问题,不需要验证 Prepare Response;Reliable Writes 强调 Client 检查 Server echo,确认内容无误后再 Execute。

    29.8 误区八:写 Characteristic 就能开启通知

    不对。

    开启 Notification / Indication 是写 CCCD Descriptor,不是写 Characteristic Value。


    30. 本篇总结

    本篇学习了 4.9 Characteristic Value Write 以及 5 种写 Characteristic Value 的子过程。

    核心内容可以总结为:

  • Characteristic Value Write 用于 Client 向 Server 写 Characteristic Value。
  • 写 Value 要使用 Characteristic Value Handle。
  • Write Without Response 使用 ATT_WRITE_CMD,没有 ATT 层响应。
  • Write Without Response 单包 Value 最大是 ATT_MTU – 3。
  • Signed Write Without Response 使用 ATT_SIGNED_WRITE_CMD,用于未加密 bearer 上的签名写。
  • Signed Write Without Response 单包 Value 最大是 ATT_MTU – 15。
  • 普通 Write Characteristic Value 使用 ATT_WRITE_REQ / ATT_WRITE_RSP。
  • 普通写成功有 ATT_WRITE_RSP,失败可有 ATT_ERROR_RSP。
  • 普通写不能写 long attribute。
  • Write Long Characteristic Value 使用 Prepare Write + Execute Write。
  • Prepare Write 每段带 Value Offset,第一段通常 offset 为 0x0000。
  • Prepare Write 单段 Part Value 最大是 ATT_MTU – 5。
  • Execute Write flags 0x01 表示提交,0x00 表示取消。
  • Reliable Writes 要求 Client 校验 Server 在 Prepare Response 中 echo 的值。
  • Properties 是能力声明,Permissions / Security 才决定当下能不能写。
  • 写 Value 和写 Descriptor 可能使用同类 ATT PDU,但 handle 指向不同,语义完全不同。
  • 一句话总结:

    Characteristic Value Write 是 Client 改 Server 数据的入口:短值可以无响应写或有响应写,签名写用于未加密但需要认证的场景,长值用 Prepare + Execute 分段提交,可靠写则在提交前让 Server echo 并由 Client 校验。

    再土味一点:

    写 Value 就像下订单。
    无响应写是“纸条塞门缝”;
    有响应写是“店员回收到”;
    长写是“订单太长分几页”;
    可靠写是“店员复述一遍,确认无误再下厨房”。
    该要回执时别图快,该分段时别硬塞,该写 CCCD 时别写 Value。
    蓝牙写入的坑,大多数不是协议阴险,而是我们把纸条、挂号信、长订单和关注按钮混成了一锅。


    31. 下一篇预告

    下一篇学习:

    第 43 篇:Characteristic Value Notification:Server 如何主动通知 Client

    覆盖章节:

    • 4.10 Characteristic Value Notification
    • 4.10.1 Single Notification
    • 4.10.2 Multiple Variable Length Notifications

    下一篇会讲:

    • Notification 为什么是 Server 主动发送;
    • 为什么 Notification 不需要 ATT 层 acknowledgment;
    • ATT_HANDLE_VALUE_NTF 的结构;
    • Notification 为什么携带 Characteristic Value Handle;
    • CCCD 如何控制 Notification;
    • Multiple Variable Length Notifications 如何一次通知多个 Value;
    • Notification 和 Indication 的关键区别;
    • Android / iOS 的通知回调底层到底对应什么;
    • 抓包时如何判断通知有没有真正发出来。

    下一篇一句话预告:

    本篇是 Client 写 Server;下一篇反过来,Server 主动推送 Client。Notification 就像公众号推送:你先写 CCCD 点关注,Server 才会把 Value 更新主动发给你。

    赞(0)
    未经允许不得转载:171主机测评 » 【Bluetooth-SIG】【CoreV6.2】【Vol3 Part G】【GATT】【42】【Characteristic Value Write Overview:Client 如何写入 Ch】
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址