news 2026/5/31 12:17:30

基于Arduino与HX711的智能饮水监测系统:从传感器到蓝牙通信全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于Arduino与HX711的智能饮水监测系统:从传感器到蓝牙通信全解析

1. 项目概述与核心思路

最近在整理工作室的旧项目时,翻出了一个几年前为学校科技展做的“智能饮水助手”原型。这个项目的初衷很简单:当时发现身边很多朋友,包括我自己,经常一忙起来就忘了喝水,一天下来饮水量严重不足。市面上的智能水杯要么价格不菲,要么功能花哨不实用。于是,我们就想,能不能用最基础的电子元件,自己动手做一个成本低、功能直接、还能通过手机简单交互的饮水提醒装置?

这个“智能饮水助手”的核心逻辑非常清晰:它本质上是一个基于重量变化的监测系统。我们用一个称重传感器(俗称“压力传感器”或“秤传感器”)垫在水杯下面,实时测量杯子的重量。当你喝水时,杯子变轻,重量减少的数值就对应了你喝下去的水量。Arduino作为大脑,负责读取这个重量数据,并进行计算和逻辑判断。最后,通过一个蓝牙模块(HC-05)将数据无线发送到手机上的一个简易App,App负责记录你的饮水历史,并在该喝水的时候通过Arduino控制蜂鸣器发出提醒。

整个项目的技术栈非常经典,是学习物联网硬件入门的绝佳案例:传感器数据采集(HX711模块)、微控制器编程(Arduino)、无线通信(蓝牙串口)。虽然原项目的代码和说明有些零散,但经过我的重新梳理和大量实践补充,下面我会把硬件连接的所有细节坑点、蓝牙配置的完整流程、以及固件编程中每个函数和参数背后的“为什么”都讲清楚。无论你是刚接触Arduino的学生,还是想做一个实用小工具的爱好者,跟着这篇指南,都能把它完整复现出来。

2. 硬件选型与电路连接详解

硬件是整个项目的物理基础,连接的正确性和稳定性直接决定了后续编程和功能能否实现。这里我们用的都是非常通用且性价比高的模块。

2.1 核心元件功能解析

  1. Arduino Uno R3:项目的控制核心。它负责运行我们编写的程序(固件),读取传感器数据,处理逻辑,并控制其他模块。选择Uno是因为其引脚布局清晰,资料丰富,非常适合学习和原型开发。
  2. HX711模块:这是一个24位高精度模数转换器(ADC)芯片模块。称重传感器(应变片)输出的信号是极其微弱的模拟电压变化,Arduino自身的ADC精度(10位)远远不够。HX711的作用就是将这个微小信号放大并转换成Arduino能够稳定读取的高精度数字信号。它通过两个数字引脚(DT和SCK)与Arduino通信,这种通信协议类似于SPI,但更简单。
  3. 5kg称重传感器:通常为铝合金梁式结构,上面贴有应变片。当受到压力时,其电阻会发生变化,从而产生电信号。选择5kg量程是考虑到一个装满水的大水杯重量通常在1-2kg,留出足够余量可以保证测量精度和传感器寿命。
  4. HC-05蓝牙串口模块:这是项目的无线通信枢纽。它本质上是一个串口透传模块,意味着它将Arduino的串口通信(TX/RX)无线化。Arduino发送给它的任何数据,它都会通过蓝牙原样发送给已配对的手机;反之亦然。HC-05有主从两种模式,本项目中使用默认的从机模式,等待手机连接。
  5. 有源蜂鸣器:提醒装置。与无源蜂鸣器不同,有源蜂鸣器内部自带振荡电路,给它一个直流电压就会以固定频率发声,控制简单,用Arduino的一个数字引脚输出高电平即可驱动。

2.2 电路连接步骤与避坑指南

连接电路时,务必在断电状态下操作。以下是经过优化的连接方案,比原图更可靠:

第一步:连接HX711与称重传感器称重传感器一般有4根线:红(E+)、黑(E-)、白(S+)、绿(S-)。它们需要与HX711模块上的对应端子焊接或通过接线端子压接。

  • 红线(E+) -> HX711的VCC
  • 黑线(E-) -> HX711的GND
  • 白线(S+) -> HX711的A+
  • 绿线(S-) -> HX711的A-

注意:不同厂家传感器的线色可能不同,请以产品说明书为准。最保险的方法是使用万用表测量:红黑线之间电阻通常约为1000欧姆,白绿线之间电阻约为1000欧姆,且红-白、红-绿、黑-白、黑-绿之间的电阻都接近500欧姆。

第二步:连接HX711模块与Arduino这里供电和通信要分开理解:

  • 供电:HX711模块需要电力才能工作。将模块的VCC引脚连接到 Arduino 的5V引脚,GND连接到 Arduino 的GND。这为整个HX711模块(包括其内部芯片和它正在激励的称重传感器)供电。
  • 通信:将HX711模块的DT(数据)引脚连接到 Arduino 的A1引脚(模拟输入引脚,但此处用作数字输入),SCK(时钟)引脚连接到A2引脚(用作数字输出)。这两个引脚用于同步数据传输。

第三步:连接HC-05蓝牙模块(关键步骤)这是最容易出错的地方。HC-05的工作电压是3.3V,而Arduino Uno的TX引脚输出是5V电平。直接连接可能会损坏蓝牙模块。

  • HC-05 VCC-> Arduino5V。模块本身有稳压电路,可以从5V降压到3.3V供自身使用。
  • HC-05 GND-> ArduinoGND
  • HC-05 TXD-> ArduinoRX (引脚0)。蓝牙模块的发送端连接到Arduino的接收端。
  • HC-05 RXD->电压分压电路-> ArduinoTX (引脚1)。这是核心!必须对Arduino发出的5V信号进行分压,降至约3.3V再给蓝牙模块。
    • 分压电路接法:准备一个2kΩ电阻和一个1kΩ电阻(接近原方案的3kΩ和6kΩ比例,但更常用)。将2kΩ电阻一端接Arduino的TX,另一端接HC-05的RXD,同时这个连接点再接1kΩ电阻到GND。这样,HC-05 RXD接收到的电压 = 5V * (1k / (2k + 1k)) ≈ 3.33V,安全可靠。

第四步:连接蜂鸣器

  • 蜂鸣器正极(长脚或标有“+”号) -> Arduino数字引脚13(通过一个100Ω限流电阻更安全)。
  • 蜂鸣器负极(短脚) -> ArduinoGND

连接完成后的整体供电逻辑是:USB线为Arduino供电,Arduino的5V引脚为HX711和HC-05模块供电。务必检查所有连接,确保没有短路(特别是5V和GND碰在一起)。

3. 蓝牙模块(HC-05)的配置与AT指令详解

很多新手会跳过这一步,直接烧录主程序,结果发现手机搜不到蓝牙或者无法通信。对HC-05进行初始配置是必不可少的一步,目的是给它设置一个容易识别的名字、配对密码和合适的通信速率。

3.1 配置电路搭建

配置时,我们需要通过Arduino向HC-05发送AT指令。此时,HC-05的RXD引脚需要接收来自Arduino TX的AT指令,但同时又要避免与主程序冲突。因此,我推荐使用一个更稳妥的“专用配置电路”方法,而不是在原电路上折腾:

  1. 从你的项目主电路中暂时取下HC-05模块。
  2. 单独准备一个Arduino(或者就用同一个,但先拔掉所有其他连线),按如下方式连接:
    • HC-05 VCC -> Arduino 5V
    • HC-05 GND -> Arduino GND
    • HC-05 TXD -> Arduino RX (引脚0)
    • HC-05 RXD -> Arduino TX (引脚1)(注意:这里不需要分压电路,因为此时蓝牙模块可能未启动,或者我们利用其进入AT模式的不同电压特性。更通用的方法是直接连接,但将HC-05的KEY/EN引脚置高来进入AT模式。)
  3. 关键操作:找到HC-05模块上的一个小按钮或一个标有“KEY”或“EN”的引脚。在不给模块通电的情况下,按住这个按钮不放,然后给模块上电(连接Arduino 5V)。此时模块上的LED会变为慢闪(例如2秒一闪),这表示它进入了AT命令模式,此时通信波特率固定为38400

3.2 使用Arduino IDE进行AT指令配置

打开Arduino IDE,上传下面这个极其简单的“AT指令转发”程序。这个程序的作用是把电脑通过USB发送给Arduino的字符,原样转发给HC-05,并把HC-05的回复显示给电脑。

void setup() { Serial.begin(38400); // 电脑与Arduino的通信速率 Serial1.begin(38400); // Arduino硬件串口1(引脚0,1)与HC-05的通信速率 } void loop() { // 从电脑读取指令,发送给HC-05 if (Serial.available()) { Serial1.write(Serial.read()); } // 从HC-05读取回复,发送给电脑 if (Serial1.available()) { Serial.write(Serial1.read()); } }

上传后,打开串口监视器(工具 -> 串口监视器)。确保右下角波特率设置为38400,行尾选择“Both NL & CR”(即新行和回车)。现在,你通过串口监视器输入的任何命令,都会直接发送给HC-05。

3.3 核心AT指令序列

在串口监视器的输入框里,依次输入并发送以下命令(每条指令后,HC-05都应回复“OK”):

  1. AT- 测试连接。如果回复OK,说明通信正常。
  2. AT+NAME=WaterAssistant- 将蓝牙名称设置为“WaterAssistant”。你可以改成任何你喜欢的名字,但避免特殊字符。
  3. AT+PSWD=1234- 将配对密码设置为“1234”。建议使用简单密码,方便测试。
  4. AT+UART=38400,0,0- 设置蓝牙通信的波特率为38400,停止位1,无校验位。这一步至关重要,必须与我们将要编写的主程序中的蓝牙通信波特率一致。参数格式为<波特率>,<停止位>,<校验位>0代表1位停止位和无校验。
  5. AT+RESET- 重启蓝牙模块,使所有设置生效。发送后模块会重启,LED恢复快闪,表示已退出AT模式,进入正常可配对状态。

实操心得:如果发送AT后没反应,首先检查接线,尤其是TX/RX是否交叉连接(设备的TX接对方的RX)。其次,确认HC-05是否成功进入了AT模式(LED慢闪)。最后,尝试在发送的指令后手动加上回车换行(这就是为什么串口监视器要选“Both NL & CR”)。

配置完成后,断开电路,将HC-05模块按2.2节第三步的描述(记得加上分压电阻!)接回你的主项目电路。现在,用手机蓝牙搜索,应该就能找到名为“WaterAssistant”的设备,使用密码“1234”配对。配对成功后,HC-05模块上的LED会由快闪变为双闪或常亮(因型号而异)。

4. 固件编程:代码逐行解析与逻辑实现

主程序负责整个设备的逻辑:初始化传感器、校准、读取重量、判断饮水动作、与手机通信、控制提醒。原项目代码提供了一个框架,但存在一些错误和不清晰的地方。下面是我重构并详细注释的完整代码。

4.1 库引入与全局变量定义

// 引入必要的库 #include "HX711.h" // 用于驱动HX711称重模块 #include "SoftwareSerial.h" // 用于创建软串口与蓝牙通信(避免占用硬件串口) // 引脚定义 #define HX711_DT_PIN A1 // HX711数据引脚接A1 #define HX711_SCK_PIN A2 // HX711时钟引脚接A2 #define BUZZER_PIN 13 // 蜂鸣器控制引脚 // 对象实例化 HX711 scale; // 创建一个HX711传感器对象,命名为scale SoftwareSerial bluetooth(10, 11); // 创建一个软串口对象,RX=10, TX=11,用于连接HC-05 // 全局变量 float calibration_factor = -40500.0; // 校准系数,初始为负值(根据传感器安装方向调整) float bottle_weight_grams = 0.0; // 空杯子的重量(皮重),单位克 float last_stable_weight = 0.0; // 上一次稳定的重量读数,用于判断变化 float water_drunk_ml = 0.0; // 本次已喝水量(毫升) float daily_goal_ml = 2000.0; // 每日饮水目标,默认2000毫升 unsigned long last_drink_time = 0; // 上次喝水的时间戳 const unsigned long REMINDER_INTERVAL = 3600000; // 提醒间隔:1小时(3600000毫秒) const float GRAMS_PER_ML = 1.0; // 重量与体积换算系数(假设水密度1g/ml)

代码解读

  • 使用SoftwareSerial库在引脚10和11上虚拟一个串口,这样我们就可以用bluetooth.println()来发送数据,而把硬件串口(引脚0,1)留出来给电脑调试使用,互不干扰。这是比原代码更合理的做法。
  • calibration_factor是核心参数。它不是一个固定值,必须通过校准获得。它表示传感器读数与真实重量(克)之间的比例关系。负号是因为传感器安装方向可能导致读数反向。
  • 我们引入了last_stable_weightwater_drunk_ml来更精确地追踪饮水过程,而不是简单判断一个阈值。

4.2 Setup() 初始化函数

void setup() { // 初始化硬件串口,用于调试输出(连接电脑) Serial.begin(9600); Serial.println(F("=== 智能饮水助手启动 ===")); // 初始化蓝牙软串口,波特率需与HC-05配置的UART波特率一致(38400) bluetooth.begin(38400); bluetooth.println(F("DEVICE READY")); // 向手机发送就绪信号 // 初始化蜂鸣器引脚为输出模式 pinMode(BUZZER_PIN, OUTPUT); digitalWrite(BUZZER_PIN, LOW); // 确保蜂鸣器初始为关闭状态 // 初始化HX711称重传感器 scale.begin(HX711_DT_PIN, HX711_SCK_PIN); scale.set_scale(calibration_factor); // 设置校准系数 scale.tare(); // 执行去皮操作,将当前重量设为零点 // 等待传感器稳定 Serial.println(F("请勿在传感器上放置任何物品...")); delay(2000); Serial.println(F("初始化完成。")); // 首次读取,获取环境零点漂移的参考值 last_stable_weight = scale.get_units(5); // 读取5次取平均,提高稳定性 Serial.print(F("当前零点读数: ")); Serial.println(last_stable_weight); // 通过串口发送简单操作指南 Serial.println(F("命令列表:")); Serial.println(F(" 't' - 执行去皮(将当前重量设为零)")); Serial.println(F(" 'c' - 开始校准流程")); Serial.println(F(" 'r' - 重置今日饮水量")); }

关键点解析

  • scale.tare():去皮功能。无论此时传感器上有什么(比如空杯子),执行后都会将当前重量定义为“0克”。这是使用前必须做的。
  • scale.get_units(5):参数5表示连续读取5次然后取平均值。这能有效滤除一些随机噪声,得到更稳定的重量值。这个技巧在传感器应用中非常常用。
  • 初始化时向蓝牙发送"DEVICE READY",可以让手机App在连接后立即知道设备已启动。

4.3 核心循环逻辑与重量处理

loop()函数以极高的频率不断运行,我们需要在其中高效地处理几件事:读取重量、判断是否喝水、检查是否需要提醒、处理串口命令。

void loop() { // 1. 读取当前重量(滤波后) float current_weight = scale.get_units(10); // 读取10次取平均,进一步平滑数据 // 2. 处理来自硬件串口(电脑)的命令 if (Serial.available()) { char cmd = Serial.read(); handleSerialCommand(cmd, current_weight); } // 3. 处理来自蓝牙(手机)的命令和数据 if (bluetooth.available()) { String btMsg = bluetooth.readStringUntil('\n'); // 读取直到换行符 btMsg.trim(); // 去除首尾空格或换行符 handleBluetoothCommand(btMsg, current_weight); } // 4. 核心逻辑:判断饮水动作 checkDrinkingAction(current_weight); // 5. 定时提醒逻辑 checkReminder(); // 6. (可选)定期向手机发送状态更新 static unsigned long last_report = 0; if (millis() - last_report > 10000) { // 每10秒报告一次 sendStatusToPhone(current_weight); last_report = millis(); } // 短暂延迟,避免循环过快 delay(50); }

重量读取的稳定性处理: 原始代码直接使用scale.get_units(),这容易受到瞬时干扰。我采用了两种滤波方法:

  1. 均值滤波get_units(10)本身就是在硬件层面进行10次采样平均。
  2. 软件阈值滤波:在checkDrinkingAction函数中,只有当重量变化超过一个“死区”阈值(例如5克)并保持一段时间,才被认为是有效动作,而不是手不小心碰了一下杯子。

4.4 关键功能函数实现

下面拆解几个最重要的子函数。

函数1:处理串口命令handleSerialCommand这个函数用于通过电脑调试,非常实用。

void handleSerialCommand(char cmd, float current_weight) { switch(cmd) { case 't': case 'T': scale.tare(); last_stable_weight = 0.0; Serial.println(F(">> 已去皮。当前重量已归零。")); bluetooth.println("STATUS:TARE_OK"); break; case 'c': case 'C': startCalibrationProcedure(); break; case 'r': case 'R': water_drunk_ml = 0.0; Serial.println(F(">> 今日饮水量已重置。")); break; default: Serial.print(F("当前重量: ")); Serial.print(current_weight); Serial.println(F(" g")); Serial.print(F("今日已喝: ")); Serial.print(water_drunk_ml); Serial.println(F(" ml")); } }

函数2:校准流程startCalibrationProcedure校准是获得准确重量的唯一方法。你需要一个已知重量的标准砝码(如500克)。

void startCalibrationProcedure() { Serial.println(F(">> 开始校准...")); Serial.println(F("请移走传感器上所有物品,然后按任意键继续。")); while(!Serial.available()); // 等待用户按键 Serial.read(); // 清空缓冲区 scale.tare(); // 去皮,确保零点准确 delay(1000); Serial.println(F("请将已知重量的标准砝码(如500g)放在传感器上,放稳后按任意键。")); while(!Serial.available()); Serial.read(); long known_weight_grams = 500; // 你的标准砝码重量,单位克 long raw_reading = scale.read_average(20); // 读取20次原始ADC平均值 // 计算校准系数:已知重量 / 原始读数 // HX711库的设计是:重量 = (原始读数 - 零点偏移) / 校准系数 // 因此,校准系数 = (原始读数 - 零点偏移) / 已知重量 // 由于之前执行了tare(),零点偏移已被归零,所以: calibration_factor = raw_reading / known_weight_grams; scale.set_scale(calibration_factor); // 应用新系数 Serial.print(F(">> 校准完成!新系数: ")); Serial.println(calibration_factor); // 可以将这个系数保存到EEPROM,下次开机自动读取,避免重复校准 }

原理剖析:校准的本质是建立传感器原始读数(一个无单位的数字)与实际物理重量(克)之间的数学关系。calibration_factor就是这个比例系数。通过已知重量的砝码,我们可以反算出这个系数。

函数3:判断饮水动作checkDrinkingAction这是项目的核心算法,需要巧妙地区分是正常喝水还是其他干扰。

void checkDrinkingAction(float current_weight) { static float drink_start_weight = 0.0; static bool drinking = false; const float DRINK_THRESHOLD = 10.0; // 变化超过10克才认为是开始/结束喝水 const unsigned long DRINK_TIMEOUT = 5000; // 超过5秒无变化认为喝水结束 float weight_change = last_stable_weight - current_weight; // 检测到重量显著减少,可能是开始喝水 if (!drinking && weight_change > DRINK_THRESHOLD) { drinking = true; drink_start_weight = last_stable_weight; Serial.println(F("检测到拿起水杯...")); } // 处于喝水状态中,且重量趋于稳定(变化很小) if (drinking && abs(weight_change) < 2.0) { static unsigned long stable_start = 0; if (stable_start == 0) { stable_start = millis(); } else if (millis() - stable_start > 1000) { // 稳定超过1秒 // 计算喝水量 float water_weight = drink_start_weight - current_weight; if (water_weight > 5.0) { // 忽略极小的变化 water_drunk_ml += water_weight * GRAMS_PER_ML; Serial.print(F("喝水量: ")); Serial.print(water_weight); Serial.println(F(" g")); Serial.print(F("今日累计: ")); Serial.print(water_drunk_ml); Serial.println(F(" ml")); // 发送到手机 bluetooth.print("DRINK:"); bluetooth.println(water_drunk_ml); last_drink_time = millis(); // 更新上次喝水时间 } // 重置状态 drinking = false; stable_start = 0; last_stable_weight = current_weight; // 更新稳定重量基准 } } else if (drinking) { // 如果重量还在变化,重置稳定计时器 // (这部分逻辑需要配合一个计时器变量,代码已简化) } // 超时处理:如果超过5秒重量还在变,可能不是喝水,而是其他干扰 if (drinking && (millis() - last_drink_time > DRINK_TIMEOUT)) { drinking = false; Serial.println(F("喝水动作超时,已重置。")); last_stable_weight = current_weight; } }

算法精髓:这里实现了一个简单的“状态机”。它不会因为重量一减少就立刻记录喝水,而是等待重量稳定下来后,计算从动作开始到稳定时的总减少量。这能有效避免拿起杯子又放下、或者只是碰了一下杯子导致的误记录。

函数4:向手机发送状态sendStatusToPhone定义一种简单的通信协议,方便手机App解析。

void sendStatusToPhone(float current_weight) { // 协议格式:TYPE:VALUE bluetooth.print("WEIGHT:"); // 当前重量 bluetooth.println(current_weight, 1); // 保留1位小数 bluetooth.print("TOTAL:"); // 累计饮水 bluetooth.println(water_drunk_ml, 0); // 整数毫升 bluetooth.print("GOAL:"); // 今日目标 bluetooth.println(daily_goal_ml, 0); // 计算并发送进度百分比 int progress = (daily_goal_ml > 0) ? (int)((water_drunk_ml / daily_goal_ml) * 100) : 0; bluetooth.print("PROGRESS:"); bluetooth.println(progress); }

5. 手机端应用构思与通信协议

原项目提供了一个Kodular开发的App下载链接。这里我提供一种更通用的思路,你可以用任何支持蓝牙串口的App(如Serial Bluetooth Terminal)进行测试,或者用MIT App InventorKodular这类图形化工具,甚至FlutterReact Native来开发自己的App。

5.1 简易通信协议设计

为了让Arduino和手机能够互相理解,我们需要约定好数据格式。上面代码中已经用到了,这里再明确一下:

  • 设备 -> 手机
    • WEIGHT:250.5当前杯子总重量(克)
    • DRINK:1250累计饮水量更新(毫升)
    • STATUS:TARE_OK去皮操作成功
    • DEVICE READY设备启动
  • 手机 -> 设备
    • TARE执行去皮命令
    • GOAL:2500设置每日目标为2500毫升
    • RESET重置今日饮水量

在手机App里,你需要建立一个蓝牙串口连接,然后不断读取来自Arduino的数据行,根据冒号前的“关键字”来解析和处理后面的“数值”。

5.2 使用现成App进行快速测试

在开发自定义App之前,强烈建议先用现成的串口调试App验证整个系统:

  1. 在手机应用商店搜索“Serial Bluetooth Terminal”或“蓝牙串口”并安装。
  2. 打开App,配对并连接“WaterAssistant”。
  3. 在App的发送框输入TARE并发送,观察Arduino串口监视器是否打印“>> 已去皮”。
  4. 拿起水杯喝一口水放回,观察App接收区是否会收到DRINK:xxx格式的数据。

这个测试能帮你快速确认硬件、蓝牙通信、核心逻辑全部工作正常,为后续开发扫清障碍。

6. 常见问题排查与调试心得

即使按照步骤操作,也可能会遇到各种问题。这里把我踩过的坑和解决方案汇总一下。

6.1 重量读数不稳定或跳变严重

  • 症状:数值在几十克甚至上百克范围内无规律跳动。
  • 可能原因及解决
    1. 供电干扰:这是最常见的原因。避免使用电脑USB口供电进行精密测量,尤其是笔记本电脑。改用手机充电器或稳压电源为Arduino供电。确保HX711模块的5V和GND连接牢固。
    2. 机械结构不稳:称重传感器必须承受垂直方向的力。检查杯子平台是否稳固,传感器是否被侧向挤压或悬空。可以用厚实、平整的底座和顶板。
    3. 软件滤波不足:尝试增加get_units()中的采样次数,或在代码中加入更复杂的滤波算法(如滑动平均滤波、卡尔曼滤波)。
    4. 校准系数错误:重新执行严格的校准流程。确保校准砝码准确,且放置时没有触碰其他物体。

6.2 蓝牙无法连接或连接后断线

  • 症状:手机搜不到设备,或配对后很快断开,数据收发不正常。
  • 可能原因及解决
    1. 电压分压电路错误:这是硬件上最大的坑。务必确认连接到HC-05 RXD的电压在3.3V左右。用万用表测量一下最保险。电阻值不精确可能导致电压偏高或偏低。
    2. 波特率不匹配:确保Arduino代码中bluetooth.begin(38400)的波特率,与用AT命令AT+UART=38400,0,0设置的波特率完全一致。
    3. 电源功率不足:当所有模块同时工作时,特别是蜂鸣器响起瞬间电流较大,可能导致Arduino的5V输出被拉低,蓝牙模块复位。尝试在Arduino的5V和GND之间并联一个100-470μF的电解电容稳压。
    4. 软件串口冲突SoftwareSerial库在某些Arduino型号上不稳定,特别是在高波特率下。可以尝试换用AltSoftSerial库(更稳定,但占用特定引脚),或者如果硬件串口空闲,直接将HC-05的TXD/RXD连接到Arduino的RX1/TX1(如果板子有的话)。

6.3 喝水量记录不准确

  • 症状:没喝水也记录,或者喝了一大口记录量却很少。
  • 可能原因及解决
    1. 阈值设置不当:检查代码中的DRINK_THRESHOLD(开始检测阈值)和判断稳定的阈值。如果环境振动大,需要适当调高DRINK_THRESHOLD(比如到15或20克)。如果水杯本身很轻,喝水动作慢,可能需要调低。
    2. 算法逻辑问题checkDrinkingAction函数中的状态机逻辑需要仔细调试。可以通过串口打印出drinkingweight_changecurrent_weight等变量的实时值,观察在一次真实的喝水动作中,这些值是如何变化的,从而调整超时时间(DRINK_TIMEOUT)和稳定判断时间。
    3. 去皮不准确:每次放上空杯子后,必须通过发送‘t’命令或手机App触发tare()操作,将空杯重量归零。杯子本身的重量必须被扣除。

6.4 项目优化与扩展方向

这个基础版本已经可以工作,但还有很大的优化和扩展空间:

  1. 数据持久化:使用Arduino的EEPROM存储calibration_factordaily_goal_ml甚至每日饮水记录,断电后不会丢失。
  2. 低功耗设计:如果想让设备电池供电,可以考虑使用Arduino Pro Mini等低功耗型号,并让设备大部分时间处于睡眠模式,仅定时唤醒检查重量或通过蓝牙中断唤醒。
  3. 无线升级(OTA):通过蓝牙接收新的固件程序并自我更新,无需再用数据线连接电脑。
  4. 多用户支持:在App端切换用户,Arduino根据不同的杯子重量(通过皮重区分)为不同用户记录数据。
  5. 云端同步:手机App将饮水数据上传到云端服务器,生成长期统计图表,并支持多设备同步。

这个项目从硬件连接到软件逻辑,完整地走通了一个物联网感知设备的开发流程。它涉及到的传感器数据采集、滤波处理、状态机逻辑、无线通信和简单协议设计,都是嵌入式开发中非常核心的概念。希望这份超详细的拆解,能帮你不仅做出这个饮水助手,更能理解背后每一步的缘由,从而具备独立设计和调试更多有趣项目的能力。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/31 12:16:22

你的竞品已在用:Gemini创意写作私有化部署方案(支持本地知识注入+品牌语音克隆+合规水印),仅限持证MCN机构申请

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;Gemini创意写作应用全景图 Gemini 不仅是强大的推理模型&#xff0c;更在创意写作领域展现出独特优势——它能理解隐喻、保持风格一致性、支持多轮叙事迭代&#xff0c;并原生适配长上下文&#xff08;…

作者头像 李华
网站建设 2026/5/31 12:16:00

联想刃7000K BIOS隐藏功能完全解锁指南:释放硬件性能潜力

联想刃7000K BIOS隐藏功能完全解锁指南&#xff1a;释放硬件性能潜力 【免费下载链接】Lenovo-7000k-Unlock-BIOS Lenovo联想刃7000k2021-3060版解锁BIOS隐藏选项并提升为Admin权限 项目地址: https://gitcode.com/gh_mirrors/le/Lenovo-7000k-Unlock-BIOS 你是否拥有一…

作者头像 李华
网站建设 2026/5/31 12:15:18

你的时间序列预测准吗?SPSS ARIMA建模常见的5个误区与避坑指南

你的时间序列预测准吗&#xff1f;SPSS ARIMA建模常见的5个误区与避坑指南在数据分析领域&#xff0c;时间序列预测一直是个既迷人又令人头疼的话题。当你在SPSS中完成了ARIMA建模&#xff0c;却发现预测结果与实际数据相差甚远时&#xff0c;那种挫败感可能让你怀疑人生。别担…

作者头像 李华
网站建设 2026/5/31 12:13:06

从4.0到5.0:一次失败的铅画纸打印实验与幼儿教具材料选择避坑指南

从4.0到5.0&#xff1a;一次失败的铅画纸打印实验与幼儿教具材料选择避坑指南 在幼儿教育实践中&#xff0c;教具的制作往往需要兼顾趣味性、安全性和耐用性。最近一次为班级设计骰子教具的经历&#xff0c;让我深刻体会到材料选择的重要性。原本希望通过升级材料来提升教具质量…

作者头像 李华
网站建设 2026/5/31 12:12:59

5秒永久保存B站视频:m4s-converter让你的珍藏永不失效

5秒永久保存B站视频&#xff1a;m4s-converter让你的珍藏永不失效 【免费下载链接】m4s-converter 一个跨平台小工具&#xff0c;将bilibili缓存的m4s格式音视频文件合并成mp4 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否曾在B站收藏了珍贵的教程…

作者头像 李华