1. 项目概述一个能“认主”的智能车位锁每次下班回家最头疼的事莫过于发现自己的固定车位又被占了。无论是小区地库还是公司园区这种“鸠占鹊巢”的情况总让人火大。传统的车位锁要么是手动机械式操作不便要么是遥控式但遥控器容易丢安全性也一般。有没有一种成本可控、自己能动手做又能真正“认主”的智能解决方案这个基于Arduino的智能密码锁停车场系统就是针对这个痛点来的。它的核心思路很简单为每个车位配一把“电子锁”由伺服电机驱动的道闸而开锁的“钥匙”则是一组预设的密码。车主通过一个公共的控制面板矩阵键盘选择车位并输入密码验证通过后对应的道闸才会升起放行。这就像给每个车位装了一个专属的保险箱不知道密码车就进不去。我选择Arduino Uno作为大脑一是因为它开源、易得社区资源丰富哪怕你是嵌入式开发的新手跟着步骤也能做出来二是它的IO口和控制能力驱动几个伺服电机和外围设备绰绰有余。整个系统由三大部分构成输入部分4x4矩阵键盘、控制与显示部分Arduino Uno I2C LCD屏、执行部分三个微型伺服电机及道闸。硬件成本不高核心逻辑清晰非常适合作为物联网和嵌入式控制的入门实战项目。2. 核心设计思路与方案选型2.1 为什么是“密码”而非“刷卡”或“车牌识别”在构思访问控制方案时我考虑过几种主流方式IC/ID卡、蓝牙/NFC手机解锁、以及车牌识别。最终选择矩阵键盘密码方案主要基于以下几点考量极致的成本与复杂度控制IC卡方案需要读卡器和卡片的额外成本且涉及卡片发行与管理。车牌识别需要摄像头和图像处理算法对硬件算力如树莓派和软件复杂度要求陡增。而一个4x4矩阵键盘成本极低密码逻辑在代码中即可完成无需任何额外介质。部署与维护的简便性密码可以随时在代码中修改无需物理更换卡片或重新录入信息。对于家庭、小型商铺或内部停车场用户群体相对固定且可控记住几位数字密码并非难事。可靠性与环境适应性矩阵键盘作为纯物理按键受环境如光线、灰尘、轻微水渍影响远小于光学识别设备。在车库这种可能光线不佳、灰尘较多的环境下物理按键的可靠性更高。明确的学习与扩展目标本项目的一个重要目的是透彻理解“输入-处理-输出”这一嵌入式核心流程。矩阵键盘的扫描、密码的比对、状态机的管理都是非常经典且基础的程序设计练习。掌握了这些后续再扩展蓝牙模块或读卡器会容易得多。当然密码方案也有其短板比如密码可能被窥视、遗忘或需要定期更换以防泄露。但在本项目设定的“低成本、高可靠性、易实现”的首要目标下它是最优解。2.2 系统架构与信号流分析整个系统的运行遵循一个清晰的“状态机”模型这对于嵌入式系统设计至关重要。我们可以把系统想象成一个有多个页面的菜单初始状态待机页面LCD屏幕显示欢迎语或提示“请选择车位[1][2][3]”。系统在此状态等待键盘输入。车位选择状态当用户按下‘1’、‘2’或‘3’时系统进入该状态。LCD更新为“车位X请输入密码____”。此时系统开始记录后续输入的数字。密码验证状态用户连续输入4位密码假设密码长度为4。每输入一位LCD上的‘_’可以变为‘*’以提供反馈增强版功能。输入完成后系统将输入的密码与存储在程序中的对应车位密码进行比对。执行与反馈状态验证成功Arduino向对应车位的伺服电机连接在D10, D11, D12引脚发送信号使其旋转特定角度如90度打开道闸。同时LCD显示“车位X欢迎进入”或类似提示。等待数秒例如5秒后伺服电机回转道闸关闭。系统状态重置回“初始状态”。验证失败LCD显示“密码错误”可能伴随短暂的提示音如果连接了蜂鸣器。等待片刻后系统自动跳回“初始状态”不执行任何动作。这个状态机确保了流程的严谨性避免了因误操作导致的状态混乱。例如在输入密码过程中如果用户迟迟不输入第四位程序应设计一个超时机制如10秒自动复位到初始状态防止系统“卡死”。2.3 关键组件选型解析主控Arduino Uno R3理由14路数字IO口6路支持PWM和6路模拟输入口完全满足3个伺服电机3路PWM、矩阵键盘8路数字IO和I2C LCD2路模拟口复用的需求。5V工作电压与伺服电机、键盘、LCD模块完美兼容。USB供电和编程极其方便生态成熟是入门和原型开发的不二之选。输入4x4矩阵薄膜键盘理由用8个IO口实现了16个按键的检测极大地节省了宝贵的IO资源。其扫描原理逐行置低读取列状态是嵌入式基础的必修课。薄膜键盘成本低耐用性对于本项目足够。输出-显示1602 LCD屏 I2C适配板理由1602屏16字符x2行足以显示所有必要的状态信息。直接驱动1602需要至少6个IO口而通过一个I2C转接板仅需2个IO口SDA, SCL即可控制这又是节省IO口的经典操作。I2C通信本身也是一个重要的学习点。输出-执行SG90微型伺服电机理由SG90价格低廉扭矩适中1.8kg/cm左右足以驱动一个轻质道闸模型。它通过PWM信号控制旋转角度通常0-180度控制简单使用Arduino内置的Servo库。选择3个独立的伺服电机是为了实现每个车位的完全独立控制逻辑清晰。供电USB电源5V/1A或以上理由系统整体功耗不高。三个伺服电机是主要耗电单元但它们并非同时工作同一时间只可能有一个在转动。一个常见的5V/1A手机充电器或充电宝足以稳定驱动整个系统无需复杂的电源管理电路。3. 硬件搭建与电路连接详解3.1 元器件清单与预处理在开始焊接或插接面包板之前请再次清点以下物料Arduino Uno 开发板 x14x4 矩阵键盘 x11602 LCD 显示屏带I2C转接板x1SG90 微型伺服电机 x3迷你面包板可选用于整理线路x1公对公、公对母杜邦线 若干USB数据线用于供电和编程x15V USB电源适配器或充电宝 x1用于制作道闸和外壳的材料如卡纸、硬纸板、木板或3D打印部件预处理建议测试所有元器件先用简单的代码单独测试每个伺服电机是否能正常转动LCD屏是否能点亮并显示字符矩阵键盘每个按键是否都能被正确识别。这一步能提前排除硬件故障避免后续联调时头疼。规划布局在桌面上大致摆放一下所有部件思考线路的走向。尽量让电源线5V和GND的路径清晰避免信号线如伺服信号线、I2C线缠绕在一起以减少潜在干扰。I2C地址确认不同的I2C LCD转接板可能有不同的地址常见的是0x27或0x3F。使用一个简单的I2C扫描程序Arduino IDE示例中有来确认你手中LCD模块的确切地址后续编程会用到。3.2 电路连接步骤与原理图解读连接遵循“电源先行信号跟进”的原则。下面是对原始电路图的详细解读和连接顺序建议第一步建立公共电源与地将Arduino Uno的5V引脚连接到面包板的正极电源总线。将Arduino Uno的GND引脚连接到面包板的负极地线总线。注意确保面包板上的电源总线是贯通的。这一步为所有外围设备提供了稳定的5V电源和公共参考地。第二步连接三个伺服电机伺服电机有三根线棕色GND、红色VCC/V、橙色信号Signal。将三个伺服电机的棕色线GND都连接到面包板的地线总线。将三个伺服电机的红色线VCC都连接到面包板的电源总线5V。将三个伺服电机的橙色线信号分别连接到Arduino的以下数字引脚车位1 伺服 -D10车位2 伺服 -D11车位3 伺服 -D12注意D9,D10,D11等引脚支持PWM输出这是控制伺服角度的关键。虽然Servo库在某些情况下可以使用非PWM引脚但为了规范建议使用标有‘~’的PWM引脚。第三步连接4x4矩阵键盘矩阵键盘通常有8个引脚R1, R2, R3, R4 (行) 和 C1, C2, C3, C4 (列)。将键盘的行引脚R1-R4依次连接到Arduino的数字引脚D2,D3,D4,D5。将键盘的列引脚C1-C4依次连接到Arduino的数字引脚D6,D7,D8,D9。注意行和列的连接顺序在编程时需要严格对应。这里的引脚分配兼顾了布局的整齐并避开了已用于伺服的D10~D12。第四步连接I2C LCD显示屏I2C转接板通常有4个引脚VCC, GND, SDA, SCL。将转接板的VCC连接到面包板的电源总线5V。将转接板的GND连接到面包板的地线总线。将转接板的SDA连接到Arduino的A4引脚。将转接板的SCL连接到Arduino的A5引脚。注意在Arduino Uno上A4和A5引脚除了模拟输入功能还被硬件固定为I2C通信的SDA和SCL线。这是I2C通信的标准引脚不可随意更改。完成以上连接后你的硬件系统就搭建完毕了。建议用不同颜色的杜邦线区分电源红色、地线黑色和信号线黄色、绿色等这样在检查和调试时会一目了然。4. 软件程序设计从状态机到代码实现4.1 核心库的引用与全局变量定义程序开始我们需要引入三个至关重要的库并定义控制整个系统逻辑的全局变量。#include Wire.h // I2C通信库用于驱动LCD #include LiquidCrystal_I2C.h // 控制I2C LCD的库 #include Keypad.h // 矩阵键盘库简化按键读取 #include Servo.h // 伺服电机控制库 // 1. LCD初始化 (地址, 列数, 行数) - 地址需根据实际扫描结果修改 LiquidCrystal_I2C lcd(0x27, 16, 2); // 2. 键盘引脚定义与初始化 const byte ROWS 4; const byte COLS 4; char keys[ROWS][COLS] { {1,2,3,A}, {4,5,6,B}, {7,8,9,C}, {*,0,#,D} }; byte rowPins[ROWS] {2, 3, 4, 5}; // 连接键盘R1-R4的引脚 byte colPins[COLS] {6, 7, 8, 9}; // 连接键盘C1-C4的引脚 Keypad keypad Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); // 3. 伺服电机对象定义 Servo servo1, servo2, servo3; #define SERVO1_PIN 10 #define SERVO2_PIN 11 #define SERVO3_PIN 12 // 4. 系统状态与密码相关变量 int systemState 0; // 0:等待选择车位, 1:等待输入密码, 2:密码验证中 int selectedParkingSpot 0; // 当前选中的车位号 (1,2,3) String inputPassword ; // 用户输入的密码 const String correctPasswords[3] {1111, 2222, 3333}; // 预设密码数组 const int PASSWORD_LENGTH 4; unsigned long inputTimeout 0; // 用于输入超时判断 const unsigned long TIMEOUT_PERIOD 10000; // 超时时间10秒关键点解析LiquidCrystal_I2C lcd(0x27, 16, 2);这里的0x27是I2C地址务必用扫描程序确认你的模块地址并修改。Keypad库它内部实现了键盘扫描的复杂逻辑我们只需要定义好行列映射就可以用keypad.getKey()轻松读取按键极大简化了代码。Servo对象每个伺服电机都需要一个独立的软件对象来控制。状态变量systemState是整个程序的大脑它决定了当前程序应该响应什么操作、显示什么内容。这种“状态机”编程模式是处理复杂流程的利器。密码存储将密码存储在String数组中索引0对应车位1清晰易懂。使用String类型方便比较但要注意在内存有限的单片机中通常建议使用字符数组(char array)这里为了可读性使用了String。4.2 主程序逻辑与状态机实现setup()函数进行初始化loop()函数则是一个永不停止的循环在其中根据systemState的值执行不同的代码块。void setup() { Serial.begin(9600); // 用于调试可观察串口输出 lcd.init(); // 初始化LCD lcd.backlight(); // 打开背光 lcd.setCursor(0, 0); lcd.print(Smart Parking Sys); lcd.setCursor(0, 1); lcd.print(Select Spot:1/2/3); // 关联伺服电机对象到控制引脚 servo1.attach(SERVO1_PIN); servo2.attach(SERVO2_PIN); servo3.attach(SERVO3_PIN); // 初始化时关闭所有道闸假设0度为关闭 closeAllGates(); } void loop() { char key keypad.getKey(); // 非阻塞式读取按键 switch (systemState) { case 0: // 状态0等待选择车位 stateWaitForSpotSelection(key); break; case 1: // 状态1等待输入密码 stateWaitForPasswordEntry(key); break; case 2: // 状态2验证密码并执行动作 (此状态通常瞬间完成或由定时器触发) // 该状态逻辑通常融合在状态1的密码输入完成判断中 break; } }状态0函数详解stateWaitForSpotSelection这个函数负责监听用户选择哪个车位。void stateWaitForSpotSelection(char key) { if (key) { if (key 1 || key 2 || key 3) { selectedParkingSpot key - 0; // 将字符1转换为数字1 systemState 1; // 进入密码输入状态 inputPassword ; // 清空旧密码输入 inputTimeout millis(); // 记录进入状态的时间用于超时 lcd.clear(); lcd.setCursor(0, 0); lcd.print(Spot ); lcd.print(selectedParkingSpot); lcd.setCursor(0, 1); lcd.print(Pass:____); // 预留4位密码显示位 } else { // 如果按了其他键可以给出错误提示例如让LCD闪烁一下 lcd.noDisplay(); delay(100); lcd.display(); } } }状态1函数详解stateWaitForPasswordEntry这是最核心的状态负责收集密码、处理退格、完成输入和超时判断。void stateWaitForPasswordEntry(char key) { // 检查是否超时 if (millis() - inputTimeout TIMEOUT_PERIOD) { lcd.clear(); lcd.print(Timeout! Reset.); delay(1500); resetToInitialState(); return; } if (key) { if (key 0 key 9) { // 只处理数字键 if (inputPassword.length() PASSWORD_LENGTH) { inputPassword key; // 追加输入字符 updatePasswordDisplay(); // 更新LCD显示例如将‘_’变为‘*’ } // 如果密码长度已够自动触发验证 if (inputPassword.length() PASSWORD_LENGTH) { verifyPassword(); } } else if (key *) { // 假设‘*’为退格键 if (inputPassword.length() 0) { inputPassword.remove(inputPassword.length() - 1); updatePasswordDisplay(); } } else if (key #) { // 假设‘#’为确认键也可用于自动触发 if (inputPassword.length() PASSWORD_LENGTH) { verifyPassword(); } } } }密码验证与执行函数verifyPassword这是状态1的后续动作完成验证后它会驱动伺服电机然后返回状态0。void verifyPassword() { lcd.clear(); lcd.setCursor(0, 0); // 注意数组索引从0开始车位号从1开始所以要减1 if (inputPassword.equals(correctPasswords[selectedParkingSpot - 1])) { lcd.print(Access Granted!); lcd.setCursor(0, 1); lcd.print(Welcome Spot ); lcd.print(selectedParkingSpot); openGate(selectedParkingSpot); // 打开对应车位的道闸 delay(5000); // 保持开门状态5秒 closeGate(selectedParkingSpot); // 关闭道闸 } else { lcd.print(Wrong Password!); delay(2000); // 显示错误信息2秒 } // 无论成功与否都重置系统 resetToInitialState(); } void resetToInitialState() { systemState 0; selectedParkingSpot 0; inputPassword ; lcd.clear(); lcd.print(Smart Parking Sys); lcd.setCursor(0, 1); lcd.print(Select Spot:1/2/3); }伺服控制函数void openGate(int spot) { switch(spot) { case 1: servo1.write(90); break; // 90度代表打开 case 2: servo2.write(90); break; case 3: servo3.write(90); break; } } void closeGate(int spot) { switch(spot) { case 1: servo1.write(0); break; // 0度代表关闭 case 2: servo2.write(0); break; case 3: servo3.write(0); break; } } void closeAllGates() { servo1.write(0); servo2.write(0); servo3.write(0); }4.3 代码优化与高级功能拓展基础版本完成后可以考虑以下优化让系统更健壮、更友好EEPROM存储密码将预设密码存储在Arduino的EEPROM中这样即使断电密码也不会丢失。还可以增加一个“管理员模式”例如长按‘A’键进入用于修改密码修改后的密码存入EEPROM。LCD动态显示在输入密码时实时将光标位置的‘_’替换为‘*’提供更好的交互反馈。这需要更精细的lcd.setCursor()控制。声音与灯光提示增加一个有源蜂鸣器在按键按下时发出“滴”声在密码错误时发出“滴滴滴”报警声。为每个车位增加一个LED如红色表示占用/关闭绿色表示空闲/打开状态一目了然。车位状态检测在每个车位下方安装一个红外对管或超声波传感器。当传感器检测到有车停入时自动将道闸关闭并锁定防止一车占多位。同时LCD上可以显示“车位X占用/空闲”。多级菜单与管理功能利用键盘上的‘A’、‘B’、‘C’、‘D’键实现菜单导航。例如进入“设置菜单”修改密码、查看停车记录等。5. 机械结构设计与组装要点电子部分工作正常后需要为它打造一个可靠的“身体”。道闸的机械结构直接决定了系统的稳定性和耐用性。5.1 道闸驱动方案选择伺服电机本身只能旋转我们需要将其旋转运动转化为道闸的升降运动。常见有几种方案直接连接摆杆这是最简单的方法。将一根长条形的摆杆可以用冰棍棒、硬塑料片或3D打印件直接固定在伺服电机的舵盘上。伺服从0度转到90度摆杆就从垂直放下拦车变为水平抬起放行。优点是极其简单缺点是力矩小只能做很轻的道闸且运动轨迹是弧线。连杆机构使用多根连杆组成四连杆机构可以将伺服电机的旋转运动近似转化为道闸的竖直升降运动更符合真实道闸的形态。这对设计和加工精度要求稍高。齿轮齿条/蜗轮蜗杆可以实现更精确的直线运动和自锁断电后位置保持但结构复杂成本高更适合进阶或对可靠性要求极高的场景。对于本项目原型方案一直接摆杆完全够用。我们可以用轻质的材料如卡纸、薄木板或PLA打印件制作道闸栏杆重量很轻。5.2 3D打印部件设计与安装如果使用3D打印机设计时需注意伺服电机支架需要完美卡住SG90电机的外壳并有螺丝孔或卡扣固定。支架底部应有较大的接触面积或预留螺丝孔以便牢固地粘在底板纸板或亚克力板上。舵盘连接件设计一个能与标准舵盘螺丝孔匹配同时另一端能紧紧夹住道闸栏杆的连接件。可以考虑设计成夹子状用一颗螺丝拧紧。道闸栏杆设计成中空的长方体以减轻重量一端有接口与连接件固定。可以在栏杆末端添加一个醒目的标志如红白相间条纹。整体底座设计一个大的底板可以将Arduino、面包板、LCD屏、键盘以及三个伺服电机支架都规划好位置固定上去使整个项目一体化更美观稳固。安装顺序建议先将所有电子部件Uno, 面包板, LCD在底板上定位用尼龙扎带或螺丝固定。将伺服电机装入打印好的支架并用热熔胶或螺丝将支架固定在底板上预设的位置。确保三个道闸的旋转轴心在同一直线上且前方没有遮挡。将道闸栏杆安装到舵盘连接件上并确保在伺服处于0度时栏杆处于垂直下落状态挡住车位入口处于90度时栏杆水平抬起让出通道。最后整理所有线材用扎带捆好保持整洁。5.3 使用替代材料的技巧如果没有3D打印机完全可以利用手边材料底座厚纸板、废弃的塑料板、木板都是好选择。纸板可以多层粘贴增加强度。伺服支架可以用乐高积木搭建或者用硬纸板折成U型槽用热熔胶大量固定。关键是确保电机不会晃动。道闸栏杆冰棍棒、一次性筷子、吸管、用纸卷成的硬纸卷都非常适合。用热熔胶与伺服舵盘连接。外壳用一个废弃的纸盒或塑料盒作为控制面板的外壳将LCD和键盘镶嵌在表面显得更规整。核心原则牢固、准确、轻量化。确保伺服电机固定死不能有活动旷量确保道闸栏杆在旋转时不会刮蹭到底座或其他部件尽量减轻栏杆重量以减少伺服电机负载。6. 系统调试、问题排查与优化实录硬件组装和软件烧录完成后真正的挑战才刚刚开始。下面是我在多次调试中遇到的典型问题及解决方法希望能帮你少走弯路。6.1 上电无反应或LCD不显示可能原因1电源问题排查首先检查USB线是否插紧电源适配器是否有输出可以换一个手机充电试试。用万用表测量Arduino的5V和GND引脚之间是否有稳定的5V电压。解决确保使用足额5V/1A以上的电源。如果使用电脑USB口供电有时会因为供电不足导致伺服电机无法工作或系统重启。可能原因2I2C LCD地址错误或接触不良排查运行一个I2C扫描程序确认屏幕上是否显示了你的模块地址如0x27。如果没有显示任何地址检查SDA、SCL线是否接反A4接SDAA5接SCL接触是否良好。检查LCD背光是否亮起有些模块有独立的背光跳线帽。解决修正接线在代码中修改为正确的I2C地址。确保所有连接插紧。6.2 矩阵键盘部分按键失灵或串键可能原因1行列引脚定义错误排查这是最常见的问题。仔细对照键盘背面的引脚标识或产品说明书确认哪一排是R1哪一列是C1。你的接线顺序必须和代码中rowPins、colPins数组的定义完全一致。解决重新检查接线或尝试交换行和列的引脚定义顺序。可以写一个简单的测试程序循环扫描并打印每个按键的坐标来验证映射关系。可能原因2上拉电阻问题排查Arduino内部有可配置的上拉电阻。Keypad库默认可能没有启用内部上拉。当按键未按下时输入引脚处于“悬空”状态容易受到干扰导致误触发。解决在setup()函数中手动为你用于键盘的rowPins和colPins设置内部上拉pinMode(pin, INPUT_PULLUP);。或者修改Keypad库的初始化参数如果库支持。6.3 伺服电机抖动、不转或角度不准可能原因1供电不足现象单个伺服转动正常但当一个伺服转动时其他伺服或整个系统如LCD出现复位、闪烁。或者伺服发出“吱吱”声但不转动。排查伺服电机启动瞬间电流很大可达500mA-1A。多个伺服共用Arduino板载的5V稳压器可能超过其负载能力约500mA。解决这是本项目最关键的供电问题必须为伺服电机提供独立供电。方法将外部5V电源如旧的手机充电器的正极5V同时接到面包板电源总线和所有伺服电机的VCC红线上。将外部电源的负极GND与Arduino的GND连接在一起共地。Arduino本身仍可由USB供电或由该外部电源经Vin引脚供电。这样就由外部电源直接为伺服电机提供大电流Arduino只提供控制信号。可能原因2信号干扰现象伺服电机无故轻微抖动或角度漂移。排查伺服电机的信号线橙线与控制线键盘、LCD的线捆扎在一起或距离过近。解决尽量将伺服电机的信号线与其他线缆分开走线。在伺服电机的VCC和GND之间就近焊接一个10uF-100uF的电解电容可以很好地滤除电源噪声。可能原因3机械负载过重或卡死现象伺服转到某个位置就停住并发热发出堵转声。解决立即断电检查道闸栏杆是否被底座或其它部件卡住。减轻栏杆重量。确保伺服支架固定牢固电机自身没有在支架内晃动。6.4 程序逻辑问题状态混乱、密码错误可能原因1变量未及时清零现象上一次输入的密码残留影响下一次验证。排查检查在每次进入新状态如从状态1返回状态0时是否将inputPassword、selectedParkingSpot等关键变量重置。解决在resetToInitialState()函数中确保所有相关变量都被正确初始化。可能原因2按键消抖处理不佳现象按一次键程序识别为多次按下。解决Keypad库通常内置了消抖逻辑。如果仍有问题可以在keypad.getKey()返回有效键值后增加一个短暂的延时delay(50)或者采用更高级的状态判断只在按键释放时才进行处理。可能原因3字符串比较问题现象明明密码对了却提示错误。排查String的比较使用equals()方法是正确的。但如果你的密码中包含了非数字字符或者输入时不小心按了其他键就会导致不匹配。可以在stateWaitForPasswordEntry函数中严格限制只接收数字键并实时在串口打印inputPassword的值进行调试。解决使用串口监视器是调试嵌入式程序的利器。在关键节点如收到按键、密码验证前通过Serial.println()打印变量值可以清晰地看到程序的实际运行逻辑。6.5 系统稳定性与抗干扰优化增加看门狗WatchdogArduino Uno内置了看门狗定时器。在代码中启用它可以在程序意外跑飞或死循环时自动重启系统避免整个停车场“瘫痪”。关键状态非易失存储使用EEPROM存储当前道闸状态开/关。这样系统意外断电重启后能够恢复断电前的道闸状态而不是全部重置为关闭避免造成车辆被误锁或误放的情况。输入防误触在键盘扫描循环中可以加入“长按确认”逻辑。例如选择车位后必须长按‘#’键2秒才进入密码输入状态防止路过不小心碰到就触发流程。这个项目从构思到调试完成我花了大约两个周末的时间。最大的收获不是做出了一个能动的模型而是完整地走通了一个物联网终端设备的开发流程需求分析、方案选型、电路设计、编程调试、机械组装、问题排查。每一个坑踩过去对“系统”二字的理解就深一分。特别是供电问题让我深刻理解了“数字逻辑是理想的物理世界是现实的”这句话。现在这个小小的停车场系统就放在我的工作台旁它不再只是一个项目而是一个随时可以扩展的智能控制平台。下次我打算给它加上无线模块用手机App来远程管理车位那又是另一个有趣的故事了。