1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 中国电信天翼物联网平台CTWing学习笔记(1)——设备接入(TCP协议)

中国电信天翼物联网平台CTWing学习笔记(1)——设备接入(TCP协议)

时间:2020-05-11 17:56:58

相关推荐

中国电信天翼物联网平台CTWing学习笔记(1)——设备接入(TCP协议)

一、平台简介

天翼物联网平台(AIoT)是中国电信倾力打造的智能终端汇聚、应用开发运行服务和轻量级应用提供的物联网平台,旨在降低物联网应用开发的准入门槛,降低智能硬件的接入门槛,提供端到端的解决方案,服务于终端开发商、个人极客开发者、能力提供商、应用开发商以及集团内部各生态圈。

官网主页:/

帮助中心:/czlks/11#see

平台架构:

二、Demo体验与SDK下载

2.1 创建产品及设备

产品信息如下:

安全类型:

一机一密:每个设备都有一个不同的特征串一型一密:同款产品下每个设备都使用同一个特征串

添加设备如下:

2.2 SDK下载及修改

SDK下载:

/sbjr/72#/callback

从平台获取接入 IP 和端口,设备 ID,认证信息:

然后根据实际获取到的设备 ID,特征串修改ctiot_tcp_sdk\sample\demo\demo.cctiot_test_init()的设备 ID和特征串。

ctiot_status ctiot_test_init(){char* ip = "180.106.148.146";int port= 8996;char* deviceId = "1517035889860492192091928466";char* passWord = "AqUFr6ntTFDFZUvAO1jELbIz8h3v6yHzH7hEBsKNxrY";char* verSion = "1.0";int certmode = 0;printf("server IP: %s\nserver Port: %d\ndeviceid: %s\npassword: %s\nversion:%s\ncertMode: %d\n",ip, port, deviceId, passWord, verSion, certmode);ctiot_init(&c, ip, port, deviceId, passWord, verSion, certmode);}

2.3 SDK编译及运行程序对接平台测试

将修改完的 SDK 工程文件夹 ctiot_tcp_sdk 放入Linux运行环境中 ,进入ctiot_tcp_sdk/sample/demo目录,执行make命令,系统自动编译生成可执行文件,运行程序tcp_client

2.3.1 设备登录

输入'l''L'登录:

平台查看终端已在线:

注:若60秒内同一ip建链、断链次数超过30次,将停止该ip连接2小时。

2.3.2 数据上报

输入's''S'进行数据上报:

在平台进行数据查看,可看到数据已上报:

可看到上报的两条数据:

对于透传产品,平台对上报的业务数据进行Base64编码处理,进行解码后:

2.3.3 指令下发

平台执行指令下发(对于透传产品,平台对下行指令做透传处理,支持字符串和十六进制两种数据类型):

平台可查看指令下发日志,可看到下发已成功:

终端查看指令下发成功:

三、TCP协议接口介绍

帮助文档:/sbjr/67#see

3.1 接口地址

3.2 消息格式

应用层数据报文以1个字节的类型字段作为分割,平台支持登录、心跳、上下行业务数据等消息类型。消息携带的参数必须包含两个字节的参数长度。

根据数据报文内容分为:

透传设备(对于透传产品,平台对上报的业务数据进行Base64编码处理,可以在产品的数据查看页面查看上报的数据内容,也可以通过订阅推送的方式,北向应用订阅设备数据变化通知,平台将上报的数据推送给北向应用。)

非透传设备(对于非透传产品,平台根据物模型对上报的业务数据进行解析,可以在产品的数据查看页面查看上报的数据内容,也可以通过订阅推送的方式,北向应用订阅设备数据变化通知,平台将上报的数据推送给北向应用。)

3.3 业务数据交互流程

3.3.1 设备登录

设备登录需要携带deviceIdpasswordversion字段,AEP对设备进行认证,认证通过0x05消息类型返回成功的响应(响应码为:0),认证失败将失败原因通过响应码带给设备。

透传设备

deviceId:10013378001

password:7Sfv-b_HDbLDyJ_K-0SkWqRGd-GE-b3rZp-upOr1kSU

version:1.0

登录请求编码为(16进制):• 登录报文标识符:0x01• deviceId_length:0x000b• deviceId:0x3130303133333738303031• password_length:0x002b• password:0x375366762d625f4844624c44794a5f4b2d30536b57715247642d47452d6233725a702d75704f72316b5355• version_length:0x0003• version:0x312e30

登录请求的完整报文为:01000b 3130303133333738303031 002b 375366762d625f4844624c44794a5f4b2d30536b57715247642d47452d6233725a702d75704f72316b5355 0003 312e30

非透传设备

deviceId:10013378001

password:7Sfv-b_HDbLDyJ_K-0SkWqRGd-GE-b3rZp-upOr1kSU

version:1.1

登录请求编码为(16进制):• 登录报文标识符:0x01• deviceId_length:0x000b• deviceId:0x 3130303133333738303031• password_length:0x002b• password:0x375366762d625f4844624c44794a5f4b2d30536b57715247642d47452d6233725a702d75704f72316b5355• version_length:0x0003• version:0x312e31

登录请求的完整报文为:01 000b 3130303133333738303031 002b 375366762d625f4844624c44794a5f4b2d30536b57715247642d47452d6233725a702d75704f72316b5355 0003 312e31

3.3.2 上行数据

设备以0x02消息类型上报数据,非透传设备平台解析后会以0x82回复响应结果。

透传设备

业务数据:“hello”

上行数据编码为(16进制):• 上行报文标识符:0x02• payload_length:0x0005• payload:0x68656c6c6f

上行数据报文完整报文为:02 0005 68656c6c6f

非透传设备

物模型如下:

服务类型、服务标识、服务ID所在位置:

属性顺序、属性标识符、属性类型所在位置:

上行数据编码为(16进制):• 上行报文标识符:0x02• payload_length:0x0012• msgId: 0x0001• serviced: 0x0001• payload:– data0:0x0064(100)– data1:0x68656C6C6F(hello)– data2:0x0005 776F726C64(world)[包含两个字节的长度0005]

上行数据完整报文为:02 0012 0001 0001 0064 68656C6C6F 0005776F726C64

上行数据响应报文编码为:• 上行响应标识符:0x82• payload_length:0x0004• msgId: 0x0001• 结果码: 0x0000

上行数据响应报文为:82 0004 0001 0000

3.3.3 下行数据

平台以0x03消息类型下发指令,非透传设备需以0x83回复指令执行结果。

注意:tcp透传没有指令缓存机制,若设备离线指令直接丢弃,不会下发,非透传有缓存机制,收到指令后若不进行回复,在指令ttl有效期内如果设备重新上线,指令会重新下发。

透传设备

业务数据:“turn off”

下行数据编码为(16进制):• 下行报文标识符:0x03• payload_length:0x0008• payload:0x7475726e206f6666

下行数据完整报文为:03 0008 7475726e206f6666

非透传设备

物模型如下:

下行指令编码为(16进制):• 下行报文标识符:0x03• payload_length:0x0009• msgId: 0x0001• serviced: 0x1f41• payload:– cmd:0x68656C6C6F(hello)

下行指令完整报文为:03 0009 0001 1f41 68656c6c6f

msgid为1,需要在响应中带回

指令响应编码为:• 下行响应标识符:0x83• payload_length:0x000b• msgId: 0x0001• 结果码: 0x0001• serviced: 0x2329• payload:– cmd:0x776F726C64 (world)

下行指令响应的完整包文为:83 000b 0001 0001 2329 776F726C64

3.3.4 心跳

设备需要周期性(5分钟以内)发送心跳报文来保持业务层会话。

心跳编码为(16进制):• 心跳标识符:0x04

心跳的完整包文为:04

四、自定义接口函数

由于原来SDK是Linux平台的程序,为了更适用于嵌入式终端程序,这里我修改成几个接口函数。

4.1 ctiot_tcp.c

/********************************************************************** INCLUDES*/#include <stdio.h>#include <string.h>#include "ctiot_tcp.h"static ctiot_status ctiot_varify_parameters(uint8_t *deviceId, uint8_t *password, uint8_t *version);/********************************************************************** PUBLIC FUNCTIONS*//**@brief 初始化终端信息@param clientInfo -[out] 终端信息@param deviceId -[in] 设备ID@param password -[in] 特征串@param version -[in] 版本@return 无*/ctiot_status CTIOT_Init(clientinfo_t *clientInfo, uint8_t *deviceId, uint8_t *password, uint8_t *version){if(CTIOT_ERROR == ctiot_varify_parameters(deviceId, password, version)){return CTIOT_ERROR;}clientInfo->deviceIdLen = strlen(deviceId);clientInfo->deviceId = strdup(deviceId);clientInfo->passwordLen = strlen(password);clientInfo->password = strdup(password);clientInfo->versionLen = strlen(version);clientInfo->version = strdup(version);return CTIOT_SUCCESS;}/**@brief 登录CTWing平台@param pMsg -[out] 消息@param pMsgLen -[out] 消息长度@param pDeviceId -[in] 设备ID@return 无*/void CTIOT_Login(clientinfo_t *context){uint8_t *pMsg = NULL;uint16_t msgLen = 0;if(CTIOT_SUCCESS != CTIOT_Encode_Login_Para(&pMsg, &msgLen, context)){iopen_debug_print("Login encode failed!\n");}Socket_SendBytes(pMsg, msgLen);free(pMsg);pMsg = NULL; }/**@brief 下行数据报文处理@param pMsg -[in&out] 消息@param pMsgLen -[in&out] 消息长度@return 无*/void CTIOT_Command_MsgHandler(char *pMsg, uint32_t *pMsgLen){uint8_t msgType = 0;uint8_t payloadLength[2] = {0};uint8_t msgId[2] = {0};uint8_t serviced[2] = {0};msgType = pMsg[0];memcpy(payloadLength, pMsg + 1, 2);memcpy(msgId, pMsg + 1+2, 2);memcpy(serviced, pMsg + 1+2+2, 2);switch(msgType){case DOWNSTREAM_MESSAGE: // 下行数据报文标识符for(int i = 0; i < *pMsgLen; i++){print("socket msg:%02x\n", pMsg[i]);}CTIOT_Command_Response(msgId);break;case LOGIN_ACK: // 注册ACK报文标识符if(pMsg[2] != CTIOT_LOGIN_FAILED || pMsg[2] != CTIOT_DEVICE_AUTH_FAILED){// 登录成功}break;case HEARTBEAT_ACK: // 心跳ACK报文标识符// 心跳成功break;default:break;}}/**@brief 封装登录请求@param ptrStream -[out] 请求消息@param ptrLen -[out] 请求消息长度@param data -[in] 需要上传的数据@param dataLen -[in] 需要上传的数据长度@return 状态码*/ctiot_status CTIOT_Encode_Login_Para(uint8_t **ptrStream, uint16_t *ptrLen, clientinfo_t *context){ctiot_status ret = CTIOT_SUCCESS;*ptrLen = 0;*ptrLen = 7 + context->deviceIdLen + context->passwordLen + context->versionLen;uint32_t i = 0,j = 0;*ptrStream = (uint8_t *)malloc(*ptrLen);if(ptrStream == NULL){ret = CTIOT_ERROR;goto exit;}//message type(*ptrStream)[0] = LOGIN_MESSAGE;i++;//deviceId(*ptrStream)[i++] = (context->deviceIdLen)>>8;(*ptrStream)[i++] = (context->deviceIdLen)>>0;for(j = 0; j < context->deviceIdLen; j++){(*ptrStream)[i++] = (context->deviceId[j]);}//password(*ptrStream)[i++] = (context->passwordLen)>>8;(*ptrStream)[i++] = (context->passwordLen)>>0;for(j = 0; j < context->passwordLen; j++){(*ptrStream)[i++] = (context->password[j]);}//version(*ptrStream)[i++] = (context->versionLen)>>8;(*ptrStream)[i++] = (context->versionLen)>>0;for(j = 0; j < context->versionLen; j++){(*ptrStream)[i++] = (context->version[j]);}if(*ptrLen != i){ret = CTIOT_ERROR;iopen_debug_print("*ptrLen error\n");goto exit;}exit:return ret;}/**@brief 封装上行数据报文@param ptrStream -[out] 上报消息@param ptrLen -[out] 上报消息长度@param data -[in] 需要上传的数据@param dataLen -[in] 需要上传的数据长度@return 状态码*/ctiot_status CTIOT_Encode_Updata_Para(uint8_t **ptrStream, uint16_t *ptrLen, uint8_t *data, uint16_t dataLen){ctiot_status ret = CTIOT_SUCCESS;*ptrLen = 0;*ptrLen = 3 + dataLen;uint32_t i = 0,j = 0;*ptrStream = (uint8_t *)malloc(*ptrLen);if(ptrStream == NULL){ret = CTIOT_ERROR;goto exit;}//message type(*ptrStream)[0] = UPSTREAM_MESSAGE;i++;//upstream data(*ptrStream)[i++] = dataLen>>8;(*ptrStream)[i++] = dataLen>>0;for(j = 0; j < dataLen; j++){(*ptrStream)[i++] = (data[j]);}if(*ptrLen != i){ret = CTIOT_ERROR;iopen_debug_print("*ptrLen error\n");goto exit;}exit:return ret;}/**@brief 封装下行数据响应@param ptrStream -[out] 指令响应消息@param ptrLen -[out] 指令响应消息长度@param data -[in] 需要上传的数据@param dataLen -[in] 需要上传的数据长度@return 状态码*/ctiot_status CTIOT_Encode_Command_Response_Para(uint8_t **ptrStream, uint16_t *ptrLen, uint8_t *data, uint16_t dataLen){ctiot_status ret = CTIOT_SUCCESS;*ptrLen = 0;*ptrLen = 3 + dataLen;uint32_t i = 0,j = 0;*ptrStream = (uint8_t *)malloc(*ptrLen);if(ptrStream == NULL){ret = CTIOT_ERROR;goto exit;}//message type(*ptrStream)[0] = DOWNSTREAM_MESSAGE_ACK;i++;//upstream data(*ptrStream)[i++] = dataLen>>8;(*ptrStream)[i++] = dataLen>>0;for(j = 0; j < dataLen; j++){(*ptrStream)[i++] = (data[j]);}if(*ptrLen != i){ret = CTIOT_ERROR;iopen_debug_print("*ptrLen error\n");goto exit;}exit:return ret; }/**@brief 保持心跳@param 无@return 无*/void CTIOT_Keep_Alive(void){uint8_t ping[1] = {PING_MESSAGE};Socket_SendBytes(ping, sizeof(ping)); // 改成自己的socket发送函数}/**@brief 指令响应@param pMsgId -[in] 消息ID@return 无*/void CTIOT_Command_Response(uint8_t *pMsgId){uint8_t *pMsg = NULL;uint16_t msgLen = 0;// msgidresultuint8_t data[4] = {0x00, 0x01, 0x00, 0x00};memcpy(data, pMsgId, 2);CTIOT_Encode_Command_Response_Para(&pMsg, &msgLen, data, sizeof(data));Socket_SendBytes(pMsg, msgLen);free(pMsg);pMsg = NULL;}/********************************************************************** LOCAL FUNCTIONS*//**@brief 校验参数@param deviceId -[in] 设备ID@param password -[in] 特征串@param version -[in] 版本@return 状态码*/static ctiot_status ctiot_varify_parameters(uint8_t *deviceId, uint8_t *password, uint8_t *version){ctiot_status ret = CTIOT_SUCCESS;if(deviceId == NULL || password == NULL || version == NULL){ret = CTIOT_ERROR;iopen_debug_print("Init paramter error!\n");}return ret;}/****************************************************END OF FILE****************************************************/

4.2 ctiot_tcp.h

注意:请修改以下地方PRODUCT_IDPASSWORDVERSIONCTIOT_Keep_Alive()中的socket发送函数,CTIOT_Command_Msghandler()中进行数据接收处理。

#ifndef _CTIOT_TCP_H_#define _CTIOT_TCP_H_/********************************************************************** INCLUDES*//********************************************************************** DEFINITIONS*/#define PRODUCT_ID "10005081" // 产品ID,根据实际修改#define PASSWORD"WbYvOBP6Zx73gy1o7pqviKkK8EDYFNjsDNJA1I34_oo" // 特征串,根据实际修改#define VERSION "1.1" // 协议版本,透传设备改成1.0,非透传设备为1.1#define PRODUCT_ID_LEN8 // 产品ID长度#define ICCID_LEN20// 设备编号长度/********************************************************************** TYPEDEFS*/typedef enum{LOGIN_MESSAGE = 0x01,//!<注册报文标识符UPSTREAM_MESSAGE = 0x02, //!<上行数据报文标识符DOWNSTREAM_MESSAGE = 0x03,//!<下行数据报文标识符PING_MESSAGE = 0x04,//!<心跳报文标识符LOGIN_ACK = 0x05, //!<注册ACK报文标识符HEARTBEAT_ACK = 0x06,//!<心跳ACK报文标识符UPSTREAM_MESSAGE_ACK = 0x82, //!<上行数据ACK报文标识符DOWNSTREAM_MESSAGE_ACK = 0x83,//!<下行数据ACK报文标识符} message_type_e;typedef struct client{uint16_t deviceIdLen;//!<deviceid长度uint8_t *deviceId; //!<deviceiduint16_t passwordLen;//!<password长度uint8_t *password; //!<passworduint16_t versionLen;//!<version长度uint8_t *version; //!<version} clientinfo_t;typedef enum{CTIOT_ERROR = -1, //!<错误CTIOT_SUCCESS = 0, //!<成功CTIOT_LOGIN_FAILED = 1, //!<登录未知错误CTIOT_DEVICE_UNREGISTERED = 2,//!<设备未注册CTIOT_DEVICE_AUTH_FAILED = 3, //!<设备认证失败CTIOT_DEVICE_HAVE_LOGINED = 4,//!<设备已登录CTIOT_READ_PACKET_NONE = 999, //!<未读取到数据} ctiot_status;/********************************************************************** API FUNCTIONS*/void CTIOT_Command_Msghandler(char *pMsg, uint32_t *pMsgLen);ctiot_status CTIOT_Init(clientinfo_t *clientInfo, uint8_t *deviceId, uint8_t *password, uint8_t *version);ctiot_status CTIOT_Encode_Login_Para(uint8_t **ptrStream, uint16_t *ptrLen, clientinfo_t *context);ctiot_status CTIOT_Encode_Updata_Para(uint8_t **ptrStream, uint16_t *ptrLen, uint8_t *data, uint16_t dataLen);ctiot_status CTIOT_Encode_Command_Response_Para(uint8_t **ptrStream, uint16_t *ptrLen, uint8_t *data, uint16_t dataLen);void CTIOT_Login(clientinfo_t *context);void CTIOT_Keep_Alive(void);void CTIOT_Command_Response(uint8_t *pMsgId);#endif /* _CTIOT_TCP_H_ */

4.3 使用方法

初始化终端信息

在这里设备ID我采取的是产品ID+iccid的形式,实际应用中把你的设备编号替换iccid的内容。

#include "ctiot_tcp.h"static clientinfo_t s_clientInfo = {0};char iccidString[21] = {0};GetIccid(iccidString);char deviceId[PRODUCT_ID_LEN + ICCID_LEN + 1] = {0};sprintf(deviceId, "%s%s", PRODUCT_ID, iccidString);char *pPassword = PASSWORD;char *pVersion = VERSION;CTIOT_Init(&s_clientInfo, deviceId, pPassword, pVersion);

登录

CTIOT_Login(&s_clientInfo);

数据上报msgid:0x00 0x01 可变可不变,按实际需求serviced:0x00 0x03 即服务ID为3,按实际情况修改data:0x01 这里为无符号整型1字节的属性,按实际情况修改Socket_SendBytes():修改成你字节的socket发送函数

uint8_t *pMsg = NULL;uint16_t msgLen = 0;// msgid serviced datauint8_t data[5] = {0x00, 0x01, 0x00, 0x03, 0x01};CTIOT_Encode_Updata_Para(&pMsg, &msgLen, data, sizeof(data));Socket_SendBytes(pMsg, msgLen);free(pMsg);pMsg = NULL;

心跳

设备需要周期性(5分钟以内)发送心跳报文来保持业务层会话。

CTIOT_Keep_Alive();

指令下发、登录响应、心跳响应

注意:tcp透传没有指令缓存机制,若设备离线指令直接丢弃,不会下发,非透传有缓存机制,收到指令后若不进行回复,在指令ttl有效期内如果设备重新上线,指令会重新下发。

int recvDataLen = Socket_Receive(recvDataBuf); // 接收服务器数据if(recvDataLen > 0){CTIOT_Command_MsgHandler(recvDataBuf, &recvDataLen);}

指令响应

void CTIOT_Command_MsgHandler(char *pMsg, uint32_t *pMsgLen){uint8_t msgType = 0;uint8_t payloadLength[2] = {0};uint8_t msgId[2] = {0};uint8_t serviced[2] = {0};msgType = pMsg[0];memcpy(payloadLength, pMsg + 1, 2);memcpy(msgId, pMsg + 1+2, 2);memcpy(serviced, pMsg + 1+2+2, 2);switch(msgType){case DOWNSTREAM_MESSAGE: // 下行数据报文标识符CTIOT_Command_Response(msgId); // 指令响应break;······default:break;}

• 由 Leung 写于 年 3 月 30 日

• 参考:电信平台对接CTWing具体实现流程

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。