5.12更新:
需要注意调用cJSON_Print函数将JSON体转化为字符串时,会开辟内存,转化后使用完毕后需要调用cJSON_free函数将内存释放掉,否则会导致内存泄漏。一旦内存泄漏会导致系统宕机。
本项目使用了W801以一机一密的方式连接至阿里云物联网平台,定时发送数据并实现了反向控制。
因为使用阿里云物联网平台,因此需要提前创建好一个设备,这个网上的示例和阿里云自己的文档已经十分完整,因此不多赘述,可以自行查阅。创建完设备的MQTT链接参数是用来进行链接的关键,如下图所示。
概述:SDK中提供的MQTT链接示例基本是可用的,但是连接阿里云时需要做如下修改。
【1】修改MQTT链接参数,同时要添加Username以及password的宏定义,订阅和发布的topic根据自己的设备来添加
【2】修改MQTT的client,username等的数组长度,SDK中的数据范围太小,而阿里云的链接参数由于经过加密因此比较长无法满足要求,需要对以下几个位置进行修改。
【3】修改原MQTTdemo里面的mqtt_demo_init函数,原函数没有账户密码登录的步骤,但是提供了相应函数,因此需要进行补充。
【1】概述:在原demo上修改,添加了一个硬件定时器,定时5s后在中断服务函数,向队列里添加一个MQTT_MSG_PUBLISH_CB标志,主循环读取到这个标志后会进行mqtt消息发送,这边还涉及到一个数据处理成为JSON格式,我这里只发送一个温度信息,格式为{“temperature”:“20”},用的就是cJSON库,这个参考下网上其他文章即可。
【2】代码如下:
//mqtt消息发送函数
static int mqtt_msg_pub(void)
{
char *temperature_msg;
temperature_msg = datatoJSON(temp_label, temperature);
mqtt_publish(&mqtt_demo_mqtt_broker, (const char *)MQTT_DEMO_RX_PUB_TOPIC, (const char *)temperature_msg, strlen(temperature_msg), 0);
tls_os_queue_send(mqtt_demo_task_queue, (void *)MQTT_DEMO_CMD_LOOP, 0);
cJSON_free(temperature_msg );
return 0;
}
//demo中原来的主循环
static void mqtt_demo_task(void *p)
{
int ret;
void *msg;
struct tls_ethif *ether_if = tls_netif_get_ethif();
if (ether_if->status)
{
wm_printf("sta ip: %v\n", ether_if->ip_addr.addr);
tls_os_queue_send(mqtt_demo_task_queue, (void *)MQTT_DEMO_CMD_START, 0);
}
for ( ; ; )
{
ret = tls_os_queue_receive(mqtt_demo_task_queue, (void **)&msg, 0, 0);
if (!ret)
{
switch((u32)msg)
{
case MQTT_DEMO_CMD_START:
do
{
ret = mqtt_demo_init();
if (ret)
break;
tls_os_queue_send(mqtt_demo_task_queue, (void *)MQTT_DEMO_CMD_LOOP, 0);
timer_pub();
}
while (0);
break;
case MQTT_DEMO_CMD_HEART:
//wm_printf("send heart ping\r\n");
mqtt_ping(&mqtt_demo_mqtt_broker);
break;
case MQTT_DEMO_CMD_LOOP:
mqtt_demo_loop();
break;
case MQTT_MSG_PUBLISH_CB: //添加一个新的标志,读取到这个标志时候,进行消息发送
mqtt_msg_pub();
break;
default:
break;
}
}
}
}
//中断函数中向队列添加标志
static void timer_pub_irq(u8 *arg)
{
//printf("timer irq\n");
tls_os_queue_send(mqtt_demo_task_queue, (void *)MQTT_MSG_PUBLISH_CB, 0);
}
//上传数据硬件定时器
int timer_pub(void)
{
u8 timer_id;
struct tls_timer_cfg timer_cfg;
timer_cfg.unit = TLS_TIMER_UNIT_MS;
timer_cfg.timeout = 5000;
timer_cfg.is_repeat = 1;
timer_cfg.callback = (tls_timer_irq_callback)timer_pub_irq;
timer_cfg.arg = NULL;
timer_id = tls_timer_create(&timer_cfg);
tls_timer_start(timer_id);
return WM_SUCCESS;
}
【1】阿里云实际上可以采用物模型进行反向控制,但是那个相对比较麻烦,这里我采用一个比较通用的办法,w801订阅了一个topic接受该topic命令并解析,再进行对应操作。这一部分逻辑与我前一篇文章,蓝牙反向控制相似,这里给出链接可以作为参考。
W801上电自动重连wifi并通过蓝牙更新账号密码
【2】代码:
static int mqtt_demo_loop(void)
{
int packet_length = 0;
int counter = 0;
counter++;
packet_length = mqtt_demo_read_packet(0, 1);
if(packet_length > 0)
{
if (MQTTParseMessageType(mqtt_demo_packet_buffer) == MQTT_MSG_PUBLISH)
{
uint8_t topic[100], *msg;
uint16_t len;
len = mqtt_parse_pub_topic(mqtt_demo_packet_buffer, topic);
topic[len] = '\0'; // for printf
len = mqtt_parse_publish_msg(mqtt_demo_packet_buffer, &msg);
msg[len] = '\0'; // for printf
//wm_printf("recvd: %s >>> %s\n", topic, msg);
aliyun_cmd_excute(msg);
}
tls_os_queue_send(mqtt_demo_task_queue, (void *)MQTT_DEMO_CMD_LOOP, 0);
}
else if (packet_length == MQTT_DEMO_READ_TIMEOUT)
{
tls_os_queue_send(mqtt_demo_task_queue, (void *)MQTT_DEMO_CMD_LOOP, 0);
}
else if(packet_length == -1)
{
wm_printf("mqtt error:(%d), stop mqtt demo!\n", packet_length);
tls_os_timer_stop(mqtt_demo_heartbeat_timer);
mqtt_demo_close_socket(&mqtt_demo_mqtt_broker);
}
return 0;
}
int aliyun_cmd_excute(char *cmd)
{
//解析Json字符串
cJSON *str_json, *str_cmd;
str_json = cJSON_Parse(cmd);
if (!str_json)
{
printf("[INFO]: JSON格式错误: %s\r\n", cJSON_GetErrorPtr());
}
else
{
str_cmd = cJSON_GetObjectItem(str_json, "cmd");
if (str_cmd->type == cJSON_Number)
{
//printf("[INFO]: cmd: %d\r\n", str_cmd->valueint);
if (str_cmd->valueint == 1)
{
temperature++;
printf("[CMD_ALIYUN]: Temperature rise by aliyun, the current temp is %d\n",temperature);
}
else if (str_cmd->valueint == 2)
{
temperature--;
printf("[CMD_ALIYUN]: Temperature drops by aliyun, the current temp is %d\n",temperature);
}
}
}
cJSON_delet(str_json );
return WM_SUCCESS;
}