前情提要
在逛淘宝的时候发现超声波模拟雷达居然敢卖这么贵,想了一下代码也不是很难,于是就写着试了下.
由于使用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
通过对于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