[i=s] 本帖最后由 WT_0213 于 2025-3-27 20:18 编辑 [/i]
在当下快节奏的现代生活中,电子 DIY 早已不单单局限于技术层面的探索,更是一种能为生活增添无限乐趣的独特生活态度。留意到此次活动提交作品的截止时间为 4 月 1 日,此前就一直想着给小朋友做一个小盒子,正好借这个机会忙里偷闲参与到活动当中,再体验一下电子 DIY 的乐趣。
活动地址:
第四期电子DIY | 实用至上,点亮便利生活
https://bbs.ai-thinker.com/forum.php?mod=viewthread&tid=45819&_dsign=c374dce6
我制作了一个稍微实用价值的指纹开锁小盒子,旨在为日常生活中的物品收纳提供更安全、便捷的解决方案。
一、设计思路
考虑到日常小物件收纳时,常担心隐私物品被他人随意翻看,便决定打造一个通过指纹识别控制开合的小盒子。利用指纹识别的唯一性,实现精准的开锁权限管理,让只有授权指纹的使用者能够打开盒子,极大提升了收纳物品的安全性。这个小盒子不仅是收纳小物件的容器,更是守护孩子隐私与专属空间的好帮手,能有效避免小朋友的宝贝被随意翻动。
二、设计理念
小朋友都有自己珍视的小玩意儿,像心爱的贴纸、秘密小纸条等,他们渴望有个安全的小天地存放这些宝贝。考虑到孩子对新奇事物的好奇心,传统锁具操作复杂,而指纹识别既有趣又便捷,凭借独一无二的指纹特征,能精准识别孩子身份,为控制盒子开合提供绝佳方式。基于此,我决心制作这款指纹开锁小盒子,给孩子打造专属且安全的收纳空间,让他们享受自主管理物品的乐趣。
三、制作流程
硬件选型

选用之前购买的 FPM383C 指纹模块,它识别精度高、响应快,小朋友轻轻一按手指,就能快速准确采集指纹信息。主控芯片选用 [主控芯片型号],其主要负责处理 FPM383C 指纹模块传来的数据,并依据结果向电机驱动模块以及其他硬件下达精准指令。
此次主控制器使用的是M61-32SU开发板,它在整个系统里扮演着重要角色。M61-32SU开发板能够高效协调主控芯片与各个模块间的信息交互,进一步优化系统运行的稳定性与流畅度,保障指纹识别、指令传输等环节有序进行。
另外,选用了一个 9g 舵机来作为盒子锁扣的直接执行者。9g 舵机体积小巧,动力输出却较为稳定,通过接收主控芯片的指令后,能精准控制锁扣的开合动作。当接收到开锁指令,舵机迅速转动,打开锁扣;锁定指令下达时,舵机反向转动,紧闭锁扣,为小盒子的开合提供了可靠的机械动力支持。
原本计划用 3D 打印技术制作盒子外壳,精心设计好了模型,满心期待能打印出一个造型酷炫、结构精巧的外壳。但在准备打印时,却发现耗材不够,无奈之下,我决定改用纸盒来制作盒子外壳。挑选了厚实且有一定韧性的纸盒,虽然没有 3D 打印外壳那样精致,但纸盒可塑性强。我用剪刀、胶水等工具,按照预先设计好的尺寸,将纸盒裁剪、折叠、粘贴,为内部电子元件搭建了一个简易却实用的 “家”。使用的是带彩色纸皮的纸盒【鞋盒
】,让小盒子看起来好看点,也弥补了没有 3D 打印外壳的遗憾。

四、功能呈现
当孩子把录入指纹的手指放在 FPM383C 识别模块上,系统瞬间就能完成识别,主控芯片通过控制 9g 舵机,迅速打开锁扣,孩子可以轻松拿取自己的宝贝。要是不小心按错指纹,系统立刻启动锁定程序,9g 舵机反向转动紧闭锁扣,防止其他人未经允许打开盒子。由于盒子做的不是那么精密,透光有缝隙,导致盒子内部的彩灯透过缝隙扩散出来,不过这样感觉反而增加了趣味性。FPM383C模块当成功开锁时,自动会闪烁灯光,指纹验证通过绿色灯光,指纹不通过红色灯光,反馈比较明显减少了代码工作量。


五、接线方式
Ai-M61-32s |
FPM383C |
舵机 |
3v3 |
Pin3 |
|
GND |
PIN6 |
|
IO23 |
PIN5 |
|
IO24 |
PIN4 |
|
IO19 |
|
信号线 |
5V |
|
电源正 |
GND |
|
电源负 |
资料参考:
【外设移植】FPM383C指纹模块+Ai-M61-32S
https://bbs.ai-thinker.com/forum.php?mod=viewthread&tid=43963
【外设移植】SG90舵机Ai-M61开发板
https://bbs.ai-thinker.com/forum.php?mod=viewthread&tid=44405
代码:
#include "FPM383C.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "bflb_mtimer.h"
#include "bflb_dma.h"
#include "bflb_uart.h"
#include "log.h"
#include "bflb_servo.h"
#include "bflb_gpio.h" //gpio头文件
#include "bflb_mtimer.h" //mtimer定时器头文件
#include "bflb_pwm_v2.h" //pwm_v2头文件
#include "bflb_clock.h" //系统时钟头文件
//控制模块LED灯颜色
uint8_t PS_BlueLEDBuf[16] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x3C,0x03,0x01,0x01,0x00,0x00,0x49};
uint8_t PS_RedLEDBuf[16] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x3C,0x02,0x04,0x04,0x02,0x00,0x50};
uint8_t PS_GreenLEDBuf[16] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x3C,0x02,0x02,0x02,0x02,0x00,0x4C};
//休眠指令-设置传感器进入休眠模式
uint8_t PS_SleepBuf[12] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x33,0x00,0x37};
//清空指纹库-删除 flash 数据库中所有指纹模板。
uint8_t PS_EmptyBuf[12] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x0D,0x00,0x11};
//取消指令-取消自动注册模板和自动验证指纹。如表 2-1 中加密等级设置为 0 或 1 情况下支持此功能
uint8_t PS_CancelBuf[12] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x30,0x00,0x34};
//自动注册模板-一站式注册指纹,包含采集指纹、生成特征、组合模板、存储模板等功能。加密等级设置为 0 或 1 情况下支持此功能。
uint8_t PS_AutoEnrollBuf[17] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x08,0x31,'\0','\0',0x04,0x00,0x16,'\0','\0'};
// 验证用获取图像-验证指纹时,探测手指,探测到后录入指纹图像存于图像缓冲区。返回确认码表示:录入成功、无手指等。
uint8_t PS_GetImageBuf[12] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x01,0x00,0x05};
//生成特征值-将图像缓冲区中的原始图像生成指纹特征文件存于模板缓冲区
uint8_t PS_GetCharBuf[13] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x04,0x02,0x01,0x00,0x08};
//搜索指纹-以模板缓冲区中的特征文件搜索整个或部分指纹库。若搜索到,则返回页码。加密等级设置为 0 或 1 情况下支持
uint8_t PS_SearchBuf[17] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x08,0x04,0x01,0x00,0x00,0xFF,0xFF,0x02,0x0C};
//删除模板-删除 flash 数据库中指定 ID 号开始的N 个指纹模板
uint8_t PS_DeleteBuf[16] = {0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x0C,'\0','\0',0x00,0x01,'\0','\0'};
//设置名为fpm383c_uart的外设句柄,用来执行串口指令的发送
struct bflb_device_s *fpm383c_uart;
//指纹ID和验证指纹的分数
uint16_t pageID,score;
//USART串口接收缓冲数组
uint8_t USART_ReceiveBuf[20];
//主循环状态标志位
uint8_t ScanStatus = 0;
struct bflb_servo_attr servo;
// 锁状态
uint8_t lock_state = 0;
/**
* @brief 获取状态
*
* @param Timeout
*/
void FPM383C_ReceiveData(uint16_t Timeout)
{
uint8_t i = 0;
// 检测缓冲区是否有数据
while(bflb_uart_rxavailable(fpm383c_uart) == 0 && (--Timeout))
{
bflb_mtimer_delay_ms(1);
}
// 轮询fpm383c_uart接收到的字符
while(bflb_uart_rxavailable(fpm383c_uart) > 0)
{
bflb_mtimer_delay_ms(2);
// 获取数据并放入缓冲数组
USART_ReceiveBuf[i++] = bflb_uart_getchar(fpm383c_uart);
if(i > 15) break;
}
}
/// @brief 初始化FPM383C指纹模块
void FPM383C_Init(){
// 声明 gpio句柄
struct bflb_device_s* gpio;
// FPM383C 模块默认波特率为 57600
struct bflb_uart_config_s cfg = {
.baudrate = 57600,
.data_bits = UART_DATA_BITS_8,
.stop_bits = UART_STOP_BITS_1,
.parity = UART_PARITY_NONE,
.flow_ctrl = UART_FLOWCTRL_NONE,
.rx_fifo_threshold = 7,
.tx_fifo_threshold = 7
};
// 初始化FPM383C指纹模块UART
gpio = bflb_device_get_by_name("gpio");
fpm383c_uart = bflb_device_get_by_name("uart1");
//将GPIO_23和GPIO_24设置为TX和RX
bflb_gpio_uart_init(gpio, GPIO_PIN_23, GPIO_UART_FUNC_UART1_TX);
bflb_gpio_uart_init(gpio, GPIO_PIN_24, GPIO_UART_FUNC_UART1_RX);
bflb_uart_init(fpm383c_uart, &cfg);
}
/// @brief USART串口发送数据
/// @param length 发送数组长度
/// @param FPM383C_DataBuf 需要发送的功能数组
void FPM383C_SendData(int length,uint8_t FPM383C_DataBuf[])
{
for(int i = 0;i<length;i++)
{
bflb_uart_put(fpm383c_uart,(uint8_t *)&FPM383C_DataBuf[i], 1);
}
}
/// @brief 发送休眠指令 确认码=00H 表示休眠设置成功。确认码=01H 表示休眠设置失败。
/// @param
void FPM383C_Sleep(void)
{
FPM383C_SendData(12,PS_SleepBuf);
}
/// @brief 验证用获取图像
/// @param timeout 接收数据的超时时间
/// @return 确认码
uint8_t FPM383C_GetImage(uint32_t timeout)
{
uint8_t tmp;
FPM383C_SendData(12,PS_GetImageBuf);
FPM383C_ReceiveData(timeout);
tmp = (USART_ReceiveBuf[6] == 0x07 ? USART_ReceiveBuf[9] : 0xFF);
memset(USART_ReceiveBuf,0xFF,sizeof(USART_ReceiveBuf));
return tmp;
}
/// @brief 将图像缓冲区中的原始图像生成指纹特征文件存于模板缓冲区。
/// @param timeout 接收数据的超时时间
/// @return 确认码
uint8_t FPM383C_GenChar(uint32_t timeout)
{
uint8_t tmp;
FPM383C_SendData(13,PS_GetCharBuf);
FPM383C_ReceiveData(timeout);
tmp = (USART_ReceiveBuf[6] == 0x07 ? USART_ReceiveBuf[9] : 0xFF);
memset(USART_ReceiveBuf,0xFF,sizeof(USART_ReceiveBuf));
return tmp;
}
/// @brief 发送搜索指纹指令
/// @param timeout 接收数据的超时时间
/// @return 确认码
uint8_t FPM383C_Search(uint32_t timeout)
{
FPM383C_SendData(17,PS_SearchBuf);
FPM383C_ReceiveData(timeout);
return (USART_ReceiveBuf[6] == 0x07 ? USART_ReceiveBuf[9] : 0xFF);
}
/// @brief 删除指定指纹指令
/// @param pageID 需要删除的指纹ID号
/// @param timeout 接收数据的超时时间
/// @return 确认码
uint8_t FPM383C_Delete(uint16_t pageID,uint32_t timeout)
{
uint8_t tmp;
PS_DeleteBuf[10] = (pageID>>8);
PS_DeleteBuf[11] = (pageID);
PS_DeleteBuf[14] = (0x15+PS_DeleteBuf[10]+PS_DeleteBuf[11])>>8;
PS_DeleteBuf[15] = (0x15+PS_DeleteBuf[10]+PS_DeleteBuf[11]);
FPM383C_SendData(16,PS_DeleteBuf);
FPM383C_ReceiveData(timeout);
tmp = (USART_ReceiveBuf[6] == 0x07 ? USART_ReceiveBuf[9] : 0xFF);
memset(USART_ReceiveBuf,0xFF,sizeof(USART_ReceiveBuf));
return tmp;
}
/// @brief 清空指纹库
/// @param timeout 接收数据的超时时间
/// @return 确认码
uint8_t FPM383C_Empty(uint32_t timeout)
{
uint8_t tmp;
FPM383C_SendData(12,PS_EmptyBuf);
FPM383C_ReceiveData(timeout);
tmp = (USART_ReceiveBuf[6] == 0x07 ? USART_ReceiveBuf[9] : 0xFF);
memset(USART_ReceiveBuf,0xFF,sizeof(USART_ReceiveBuf));
return tmp;
}
/// @brief 发送控制灯光指令
/// @param PS_ControlLEDBuf 不同颜色的协议数据
/// @param timeout 接收数据的超时时间
/// @return 确认码
uint8_t FPM383C_ControlLED(uint8_t PS_ControlLEDBuf[],uint32_t timeout)
{
uint8_t tmp;
FPM383C_SendData(16,PS_ControlLEDBuf);
FPM383C_ReceiveData(timeout);
tmp = (USART_ReceiveBuf[6] == 0x07 ? USART_ReceiveBuf[9] : 0xFF);
memset(USART_ReceiveBuf,0xFF,sizeof(USART_ReceiveBuf));
return tmp;
}
/// @brief 验证指纹是否注册
/// @param
void FPM383C_Identify(void)
{
if(FPM383C_GetImage(2000) == 0x00)
{
if(FPM383C_GenChar(2000) == 0x00)
{
struct bflb_device_s *led = bflb_device_get_by_name("gpio");
if(FPM383C_Search(2000) == 0x00)
{
score = (int)((USART_ReceiveBuf[10] << 8) + USART_ReceiveBuf[11]);
LOG_E("解锁成功 指纹ID:%d\r\n",(int)score);
FPM383C_ControlLED(PS_GreenLEDBuf,1000);
bflb_gpio_init(led, GPIO_PIN_14, GPIO_OUTPUT);
bflb_gpio_set(led, GPIO_PIN_14);
bflb_mtimer_delay_ms(1000);
bflb_gpio_reset(led, GPIO_PIN_14);
// 重置接收数据缓存
memset(USART_ReceiveBuf,0xFF,sizeof(USART_ReceiveBuf));
if(!lock_state){
lock_state = 1;
servo = bflb_servo_attach(GPIO_PIN_19, PWM_CH3);
bflb_servo_write(&servo, 80);
bflb_mtimer_delay_ms(800);
bflb_servo_stop(&servo);
}
return;
}else{
LOG_E("解锁失败\r\n");
bflb_gpio_init(led, GPIO_PIN_12, GPIO_OUTPUT);
bflb_gpio_set(led, GPIO_PIN_12);
bflb_mtimer_delay_ms(1000);
bflb_gpio_reset(led, GPIO_PIN_12);
// 重置接收数据缓存
memset(USART_ReceiveBuf,0xFF,sizeof(USART_ReceiveBuf));
if(lock_state){
lock_state = 0;
servo = bflb_servo_attach(GPIO_PIN_19, PWM_CH3);
bflb_servo_write(&servo, 10);
bflb_mtimer_delay_ms(800);
bflb_servo_stop(&servo);
}
return;
}
}
}
}
/// @brief 自动注册
/// @param pageID 输入需要注册的指纹ID号,取值范围0—59
/// @param timeout 设置注册指纹超时时间,因为需要按压四次手指,建议大于10000(即10s)
void FPM383C_Enroll(uint16_t pageID,uint16_t timeout)
{
LOG_E("注册指纹ID: %d\r\n", pageID);
PS_AutoEnrollBuf[10] = (pageID>>8);
PS_AutoEnrollBuf[11] = (pageID);
PS_AutoEnrollBuf[15] = (0x54+PS_AutoEnrollBuf[10]+PS_AutoEnrollBuf[11])>>8;
PS_AutoEnrollBuf[16] = (0x54+PS_AutoEnrollBuf[10]+PS_AutoEnrollBuf[11]);
FPM383C_SendData(17,PS_AutoEnrollBuf);
FPM383C_ReceiveData(timeout);
if(USART_ReceiveBuf[9] == 0x00)
{
LOG_E("指纹注册完成\r\n");
// 亮绿灯2秒
FPM383C_ControlLED(PS_GreenLEDBuf,2000);
// 重置接收数据缓存
memset(USART_ReceiveBuf,0xFF,sizeof(USART_ReceiveBuf));
return;
}
else if(timeout == 0)
{
// 超时取消注册
FPM383C_SendData(12,PS_CancelBuf);
bflb_mtimer_delay_ms(50);
// 重置接收数据缓存
memset(USART_ReceiveBuf,0xFF,sizeof(USART_ReceiveBuf));
}
// 亮红灯2秒
FPM383C_ControlLED(PS_RedLEDBuf,2000);
}
附件:fpm383c_lock.zip
保险箱3D图用freeCAD做的,源文件在最下面。








附件:BOX.zip