Arduino+HX711称重传感器实战:从零打造高精度厨房电子秤(附校准技巧)
Arduino+HX711称重传感器实战:从零打造高精度厨房电子秤(附校准技巧)
不知道你有没有过这样的经历:照着菜谱准备食材,上面写着“面粉150克”,你手忙脚乱地翻出家里那个用了好几年、屏幕都有些模糊的厨房秤,放上碗,按下归零,小心翼翼地倒面粉,结果数字跳来跳去,最后停在152克——算了,差不多就行。但如果你是个对烘焙精度有要求的爱好者,或者正在严格控制饮食需要精确计量,这种“差不多”可能会毁掉一整盘马卡龙,或是让每日的营养摄入计算失去意义。
市面上的厨房电子秤,精度稍好一些的价格就不菲,而且功能单一。有没有可能自己动手做一个?成本更低、精度更高,还能根据自己的需求定制功能,比如一键切换克与盎司、记录每次的称重数据,甚至接入智能家居系统?答案是肯定的,而且门槛远比你想象的低。
今天,我们就用最常见的Arduino开发板,搭配一枚小小的HX711模块和一个称重传感器,从零开始,打造一台专为厨房环境优化的高精度电子秤。这不仅仅是简单的模块拼接,我们会深入探讨如何针对厨房常见的温湿度变化、台面轻微震动等干扰进行优化,分享真正可靠的砝码校准技巧,并教你用亚克力板制作一个既美观又防潮的称重托盘。你会发现,创客的乐趣不仅在于结果,更在于这个亲手搭建、调试并最终让一个想法“活”起来的过程。
1. 核心器件解析:为什么是HX711与称重传感器?
在开始动手之前,我们有必要先搞清楚手中的“武器”。市面上测量重量的方案很多,为什么DIY电子秤领域,HX711和电阻应变式称重传感器的组合几乎成了标准答案?这背后是精度、成本与易用性的完美平衡。
称重传感器,本质上是一个电阻应变片构成的电桥。你可以把它想象成一根微型的“弹簧”。当它受到压力时,内部的金属丝会被拉伸或压缩,导致电阻值发生极其微小的变化。一个典型的称重传感器通常有四个应变片,连接成惠斯通电桥。在没有负载时,电桥平衡,输出为零;当放置重物时,桥臂电阻变化,会输出一个与重量成正比的毫伏级差分电压信号。
注意:厨房常用的传感器量程一般在1kg至5kg之间。量程并非越大越好,选择一个接近你常用重量范围的传感器,能获得更好的相对精度。例如,主要称量调料和面粉,1kg量程比5kg更合适。
这个信号有多微弱呢?可能只有0-几毫伏。Arduino开发板自带的ADC(模拟数字转换器)引脚,虽然能读取模拟电压,但其分辨率和对于微小信号的捕捉能力远远不够。直接连接,读数会充满噪声且无法反映真实重量。这时,就需要一个专业的“信号放大器”和“高精度ADC”——这就是HX711模块的舞台。
HX711是一款专为电子秤设计的24位A/D转换器芯片。它的核心价值在于:
- 高精度24位ADC:提供高达2^24(约1677万)个数字输出代码,能分辨极其微小的重量变化。
- 集成可编程增益放大器(PGA):提供32、64、128倍增益可选,能直接将传感器输出的微弱信号放大到适合处理的水平。
- 内置稳压电路:可以直接为传感器提供稳定的激励电压,简化外部电路。
- 简单的二线串行接口:只需连接单片机的两个数字IO口进行通信,极大简化了接线。
两者结合,HX711负责将传感器“细微的叹息”放大并清晰地“翻译”成数字语言,交给Arduino处理。下表对比了DIY方案与普通市售厨房秤的核心差异:
| 特性维度 | DIY Arduino + HX711方案 | 普通市售厨房秤 |
|---|---|---|
| 核心精度潜力 | 依赖HX711的24位ADC,理论精度高,可达0.1g级别 | 取决于商用方案,中端产品多在0.1g-1g |
| 成本 | 低(传感器+HX711+Arduino Nano,总成本可控制在50元内) | 中低端产品价格相近,高精度产品昂贵 |
| 灵活性 | 极高,可自由编程,添加单位切换、数据记录、蓝牙/WiFi上传等功能 | 固化功能,无法扩展 |
| 可维护性 | 模块化设计,哪个部分坏了换哪个,传感器可单独更换 | 一体化设计,损坏通常需整体更换 |
| 学习价值 | 涉及电路、传感器、编程、校准等全方位实践 | 无 |
所以,选择这套方案,你得到的不仅仅是一个秤,更是一个可无限扩展的智能硬件平台。接下来,我们就进入实战环节。
2. 硬件搭建:从散件到原型的每一步
准备好以下材料,我们就可以开始组装了。清单力求精简,都是容易购得的模块:
- 主控:Arduino Uno 或 Nano × 1(推荐Nano,体积更小巧)
- 核心模块:HX711称重模块 × 1
- 传感器:电阻应变式称重传感器(推荐1kg或5kg,根据需求选择)× 1
- 显示:0.96寸OLED显示屏(I2C接口)或 LCD1602(I2C接口)× 1(OLED更省空间且清晰)
- 结构件:亚克力板(用于制作托盘和支架)、M3螺丝螺母套件、杜邦线(公对公、公对母)若干
- 校准工具:标准砝码(如100g、200g、500g。若无,可用已知重量的未开封包装食品替代,如500ml矿泉水约500g)
2.1 电路连接:一张图看懂的接线逻辑
接线是项目的基础,遵循“电源-信号-地”的清晰路径。下图清晰地展示了所有模块间的连接关系,建议在焊接或插接前先对照检查一遍。
[接线关系示意图 - 文字描述版]
称重传感器 (4线) -> HX711模块
红色 (E+ 或 VCC) -> E+
黑色 (E- 或 GND) -> E-
绿色/白色 (A+ 或 S+) -> A+
白色/绿色 (A- 或 S-) -> A-
HX711模块 -> Arduino Nano
VCC -> 5V
GND -> GND
DT (数据) -> D2 (可自定义)
SCK (时钟) -> D3 (可自定义)
OLED显示屏 (I2C) -> Arduino Nano
VCC -> 5V
GND -> GND
SDA -> A4 (或 Nano 的 D4)
SCL -> A5 (或 Nano 的 D5)
提示:HX711的DT和SCK引脚可以连接到Arduino任意空闲的数字引脚,只需在后续代码中相应修改即可。使用I2C显示屏时,务必确保SDA和SCL正确连接。
2.2 机械结构制作:亚克力托盘的防潮与稳定设计
厨房环境多水、多油,一个裸露的传感器和电路板显然不行。我们需要为它打造一个“家”。
设计思路:采用双层亚克力板结构。底层固定Arduino和HX711模块;上层作为称重托盘,通过四根立柱与底层连接,传感器则安装在底层中心,其受力点与托盘中心刚性连接。这样,托盘上的重量会直接、垂直地传递给传感器,避免侧向力影响精度。
制作步骤:
- 设计图纸:使用激光切割软件(如LaserCAD)或在线工具,设计两块大小相同的亚克力板(如10x10cm),以及四根高度约2-3cm的亚克力立柱。在底板中心开孔,用于固定传感器;在四角开孔,用于安装立柱和固定主板。
- 切割与组装:
- 将传感器用螺丝固定在底板中心。
- 将四根立柱用螺丝固定在底板四角。
- 将Arduino和HX711用铜柱或螺丝固定在底板上。
- 将顶板(托盘)通过螺丝与立柱上端固定。关键一步:在传感器受力点与托盘之间,需要用一个小的金属垫片或专用的传力件进行刚性连接,确保力能有效传递。
- 防潮处理:在所有电路板表面喷涂三防漆(绝缘漆),这是保护电路免受厨房水汽侵蚀最有效的方法。传感器本身通常有密封,但接线处可以用热熔胶或硅胶做轻微密封。
这个自制的结构不仅解决了放置问题,其刚性连接方式也比许多市售DIY套件的软性连接(如用塑料柱支撑)精度更高、重复性更好。
3. 软件驱动与基础读数:让Arduino“感知”重量
硬件就绪后,我们需要让Arduino学会与HX711对话,并计算出重量值。这里会涉及库的安装和基础代码的编写。
3.1 安装HX711库
打开Arduino IDE,依次点击 工具 -> 管理库...,在搜索框中输入“HX711”,找到由 Bogdan Necula 等人维护的 HX711 库进行安装。这个库成熟稳定,函数丰富。
3.2 编写初始测试代码
我们先写一个最简单的程序,读取HX711的原始ADC值,验证硬件连接是否正常。
#include "HX711.h"
// 定义HX711连接的引脚 (DT, SCK)
HX711 scale;
#define LOADCELL_DOUT_PIN 2
#define LOADCELL_SCK_PIN 3
void setup() {
Serial.begin(115200);
Serial.println("HX711 初始化和测试");
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
// 初始化库,默认使用通道A,增益128
}
void loop() {
if (scale.is_ready()) {
long rawReading = scale.read(); // 获取原始ADC值
Serial.print("原始读数: ");
Serial.println(rawReading);
} else {
Serial.println("HX711未就绪,请检查连接。");
}
delay(500);
}
上传代码后,打开串口监视器(波特率设为115200)。你应该能看到一串不断变化的数值。此时,用手轻轻按压或松开托盘,观察数值应有大幅度的增减变化。这说明传感器和HX711的通信是正常的。这个原始值本身没有单位,它只是一个与施加的力成正比的数字。
4. 校准的艺术:从原始值到精确克重的关键一跃
校准是电子秤制作的灵魂,也是最容易出错的环节。校准的目的,是找到原始ADC读数与实际重量之间的线性比例关系(斜率)和零点偏移(截距)。公式很简单:重量 = (原始读数 - 偏移量) / 比例系数。
4.1 单点校准法(快速上手)
对于厨房秤,如果量程不大且对绝对精度要求不是极端苛刻,单点校准是最高效的方法。你需要一个已知重量的标准砝码(如100g)。
操作流程:
- 确保秤盘空载,上传并运行以下校准代码。
- 根据串口提示,先进行“去皮”(Tare),此时偏移量被设定。
- 根据提示放上已知重量的砝码。
- 程序会自动计算比例系数,并显示当前测量值。
#include "HX711.h"
HX711 scale;
#define LOADCELL_DOUT_PIN 2
#define LOADCELL_SCK_PIN 3
// 已知砝码的重量(单位:克)
const float KNOWN_WEIGHT = 100.0; // 例如100g砝码
float scaleFactor = 1.0; // 比例系数,将通过校准得出
void setup() {
Serial.begin(115200);
Serial.println("=== HX711 单点校准程序 ===");
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
scale.set_scale(); // 初始化比例系数为1
scale.tare(); // 去皮,将当前读数设为偏移量
Serial.println("请移走秤盘上所有物品...");
delay(3000);
Serial.println("正在计算零点偏移...完成。");
Serial.println("现在请将已知重量的砝码(" + String(KNOWN_WEIGHT) + "g)放在秤盘中心。");
Serial.println("放置好后,请等待5秒...");
delay(5000);
long rawReadingWithWeight = scale.read_average(10); // 读取10次取平均
scaleFactor = rawReadingWithWeight / KNOWN_WEIGHT; // 计算比例系数
Serial.print("计算得到的比例系数 (scale factor) 为: ");
Serial.println(scaleFactor, 6); // 打印6位小数
scale.set_scale(scaleFactor); // 设置比例系数
Serial.println("校准完成!当前读数:");
}
void loop() {
if (scale.is_ready()) {
float currentWeight = scale.get_units(5); // 读取5次平均,并应用比例系数
Serial.print("重量: ");
Serial.print(currentWeight, 1); // 显示1位小数
Serial.println(" g");
}
delay(1000);
}
4.2 两点校准法(追求高精度)
单点校准假设零点绝对准确且线性完美。但传感器可能存在微小的非线性或零点温漂。两点校准通过“空载”和“满量程附近一个点”来确定一条更准确的直线,能更好地补偿这些误差。
操作流程:
- 空载状态下,记录一个原始读数作为零点参考(或直接
tare())。 - 放置一个接近量程50%-80%的已知重物(如对于1kg传感器,放500g砝码)。
- 程序通过两点的差值计算比例系数。
核心计算逻辑如下:
// 假设:
// raw_zero: 空载时的原始读数
// raw_weight: 放置已知重量KNOWN_WEIGHT时的原始读数
// 则比例系数 scaleFactor = (raw_weight - raw_zero) / KNOWN_WEIGHT;
在实际代码中,我们可以先tare()得到raw_zero(此时为0),然后读取砝码的raw_weight,计算scaleFactor。许多HX711库的get_units()函数内部已经封装了这个计算。
4.3 厨房环境下的校准避坑指南
- 预热:电子元件通电后性能会逐渐稳定。校准前,让系统通电工作5-10分钟。
- 环境稳定:避免在校准和称重时有风吹、台面震动。将秤放在水平、坚固的台面上。
- 砝码放置:始终将砝码放在托盘中心,确保受力垂直。
- 温漂补偿:如果发现早上和晚上的读数有微小差异(特别是金属传感器受温度影响),可以考虑在代码中存储一个温度补偿系数,但这属于进阶优化。
- 验证:校准后,用多个不同重量的砝码(如50g, 200g)进行验证,检查线性度。
校准完成后,那个至关重要的scaleFactor(比例系数)应该被记录下来,并硬编码到你的最终应用程序中,避免每次上电都需要重新校准。
5. 功能增强:打造一个实用的厨房智能秤
基础称重实现了,但一个真正好用的厨房秤还需要一些贴心功能。让我们用代码为它赋能。
5.1 单位切换(克/盎司/毫升)
对于烘焙和跨国菜谱,单位切换是刚需。我们可以在程序中预设转换系数,并通过按钮切换。
#include <Wire.h>
#include <Adafruit_SSD1306.h> // OLED库
#include "HX711.h"
HX711 scale;
#define LOADCELL_DOUT_PIN 2
#define LOADCELL_SCK_PIN 3
#define BUTTON_PIN 4 // 用于切换单位的按钮
Adafruit_SSD1306 display(128, 64, &Wire, -1);
enum WeightUnit { GRAM, OUNCE, ML_WATER }; // 枚举定义单位
WeightUnit currentUnit = GRAM;
float currentWeight = 0.0;
// 转换系数
const float GRAMS_PER_OUNCE = 28.3495;
const float ML_PER_GRAM_WATER = 1.0; // 简化:1克水 ≈ 1毫升
void setup() { /* 初始化scale, display, 校准scaleFactor */ }
void loop() {
// 检测按钮切换单位
if (digitalRead(BUTTON_PIN) == LOW) {
delay(50); // 消抖
if (digitalRead(BUTTON_PIN) == LOW) {
switch(currentUnit) {
case GRAM: currentUnit = OUNCE; break;
case OUNCE: currentUnit = ML_WATER; break;
case ML_WATER: currentUnit = GRAM; break;
}
while(digitalRead(BUTTON_PIN) == LOW); // 等待松开
}
}
// 读取重量
currentWeight = scale.get_units(5);
// 单位转换
float displayValue = currentWeight;
String unitStr = "g";
switch(currentUnit) {
case OUNCE:
displayValue = currentWeight / GRAMS_PER_OUNCE;
unitStr = "oz";
break;
case ML_WATER:
displayValue = currentWeight * ML_PER_GRAM_WATER; // 假设称的是水
unitStr = "ml";
break;
}
// 在OLED上显示
display.clearDisplay();
display.setTextSize(2);
display.setCursor(0,0);
display.print(displayValue, 1); // 显示1位小数
display.println(unitStr);
display.display();
delay(200);
}
5.2 去皮与累计功能
去皮功能在混合多种食材时非常有用。累计功能则可以计算连续添加物品的总重。
// 在全局变量中定义
float tareOffset = 0;
float cumulativeWeight = 0;
bool cumulativeMode = false;
// 在loop中检测两个不同的按钮
void loop() {
// ... 单位切换代码 ...
float rawWeight = scale.get_units(5);
// 按钮A:去皮
if (digitalRead(BUTTON_A_PIN) == LOW) {
tareOffset = rawWeight;
scale.set_offset(scale.get_offset() + tareOffset); // 库函数方式更佳
}
// 按钮B:累计模式开关/清零
if (digitalRead(BUTTON_B_PIN) == LOW) {
delay(50);
if (digitalRead(BUTTON_B_PIN) == LOW) {
if (!cumulativeMode) {
cumulativeMode = true;
cumulativeWeight = 0;
} else {
// 长按清零累计值
cumulativeWeight = 0;
}
while(digitalRead(BUTTON_B_PIN) == LOW);
}
}
float netWeight = rawWeight - tareOffset;
if (netWeight < 0.1) netWeight = 0; // 忽略微小噪声
if (cumulativeMode && netWeight > 0.5) { // 检测到稳定新物品
cumulativeWeight += netWeight;
// 这里可以添加逻辑,在物品稳定后(如重量2秒不变)才累加
}
// 显示净重或累计重...
}
5.3 数据记录与简单统计(进阶)
通过添加一个实时时钟模块(如DS3231)和SD卡模块,你可以让电子秤记录每次称重的时间、物品和重量,生成简单的日志文件,用于分析饮食或烘焙实验。
6. 优化与调试:解决那些烦人的小问题
即使按照步骤操作,你可能还是会遇到一些典型问题。这里提供一份快速排查指南。
-
问题:读数不稳定,数值跳动。
- 检查电源:确保Arduino和HX711供电稳定。尝试使用外部5V电源适配器为Arduino供电,而非USB口。
- 检查接线:DT和SCK信号线是否过长?尽量缩短至15cm内。传感器线是否可靠连接?
- 软件滤波:在代码中增加软件滤波。
scale.get_units(10)中的参数10就是读取次数,增大它(如20)可以平滑数据,但会降低响应速度。也可以使用移动平均滤波。// 简单的移动平均滤波示例 #define FILTER_SIZE 5 float weightBuffer[FILTER_SIZE]; int bufferIndex = 0; float filteredWeight = 0; float raw = scale.get_units(3); weightBuffer[bufferIndex] = raw; bufferIndex = (bufferIndex + 1) % FILTER_SIZE; filteredWeight = 0; for (int i=0; i<FILTER_SIZE; i++) { filteredWeight += weightBuffer[i]; } filteredWeight /= FILTER_SIZE;
-
问题:放上重物后读数归零或为负。
- 传感器方向:确认传感器受力方向正确。通常传感器上有一个箭头标记,指示受力面。
- 接线错误:检查传感器四根线(红黑绿白)与HX711模块的E+, E-, A+, A-是否一一对应。不同厂家颜色编码可能不同,以说明书为准。
-
问题:校准后,换一个重物测试误差很大。
- 线性度:你的传感器可能在量程内线性不佳。尝试用两点校准,并使用量程中段的砝码进行校准。
- 机械结构:检查托盘与传感器的连接是否牢固,是否有翘曲或侧向力。确保结构刚性。
-
问题:每次断电重启后需要重新校准。
- 保存系数:将最终校准得到的
scaleFactor和offset(通过scale.get_offset()获得)保存到Arduino的EEPROM中。上电时从EEPROM读取并设置。#include <EEPROM.h> struct ScaleCalibration { float scaleFactor; long offset; } calData; void saveCalibrationToEEPROM() { EEPROM.put(0, calData); } void loadCalibrationFromEEPROM() { EEPROM.get(0, calData); scale.set_scale(calData.scaleFactor); scale.set_offset(calData.offset); }
- 保存系数:将最终校准得到的
完成以上所有步骤后,你得到的将不再是一个简单的测量工具。它是一台完全按照你的厨房习惯定制的高精度秤,你了解它的每一个细节,可以随时修复或升级它。从一堆散件到一个稳定工作的设备,这种成就感是购买任何成品都无法替代的。更重要的是,在这个过程中积累的关于传感器、信号处理、校准和结构设计的经验,会让你在面对下一个创意项目时更加游刃有余。
更多推荐

所有评论(0)