代码仓库地址:https://github.com/idrey/CC3200_Onenet
0x01 介绍
CC3200是TI公司推出的一款WIFI MCU,支持多种网络协议。信息物理系统课程中使用的是CC3200 Launchpad,上面还带有tmp006温度传感器和加速度传感器,通过I2C可以和这两个传感器通信,获取对应的数据。
Onenet是中国移动推出的物联网云平台,支持主流的物联网协议,本次实验将使用CC3200通过MQTT协议与Onenet平台完成基本的连接。
0x02 实验环境
- CCS 6.1
- MobaXterm(串口调试)
- CC3200 SDK 1.3
0x03 场景设置
CC3200实时上传温度信息至Onenet,应用端显示温度信息并控制CC3200工作。
0x04 Onenet侧配置
选择MQTT物联网套件(新版)创建产品及设备,这里创建产品的是iottest,设备命名为CC3200
0x05 CC3200 MQTT_CLIENT例程
在CC3200SDK中有mqtt_client的例程,使用CCS导入该工程。为了连接上Onenet云平台,我们需要修改其中一些配置。
首先,根据开发指南,需要把服务器地址修改为mqtts.heclouds.com
,把端口号修改为1883
,为了操作简便,这里使用的是非加密端口。
根据协议规范,我们使用的是MQTT 3.1.1版本,需要修改原定义为#define SERVER_MODE MQTT_3_1_1
。Onenet不支持Retain特性,需要修改原定义为#define RETAIN 0
。
接下来,配置user_connect_config[]
。其中,ClientID字段为cc3200
,是在Onenet里创建的设备名称,UserName字段为439074
,是在Onenet里创建的产品ID,在平台中获取该ID并替换该字段。紧随其后的是用于认证的Password,参见token生成示例 - python,在代码里需要替换为自己的ID和access_key。
定义topic: 在这里我们需要用到两个topic,一个用于发布消息,一个用于订阅消息。在原来的例程中定义了两个用于发布消息的topic,我们只需要使用一个,为了方便,把PUB_TOPIC_FOR_SW2
设置为$sys/439074/cc3200/dp/post/json
,参见数据点topic簇,使用时请替换为对应的pid和device_name。
此外,我们还需要订阅topic用于接受命令,这里把TOPIC1
设置为$sys/439074/cc3200/cmd/request/#
,#
是通配符,参见设备命令topic簇
定义好topic后,我们需要进一步配置user_connect_config[]
。由于我们只订阅了1个topic,我们需要修改#define TOPIC_COUNT
为1。
然后把user_connect_config[]
中的{QOS2, QOS2, QOS2}
改为{QOS1}
或{QOS0}
,并把will的参数设置为NULL。完整的user_connect_config[]
如下所示:
接下来我们需要实现发布消息的功能,发布消息前首先需要获取温度信息。
0x06 获取温度传感器信息
CC3200上面使用的是tmp006温度传感器,Datasheet,User’s Guide。使用I2C前,我们还得配置好管脚,需要修改原来的pinmux.c
。
根据I2C的例程文档,温度传感器的地址为0x41,储存环境温度的寄存器地址为0x01。在这里我们只需要读取温度传感器的数据,把I2C里面的函数ProcessReadRegCommand
修改为
unsigned int ProcessReadRegCommand(char *regStr)
{
unsigned char ucDevAddr, ucRegOffset, ucRdLen;
unsigned char aucRdDataBuf[256];
char *pcErrPtr;
ucDevAddr = (unsigned char)strtoul(devStr, &pcErrPtr, 16);
ucRegOffset = (unsigned char)strtoul(regStr, &pcErrPtr, 16);
ucRdLen = (unsigned char)2;
//RETERR_IF_TRUE(ucLen > sizeof(aucDataBuf));
//
// Write the register address to be read from.
// Stop bit implicitly assumed to be 0.
//
RET_IF_ERR(I2C_IF_Write(ucDevAddr,&ucRegOffset,1,0));
//
// Read the specified length of data
//
RET_IF_ERR(I2C_IF_Read(ucDevAddr, &aucRdDataBuf[0], ucRdLen));
// UART_PRINT("I2C Read From address complete\n\r");
//
// Display the buffer over UART on successful readreg
//
unsigned int f = (unsigned int)aucRdDataBuf[0]; // Byte 1
unsigned int s = (unsigned int)aucRdDataBuf[1]; // Byte 2
unsigned int ans = f;
ans = ans << 8;
ans = ans + s;
// UART_PRINT("%d %x\n\r", ans, ans);
return ans;
}
其中devStr
为在外面定义的函数常量,在这里定义为0x41
,也即温度传感器的地址。这里得到的为原始字节数据,需要做进一步转换,转换公式为temp = (float)(temp_die >> 2)*0.03125;
。
除此之外,在读取温度前,还得使用下列代码检查传感器是否配置好。
do {
config = ProcessReadRegCommand(regStr);
}while((config & 0x0080) != 0x0080);
0x07 上传温度信息至Onenet
配置好后,即可与平台进行连接并发布消息。Onenet要求上传的消息格式为JSON格式,所以这里我们引入cJSON。书写下列函数把温度信息写入JSON。
char *getJson(char* temp) {
cJSON* cjson_test = NULL;
cJSON* cjson_dp = NULL;
cJSON* cjson_temp = NULL;
cJSON* cjson_v = NULL;
/* 创建一个JSON数据对象(链表头结点) */
cjson_test = cJSON_CreateObject();
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(cjson_test, "id", 123);
cjson_dp = cJSON_CreateObject();
cjson_v = cJSON_CreateObject();
cJSON_AddStringToObject(cjson_v, "v", temp);
cjson_temp = cJSON_CreateArray();
cJSON_AddItemToArray(cjson_temp, cjson_v);
cJSON_AddItemToObject(cjson_dp, "temp", cjson_temp);
cJSON_AddItemToObject(cjson_test, "dp", cjson_dp);
return cJSON_Print(cjson_test);
}
例如,生成的JSON消息为:
{
"id": 123,
"dp": {
"tmp": 25
}
}
然后便可Publish出去
data_sw2 = (unsigned char*)getJson(temp_str);
sl_ExtLib_MqttClientSend((void*)local_con_conf[iCount].clt_ctx,
pub_topic_sw2,data_sw2,strlen((char*)data_sw2),QOS1,RETAIN);
UART_PRINT("\n\r CC3200 Publishes the following message \n\r");
UART_PRINT("Topic: %s\n\r",pub_topic_sw2);
UART_PRINT("Data: %s\n\r",data_sw2);
0x08 接收消息与处理
接下来我们修改Mqtt_Recv
函数,该函数是收到消息的回调函数,在里面可以书写收到下发命令的处理逻辑。在这里,我们打算收到平台下发的命令后,改变工作状态,并翻转LED的亮灭。首先我们需要修改前面几行为:
char *output_str=(char*)malloc(pay_len+1);
memset(output_str,'\0',pay_len+1);
strncpy(output_str, (char*)payload, pay_len);
output_str[pay_len]='\0';
原来的代码是提取订阅的topic字符串,这里我们修改后,提取的是下发设备收到的消息。然后书写处理逻辑
if(strncmp(output_str,RECV_MSG, pay_len) == 0)
{
flag_red = 1 - flag_red;
ToggleLedState(LED1);
UART_PRINT("OK\n\r");
}
在这其中我们使用了RECV_MSG
宏定义,比如#define RECV_MSG "1234"
,当收到的消息为1234时,会对flag_red
取反,该变量的值指示了板子的工作状态。然后调用ToggleLedState(LED1)
翻转LED,并在串口输出"OK"。接下来的代码复用例程中的即可。
0x09 应用端(APP)
- 数据获取
- 下发命令
0x0A 应用端(Web)
- 加入百度地图
- 前端
- 后端