SiliconLabs NCP 串口协议(EZSP)探究

剔除掉我自己写的联网应用;

保证mqtt连接成功,修改mqtt代理的地址

// User options for plugin Gateway MQTT Transport
#if 1
#define EMBER_AF_PLUGIN_TRANSPORT_MQTT_BROKER_ADDRESS "tcp://test.mosquitto.org:1883"
#else
#define EMBER_AF_PLUGIN_TRANSPORT_MQTT_BROKER_ADDRESS "tcp://localhost:1883"
#endif

《UG101 UART GATEWAY PROTOCOL REFERENCE: FOR THE EMBER EZSP NETWORK CO-PROCESSOR.pdf》讲了host与NCP通信的frame format

《UG100: EZSP Reference Guide.pdf》

1a c1 02 02 9b 7b 7e

1a:cancel byte

C1:control byte

c1 02 02:按照CRC-CCITT计算校验和

扫描二维码关注公众号,回复: 5855595 查看本文章


50 27 21 57 54 29 17 CF DB 94 4A 5A 08 7E   

 

Data Field就是在《UG100: EZSP Reference Guide》中的EZSP内容,这才是核心,本文UG101只是协议框架格式

上面DATA Field的格式就是下面这个protocol format,没有看到那里有数据长度的字段??


WEAK_TEST EzspStatus serialSendCommand(void)
{
  EzspStatus status;
  uint8_t cnt;

  if (!checkConnection()) {
    ezspTraceEzspVerbose("serialSendCommand(): EZSP_NOT_CONNECTED");
    return EZSP_NOT_CONNECTED;
  }

  ezspTraceEzspFrameId("send command", ezspFrameContents);
  vDEBUG_PRINTF_FUNC_NAME(TRACE_FUNC_NAME);
  //ezspFrameContents就是没有加密前的串口数据,onoff命令打印出来如下:
  //2f 00 ff 00 34 00 3b 7f 04 01 06 00 01 0b 40 10 00 00 00 01 03 01 00 01//on
  //2f 00 ff 00 34 00 3b 7f 04 01 06 00 01 0b 40 10 00 00 00 01 03 01 00 00//off
  //从结构看,ezspFrameContents还不是按照ezsp的格式?
  //0x7f3b 是目的短地址,0x0104应该是profileID,0x0006是clusterid,0b是endpoint,
  for(cnt=0;cnt<ezspFrameLength;cnt++)
  	{
  		vDEBUG_PRINTF(TRACE_FUNC_NAME,"%02x ",ezspFrameContents[cnt]);
  	}
  vDEBUG_PRINTF_FUNC_NAME_OUT(TRACE_FUNC_NAME);
  status = ashSend(ezspFrameLength, ezspFrameContents);
  if (status != EZSP_SUCCESS) {
    ezspTraceEzspVerbose("serialSendCommand(): ashSend(): 0x%x", status);
    return status;
  }
  waitingForResponse = true;
  ezspTraceEzspVerbose("serialSendCommand(): ID=0x%x Seq=0x%x",
                       ezspFrameContents[EZSP_FRAME_ID_INDEX],
                       ezspFrameContents[EZSP_SEQUENCE_INDEX]);
  waitStartTime = halCommonGetInt16uMillisecondTick();
  return status;
}

 //2f 00 ff 00 34 00 3b 7f 04 01 06 00 01 0b 40 10 00 00 00 01 03 01 00 00//off

 函数serialSendCommand是加密前的原始数据!


  //从结构看,ezspFrameContents还不是按照ezsp的格式?帧的组成全面在ezspSendUnicast函数的七个函数里组帧的

EmberStatus ezspSendUnicast(
  EmberOutgoingMessageType type,
  EmberNodeId indexOrDestination,
  EmberApsFrame *apsFrame,
  uint8_t messageTag,
  uint8_t messageLength,
  uint8_t *messageContents,
  uint8_t *sequence)
{
  uint8_t status,i;
  vDEBUG_PRINTF_FUNC_NAME(TRACE_FUNC_NAME);
  startCommand(EZSP_SEND_UNICAST);
  appendInt8u(type);
  appendInt16u(indexOrDestination);
  appendEmberApsFrame(apsFrame);
  appendInt8u(messageTag);
  appendInt8u(messageLength);
  appendInt8uArray(messageLength, messageContents);

  EzspStatus sendStatus = sendCommand();
  if (sendStatus == EZSP_SUCCESS) {
    status = fetchInt8u();
    *sequence = fetchInt8u();
    return status;
  }
  return sendStatus;
}

接下来我们一个一个看这七个函数 

static void startCommand(uint8_t command)
{
  // Send initial EZSP_VERSION command with old packet format for old Hosts/NCPs
  if (command == EZSP_VERSION && !initialEzspVersionSent) {
    ezspWritePointer = ezspFrameContents + EZSP_PARAMETERS_INDEX;
    serialSetCommandByte(EZSP_FRAME_ID_INDEX, command);
  } else {
    // convert to extended frame format
    ezspWritePointer = ezspFrameContents + EZSP_EXTENDED_PARAMETERS_INDEX;
    serialSetCommandByte(EZSP_FRAME_ID_INDEX, EZSP_EXTENDED_FRAME_ID);
    serialSetCommandByte(EZSP_EXTENDED_FRAME_ID_INDEX, command);
  }
}

#define EZSP_SEQUENCE_INDEX       0
#define EZSP_FRAME_CONTROL_INDEX  1
#define EZSP_FRAME_ID_INDEX       2
#define EZSP_PARAMETERS_INDEX     3

#define EZSP_EXTENDED_FRAME_ID    0xFF

#define EZSP_EXTENDED_MIN_FRAME_LENGTH     5
#define EZSP_EXTENDED_FRAME_CONTROL_INDEX  3
#define EZSP_EXTENDED_FRAME_ID_INDEX       4
#define EZSP_EXTENDED_PARAMETERS_INDEX     5

//2f 00 ff 00 34 00 3b 7f 04 01 06 00 01 0b 40 10 00 00 00 01 03 01 00 00//off 

根据EZSP协议:

Frame[0]:帧序号

Frame[1]:帧控制域

Frame[2]:遗留帧ID(上个版本这个字段是帧ID,现在这个字段是保留位,默认FF)

Frame[3]:扩展帧控制域,新版本协议新增的字段,现在两个字节的帧控制域

Frame[4]:新版本的帧ID

Frame[5]:帧参数

即知:Frame[2]=FF,这是新版本格式帧;

           Frame[4]=34,是FrameID

        EZSP_EXTENDED_FRAME_ID(0xFF)和EZSP_SEND_UNICAST(0x34)

appendInt8u(type);

void appendInt8u(uint8_t value)
{
  if (ezspWritePointer - ezspFrameContents < EZSP_MAX_FRAME_LENGTH) {
    *ezspWritePointer = value;
  }
  ezspWritePointer++;
}

//2f 00 ff 00 34 00 3b 7f 04 01 06 00 01 0b 40 10 00 00 00 01 03 01 00 00//off 

Frame[5]:发送方式,单播、广播、绑定等,示例帧是EMBER_OUTGOING_DIRECT(0x00)

appendInt16u(indexOrDestination);

void appendInt16u(uint16_t value)
{
  appendInt8u(LOW_BYTE(value));
  appendInt8u(HIGH_BYTE(value));
}

//2f 00 ff 00 34 00 3b 7f 04 01 06 00 01 0b 40 10 00 00 00 01 03 01 00 00//off 

从appendInt16u看Frame[6]、Frame[7]是目的短地址。

appendEmberApsFrame(apsFrame);

void appendEmberApsFrame(EmberApsFrame *value)
{
  appendInt16u(value->profileId);
  appendInt16u(value->clusterId);
  appendInt8u(value->sourceEndpoint);
  appendInt8u(value->destinationEndpoint);
  appendInt16u(value->options);
  appendInt16u(value->groupId);
  appendInt8u(value->sequence);
}

//2f 00 ff 00 34 00 3b 7f 04 01 06 00 01 0b 40 10 00 00 00 01 03 01 00 00//off 

含义如函数appendEmberApsFrame中的变量定义便知。

 appendInt8u(messageTag);

//2f 00 ff 00 34 00 3b 7f 04 01 06 00 01 0b 40 10 00 00 00 01 03 01 00 00//off 

还不知道messageTag是啥含义,这里的messageTag=01.

  appendInt8u(messageLength);
  appendInt8uArray(messageLength, messageContents);

//2f 00 ff 00 34 00 3b 7f 04 01 06 00 01 0b 40 10 00 00 00 01 03 01 00 00//off 

这后面四个数据我是清楚的,就是相对于是应用层的ZCL层的内容,03是数据长度,后面是zcl的数据,

即appZclBufferLen和 appZclBuffer。

status = emberAfSendUnicast(EMBER_OUTGOING_DIRECT,
                                nodeId,
                                &localApsFrame,
                                appZclBufferLen,
                                appZclBuffer);

appZclBuffer的内容就是命令zcl on-off off所产生的数据,即函数zclOnOffOffCommand组的数据的一部分,

因为cluster被写到localApsFrame结构体了,没作为命令函数。

static void zclOnOffOffCommand(void) {
  zclSimpleClientCommand( ZCL_ON_OFF_CLUSTER_ID,
                          ZCL_OFF_COMMAND_ID);
}

void zclBufferSetup(uint8_t frameType, uint16_t clusterId, uint8_t commandId)
{
  uint8_t index = 0,j=0;
  emAfApsFrameClusterIdSetup(clusterId);
  vDEBUG_PRINTF_FUNC_NAME(TRACE_OFF);
  appZclBuffer[index++] = (frameType
                           | ZCL_FRAME_CONTROL_CLIENT_TO_SERVER
                           | (mfgSpecificId != EMBER_AF_NULL_MANUFACTURER_CODE
                              ? ZCL_MANUFACTURER_SPECIFIC_MASK
                              : 0)
                           | (disableDefaultResponse
                              ? ZCL_DISABLE_DEFAULT_RESPONSE_MASK
                              : 0));
  if (mfgSpecificId != EMBER_AF_NULL_MANUFACTURER_CODE) {
    appZclBuffer[index++] = (uint8_t)mfgSpecificId;
    appZclBuffer[index++] = (uint8_t)(mfgSpecificId >> 8);
  }
  appZclBuffer[index++] = emberAfNextSequence();
  appZclBuffer[index++] = commandId;
  appZclBufferLen = index;
#if 0
	for(j=0;j<appZclBufferLen;j++)
	{
		vDEBUG_PRINTF(TRACE_APP,"%02x ",appZclBuffer[j]);
	}
  vDEBUG_PRINTF(TRACE_APP,"\n");
#endif
}

从函数zclBufferSetup(frameControl, clusterId, commandId)知数据长度后面的三个数据分别是

zcl header、sequence、zcl commandID--->01 00 00//off

                                                                     01 00 01//on

因为zcl on-off命令是不带参数的命令,从zclSimpleCommand函数的后半部分看,

zcl command的参数就是跟在commandid后面的

读属性:

//2f 00 ff 00 34 00 3b 7f 04 01 00 00 01 0b 40 10 00 00 00 01 05 00 00 00 05 00

读basic cluster attributeid=0x0005的属性,红色的00就是zcl read commandID,位置与一般的zcl cluster commandid一致。

写属性:

2f 00 ff 00 34 00 3b 7f 04 01 06 00 01 0b 40 10 00 00 00 01 03 01 00 01//on
2f 00 ff 00 34 00 3b 7f 04 01 06 00 01 0b 40 10 00 00 00 01 03 01 00 00//off
2f 00 ff 00 34 00 3b 7f 04 01 00 00 01 0b 40 10 00 00 00 01 05 00 00 00 05 00
2f 00 ff 00 34 00 3b 7f 04 01 00 00 01 11 40 10 00 00 00 01 16 00 00 02 0c 00

42 0f 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0f

写basic cluster attributeid=0x000C的属性,红色的02就是zcl write commandID,位置与一般的zcl cluster commandid一致。

后面是attributeid 0x000c,数据类型0x42,字符串类型,字符串长度0x0f,

字符串内容后面是00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0f

总结

 串口数据加密前的明文最后一站是函数 serialSendCommand

SequenceNumber(1byte)+

FrameControl(1 byte)+

Legacy Frame ID(固定0xFF)+

Extended Frame Control(1 byte)+

FrameID(1Byte一般都是0x34)+

通信方式(1byte)+ 

Network shortAddress(2bytes)+ 

ProfileID(2bytes)+

ClusterID(2bytes)+ 

sourceEp(1byte)+

destEp(1 byte)+

option(2 bytes)+

groupId(2 bytes)+

sequenceNumber(1byte)+

messageTag(1byte)+ 

zclDataLen(1byte aps payload的数据长度)+

zcl data(zclDataLen bytes)

其中zcl data的格式为抓包的APS payload层:

zcl header、sequence、zcl commandID、CommandPayload

Siliconlabs的代码相对来说不那么花哨,很扎实朴素,没有指针和动态分配漫天飞的!!!

猜你喜欢

转载自blog.csdn.net/H542723151/article/details/82957487