AT 指令做 MTQQ 连接 | 学习笔记

简介: 快速学习 AT 指令做 MTQQ 连接

开发者学堂课程【物联网平台开发全栈教程AT 指令做 MTQQ 连接学习笔记,与课程紧密联系,让用户快速学习知识。

课程地址:https://developer.aliyun.com/learning/course/562/detail/7701


AT 指令做 MTQQ 连接


基本内容

一、  单片机程序的实现

二、  简单的子函数


一、单片机程序的实现

1AT 指令

AT 指令的说明书可以在网上找到,在百度搜索“上海庆科”-找到“官网”-找到“硬件”-找到“EM2086-然后点击“AT 固件说明及下载”-进入页面之后点击左侧的“指令说明”-就可以直接的找到相关的 AT 指令该怎么使用。也可以在官网首页将页面滑动到下方,点击“通用指令条例”。这节课主要讲述的是“Socket 通信条例”。

2Socket 通信条例

目前该课程是工作在 STATION 模式下的 TCP 客户端, 就是要去连接下其他发出的 WIFI 信号,然后慢慢地去参考模式,在参考模式的最后是透传模式, 透传模式最后就是数据的双向通信。

1UDP 广播(服务器端)

2)单播(客户端)

3、单片机程序里面主要新建了一个 WIFI.c,在里面可以首先看 WIFI.h,WIFI.h 里面可以学习到它实现了什么功能。

a#ifndef __WIFI_H

#define  __WIFI_H

#include

//初始化 WIFI,包括链接 WIFI,进入 TCP 透传,设置 SSID 等参数

void Init WIFI (void) ;

1)第一个最重要的是初始化 WIFI,在初始化 WIFI 之前,先做一个简单的工作,编译了一个程序,这个程序是发送AT 指令的函数。

发送 AT 指令:

//发送 AT 指令.这里会等待回复的 OK,否则会重发

void Send AT (unsigned char *Str)

//首先这个字符串是以地址的形式传参数过来的,传输过来之后同 WIFI 发送字符串这个子函数将对于的AT指令发出去,发出去之后将在这个死循环里一直等待,等待串口收到。

{

unsigned char Dat=0;

unsigned char Count=0;

unsigned char Loop Count=0;

unsigned char ReSend Count=0;

WIFITSend str(Str); //先通过串口发送出去

while(1) //等待回复

{

Delaylms(50) ;

Loop Count++;

if (Loop Count >= 100)

{

ReSend Count++;

if (ReSend Count< 3 )

{

//在交互的时候,每次发送一个信号,就会返回一个 ok

如果没有返回 ok,那就继续重复发送;这里简单的定义一下,重复发送三次。

//当发送了三次,失败之后,该程序就会退出。这里就是检测有没有 OK 产生。简单理解就是 Send_AT 就是这个作用,它要保证数据可靠一点的发送过去,这样写的程序不是工业级的应用,只是做一个参考,简单的用法。这里只是简单的介绍。

Loop_Count = 0;

send_SErl ("\r\n 重发指令: ");

Send_Strl (str) ;

send_Strl ("\r\n") ;

WIFI_Send str(str);//重发一遍

}

else

{

send_SErl ("\r\n发送失败: ");

Send_Strl (str) ;

Send Strl ("lrn") ;

return;//重发失败,退出

}

}

if(Get_ Byte_ WIFI (&Datq )

{

if (Dat == 'O' )

{

Delaylms (20) ;

Get Byte WIFI (&Dat) ;

if (Dat == 'K')

{

send strl ("\r\n成功执行一条指令: ");

Send Strl (Str) ;

send strl ("\r\n") ;

return;

}

}

}


二、简单的子函数

下面又写了几条简单的子函数;

1、拼接两条字符串,把字符串2添加到字符串1后面;

它也是按照地址的形式传参的,在传参的时候,所定义的字符串1, 它在内存里的空间要足够的大, 不能说将字符串2添加到字符串1后面就溢出了, 数据在内存里就会出现问题, 正常情况下如果要写这个函数如果要做到通用, 最好在程序里面限制字符串1和字符串2的长度, 但是这个子函数是针对于这个特殊场合所使用的,要必须非常清楚字符串1和字符串2的大小,才可以不做限制。

2、拼接两条字符串,把字符串2添加到字符串1前面;

3、初始化 WIFI,包括链接 WIFI,进入 TCP 透传,设置 SSID 等参数

void Init WIFI (void)

{

unsigned char DataBuf [256];

// 定义一个空间,保证它是256字节的, 所有的内容加起来不可能超过256,将字符串2添加到字符串1后面就溢出了,这样就查不出哪个步骤出现了问题。

unsigned char DataBuf2[30];

//主要是给 WIFI 密码用的, 所以限定了密码的最大长度是30

unsigned int DataLen=0;

unsigned int i=0;

//首先发送,退出透传模式 ; 不管程序有没有进入,先退出;

Send AT("+++") ;

Send AT("ATr\n\") ; // 然后发送两个 AT

Send AT ("ATr\n\") ;

//关闭回显;

//回显就是正常情况下发送一个程序,然后模组返回一个程序; 如果不希望它返回什么程序过来,将它关闭就可以了;

Send AT ("AT+UARTE=OFFlrn") ;

//打开 TCP EVENT(事件)

// 刚进入透传模式的时候,这个事件没有被关闭, 在打开的状态,这个事件就会被发送过来,会影响透传,本身是透传,突然来了一个事件,此时将无法判断这是模组发的事件还是服务器发的透传数据,所以说到后面会在合适的场合把它给关掉。

Send AT ("AT+WEVENT=ON\r\n") ;

Send AT ("AT+CIPEVENT=ON\r\n") ;

//首先关闭 TCP1通道,用于调试使用 ;

Send AT ("\r\n AT+CIPSTOP=1\r\n") ;

//这里是关闭 TCP 通道,它是干什么用的?首先要设置 TCP1,之前要关闭它,在这里,就不管它有没有打开,反正最后要关闭它,如果之前打开过,那么正好关掉。如果之前什么都没有操作,那反正要关闭一次,为了保证万一哪天打开了 TCP1,一定要在设置它之前把它给关掉,这里要注意,这是个1,之后要和这个1有关系的。

//查看 Flash 内容,WIFI SSID 部分,如果是 FF 00,表示第一次开机 ,那么就会直接进入死循环,执行 CLI 逻辑。CLI 逻辑执行的目的就是为了让 MQTT 这个小工具相关的逻辑去执行,因为只有它打开之后,才可以读取单片机的数据。接着写一些内容在 MQTT 这个小工具的 flash 里面。

DataLen = Read One Byte (SSID Addr) ;

DataLen <<= 8;

DataLen += Read One Byte (SSID Addr+1) ;

if( (DataLen == 0XFFFF) I (DatlaLen == 0) )

{

Send Strl (“\r\n 设备第一次开机,请进行设置! \r\n");

while (1)

{

CLI(); //执行 CLI 逻辑

}

}

按下 reset 复位,就可以跳过这些,程序再次执行的时候就会跳过“查看 Flash”。

//设置模块做 TCP Client 的参数

Read_Flash_Message(MQTT_URL_Addr,DataBuf,&DataLen);

// 此时设置了 MQTT 里面的地址;并且读取 flash 里面的信息,

4、例子:打开 Message 函数

//保存内容到 Flash

void Save Flash Message (unsigned int Addr, unsigned char *Buf, unsigned int Len)

{

unsigned int i=0;

Erase_ IAP (Addr); //擦除对应扇区

if(Len >= 511)

Len = 511;

// 首先在地址前面的两个字节中写入长度, 意思是指这个 Buf 有多长,将它放入最前面。

write_ One_ Byte (Addr, Len>>8);//长度写入首地址

write One_ Byte (Addr+1, (Len&0X00FF));//长度写入首地址

for(i=0;i//顺序存储有效信息

// 接着从第三个字节开始往里面保存有效信息, 只有这样保存信息,才能够如下列程序中所写的那样读取数据。

{

write_ One_ Byte ( (Addr+2+i),Buf [i] ) ;

}

//flash 里面读取信息

void Read Flash Message (unsigned int Addr , unsigned char *Buf, unsigned int *Len)

第一个参数是地址,这个是 flash

里面的地址, 第二个参数是缓冲区域,就是读取之后要放入缓冲区里, 第三个参数是长度,长度也需要放在缓冲区里面传参。

// 分析这里面的程序就会发现:首先,在 flash 的地址里面读取长度,flash 的前面两个字节其实是在 flash 里面存入的信息,必须所含有的一个数字。

{

unsigned int i-0;

*Len - Read One_ Byte (Addr); // 首先读取长度信息

*Len <<= 8;

*Len Read_ One_ Byte (Addr+1) ;

// 将长度进行拆分,放入 flash 的前面两个字节中,接着从,flash 的前面两个字节开始就是 Buf。这就是所有的内容。最前面的两个字节被跳过了,这样写程序必须有个前提:怎样读取数据,就必须怎样保存数据。

if(*Len >= 511)

*Len = 511;

for(i-0;i<*Len;i++)

{

Buf [i] . Read_ One_ Byte (Addz+2+i) ;

}

问:这些页数是从哪里来的?

答:观察单片机的手册,在第九章 EEPROM 的应用,该程序是15W4K32S4系列的,型号是4K48S44K48S4EEPROM 10K 个字节,从 IAP 指令读取的时候是从0000开始的,它有20个扇区,每一个扇区有520个字节才够用,所以说,每一个扇区存放一个内容在该程序中完全可以;在程序中读取的时候直接使用宏定义来读取,这样方便程序设计者进行程序设计,比如说 MQTT_UL 它的地址就是

0X0400,这样就可以方便理解 Buf 的长度是如何保存的,这样就可以知道在发送的时候 Buf 它的长度是多大, 缓冲区到底有多大,每一次都有长度,这样会很方便编程,比如说在前面添加或者在后面添加的时候,都会有一个DataBuf,包括长度在哪里会有用?其实只在发送的时候有用,比如发送 connect,就可以根据长度一个字节一个字节的将程序发送,此时,一定是正确的。

Sum_str_Tail(DataBuf,",1883r\n\");//在后面加上

// 1883是它的端口是固定的一个数据;

Sum_str_Header(DataBuf,"AT+CIPSTART=1,tcp_client,") ;

// 在前面加上

send AT (DataBuf);//AT指令发射出去

//链接 WIFI 到路由器 station 模式,它主要读取的是 SSID WIFI Pass Addr, 下面的这两行代码和在“设置模块做TCP Client 的参数”中的代码会在接下来的课程中讲解,

Read Flash Message (SSID Addr, DataBuf, &DataLen) ;

Read Flash Message (WIFI Pass Addr, DataBuf2, &DataLen) ;

Sum Str Header (DataBuf, "AT+WJAP=") ;

Sum str Tail (DataBuf,",") ;

Sum Str Tail (DataBuf, DataBuf2) ;

Sum str Tail (DataBuf, "lrn") ;

Send AT (DataBuf) ;

Clear WIFI() ;

//等待 TCP 链接成功事件 ,此时该程序认为已经连接到了可靠的 Wi-Fi, 等待程序发送一个 EVENT 事件过来;

while (l)

{

Delaylms(10) ;

if (Get_ Mess_ Count_ WIFI () )

{

DataLen=0;

while (Get_ Byte_ WIFI (&DataBuf [DataLen++]) )

{

Send Data1 (DataBuf [DataLen-1]) ;

}

DataBuf [DataLen++] =0;

if (Compare str(DataBuf, "SERVER, CONNECTED") != 255 )

{

// Compare str 这是一个子函数,       它是比较字符串, 他就是比较一下再 str1里面有没有包含完整的 str2, 如果有的话就会反应 str2str1里面的位置, 如果没有就会直接返回255 。所以需要对比一下是不是255, 如果不是255那就说明里面包含了完整的 SERVER, CONNECTED, 然后就会继续进行下面的相关操作。

// 上面这段代码会一直检测它是否发送了一个 EVENT 事件过来?

1)发送 CONNECT 报文,并等待服务器返回代码。

void MQTT connect (void) ;

{

unsigned char DataBuf [256] ;

unsigned int DataLen=0;

unsigned char Dat=0;

unsigned int i=0;

Delaylms (2000) ;

//首先需要延时,因为刚刚延时成功,延时2秒左右, 这个不一定准确,它是大概的2秒,因为有可能是1.几秒,

//先发送断开链接,断开链接是1000,再讲解 MQTT 的时候已经讲解了这部分内容,

unsigned char MQTT_Heart[]={0xc0, 0x00};

unsigned char MQTT_Heart_Reply[]={0xd0, 0x00};

unsigned char MQTT_Disconnect[]={0xe0, 0x00,};

unsigned int MQTT_Heart_Count=0;

unsigned int MQTT_PUB_Count=0;

//防止上次链接还在线;不管他有没有连接成功,先断开一次。

for(i=0 ; i<2 ; i++)

{

WIFI_Send_Byte (MQTT_DisConnect[i]) ;

}

Clear WIFI();//清空 WIFI 接收缓冲区

Delaylms (2000) ;

//链接 MQTT 服务器

Read_Flash_Message(MQTT_Connect_Addr,DataBuf,&DataLen);

//读取 MQTT 报文;

//此时发送 connect 里面的报文,读取 Flash 里面的报文到 DataBuf;将 DataBuf 发送出去,

for(i=0;i

{

WIFI Send Byte (DataBuf[i]) ;

}

//等待服务器返回

Delaylms (2000) ;

Dat = 0;

while(Get_Byte_WIFI (&DataBuf [Dat++]));

//获取接受缓冲区数据

if ( (DataBuf [0]==0X20) & (DataBuf [1]==0X02) )

{

Send Strl ("r\n\ 链接 MQTT 服务器成功 \r\n”)

// 等待服务器返回20020000, 如果有20020000则成功,如果没有20020000则失败,

LED3 = 1;

}

else

{

Send_ Strl ("r\n\ 链接 MQTT 服务器失败 \r\n”) ;

for(i=0;i

{

WIFI_Send_Byte (DataBuf[i]) ;

}

Delaylms (2000) ;

}

//订阅主题 服务器设置设备属性用来接收服务器下发的消息

Read_Flash_Message(MQTT_Sub_Addr, DataBuf, &DataLen) ;

//读取 MQTT 报文,flash 里面读出主题,DataLen 为主题的长度,DataBuf 为主题的发送区,

for(i=0; i

{

WIFI_ Send Byte (DataBuf[i]) ;

//通过 WIFI 将它发送出去,

}

//等待服务器返回

Delaylms (2000) ;

Dat=0;

while(Get_ Byte_ WIFI (&DataBuf [Dat++]));

//获取接受缓冲区数据

if( (DataBuf [0]==0X90) & (DataBuf[l]==0X03))

{

Send strl("r\n\ 订阅属性成功\r\n”);

}

else

{

Send strl("r\n\ 订阅属性失败\r\n");

//发送出去之后检查是否有9003,有9003成功,没有9003失败;

for(i=0;i

{

WIFI_Send Byte (DataBuf [i]) ;

{

Delaylms (2000) ;

}

发送完之后就会进入主循环了,打开 WIFI.c 程序文件。

CLI();//执行 CLI 逻辑

Send_ Heart();//定时发送心跳包.

Analyze_ MQTT_ Read();//解析服务器下发的消息

Pub_ Temperature();//定时,上报温度信息

其实,这些程序都是极其简单的逻辑,容易理解, 在讲解了理论知识之后,逻辑无非就是编写代码比较浪费时间, 最重要的一点就是如何把数据从 flash 里面读取出来。

2)发送心跳放到主循环中定时器标志位到了就发送心跳。

void Send Heart (void) ;

3)接收MQTT服务器发送过来的数据,解析服务器下发的数据。

void Analyze MQTT Read (void) ;

4)定时,上报温度数据。

void Pub_ Temperature(void);

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
6月前
|
传感器 芯片
如何使用中断源
如何使用中断源
56 1
|
存储 Shell 开发者
E906的指令|学习笔记
快速学习 E906的指令
533 0
|
2月前
|
运维 监控 Linux
探究-ping指令的使用
【9月更文挑战第2天】`ping` 指令是网络诊断工具,通过发送 ICMP 回显请求并接收应答,测试网络连接的可达性和响应时间。在 Windows、Linux 和 macOS 中均可使用。主要参数包括 `-t`(持续监测)、`-n`(指定次数)和 `-l`(数据包大小)。结果分析关注回显时间、数据包丢失率和 TTL 值,适用于网络故障排查、性能评估和服务器监控。掌握 `ping` 的使用方法可帮助管理和优化网络连接。
|
6月前
I/O设备与主机信息传送的方式(程序查询方式,程序中断方式,DMA方式)
I/O设备与主机信息传送的方式(程序查询方式,程序中断方式,DMA方式
285 0
|
6月前
指令模式
指令模式。
23 1
Ping 命令配置 -c、-i、-w 指令使用
Ping 命令配置 -c、-i、-w 指令使用
302 0
|
Linux Shell
4.2.2 基础指令的操作
4.2.2 基础指令的操作
91 0
|
Linux
Linux驱动操作地址(寄存器)的一些方式
Linux驱动操作地址(寄存器)的一些方式
152 0
|
C语言 Perl
西门子S7-1200的MODBUS通信如何组态,通信装载指令、主站设置指令、从站设置指令各参数的含义是什么
本篇我们来学习西门子S7-1200的MODBUS通信如何组态,通信装载指令、主站设置指令、从站设置指令各参数的含义是什么。
西门子S7-1200的MODBUS通信如何组态,通信装载指令、主站设置指令、从站设置指令各参数的含义是什么
|
C语言 Perl
西门子S7-1200的MODBUS通信装载指令、主站设置指令、从站设置指令各参数的含义是什么?
本篇我们来介绍西门子S7-1200的MODBUS通信装载指令、主站设置指令、从站设置指令各参数的含义是什么。
西门子S7-1200的MODBUS通信装载指令、主站设置指令、从站设置指令各参数的含义是什么?