基于STM32F1的环境光与微距离检测系统
环境光与距离检测
一、项目需求
1.开发板: STM32F103ZET6开发板
2. 项目要求:
(1). 当环境光强达到一定值时,蜂鸣器报警提示;
(2). 当距离传感器检测到某一距离前有遮挡时开始不断报警;
(3). 设计Android手机APP,显示环境光信息。
(4). WIFI采用正点原子的ESP8266模块,WIFI这边设置为AP模式,创建TCP服务器,手机端连接ESP8266的WIFI,打开手机APP连接WIFI创建的TCP服务器,接收WIFI发送过来的光强度进行实时显示。
(5). OLED显示屏 实时显示环境光与距离传感器的值,按键切换显示页面
二、Android手机APP编译说明
Android手机APP采用C++ 与QT 框架设计。
核心代码:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("环境光显示");
SetStyle(":/CarControl.qss");
/*1. 实例化 QNetworkAccessManager*/
manager = new QNetworkAccessManager(this);
//设置并打开数据库
if (QSqlDatabase::contains(LOG_IN_DATABASE_CONNECT_NAME))
{
database = QSqlDatabase::database(LOG_IN_DATABASE_CONNECT_NAME);
}
else
{
//数据库类型
database = QSqlDatabase::addDatabase("QSQLITE",LOG_IN_DATABASE_CONNECT_NAME);
database.setDatabaseName(LOG_IN_DATABASE_NAME); //数据库名称
database.setUserName("xl"); //用户名
database.setPassword("123456"); //密码
}
//打开数据库,如果数据库存在就打开,不存在就自动创建
if(database.open()==false)
{
Log_Text_Display("数据库打开失败.请检查程序运行路径和权限.\n");
}
else
{
Log_Text_Display("连接数据库成功.\n");
}
//数据库: 建表,如果存在就不创建,不存在就创建
QSqlQuery sql_query(database);
//下面语句查询名称为CarData 的表是否存在.
sql_query.exec(QString("select count(*) from sqlite_master where type='table' and name='%1'").arg("CarData"));
if(sql_query.next())
{
if(sql_query.value(0).toInt()==0)
{
Log_Text_Display("数据库表是不存在的.准备创建.\n");
//创建表格 创建表格语句:create table <table_name> (f1 type1, f2 type2,…);
/* CREATE TABLE 是告诉数据库系统创建一个新表的关键字。
* CREATE TABLE 语句后跟着表的唯一的名称
* 或标识*/
/*下面的语句: 创建一个名称为CarData的表,字段分别是存放 时间、温度、湿度、经度、纬度*/
QString create_sql = "create table CarData(id int primary key, time varchar(100),light varchar(100))";
sql_query.prepare(create_sql);
if(!sql_query.exec())
{
Log_Text_Display("数据库表创建失败.\n");
}
else
{
Log_Text_Display("数据库表创建成功.\n");
}
}
else
{
Log_Text_Display("数据库表是存在的.不需要创建.\n");
}
}
ui->stackedWidget->setCurrentIndex(0);
}
Widget::~Widget()
{
delete ui;
}
//设置指定样式
void Widget::SetStyle(const QString &qssFile) {
QFile file(qssFile);
if (file.open(QFile::ReadOnly)) {
QString qss = QLatin1String(file.readAll());
qApp->setStyleSheet(qss);
QString PaletteColor = qss.mid(20, 7);
qApp->setPalette(QPalette(QColor(PaletteColor)));
file.close();
}
}
/*
工程: SmartHome
日期: 2021-04-26
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 日志显示
*/
void Widget::Log_Text_Display(QString text)
{
QPlainTextEdit *plainTextEdit_log=ui->plainTextEdit_log;
//设置光标到文本末尾
plainTextEdit_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
//当文本数量超出一定范围就清除
if(plainTextEdit_log->toPlainText().size()>1024*4)
{
plainTextEdit_log->clear();
}
plainTextEdit_log->insertPlainText(text);
//移动滚动条到底部
QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();
if(scrollbar)
{
scrollbar->setSliderPosition(scrollbar->maximum());
}
}
/*
工程: CarControl
日期: 2021-04-27
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 连接服务器
*/
void Widget::on_pushButton_connect_clicked()
{
if(ui->pushButton_connect->text()=="连接"){
//开始连接服务器
NewClinet();
}
else
{
if(LocalTcpClientSocket)
{
LocalTcpClientSocket->close();
}
}
}
//客户端模式:创建客户端
void Widget::NewClinet()
{
if(LocalTcpClientSocket)
{
LocalTcpClientSocket->close();
delete LocalTcpClientSocket;
LocalTcpClientSocket=nullptr;
}
/*1. 创建本地客户端TCP套接字*/
LocalTcpClientSocket = new QTcpSocket;
/*2. 设置服务器IP地址*/
QString Ipaddr=ui->lineEdit_ip->text();
QHostAddress FarServerAddr(Ipaddr);
/*3. 连接客户端的信号槽*/
connect(LocalTcpClientSocket,SIGNAL(connected()),this,SLOT(LocalTcpClientConnectedSlot()));
connect(LocalTcpClientSocket,SIGNAL(disconnected()),this,SLOT(LocalTcpClientDisconnectedSlot()));
connect(LocalTcpClientSocket,SIGNAL(readyRead()),this,SLOT(LocalTcpClientReadDtatSlot()));
/*4. 尝试连接服务器主机*/
int prot=ui->lineEdit_port->text().toInt();
LocalTcpClientSocket->connectToHost(FarServerAddr,prot);
}
//客户端模式:响应连接上服务器之后的操作
void Widget::LocalTcpClientConnectedSlot()
{
ui->pushButton_connect->setText("断开");
Log_Text_Display("成功连接服务器...\n");
}
//客户端模式:断开服务器
void Widget::LocalTcpClientDisconnectedSlot()
{
ui->pushButton_connect->setText("连接");
Log_Text_Display("与服务器断开连接...\n");
}
//客户端模式:读取服务器发过来的数据
//温湿度: #123.66
void Widget::LocalTcpClientReadDtatSlot()
{
QString array=LocalTcpClientSocket->readAll();
QDateTime time = QDateTime::currentDateTime(); //获取系统现在的时间
QString time_str = time.toString("yyyy-MM-dd hh:mm:ss"); //设置显示格式
QString text;
text +=array;
text +="\n";
Log_Text_Display(text);
//解析数据
if(array.at(0)=='#')
{
array.remove('#');
QString T_str=array;
ui->label_light->setFont(QFont("Times", 60, QFont::Bold));
ui->label_light->setAlignment(Qt::AlignHCenter); //水平居中
ui->label_light->setText(T_str+"%");
//保存数据到数据库
QSqlQuery sql_query(database);
//查询最大ID
QString select_max_sql = "select max(id) from CarData";
int max_id = 0;
sql_query.prepare(select_max_sql);
if(!sql_query.exec())
{
Log_Text_Display("数据库最大ID查找失败.\n");
}
else
{
while(sql_query.next())
{
max_id = sql_query.value(0).toInt();
}
Log_Text_Display(QString("data base max id:%1\n").arg(max_id));
//添加数据
//插入数据 插入语句:insert into <table_name> values (value1, value2,…);
QString insert_sql = tr("insert into CarData values(?,?,?)");
sql_query.prepare(insert_sql);
sql_query.addBindValue(max_id+1); //id
sql_query.addBindValue(time_str); //时间
sql_query.addBindValue(T_str); //环境光
if(!sql_query.exec())
{
Log_Text_Display("数据插入失败.\n");
}
}
}
}
/*
工程: AmbientLightDisplay
日期: 2021-05-03
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 历史环境光页面
*/
void Widget::on_pushButton_history_clicked()
{
ui->stackedWidget->setCurrentIndex(1);
}
/*
工程: AmbientLightDisplay
日期: 2021-05-03
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 当前环境光页面
*/
void Widget::on_pushButton_current_clicked()
{
ui->stackedWidget->setCurrentIndex(0);
}
/*
工程: AmbientLightDisplay
日期: 2021-05-03
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 删除全部
*/
void Widget::on_pushButton_del_all_clicked()
{
QString cmd;
cmd = QString("delete from CarData");
QSqlQuery sql_query(database);
if(!sql_query.exec(cmd))
{
Log_Text_Display("delete table Error.\n");
}
}
/*
工程: AmbientLightDisplay
日期: 2021-05-03
作者: DS小龙哥
环境: win10 QT5.12.6 MinGW32
功能: 查看历史数据
*/
void Widget::on_pushButton_ViewHistory_clicked()
{
//指定操作的数据库
QSqlQuery sql_query(database);
//查询全部数据
sql_query.prepare("select * from CarData");
if(!sql_query.exec())
{
Log_Text_Display("数据库查询错误.\n");
}
else
{
while(sql_query.next())
{
// int id = sql_query.value(0).toInt(); //ID
QString time = sql_query.value(1).toString(); //时间
QString T_str = sql_query.value(2).toString(); //光照
QString show_text=QString("%1,%2%\n").arg(time).arg(T_str);
QPlainTextEdit *plainTextEdit_log=ui->plainTextEdit_log;
//设置光标到文本末尾
plainTextEdit_log->moveCursor(QTextCursor::End, QTextCursor::MoveAnchor);
plainTextEdit_log->insertPlainText(show_text);
//移动滚动条到底部
QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();
if(scrollbar)
{
scrollbar->setSliderPosition(scrollbar->maximum());
}
}
}
}
三、Android手机APP效果
四、所需硬件介绍
4.1 STM32F103ZET6开发板
STM32系列专为要求高性能、低成本、低功耗的嵌入式应用设计的ARM Cortex®-M0,M0+,M3, M4和M7内核(ST's product portfolio contains a comprehensive range of microcontrollers, from robust, low-cost 8-bit MCUs up to 32-bit ARM-based Cortex®-M0 and M0+, Cortex®-M3, Cortex®-M4 Flash microcontrollers with a great choice of peripherals. ST has also extended this range to include an ultra-low-power MCU platform) [1] 。
按内核架构分为不同产品:
主流产品(STM32F0、STM32F1、STM32F3)、超低功耗产品(STM32L0、STM32L1、STM32L4、STM32L4+)、高性能产品(STM32F2、STM32F4、STM32F7、STM32H7)
4.2 0.96寸OLED显示屏—SPI接口
OLED显示屏是利用有机电自发光二极管制成的显示屏。由于同时具备自发光有机电激发光二极管,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。
4.3 环境光传感器
这款BH1750环境光传感器内置16位的模数转换器,它能够直接输出一个数字信号,不需要再做复杂的计算。这是一种更精良的和容易使用简易电阻器的版本,通过计算电压,来获得有效的数据。这款环境光传感器能够直接通过光度计来测量。光强度的单位是流明"lx"。当物体在均匀的光照下它能够在每平方米获得1lx的光通量,它们的光强度是1lx。有时为了充分利用光源,你可以增加一个光源的反射装置。那样在某些方向就能获得更多的光通量,以增加被照表面的亮度。
应用
这款传感器主要测量的光强度有:
汽车的车前灯
手电筒
摄影灯光
便携式游戏机
数码相机
调节手机LCD屏和按键区域的背光灯等
技术规格
供电电压:+3-5V
接口:I2C
量程和精度:1~65535 lx
可以选择I2C地址的两种形式
微小的测量变化(+/-20%)
尺寸:0.85x0.63x0.13"(21x16x3.3mm)
光亮度数据参考:
晚上: 0.001-0.02;
月夜: 0.02-0.3;
多云室内: 5-50;
多云室外: 50-500;
晴天室内: 100-1000;
夏天中午光照下: 大约10*6能量;
阅读书籍时的照明度: 50-60;
家庭录像标准照明度:1400
4.4 ESP8266串口WIFI
ESP8266是一款串口WiFi模块,内部集成MCU能实现单片机之间串口同信;这款模块简单易学,体积小,便于嵌入式开发。
根据原理图,把模块连接到开发板上,通过配置开发板串口便可以向ESP8366写入指令,配置模块不同的工作模式;还可以直接使用USB转TTL模块连接,通过串口助手发送相应指令,同样可以配置ESP8266的相关信息以及工作模式,当然也可以读取产品信息。
ESP8266模块我们可以理解成一个单片机带有WiFi功能,我们使用自己单片机进行控制时,只需要让两个单片机相互之间通信,指令相同,执行相应的操作---------就得知道指令是什么,我们知道格式之后便可以把正确信息读出来以及发送的指令ESP8266模块能够正确识别,才能得到我们想要的数据以及实现相应的功能。
性能稳定: ESP8266EX 的工作温度范围大,且能够保持稳定的性能,能适应各种操作环境。
高度集成: ESP8266EX 集成了 32 位 Tensilica 处理器、标准数字外设接口、天线开关、射频 balun、功率放大器、低噪放大器、过滤器和电源管理模块等,仅需很少的外围电路,可将所占 PCB 空间降低。
低功耗: ESP8266EX 专为移动设备、可穿戴电子产品和物联网应用而设计,通过多项专有技术实现了超低功耗。ESP8266EX 具有的省电模式适用于各种低功耗应用场景。
32 位 Tensilica 处理器: ESP8266EX 内置超低功耗 Tensilica L106 32 位 RISC 处理器,CPU 时钟速度最高可达 160 MHz,支持实时操作系统 (RTOS) 和 Wi-Fi 协议栈,可将高达 80% 的处理能力留给应用编程和开发。
4.5 有源蜂鸣器
有源蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。蜂鸣器主要分为压电式蜂鸣器和电磁式蜂鸣器两种类型。蜂鸣器在电路中用字母“H”或“HA”(旧标准用“FM”、“LB”、“JD”等)表示。
有源蜂鸣器和无源蜂鸣器的简易区别方法
有源蜂鸣器和无源蜂鸣器的根本区别是产品对输入信号的要求不一样;有源蜂鸣器工作的理想信号是直流电,通常标示为VDC、VDD等。因为蜂鸣器内部有一简单的振荡电路,能将恒定的直流电转化成一定频率的脉冲信号,从而实现磁场交变,带动铝片振动发音。但是在某些有源蜂鸣器在特定的交流信号下也可以工作,只是对交流信号的电压和频率要求很高,此种工作方式一般不采用。
无源蜂鸣器没有内部驱动电路,有些公司和工厂称为讯响器,国标中称为声响器。无源蜂鸣器工作的理想信号方波。如果给予直流信号蜂鸣器是不响应的,因为磁路恒定,钼片不能振动发音。
4.6 ATK-VL53L0X激光测距模块
VL53L0X是基于意法半导体专利FlightSense技术的第二代激光测距传感器。
VL53L0X完全集成并嵌入一个符合人体安全的激光传感器,先进的滤波器和超快光子探测阵列。
它能够通过快速、准确、可靠的解决方案进行更长距离的测量,为新的应用开启了大门,进一步完善了ST FlightSense产品系列。
主要优势:
[1] 不到30ms即可提供最长2米的绝对距离读值(单位为mm)
[2] 快速模式:50 Hz快速测距操作
[3] 高精度
[4] 低功耗
[5] 4.4×2.4×1 mm回流焊封装
[6] 先进的环境光抑制的设计
[7] 采用940nm不可见光
[8] 需求光学盖片支持
目标应用:
[1] 摄像机辅助(超快速自动对焦和景深图)
[2] 智能手机或笔记本电脑节能检测
[3] 手势控制
[4] 无人机
[5] 机器人和工业控制
[6] IoT
[7] 家用电器
技术
VL53L0X包含基于一个SPAD(单光子雪崩二极管)感应器陈列和一个符合人体一级安规的VCSEL(垂直腔面发射激光器)的集成940nm光源,与嵌入式微控制器上运行的算法一起使用时,可以直接确定以毫米为单位的目标物体的距离,即使是在具有挑战性的工作条件下,并且与目标反射率无关。VL53L0X采用一个超低功耗的系统架构,非常适合用于无线和IoT应用。VL53L0X具有完整的文档包,例如源代码和软件API(应用编程接口),它与广发的微控制器和处理器兼容。
模块设计
凭借其4.4×2.4×1 mm的小巧尺寸和回流焊兼容性,VL53L0X易于集成在产品的主PCB或软板上,可以隐藏在各种盖片材料下。目前VL53L0X是唯一集成了940nm波长VCSEL的产品,为不可见光,也不易受背景环境照明的影响。
系统框图
五、设备端代码
5.1 STM32模块接线说明
接线说明在main函数里已经写好了,照着接。
注意: 因为外接的模块比较多,明确标注的电源接口可能不够用,但是开发板有一排接LCD的IO口和一排Jlink下载器的接口,里面都有电源排针,自己对着原理图找到电源的排针接线就行。
5.2 设备运行效果
APP有Android手机版本和Windows系统版本,我这里使用Windows版本测试,Android是一样的效果。
1. 将各个模块线接好,确保没有问题(不要把电源接错了,接线看清楚)。
2. 编译程序,再下载程序到设备。
3. 打开串口看程序初始化是否正常。
正确打印信息如下: 服务器IP地址是192.168.4.1
此刻在OLED小屏幕上可以看到测量的距离和当前光照强度。
4. 电脑或者手机打开WIFI搜索ESP8266的热点进行连接
然后APP上就会显示收到的环境光信息。
注意注意:IP地址要改为192.168.4.1 端口号: 8080
如果运行一段时间发现环境光没有更新,只需要断开服务器再次连接即可。
5.3 STM32的mian.c代码
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
#include <string.h>
#include "timer.h"
#include "esp8266.h"
#include "oled.h"
#include "fontdata.h"
#include "bh1750.h"
#include "iic.h"
#include "vl53l0x.h"
/*
硬件连接方式:
ESP8266串口WIFI模块:
PB10--RXD 模块接收脚
PB11--TXD 模块发送脚
GND---GND 地
VCC---VCC 电源(3.3V~5.0V)
OLED显示屏接线:
D0----SCK-----PB14
D1----MOSI----PB13
RES—复位(低电平有效)—PB12
DC---数据和命令控制管脚—PB1
CS---片选引脚-----PA7
GND---GND 地
VCC---VCC 电源(3.3V)
蜂鸣器: PA6 (高电平响)
GND---GND 地
VCC---VCC 电源(5.0V)
IO----PA6
BH1750环境光检测模块:
SDA-----PB7
SCL-----PB6
GND---GND 地
VCC---VCC 电源(3.3V~5.0V)
ATK-VL53L0X激光测距模块:
XSH----PA15
INT----中断脚(没有使用)
SCL---PC11
SDA---PC10
GND---GND 地
VCC---VCC 电源(3.3V~5.0V)
板载的设备:
LED硬件连接: PB5 PE5
KEY硬件连接:PE3 PE4
*/
#define ESP8266_WIFI_AP_SSID "ESP8266_STM32" //创建的热点名称--不要出现中文、空格等特殊字符
#define ESP8266_AP_PASSWORD "12345678" //创建的热点密码
char esp8266_message[200];//上传数据缓存区
char OLED_ShowBuff[100];
u8 ESP8266_Stat=0;
/*
函数功能: 显示光强度、测量距离
*/
void ShowLightDistance(float light,vu16 distance)
{
sprintf(OLED_ShowBuff,"L: %.2f%%",light);
OLED_ShowString(20,16*1,16,OLED_ShowBuff);
sprintf(OLED_ShowBuff,"D: %dMM",distance);
OLED_ShowString(20,16*2,16,OLED_ShowBuff);
}
/*
函数功能: ESP8266显示页面
*/
void ESP8266_ShowPageTable(void)
{
if(ESP8266_Stat)OLED_ShowString(0,16*0,16,"WIFI STAT:ERROR");
else OLED_ShowString(0,16*0,16,"WIFI STAT:OK");
//显示字符串
sprintf((char*)OLED_ShowBuff,"%s",ESP8266_WIFI_AP_SSID);
OLED_ShowString(0,16*1,16,OLED_ShowBuff);
sprintf((char*)OLED_ShowBuff,"%s",ESP8266_AP_PASSWORD);
OLED_ShowString(0,16*2,16,OLED_ShowBuff);
}
int main()
{
u32 time_cnt=0;
u8 key;
u8 page=0;
float light=0; //环境光
vu16 Distance_data=0;//距离
delay_ms(1000);
delay_ms(1000);
LED_Init();
KEY_Init();
IIC_Init();
BEEP_Init();
//距离传感器
vl53l0x_init(&vl53l0x_dev);//vl53l0x初始化
vl53l0x_adjust(&vl53l0x_dev); //校准
vl53l0x_set_mode(&vl53l0x_dev,Default_Mode);//配置测量模式
//OLED初始化
OLED_Init(0xc8,0xa1); //OLED显示屏初始化--正常显示;
//清屏
OLED_Clear(0);
USART1_Init(115200);
TIMER1_Init(72,20000); //超时时间20ms
USART3_Init(115200);//串口-WIFI
TIMER3_Init(72,20000); //超时时间20ms
USART1_Printf("正在初始化WIFI请稍等.\n");
if(ESP8266_Init())
{
ESP8266_Stat=1;
USART1_Printf("ESP8266硬件检测错误.\n");
}
else
{
ESP8266_Stat=ESP8266_AP_TCP_Server_Mode(ESP8266_WIFI_AP_SSID,ESP8266_AP_PASSWORD,8080);
if(ESP8266_Stat)
{
USART1_Printf("WIFI配置错误,状态码:%d\r\n",ESP8266_Stat);
}
else
{
USART1_Printf("WIFI配置成功.\r\n");
}
}
while(1)
{
//按键
key=KEY_Scan(0);
if(key==1)
{
//清屏
OLED_Clear(0);
//翻页
if(page>=1)
{
page=0;
}
else
{
page++;
}
LED1=!LED1; //LEd状态灯
}
else if(key==2)
{
LED1=!LED1; //LEd状态灯
time_cnt=0;
}
//接收WIFI返回的数据
if(USART3_RX_FLAG)
{
USART3_RX_BUFFER[USART3_RX_CNT]='\0';
//USART1_Printf("USART3_RX_BUFFER:%s\n",USART3_RX_BUFFER);
USART3_RX_CNT=0;
USART3_RX_FLAG=0;
}
//定时传输给app上位机
delay_ms(10);
time_cnt++;
if(time_cnt==30)
{
time_cnt=0;
//状态灯 --表示程序还活着
LED2=!LED2;
//读取光强度
light=Read_BH1750_Data();
// #123.66
sprintf(esp8266_message,"#%0.2f",light);
//向手机APP上传数据
ESP8266_ServerSendData(0,(u8*)esp8266_message,strlen((char*)esp8266_message));
Distance_data=vl53l0x_general_start(&vl53l0x_dev);//执行一次测量
printf("d: %4imm\r\n",Distance_data);//打印测量距离
//当距离小于100mm 时,开启蜂鸣器 否则关闭
if(Distance_data<100)
{
BEEP=1;
}
else
{
BEEP=0;
}
//当光强大于指定值,蜂鸣器响 否则关闭
if(light>2000)
{
BEEP=1;
}
else
{
BEEP=0;
}
}
//OLED显示屏
if(page==0)
{
ShowLightDistance(light,Distance_data);
}
else if(page==1)
{
ESP8266_ShowPageTable();
}
}
}
- 点赞
- 收藏
- 关注作者
评论(0)