You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32 Arduino写入ANCS控制点特征遇0xA1格式错误求助

问题:ANCS Control Point写入返回0xA1(命令格式错误)

环境与背景

  • 使用Arduino ESP32,遵循ANCS规范向Control Point特征写入命令
  • 尝试获取通知的App Identifier属性,但写入后返回错误码0xA1:Invalid command (0xA1). The command was improperly formatted.

命令定义代码

uint8_t notificationAttributesCommand[7];

notificationAttributesCommand[0] = 0x00;  // Command ID: Get Notification Attributes
notificationAttributesCommand[1] = (uint8_t)(notificationUID >> 24);  // Notification UID (Most significant byte)
notificationAttributesCommand[2] = (uint8_t)(notificationUID >> 16);  // Second byte
notificationAttributesCommand[3] = (uint8_t)(notificationUID >> 8);   // Third byte
notificationAttributesCommand[4] = (uint8_t)(notificationUID);        // Least significant byte

// Attribute IDs and their maximum length (as per your specification)
notificationAttributesCommand[5] = 0x01;  // Attribute ID for App Identifier

发送逻辑(loop函数)

void loop() {
    if (newNotificationFlag) {
        // Send the command to the Control Point characteristic
        if (pControlPointCharacteristic != nullptr) {
            Serial.println("Sending Get Notification Attributes command to Control Point...");
            if (pControlPointCharacteristic->canWrite()) {
                Serial.println("before writing to pControlPointCharacteristic!");
                pControlPointCharacteristic->writeValue(notificationAttributesCommand, sizeof(notificationAttributesCommand), true);
                Serial.println("after writing to pControlPointCharacteristic!");
            } else {
              Serial.println("loop(): Control Point Characteristic cannot be written.");
            }
        } else {
            Serial.println("Control Point characteristic is null");
        }
        // Reset the flag
        newNotificationFlag = false;
    }
}

修改的日志输出代码(BLERemoteCharacteristic.cpp)

case ESP_GATTC_WRITE_CHAR_EVT:
    {
      log_d("case ESP_GATTC_WRITE_CHAR_EVT");
      // Determine if this event is for us and, if not, pass onwards.
      if (evtParam->write.handle != getHandle()) {
        break;
      }

      // Check the write status to confirm if it was successful
      // Check the write status to confirm if it was successful or if an error occurred
      if (evtParam->write.status == ESP_GATT_OK) {
          log_d("Write to Control Point characteristic was successful.");
      } else {
          // Handle specific ANCS error codes
          switch (evtParam->write.status) {
              case 0xA0:
                  log_d("Write to Control Point characteristic failed. Error: Unknown command (0xA0). The commandID was not recognized by the NP.");
                  break;
              case 0xA1:
                  log_d("Write to Control Point characteristic failed. Error: Invalid command (0xA1). The command was improperly formatted.");
                  break;
              case 0xA2:
                  log_d("Write to Control Point characteristic failed. Error: Invalid parameter (0xA2). One of the parameters (e.g., NotificationUID) does not refer to an existing object on the NP.");
                  break;
              case 0xA3:
                  log_d("Write to Control Point characteristic failed. Error: Action failed (0xA3). The action was not performed.");
                  break;
              default:
                  log_d("Write to Control Point characteristic failed. Unknown error status: %d", evtParam->write.status);
                  break;
          }
      }

      // There is nothing further we need to do here.  This is merely an indication
      // that the write has completed and we can unlock the caller.
      m_semaphoreWriteCharEvt.give();
      log_d("after m_semaphoreWriteCharEvt.give, There is nothing further we need to do here.  This is merely an indication that the write has completed and we can unlock the caller.");
      break;
    }  // ESP_GATTC_WRITE_CHAR_EVT

核心错误日志

Write to Control Point characteristic failed. Error: Invalid command (0xA1). The command was improperly formatted.

问题分析与修正

错误的核心是Get Notification Attributes命令的格式不符合ANCS规范:
根据ANCS规范,该命令的结构必须包含:

  1. 1字节:Command ID(0x00)
  2. 4字节:Notification UID(大端字节序)
  3. 重复的(Attribute ID + 2字节最大长度) 字段对:每个需要获取的属性,都必须同时指定属性ID和期望返回的最大长度(大端字节序),二者缺一不可。

你的代码只添加了Attribute ID(0x01),但缺失了对应的2字节最大长度字段,导致命令结构不完整,触发0xA1错误。

修正后的命令代码

uint8_t notificationAttributesCommand[9];

notificationAttributesCommand[0] = 0x00;  // Command ID: Get Notification Attributes
notificationAttributesCommand[1] = (uint8_t)(notificationUID >> 24);  // Notification UID MSB
notificationAttributesCommand[2] = (uint8_t)(notificationUID >> 16);
notificationAttributesCommand[3] = (uint8_t)(notificationUID >> 8);
notificationAttributesCommand[4] = (uint8_t)(notificationUID);        // Notification UID LSB

// Attribute ID + 2字节最大长度(大端字节序)
notificationAttributesCommand[5] = 0x01;  // Attribute ID: App Identifier
notificationAttributesCommand[6] = 0x00;  // 最大长度高位(示例:255字节)
notificationAttributesCommand[7] = 0xFF;  // 最大长度低位
// 若需要获取多个属性,可继续追加(Attribute ID + 2字节长度)的组合

额外注意事项

  • 命令总长度计算公式:1(Command ID) + 4(UID) + N*3,其中N是要获取的属性数量(每个属性占3字节)
  • 最大长度字段必须使用大端字节序,即使只需要短内容,也不能省略这两个字节

内容的提问来源于stack exchange,提问作者Đức Thanh Nguyễn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 17:22:09