ESP32+hcsr04的超声波模拟雷达

前情提要

在逛淘宝的时候发现超声波模拟雷达居然敢卖这么贵,想了一下代码也不是很难,于是就写着试了下.

由于使用0.96寸oled无法实现较好效果,只能看看

使用0.96寸oled,hcsr04,esp32,sg90舵机.

模拟雷达效果图

 连线

io16接舵机信号线,io21和io22是i2c通信,21接屏幕SDA,22接SCL,io15接超声波trig,io2接echo,

屏幕以及超声波使用3.3v,舵机使用下方5v

软件

vscode+platformIO,比arduino好用点,主要是代码补全和调试界面舒服多了.

安装教材PlatformIO+VScode+ESP32开发环境搭建教程

建立项目

选择你的开发板,路径,开发语言.

由于需要做到模拟雷达的效果,所以需要更好的oled库,导入u8g2库,使用也很简单通过platformio中搜索u8g2库添加到该项目,同理,添加esp32servo库,将舵机库引入项目

随后直接添加

库函数和常数

#include <Arduino.h>
#include <U8g2lib.h>
#include <math.h>
#include<esp32servo.h>
Servo myservo;
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);   // All Boards without Reset of the Display

深入学习Arduino u8g2 OLED库,一篇就够

通过对于u8g2的代码详解,我们找到了对应0.96寸oled驱动ssd1306的绑定,硬件i2c.同时引入servo对象,由于要用到角度计算所以添加math库.

#define PI 3.1415926535897932384626433832795
#define val PI / 180
#define max 320//扫描范围
#define speed 2//扫描速度
#define times 2//每个点采样次数
hw_timer_t *tim1 = NULL;
Servo myservo;
float siny[181];//角度数据
float cosx[181];
int pos[181];//位置数据
int tim1_IRQ_count = 0;

利用sin,cos数据记录每个角度的正余弦值,pos记录该点距离,speed,time,max参数在后面会用到

超声波测距

使用定时器的方式计算距离,测量高电平时间可以得出距离.

定时器初始化

  tim1 = timerBegin(0, 80, true);//1mhz
  timerAttachInterrupt(tim1, tim1Interrupt, true);//更新中断
  timerAlarmWrite(tim1, 10, true);//计数次数10,10微秒中断一次
  timerAlarmEnable(tim1);//开启定时器

中断函数

void tim1Interrupt()
{//中断服务函数
  tim1_IRQ_count++;
}

实现

注释很清楚了

float getdistance(){
  float distance,distance_mm;
  int time_end;
	digitalWrite(15,HIGH);						//输出高电平
	delayMicroseconds(15);										//延时15微秒
	digitalWrite(15,LOW);						//输出低电平
	tim1_IRQ_count=0;
	while(digitalRead(2)==0){
	if(tim1_IRQ_count>800)//取决于测量范围,测量范围越长越大,不够大的话会出现测距返回0
		break;
	}		//等待低电平结束
	tim1_IRQ_count=0;												//计时清零
	while(digitalRead(2)==1);		//等待高电平结束
  
	time_end=tim1_IRQ_count;										//记录结束时的时间

	if(time_end/100<38&&time_end>0)									//判断是否小于38毫秒,大于38毫秒的就是超时,直接调到下面返回0
	{
		distance=(time_end*346)/2;						//计算距离,25°C空气中的音速为346m/s
		distance_mm=distance/100;						//因为上面的time_end的单位是10微秒,所以要得出单位为毫米的距离结果,还得除以100
	}
	return distance_mm;									//返回测距结果
}

oled显示

由于要实现保存扫描过的点数据,所以每一次转动都要循环以前的数据

distance为当前距离,i为当前角度

0-180

void showleftpos(int distance,int i){
    int x=64+cosx[i]*(64);
    int y=64-siny[i]*(64);
    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_helvB08_tf);
    for(int j=0;j<i;j++){
      u8g2.drawPixel(64+cosx[j]*(pos[j]/(max/64)),64-siny[j]*(pos[j]/(max/64)));
    }
    u8g2.drawLine(64,64,x,y);
    u8g2.setCursor(0,10);
    u8g2.print(distance);
    u8g2.setCursor(110,10);
    u8g2.print(i);
    u8g2.sendBuffer();
}

180-0

void showrightpos(int distance,int i){
    int x=64+cosx[i]*(64);
    int y=64-siny[i]*(64);
    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_helvB08_tf);
    for(int j=180;j>i;j--){
      u8g2.drawPixel(64+cosx[j]*(pos[j]/(max/64)),64-siny[j]*(pos[j]/(max/64)));
    }
    u8g2.drawLine(64,64,x,y);
    u8g2.setCursor(0,10);
    u8g2.print(distance);
    u8g2.setCursor(110,10);
    u8g2.print(i);
    u8g2.sendBuffer();
}

多次测距取平均值

超声波测量有时候会接受到错误回波导致数据过大或者过小,因为不好判断所以只取消了0的情况,当为0时,除数减一.最后结果如果大于测量范围则取最大max

int manydis(int n)//测量取平均值
{ float distancen[n];
  float alldistance;
  int distance;
  for(int k=0;k<n;k++){
      distancen[k]=getdistance();
      if(distancen[k]==0)
        n--;
      delay(5);
      alldistance+=distancen[k];
    }
    if(n==0)
      distance=max;
    else
      distance=int(alldistance)/n;
      if(distance>max)
          distance=max;
    return distance;
}

初始化函数和主循环

初始化函数中计算正余弦值,以及绑定pwm输出引脚.

void setup(void) {
  u8g2.begin();
  for(int i=0;i<=180;i++){
    siny[i]=sin(float(i)*val);
    cosx[i]=cos(float(i)*val);
  }
  tim1 = timerBegin(0, 80, true);
  timerAttachInterrupt(tim1, tim1Interrupt, true);//更新中断
  timerAlarmWrite(tim1, 10, true);//计数次数10
  timerAlarmEnable(tim1);//开启定时器
  myservo.attach(16);//绑定io16
  pinMode(15,OUTPUT);
  digitalWrite(15,HIGH);
  pinMode(4,OUTPUT);
  digitalWrite(4,LOW);
}

 speed加大能加快转动速度,越大超声波取样点越稀疏,times是每个点采样次数.

void loop(void) {
  u8g2.clearBuffer();
  int distance;
  for(int i=0;i<181;i+=speed)
  { 
    myservo.write(i);
    distance=manydis(times);
    pos[i]=distance;
    showleftpos(distance,i);
  }
  for(int i=180;i>0;i-=speed)
  { 
    myservo.write(i);
    distance=manydis(times);
    pos[i]=distance;
    showrightpos(distance,i);
  }
}

全部代码

#include <Arduino.h>
#include <U8g2lib.h>
#include <math.h>
#include<esp32servo.h>
#define PI 3.1415926535897932384626433832795
#define val PI / 180
#define max 320//扫描范围
#define speed 2//扫描速度
#define times 2//每个点采样次数
Servo myservo;
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);   // All Boards without Reset of the Display
hw_timer_t *tim1 = NULL;
float siny[181];//角度数据
float cosx[181];
int pos[181];//位置数据
int tim1_IRQ_count = 0;

void tim1Interrupt()
{//中断服务函数
  tim1_IRQ_count++;
}
float getdistance(){
  float distance,distance_mm;
  int time_end;
	digitalWrite(15,HIGH);						//输出高电平
	delayMicroseconds(15);										//延时15微秒
	digitalWrite(15,LOW);						//输出低电平
	tim1_IRQ_count=0;
	while(digitalRead(2)==0){
	if(tim1_IRQ_count>800)//取决于测量范围,测量范围越长越大,不够大的话会出现测距返回0
		break;
	}		//等待低电平结束
	tim1_IRQ_count=0;												//计时清零
	while(digitalRead(2)==1);		//等待高电平结束
  
	time_end=tim1_IRQ_count;										//记录结束时的时间

	if(time_end/100<38&&time_end>0)									//判断是否小于38毫秒,大于38毫秒的就是超时,直接调到下面返回0
	{
		distance=(time_end*346)/2;						//计算距离,25°C空气中的音速为346m/s
		distance_mm=distance/100;						//因为上面的time_end的单位是10微秒,所以要得出单位为毫米的距离结果,还得除以100
	}
	return distance_mm;									//返回测距结果
}
void setup(void) {
  u8g2.begin();
  for(int i=0;i<=180;i++){
    siny[i]=sin(float(i)*val);
    cosx[i]=cos(float(i)*val);
  }
  tim1 = timerBegin(0, 80, true);
  timerAttachInterrupt(tim1, tim1Interrupt, true);//更新中断
  timerAlarmWrite(tim1, 10, true);//计数次数10
  timerAlarmEnable(tim1);//开启定时器
  myservo.attach(16);//绑定io16
  pinMode(15,OUTPUT);
  digitalWrite(15,HIGH);
  pinMode(4,OUTPUT);
  digitalWrite(4,LOW);
}

int manydis(int n)//测量取平均值
{ float distancen[n];
  float alldistance;
  int distance;
  for(int k=0;k<n;k++){
      distancen[k]=getdistance();
      if(distancen[k]==0)
        n--;
      delay(5);
      alldistance+=distancen[k];
    }
    if(n==0)
      distance=max;
    else
      distance=int(alldistance)/n;
      if(distance>max)
          distance=max;
    return distance;
}
void showleftpos(int distance,int i){
    int x=64+cosx[i]*(64);
    int y=64-siny[i]*(64);
    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_helvB08_tf);
    for(int j=0;j<i;j++){
      u8g2.drawPixel(64+cosx[j]*(pos[j]/(max/64)),64-siny[j]*(pos[j]/(max/64)));
    }
    u8g2.drawLine(64,64,x,y);
    u8g2.setCursor(0,10);
    u8g2.print(distance);
    u8g2.setCursor(110,10);
    u8g2.print(i);
    u8g2.sendBuffer();
}
void showrightpos(int distance,int i){
    int x=64+cosx[i]*(64);
    int y=64-siny[i]*(64);
    u8g2.clearBuffer();
    u8g2.setFont(u8g2_font_helvB08_tf);
    for(int j=180;j>i;j--){
      u8g2.drawPixel(64+cosx[j]*(pos[j]/(max/64)),64-siny[j]*(pos[j]/(max/64)));
    }
    u8g2.drawLine(64,64,x,y);
    u8g2.setCursor(0,10);
    u8g2.print(distance);
    u8g2.setCursor(110,10);
    u8g2.print(i);
    u8g2.sendBuffer();
}
void loop(void) {
  u8g2.clearBuffer();
  int distance;
  for(int i=0;i<181;i+=speed)
  { 
    myservo.write(i);
    distance=manydis(times);
    pos[i]=distance;
    showleftpos(distance,i);
  }
  for(int i=180;i>0;i-=speed)
  { 
    myservo.write(i);
    distance=manydis(times);
    pos[i]=distance;
    showrightpos(distance,i);
  }
}

项目打包

链接: https://pan.baidu.com/s/1nKj00b3Afr2ZDAGRvugrTg 提取码: 2sir 复制这段内容后打开百度网盘手机App,操作更方便哦

模拟雷达提取码2sir

  • 34
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值