基于STM32F103的二维码识别项目——ESP01-8266篇

无线手持二维码识别项目是中山大学电子与信息工程学院(微电子学院)的工程应用训练课程的设计要求。项目是基于STM32F103开发的,开源代码:stm32f103zet6-qrcode-detect,bilibili:基于STM32F1的无线手持二维码识别项目。CSDN总目录:基于STM32F103的二维码识别项目。项目移植了Zbar库进行二维码识别,而非使用二维码识别模块。

此篇文章讲述ESP01模块相关的设计。

文章目录

为了实现检测到二维码后将二维码的信息传到上位机,我使用了ESP01S模块,核心思想为ESP01S开启热点,建立web服务,电脑端通过连接热点访问相应的地址从而获得数据。

注意有两款,博主使用的是ESP-01.

在这里插入图片描述

一、 开发环境搭建

进入Arduino IDE 2.3.2安装软件,之后打开软件,文件–>首选项…

在这里插入图片描述

复制http://arduino.esp8266.com/stable/package_esp8266com_index.json到下方位置,然后确定。

在这里插入图片描述

重启IDE后,工具–>开发板–>开发板管理器…

在这里插入图片描述

搜索ESP8266进行安装,安装失败就用梯子。

在这里插入图片描述

按照下面的选项选择。

在这里插入图片描述

二、 ESP8266代码详解

我们的核心思想是在ESP8266上创建一个WiFi热点和Web服务器。它通过串口接收数据,并在客户端请求时通过Web接口提供这些数据。这样,用户可以通过连接到ESP8266创建的WiFi热点并访问其IP地址,获取串口接收到的数据。


const char *ssid = "ESP8266_HOTSPOT";
const char *password = "lpflpflpf";

ssid:定义WiFi热点的名称。

password:定义WiFi热点的密码。


ESP8266WebServer server(80);
String data;

server:创建一个Web服务器实例,监听80端口。

data:用于存储从串口接收到的数据。


void handleRoot() {
  server.send(200, "text/plain", data);
  data = ""; // 清空数据
}

handleRoot() 函数用于处理对 Web 服务器根路径(“/”`)的请求。当客户端请求根路径时,将调用此函数来生成响应。

总体而言,handleRoot() 函数会向客户端发送一个 HTTP 响应,其中包含 data 变量中的数据。

1. server.send(200, “text/plain”, data); 这行代码用于向客户端发送 HTTP 响应。它接受三个参数:

  • statusCode: HTTP 状态码,指示请求是否成功。在此示例中,使用 200 表示请求成功。
  • contentType: 响应内容的类型。在此示例中,使用 "text/plain" 表示响应内容是纯文本。
  • data: 要发送给客户端的数据。在此示例中,使用 data 变量存储的数据。

2. data = ""; // 清空数据 这行代码用于清空 data 变量。


void setup() {
  Serial.begin(115200); // 初始化硬件串口
  
  // 设置热点模式
  WiFi.softAP(ssid, password);
  IPAddress IP = WiFi.softAPIP();
  Serial.print("AP IP address: ");
  Serial.println(IP);

  // 设置Web服务器的路由
  server.on("/", handleRoot);
  server.begin();
  Serial.println("HTTP server started");
}

核心思想即将ESP8266设置为 Wi-Fi 热点并启动一个 Web 服务器,可由连接到网络的设备访问。 Web 服务器的具体功能将取决于 handleRoot() 函数的实现。

1. 初始化串口通信:

  • 首先使用 Serial.begin() 函数初始化硬件串口,并将波特率设置为 115200 bps。

2. 创建 Wi-Fi 热点(AP):

WiFi.softAP() 函数用于将微控制器配置为 Wi-Fi 热点。这允许其他设备无线连接到ESP8266。该函数接受两个参数:

  • ssid:Wi-Fi 网络的 SSID(服务设置标识符),即扫描可用网络时显示给设备的名称。
  • password:Wi-Fi 网络的密码,连接设备需要该密码。

使用 WiFi.softAPIP() 函数获取 Wi-Fi 热点的 IP 地址,并使用 Serial.print()Serial.println() 将其打印到串口监视器中。可以提供了可用于访问ESP8266上托管的 Web 服务器的 IP 地址。

3. 启动 Web 服务器:

使用 server 变量创建了一个 HTTP 服务器对象的实例。server.on("/", handleRoot) 函数定义了 Web 服务器的路由,指定当收到对根路径(“/”)的请求时,应调用handleRoot()` 函数来处理请求。

server.begin() 函数在 Wi-Fi 热点的 IP 地址上启动 Web 服务器。这使得连接到网络的设备可以访问 Web 服务器。最后,使用 Serial.println() 将指示 Web 服务器已启动的消息打印到串口监视器中。


void loop() {
  server.handleClient();

  // 接收串口数据
  if (Serial.available()) {
    data = Serial.readStringUntil('\n');
    Serial.print("Received data: ");
    Serial.println(data);
  }
}

loop() 函数会不断循环执行以下操作:检查是否有连接到 Web 服务器的客户端,并处理来自客户端的请求。检查是否有数据可从串口读取。如果有数据可从串口读取,则将数据读入字符串变量并打印到串口监视器上。

1.处理 Web 服务器客户端:

  • server.handleClient(); 这行代码用于处理连接到 Web 服务器的客户端。它会检查是否有任何新的客户端连接请求,并处理来自已连接客户端的任何数据。

2. 接收串口数据 (Serial Communication):

  • if (Serial.available()) { … }, 这部分代码用于检查是否有数据可从串口读取。

  • Serial.available() 函数会检查串口缓冲区中是否有可用的字节。

  • 如果有可用字节(即 Serial.available() 返回 true),则程序会继续执行 if 语句内部的代码。

  • data = Serial.readStringUntil(‘\n’); 这行代码从串口读取数据并将其存储在 data字符串变量中。

    • Serial.readStringUntil('\n') 函数会一直读取数据,直到遇到换行符 (‘\n’) 为止。
    • 读取到的所有数据将被作为一个字符串存储在 data 变量中。
  • Serial.print("Received data: "); 和 Serial.println(data); 这两行代码用于将接收到的数据打印到串口监视器上。

    • Serial.print() 函数打印一条消息。
    • Serial.println() 函数打印一条消息并添加一个换行符。

在这里插入图片描述

三、 上位机代码详解

核心思想:创建了一个使用 PyQt5 的简单 GUI 应用程序,用于从指定的 URL 获取数据并显示在界面上。程序包含两个主要类:`DataFetcher` 和 `MainWindow`。最后使用pyinstaller封装为一个exe文件。

DataFetcher 类继承自 QThread,用于在后台线程中定期发送 HTTP 请求获取数据。它有两个信号:status_updatedata_update,分别用于更新连接状态和数据内容。在 run 方法中,线程每隔 2 秒发送一次 HTTP 请求,如果连接成功且有数据返回,则发出相应的信号更新主界面;如果连接失败或无数据,则发出错误信号。stop 方法用于停止线程的运行。

MainWindow 类继承自 QWidget,负责初始化和管理 GUI 界面。它的 initUI 方法设置了窗口标题,并添加了一个垂直布局,其中包括一个显示连接状态的标签、一个控制数据显示的复选框、一个只读的文本编辑框用于显示数据,以及一个导出数据的按钮。update_status 方法用于更新连接状态标签的文本,update_data 方法用于在复选框选中时将数据追加到文本编辑框中,toggle_data_display 方法用于显示或隐藏数据,export_data 方法将文本编辑框中的数据导出到 data_output.txt 文件中。closeEvent 方法在窗口关闭时停止数据获取线程。

程序的入口部分创建了 QApplication 实例并运行应用程序。实现了一个简单的数据获取和显示功能,并提供了导出数据的选项。通过这种方式,用户可以实时监控从指定 URL 获取的数据,并根据需要导出这些数据。

import sys
import requests
import time
from PyQt5.QtWidgets import (QApplication, QWidget, QVBoxLayout, QLabel, QPushButton, QTextEdit, QCheckBox)
from PyQt5.QtCore import QThread, pyqtSignal

sys:处理系统相关的操作,比如退出应用程序。

requests:用于发送HTTP请求。

time:提供时间相关的功能,如睡眠。

PyQt5.QtWidgets:包含各种PyQt5的GUI组件。

PyQt5.QtCore:包含核心的PyQt5功能,如信号和线程。

class DataFetcher(QThread):
    status_update = pyqtSignal(str)
    data_update = pyqtSignal(str)

    def __init__(self, url):
        super().__init__()
        self.url = url
        self.running = True

    def run(self):
        while self.running:
            try:
                response = requests.get(self.url)
                if response.status_code == 200:
                    self.status_update.emit("连接成功")
                    data = response.text.strip()
                    if data:
                        self.data_update.emit(f"检测结果为:{data}")
                    else:
                        self.data_update.emit("无结果")
                else:
                    self.status_update.emit("无结果")
            except requests.ConnectionError:
                self.status_update.emit("连接失败")
            time.sleep(2)

    def stop(self):
        self.running = False

DataFetcher继承自QThread,用于在后台线程中获取数据。

status_updatedata_update是自定义信号,用于更新GUI。

__init__:初始化线程,设置URL和运行状态。

run:主线程循环,定期(每2秒)发送HTTP请求,并根据响应更新状态和数据。

stop:停止线程。

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.initUI()
        self.data_fetcher = DataFetcher("http://192.168.4.1/")
        self.data_fetcher.status_update.connect(self.update_status)
        self.data_fetcher.data_update.connect(self.update_data)
        self.data_fetcher.start()

    def initUI(self):
        self.setWindowTitle("数据获取")

        self.layout = QVBoxLayout()

        self.status_label = QLabel("连接中……")
        self.layout.addWidget(self.status_label)

        self.data_check = QCheckBox("显示数据")
        self.data_check.stateChanged.connect(self.toggle_data_display)
        self.layout.addWidget(self.data_check)

        self.data_display = QTextEdit()
        self.data_display.setReadOnly(True)
        self.data_display.setVisible(False)
        self.layout.addWidget(self.data_display)

        self.export_button = QPushButton("导出数据")
        self.export_button.clicked.connect(self.export_data)
        self.layout.addWidget(self.export_button)

        self.setLayout(self.layout)

    def update_status(self, status):
        self.status_label.setText(status)

    def update_data(self, data):
        if self.data_check.isChecked():
            self.data_display.append(data)

    def toggle_data_display(self, state):
        self.data_display.setVisible(state)

    def export_data(self):
        with open("data_output.txt", "w") as file:
            file.write(self.data_display.toPlainText())

    def closeEvent(self, event):
        self.data_fetcher.stop()
        self.data_fetcher.wait()
        event.accept()

MainWindow继承自QWidget,表示主窗口。

__init__:初始化窗口,设置UI和数据获取线程。

initUI:创建并布局GUI组件,包括状态标签、显示数据复选框、数据显示区域和导出按钮。

update_status:更新状态标签。

update_data:在数据复选框被选中的情况下,更新数据显示区域。

toggle_data_display:根据复选框状态显示或隐藏数据区域。

export_data:将数据导出到文件。

closeEvent:在关闭窗口时停止数据获取线程。

在这里插入图片描述

四、 STM32程序详解

//esp32
void USART2_Init(void) {
    // 1. Enable the clock for USART2 and GPIOA
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 2. Configure PA2 as USART2 TX (Alternate Function Push Pull)
    GPIO_InitTypeDef GPIO_InitStruct;
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 3. Configure PA3 as USART2 RX (Input, Floating)
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);

    // 4. Configure USART2 settings
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_Init(USART2, &USART_InitStructure);

    // 5. Enable USART2
    USART_Cmd(USART2, ENABLE);
}

STM32微控制器上初始化USART2外设,用于串行通信。上面的配置实现了:启用USART2和GPIOA的时钟,配置PA2为USART2的TX引脚(推挽复用功能),配置PA3为USART2的RX引脚(浮空输入模式),设置USART2的通信参数(波特率115200、字长8、停止位0、校验方式无、通信模式和硬件流控制),最后使能USART2。

void Serial_SendByte2(uint8_t Byte)
{
	USART_SendData(USART2, Byte);
	while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
}

void Serial_SendArray2(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)
	{
		Serial_SendByte2(Array[i]);
	}
}

void Serial_SendString2(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)
	{
		Serial_SendByte2(String[i]);
	}
}

接下来的这些函数定义实现了发送字符串的函数,之后在主函数调用即可。

Serial_SendString2((char* )data);

这个data就是调用Zbar库识别后的结果。

五、 无烧录器烧录程序进ESP8266

注意在Arduino IDE.exe写入二中展示的代码后准备烧录进ESP8266。

建议购买烧录器直接烧录,十分方便。直接对着插上,点击烧录即可。

在这里插入图片描述

但是,我当时没有买烧录器,只有USB转串口,怎么办?

ESP8266STM32
3.3V3.3V
RST不接线
EN3.3V
TXRX
RXTX
IO0GND
IO2不接线
GNDGND

首先,我们现需要动的是两根线,3.3V和IO0,3.3V拔掉相当于ESP8266重启。第一步,按照上面的进行接线,然后3.3V的先别接,这个时候注意IO0接地连了,然后,按上面的箭头按钮准备烧录,当看到*Connect…*的时候,迅速把3.3V插上。

在这里插入图片描述

然后,等待烧录成功。

在这里插入图片描述

然后把3.3V和IO0都拔掉,再把3.3V插上即可正常工作了(正常工作保持IO0悬空)。

简单来说:IO0接地进入烧录模式,IO0悬空正常工作,每次进入模式需要关电重启。

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值