一、项目概述
随着电子商务的迅猛发展,快递取件的智能化和便捷性需求日益增长。本项目旨在设计一款基于STM32F103C8T6单片机的扫码取件系统,结合语音播报模块、WiFi模块、显示模块、舵机控制电路和按键电路,实现高效、智能的取件功能。用户通过扫描二维码即可快速取件,同时系统通过语音提示和LCD显示提供友好的用户体验。
技术栈关键词
-
单片机: STM32F103C8T6
-
无线通信模块: ESP8266
-
显示模块: LCD1602
-
舵机控制: PWM技术
-
语音播报模块: DFPlayer Mini
-
按键输入: 机械按键
-
数据记录: 数据库(SQLite或MySQL)
二、系统架构
本项目的系统架构设计围绕扫码取件的核心功能展开,合理划分各个功能模块并确保它们之间的有效通信。以下是系统组件及其交互关系的架构图。
组件选择
-
单片机: STM32F103C8T6,具备丰富的外设接口和强大的处理能力。
-
WiFi模块: ESP8266,用于实现与云服务的通信。
-
显示模块: LCD1602,提供用户友好的界面显示。
-
舵机控制电路: 使用PWM信号控制舵机的开关,方便实现物品的取出。
-
语音播报模块: DFPlayer Mini,用于实现语音提示功能。
-
数据记录: 使用SQLite或MySQL数据库记录取件信息,便于管理和追踪。
三、环境搭建和注意事项
开发环境
-
IDE: STM32CubeIDE,支持STM32系列开发的集成开发环境。
-
库: STM32 HAL库、ESP8266库、LCD库、DFPlayer库。
-
调试工具: ST-Link V2,便于对STM32进行烧录和调试。
注意事项
-
电源管理: 确保各模块的供电稳定,避免因电源不足导致模块故障。
-
信号干扰: 在设计电路时,尽量避免信号线与电源线并行放置,以减少干扰。
-
模块兼容性: 在选择模块时,确保它们的电压和通信协议兼容。
-
安全性考虑: 需要在系统中引入用户身份验证和数据加密措施,确保系统安全。
四、代码实现过程
1. 系统初始化
在系统初始化过程中,使用HAL库对各个模块进行初始化,包括串口、WiFi、舵机和显示模块。这一过程是确保系统正常工作的前提。
#include "main.h"
// 初始化系统各模块
void System_Init(void) {
HAL_Init(); // 初始化HAL库
SystemClock_Config(); // 配置系统时钟
MX_GPIO_Init(); // 初始化GPIO
MX_USART2_UART_Init(); // 初始化USART,用于与ESP8266通信
MX_I2C1_Init(); // 初始化I2C,用于LCD
WiFi_Init(); // 初始化WiFi模块
Servo_Init(); // 初始化舵机
Voice_Init(); // 初始化语音模块
LCD_Init(); // 初始化LCD模块
}
2. 扫码取件逻辑
实现扫码取件的核心逻辑,用户通过扫码输入取件信息,系统进行舵机控制并播报语音提示。以下是主要的实现步骤:
#include "wifi.h"
#include "servo.h"
#include "voice.h"
#include "lcd.h"
#include "database.h"
// 扫描取件
void Scan_Item(void) {
char qr_code[20]; // 存储二维码信息
if (Get_QR_Code(qr_code)) { // 获取二维码信息
Unlock_Servo(); // 控制舵机开启
Play_Voice("取件成功,请取走物品"); // 播放语音提示
LCD_Show(qr_code); // 在LCD上显示二维码信息
Record_Item(qr_code); // 记录取件信息到数据库
} else {
Play_Voice("扫码失败,请重试"); // 播放错误提示
LCD_Show("扫码失败"); // 在LCD上显示错误信息
}
}
代码说明
-
Get_QR_Code(qr_code)
: 函数用于获取用户扫描的二维码信息,返回值为true则表示扫码成功。 -
Unlock_Servo()
: 函数用于控制舵机打开,允许用户取走物品。 -
Play_Voice("取件成功,请取走物品")
: 播放成功取件的语音提示。 -
LCD_Show(qr_code)
: 在LCD屏幕上显示扫描到的二维码信息,方便用户确认。 -
Record_Item(qr_code)
: 将二维码信息记录到数据库,便于后续的管理和追踪。 -
如果扫码失败,则系统会给出相应的语音和LCD提示。
3. 舵机控制模块
舵机控制模块负责根据扫码结果控制舵机的开关,以便用户能够取走物品。
#include "servo.h"
// 舵机初始化
void Servo_Init(void) {
// 配置TIM和舵机控制引脚
// 假设使用TIM2进行PWM控制
TIM_HandleTypeDef htim2;
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72 - 1; // 设置预分频
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 20000 - 1; // 20ms周期
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim2);
// 配置PWM通道
TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 1500; // 初始位置为中立
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 启动PWM
}
// 解锁舵机
void Unlock_Servo(void) {
// 旋转舵机到打开位置
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 2000); // 2ms脉冲,舵机打开
HAL_Delay(3000); // 保持3秒
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 1000); // 1ms脉冲,舵机回到关闭位置
}
代码说明
-
Servo_Init()
: 初始化舵机控制,配置定时器和PWM通道,以控制舵机的旋转。 -
Unlock_Servo()
: 通过设置PWM的脉冲宽度来控制舵机的开关。2000
微秒对应舵机的打开位置,1000
微秒对应关闭位置。
4. 语音播报模块
语音播报模块负责将系统状态通过语音提示给用户。
#include "voice.h"
#include "software_timer.h"
// DFPlayer Mini的串口句柄
extern UART_HandleTypeDef huart2;
// 初始化语音模块
void Voice_Init(void) {
// DFPlayer Mini初始化代码
// 假设通过串口与DFPlayer Mini通信
HAL_UART_Transmit(&huart2, (uint8_t *)"DFPLAY", 6, HAL_MAX_DELAY); // 发送初始化命令
}
// 播放语音文件
void Play_Voice(const char* voice_file) {
char command[20];
snprintf(command, sizeof(command), "PLAY %s", voice_file); // 格式化播放命令
HAL_UART_Transmit(&huart2, (uint8_t *)command, strlen(command), HAL_MAX_DELAY); // 发送播放命令
}
// 中断回调,DFPlayer Mini的响应处理
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART2) {
// 处理DFPlayer Mini的响应
// 这里可以解析并处理DFPlayer Mini返回的数据
}
}
代码说明
-
Voice_Init()
: 初始化语音模块,发送DFPlayer Mini的初始化命令以确保模块准备就绪。 -
Play_Voice(const char* voice_file)
: 根据传入的文件名格式化播放命令并通过UART发送给DFPlayer Mini。 -
HAL_UART_RxHalfCpltCallback()
: UART接收中断回调函数,用于处理DFPlayer Mini的返回响应。可以在这里解析和处理DFPlayer Mini的状态信息。
5. LCD显示模块
LCD显示模块负责显示取件信息、错误提示等。
#include "lcd.h"
// 初始化LCD模块
void LCD_Init(void) {
// 初始化LCD控制引脚和配置
HAL_Delay(15); // 等待LCD启动
LCD_Send_Command(0x38); // 设置为8位模式
LCD_Send_Command(0x0C); // 开启显示,不显示光标
LCD_Send_Command(0x01); // 清屏
HAL_Delay(2); // 清屏延迟
}
// 发送命令到LCD
void LCD_Send_Command(uint8_t cmd) {
HAL_GPIO_WritePin(LCD_RS_GPIO_Port, LCD_RS_Pin, GPIO_PIN_RESET); // RS设置为低
HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_SET); // 使能高
HAL_Delay(1); // 确保稳定
// 发送高四位
HAL_GPIO_WritePin(LCD_D4_GPIO_Port, LCD_D4_Pin, (cmd & 0x10) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_D5_GPIO_Port, LCD_D5_Pin, (cmd & 0x20) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_D6_GPIO_Port, LCD_D6_Pin, (cmd & 0x40) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_D7_GPIO_Port, LCD_D7_Pin, (cmd & 0x80) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_RESET); // 使能低
HAL_Delay(1);
// 发送低四位
HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(LCD_D4_GPIO_Port, LCD_D4_Pin, (cmd & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_D5_GPIO_Port, LCD_D5_Pin, (cmd & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_D6_GPIO_Port, LCD_D6_Pin, (cmd & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_D7_GPIO_Port, LCD_D7_Pin, (cmd & 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_RESET); // 使能低
HAL_Delay(1);
}
// 显示字符串
void LCD_Show(const char* str) {
LCD_Send_Command(0x01); // 清屏
HAL_Delay(2); // 清屏延迟
HAL_GPIO_WritePin(LCD_RS_GPIO_Port, LCD_RS_Pin, GPIO_PIN_SET); // RS设置为高,进入数据模式
while (*str) {
HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_SET); // 使能高
// 发送字符数据
LCD_Send_Data(*str++);
HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_RESET); // 使能低
HAL_Delay(1);
}
}
// 发送数据到LCD
void LCD_Send_Data(uint8_t data) {
// 发送高四位
HAL_GPIO_WritePin(LCD_D4_GPIO_Port, LCD_D4_Pin, (data & 0x10) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_D5_GPIO_Port, LCD_D5_Pin, (data & 0x20) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_D6_GPIO_Port, LCD_D6_Pin, (data & 0x40) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_D7_GPIO_Port, LCD_D7_Pin, (data & 0x80) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_SET); // 使能高
HAL_Delay(1);
HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_RESET); // 使能低
HAL_Delay(1);
// 发送低四位
HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_SET);
HAL_GPIO_WritePin(LCD_D4_GPIO_Port, LCD_D4_Pin, (data & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_D5_GPIO_Port, LCD_D5_Pin, (data & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_D6_GPIO_Port, LCD_D6_Pin, (data & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_D7_GPIO_Port, LCD_D7_Pin, (data & 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_E_GPIO_Port, LCD_E_Pin, GPIO_PIN_RESET); // 使能低
HAL_Delay(1);
}
代码说明
-
LCD_Show(const char* str)
: 函数用于在LCD上显示字符串。首先清屏,然后逐字符发送数据到LCD。 -
LCD_Send_Data(uint8_t data)
: 将字符数据发送到LCD的函数,分为高四位和低四位分别发送。
6. 数据记录模块
数据记录模块负责将每次取件的信息记录到数据库,以便后续管理和查询。
#include "database.h"
#include "sqlite3.h"
// 数据库句柄
sqlite3 *db;
// 数据库初始化
void Database_Init(void) {
int rc = sqlite3_open("items.db", &db); // 打开或创建数据库
if (rc) {
LCD_Show("数据库打开失败"); // 显示错误信息
return;
}
const char *sql = "CREATE TABLE IF NOT EXISTS records (id INTEGER PRIMARY KEY AUTOINCREMENT, qr_code TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)";
char *errMsg;
rc = sqlite3_exec(db, sql, 0, 0, &errMsg);
if (rc != SQLITE_OK) {
LCD_Show("创建表失败"); // 显示错误信息
sqlite3_free(errMsg);
}
}
// 记录取件信息
void Record_Item(const char* qr_code) {
char sql[256];
snprintf(sql, sizeof(sql), "INSERT INTO records (qr_code) VALUES ('%s')", qr_code);
char *errMsg;
int rc = sqlite3_exec(db, sql, 0, 0, &errMsg);
if (rc != SQLITE_OK) {
LCD_Show("记录失败"); // 显示错误信息
sqlite3_free(errMsg);
}
}
// 关闭数据库
void Database_Close(void) {
if (db) {
sqlite3_close(db); // 关闭数据库连接
}
}
代码说明
-
Database_Init()
: 初始化数据库,打开或创建数据库文件,并创建记录表(如果不存在)。如果发生错误,会在LCD上显示相应的错误信息。 -
Record_Item(const char* qr_code)
: 将每次取件的二维码信息插入到数据库中。若插入失败,则在LCD上显示错误信息。 -
Database_Close()
: 关闭数据库连接,确保释放资源。
7. 主函数
在主函数中,我们将调用初始化函数,并循环监听按键和扫码事件。
#include "main.h"
int main(void) {
System_Init(); // 初始化系统
Database_Init(); // 初始化数据库
while (1) {
if (Is_Scan_Event()) { // 检测是否有扫码事件
Scan_Item(); // 处理扫码取件
}
if (Is_Button_Pressed()) { // 检测按键输入
// 处理按键逻辑,例如重新扫码、查看记录等
LCD_Show("按键被按下"); // 示例响应
}
}
Database_Close(); // 关闭数据库
}
代码说明
-
System_Init()
: 调用系统初始化函数,设置所有模块。 -
Database_Init()
: 调用数据库初始化函数,准备数据存储。 -
Is_Scan_Event()
: 伪函数,用于检测扫码事件(实际实现需根据扫码模块的具体情况)。 -
Is_Button_Pressed()
: 伪函数,用于检测按键输入(实际实现需根据按键模块的具体情况)。 -
Scan_Item()
: 调用扫码取件处理函数。 -
Database_Close()
: 在程序退出前关闭数据库,释放资源。
五、项目总结
本项目实现了一个基于STM32F103C8T6单片机的扫码取件智能系统,主要功能包括:
-
扫码取件: 用户通过扫描二维码获取物品,系统实时控制舵机开启取件口,并通过语音播报提供取件反馈。
-
语音提示: 系统通过DFPlayer Mini模块播报取件状态,增强用户体验。
-
LCD显示: 实时在LCD上显示扫码结果和系统状态,提供直观的信息反馈。
-
数据记录: 通过SQLite数据库记录每次取件的信息,便于管理和追踪。