小车联网-通过ESP8266将速度发送到客户端
实现目标:
客户端通过网络发送启动信息控制小车启动,小车将速度实时地发送给客户端查看,同时也能在OLED屏上显示速度信息
基于小车测速并通过OLED显示的代码进行修改
程序
程序文件
1.main.c:定时器0、定时器2、串口初始化函数的调用,自动发送AT指令函数,外部中断初始化函数,OLED屏初始化函数,while循环内根据标志位将速度数据通过串口发送到客户端,在OLED上显示
2.Motor.c:小车前进、后退、左转、右转和停止的函数
3.Delay.c:延时函数
4.Timer0.c:定时器0初始化,中断处理函数PWM控制小车前进
5.Timer2.c:定时器2初始化,中断处理函数定义1秒,接收外部中断的变量,然后置0,开启下一次计算
6.Int0.c:外部中断0初始化,测速模块有下降沿来,则中断处理函数变量++
7.IIC.c:IIC协议,供OLED屏使用
8.OLED.c:OLED写命令、写数据、初始化、清屏、显示等函数
9.WIFI.c:AT指令数组,设置为AP模式,以及根据应答调用串口发送函数发送AT指令的函数,和发送指定通道和数据长度的AT指令
1.添加WIFI.c文件
将之前ESP8266当AP模式的代码模块化后,整理出WIFI.c源文件,并将源文件复制到本次代码的工程目录下,然后在keil中添加进来,
主要是用来设置AP模式的AT指令数组,需要加code,将数组放到ROM中,不然代码太大编译不通过,然后是应答的标志位,两个通过串口发送AT指令的函数
相应的在WIFI.h头文件中添加函数的声明
#include <REGX52.H>
#include <string.h>
#include "Usart.h"
#include "Delay.h"
//AP模式AT指令
code char CWMODE[] = "AT+CWMODE=2\r\n";
code char CIPMUX[] = "AT+CIPMUX=1\r\n";
code char CIPSERVER[] = "AT+CIPSERVER=1\r\n";
code char CIPSEND[] = "AT+CIPSEND=0,13\r\n";
unsigned char AT_OK_Flag = 0; //收到OK应答标志
unsigned char Client_Connect_Flag = 0; //收到客户端连接标志
unsigned char Client_Disconnect_Flag = 0; //客户端断开连接标志
/**
* @brief 自动发送AT指令
* @param 无
* @retval无
*/
void Automatic_connection()
{
//设置为AP模式,防止自动连接WIFI干扰应答
Uart_SendString(CWMODE);
while(!AT_OK_Flag);
AT_OK_Flag = 0;
//使能多连接
Uart_SendString(CIPMUX);
while(!AT_OK_Flag);
AT_OK_Flag = 0;
Delay1ms(2000);
//开启TCP Server
Uart_SendString(CIPSERVER);
while(!AT_OK_Flag);
AT_OK_Flag = 0;
//等待客户端连接
while(!Client_Connect_Flag);
}
/**
* @brief 自动发送数据
* @param 无
* @retval无
*/
void Automatic_send_data()
{
Uart_SendString(CIPSEND);
}
2.Usart.c头文件的中断处理函数中添加AT应答的判断语句
结合之前ESP8266的串口通信,在小车的串口中断中直接添加判断语句即可
/**
* @brief 串口中断处理函数
* @param 无
* @retval无
*/
void Uart_Rountine() interrupt 4
{
static unsigned int i = 0;
unsigned char temp;
if(RI)
{
RI = 0;
temp = SBUF;
//0和O是ESP8266的应答信号
if(temp == 'F' || temp == 'B' || temp == 'L' || temp == 'R' || temp == 'S'||temp == 'Q'
||temp == 'M' || temp == '0' || temp == 'O')
{
i = 0;
}
rec[i++] = temp;
/*收到客户端连接成功,返回0,CONNECT应答信号*/
if(rec[0] == '0' && rec[2] == 'C')
{
Client_Connect_Flag = 1; //客户端连接标志位置1
memset(rec,'\0',SIZE);
}
/*如果收到OK应答,则rec数组内存放的是OK,所以直接判断下标0位和1位就行*/
if(rec[0] == 'O' && rec[1] == 'K')
{
AT_OK_Flag = 1; //OK应答标志位置1
memset(rec,'\0',SIZE);
}
/*如果客户端自己断开,则8266会返回0,CLOSED应答,标志位置1让main函数中停止发送数据
因为ESP8266应答信息多了判断会出错,尽量跟其他判断区分开,搞清楚一个应答中哪几个
字母会放在数组的第0位*/
if(rec[1] == 'E' && rec[2] == 'D')
{
Client_Disconnect_Flag = 1;
memset(rec,'\0',SIZE);
}
//前进信号Forward
if(rec[0] == 'F' && rec[1] == 'o')
{
GoForward();
//收到前进信号后延时400ms,再被main函数停止,延时时间越小,就越接近点动模式
Delay1ms(400);
i = 0;
memset(rec,'\0',SIZE);
}
//后退信号Back
if(rec[0] == 'B' && rec[1] == 'a')
{
GoBack();
Delay1ms(400); //延时
i = 0;
memset(rec,'\0',SIZE);
}
//左转信号Left
if(rec[0] == 'L' && rec[1] == 'e')
{
GoLeft();
/*左转的延时少一点,可通过点动方式一点一点地调方向,
如果延时大的话,按一下就转动很大,不便调方向*/
Delay1ms(200);
i = 0;
memset(rec,'\0',SIZE);
}
//右转信号Right
if(rec[0] == 'R' && rec[1] == 'i')
{
GoRight();
Delay1ms(200); //右转的延时跟左转同理
i = 0;
memset(rec,'\0',SIZE);
}
//收到停止信号,speed记得要清零
if(rec[0] == 'S' && rec[1] == 't')
{
speed = 0;
Stop();
i = 0;
memset(rec,'\0',SIZE);
}
//快速
if(rec[0] == 'Q' && rec[1] == 'u')
{
speed = 35;
i = 0;
memset(rec,'\0',SIZE);
}
//中速
if(rec[0] == 'M' && rec[1] == 'i')
{
speed = 25;
i = 0;
memset(rec,'\0',SIZE);
}
//慢速
if(rec[0] == 'S' && rec[1] == 'l')
{
speed = 15;
i = 0;
memset(rec,'\0',SIZE);
}
//没收到信号,小车停止
if(rec[0] == '\0' && rec[1] == '\0')
{
Stop();
i = 0;
memset(rec,'\0',SIZE);
}
if(i == SIZE){i = 0;}
}
}
3.main函数中调用WIFI.c的自动发送AT指令函数以及发送数据的前提AT指令
extern unsigned int ResultSpeed; //速度变量
extern unsigned char signal; //发送标志,当定时器2将其改为1时串口发送数据
char recspeed[24];
extern unsigned char Client_Disconnect_Flag; //客户端断开连接标志
extern code char CIPSEND[]; //AT指令,往0通道发送13个字节
void main()
{
Timer0Init();
Timer2Init();
UartInit();
Delay1ms(1000); //给ESP8266模块上电时间
Automatic_connection(); //自动发送AT指令,最后等待客户端连接
Int0_Init();
OLED_Init(); //OLED初始化
OLED_Clear(); //先清屏
OLED_P8x16Str(1,0,"******BMW******"); //显示一个字符串
while(1)
{
//定时器2那边如果将标志位改为1,则进行数组格式组装并发送
if(signal == 1)
{
//sprintf将ResultSpeed按格式组建好后放到数组中去
sprintf(recspeed,"speed:%d cm/s ",ResultSpeed);
/*当客户端断开连接标志位为0则继续发送数据,检测到断开应答时置1,则不再发送数据*/
if(Client_Disconnect_Flag == 0)
{
Uart_SendString(CIPSEND); //开启发送数据到客户端的AT指令
Delay1ms(1000);
Uart_SendString(recspeed); //通过串口发送速度,得到AT指令则发送到客户端上
OLED_P8x16Str(3,3,recspeed); //通过OLED显示速度
signal = 0;
}
}
}
}
接线方式:
1.查看通信情况
如果是想通过串口查看通信情况的,可以将CH340接上,ESP8266的TX通过分线接单片机的RX和CH340的RX,ESP8266的RX通过分线接单片机的TX和CH340的RX,因为小车的电源由两节18650电池供电,经过降压模块得到5V,要给单片机、电机驱动、测速模块供电,所以ESP8266的电源需要额外供给,把模块的地全接在面包板上
2.不查看通信情况
查看通信情况一般是前期测试代码正确性所用的方法,真正使用ESP8266时是不用查看通信情况的,若小车上有3.3V电源,则直接把ESP8266接上去,小车电源也由板载的18650电池供电,此时小车就完全是独立的状态,通过发送AT指令给ESP8266设置为AP模式,则此时小车就相当于一个服务器,当用客户端连接上服务器时,就能通过网络控制小车了
最终实现:
不用串口助手查看通信情况,小车上电后,发送AT指令给ESP8266开启服务器模式,等待一两秒客户端点击连接,成功连上服务器,小车的速度通过网络发送到客户端上,客户端也能通过网络发送前进后退等指令控制小车启动
需要注意的地方:
如果小车本身的接线没改变,ESP8266按之前的接线方式,即用分线将ESP8266的应答信息通过TX分别发送到单片机和电脑串口助手上,分线的部分没有问题,问题出在了单片机使用的数据线供电上,还记得之前说用充电宝给单片机供电,是无法通过电脑串口查看到ESP8266的应答信息的,这是因为:单片机用电脑USB供电是通过电脑与CH340和ESP8266共地了,因为共地才能进行通信,所以能在电脑串口上查看到ESP8266的应答信息,如果用充电宝给单片机供电,则单片机与CH340、ESP8266是不共地的,通信基准不同,所以是无法通信的
只要抓住一个准则,就是共地,想办法解决ESP8266 3.3V供电问题,其实就能实现远程客户端控制小车
效果演示:【基于ESP8266的网络控制小车】 https://www.bilibili.com/video/BV1aB4y1m7xG?share_source=copy_web&vd_source=d6b62ea36d09d5899101585761eec921