پروژه سنجش وزن • لودسل و HX711 • ESP32 / Arduino
برای ساخت یک ترازوی رومیزی، خواندن عدد خام کافی نیست: نصب مکانیکی، سیمبندی پل، تغذیه و کالیبراسیون باید کنار هم درست باشند. در این آموزش یک لودسل چهار سیمه را با HX711 میخوانیم، صفر و ضریب تبدیل را با جرم مرجع تعیین میکنیم و خطاهای رایج را قدمبهقدم جدا میکنیم.
این پروژه برای نمونهسازی ترازو، پایش مقدار مواد و آموزش ابزار دقیق است؛ دقت تضمینشده یا تأییدیه ترازوی تجاری ندارد.

در یک نمونه اولیه میتوان جرم قطعات کوچک روی سینی، تغییر مقدار مواد داخل یک ظرف سبک یا تکرارپذیری یک سازوکار توزین را ثبت کرد. اگر هدف فروش محصول است، اول بازه بار، خطای مجاز، شرایط دمایی و روش نصب را مشخص کنید؛ بعد ظرفیت لودسل را انتخاب کنید. اضافه کردن چند رقم اعشار به نمایشگر جای این مرحله را نمیگیرد.
دلیل انتخاب این موضوع برای بازار ایران فقط نام آشنا نیست: ماژول HX711 و لودسل مکمل در ECA عرضه میشوند و صفحه ماژول پرسشهای دارای نشان خریدار درباره راهاندازی با ESP32 دارد. این نشانه کاربرد و تقاضاست، نه آمار فروش یا تضمین مشتری. لینک دقیق و تاریخ بررسی پایین مقاله آمده است.
لودسل تغییر شکل مکانیکی را به تغییر سیگنال الکتریکی پل تبدیل میکند. HX711 خودِ حسگر وزن نیست؛ تقویتکننده و مبدل آنالوگ به دیجیتال برای خواندن سیگنال کوچک پل است. خروجی آن ابتدا یک عدد خام است و برای تبدیل به گرم به صفر و ضریب کالیبراسیون نیاز داریم.
زنجیره پروژه چنین است: بار روی سازه → لودسل → ورودی تفاضلی HX711 → عدد خام → کالیبراسیون → جرم تخمینی. خرابی یا خطای هر مرحله میتواند نتیجه نهایی را تغییر دهد. دسته فشار، وزن و جریان را برای ادامه این مسیر ببینید.
| مشخصه تراشه | مقدار | نتیجه برای پروژه |
|---|---|---|
| خروجی ADC | ۲۴ بیت علامتدار | تعداد بیت، دقت تضمینشده ترازو نیست. |
| بهره ورودی | A: ۱۲۸ یا ۶۴؛ B: ۳۲ | کد این مقاله کانال A و بهره ۱۲۸ را به کار میبرد. |
| نرخ خروجی با نوسانساز داخلی | ۱۰ یا ۸۰ نمونه در ثانیه | پایه RATE تعیینکننده است؛ همه ماژولها کلید ندارند. |
| زمان نشست خروجی | ۴۰۰ میلیثانیه در ۱۰ SPS؛ ۵۰ در ۸۰ SPS | پس از روشنشدن یا تغییر تنظیمات، خروجی فوراً پایدار نیست. |
مرجع: دیتاشیت HX711، شرکت AVIA، نسخه ۲، جدولهای ۱ تا ۳. مشخصات تراشه را با ظرفیت و دقت لودسل یا کیفیت برد فروشگاهی یکی نگیرید.
وزن خود سینی هم بخشی از بار مکانیکی است؛ صفر کردن نرمافزاری آن را از ظرفیت فیزیکی حذف نمیکند. این متن ظرفیت یا حد اضافهبار تازهای برای حسگر تعیین نمیکند. لودسل سه سیمه نیمپل را هم نباید بدون ساخت پل مناسب، جای نمونه چهار سیمه فرض کرد.
ابتدا شماتیک و نوشتههای همان ماژول را بررسی کنید. برای نمونه سازگار با ۳٫۳ ولت، تغذیه و منطق HX711 را با ESP32 همسطح نگه دارید. در Uno میتوان از ماژول سازگار با ۵ ولت استفاده کرد. در بردهایی با تغذیه آنالوگ و دیجیتال جدا، هر پایه وظیفه خودش را دارد؛ VIN، VCC و VIO الزاماً مترادف نیستند.
روی بعضی بردهای عمومی، رگولاتور تحریک لودسل با ورودی ۳٫۳ ولت به ولتاژ نامی خودش نمیرسد. بنابراین هم ولتاژ E+ نسبت به E− و هم سازگاری آن با لودسل را کنترل کنید. راهنمای پایههای نسخه Adafruit یک مثال مستند است، نه نقشه همه بردهای بازار.
| پایه ماژول | ESP32 در این کد | Arduino Uno در این کد | نکته |
|---|---|---|---|
| VCC / VIN | ۳V۳ برای ماژول سازگار | ۵V برای ماژول سازگار | شرایط بخش تغذیه را اول بررسی کنید. |
| GND | GND | GND | زمین مشترک |
| DOUT / DT / DATA | GPIO19 | D2 | خروجی داده از HX711 |
| PD_SCK / SCK | GPIO18 | D3 | کلاک از میکروکنترلر |
| E+ و E− | تحریک مثبت و منفی لودسل | مطابق سند همان حسگر | |
| A+ و A− | خروجی سیگنال مثبت و منفی پل | کانال مورد استفاده این برنامه | |
این شمارهها انتخاب نرمافزاری مثالاند؛ روی ESP32-S3، بردهای خاص یا بردی که پایهها مصرف دیگری دارند، پایه آزاد مناسب انتخاب و ثابتهای ابتدای کد را اصلاح کنید. رنگ سیمها استاندارد همگانی نیست. اگر تنها راهنمای شما یک عکس مشابه است، قبل از روشنکردن اطلاعات مدل واقعی را پیدا کنید.
دو خط DATA و SCK به معنی I2C نیستند؛ این تراشه آدرس I2C ندارد و اسکنر I2C آن را پیدا نمیکند. پروتکل اختصاصی آن خروجی ۲۴ بیتی و پالسهای انتخاب کانال و بهره دارد. بالا ماندن PD_SCK بیش از ۶۰ میکروثانیه، تراشه را وارد حالت کممصرف میکند؛ نوشتن درایور بدون توجه به زمانبندی میتواند خواندن را مختل کند.
در این پروژه از کتابخانه bogde/HX711 نسخه ۰٫۷٫۵ استفاده میکنیم. پیش از هر خواندن، آمادهشدن داده را با مهلت محدود بررسی میکنیم تا نبود ماژول به انتظار بیپایان در مرحله آمادهشدن منجر نشود. فقط همین حلقه برنامه باید مسئول خواندن ADC باشد.
برای لودسل تیرکی تکنقطهای، طبق نقشه سازنده یک سمت روی پایه و سمت دیگر به سینی متصل میشود و ناحیه تغییر شکل باید آزاد بماند. این روش را به همه شکلهای لودسل تعمیم ندهید. تماس سینی با بدنه، کابل کشیده، پیچ نامناسب یا بار خارج از محل تعیینشده میتواند بخشی از نیرو را از مسیر حسگر خارج کند.
آزمون اولیه را با بار کم و کنترلشده انجام دهید؛ با فشار دست یا ضربه، ظرفیت ایمن معلوم نمیشود. قبل از تغییر سیمبندی، تغذیه را قطع کنید. اگر ماژول داغ شد، ادامه آزمایش با روشنوخاموش کردن مکرر مناسب نیست؛ اتصال کوتاه و قطبیت تغذیه را بررسی کنید.
t بفرستید. میانگین خام به عنوان صفر ثبت میشود.c 500 بفرستید؛ فقط اگر جرم مرجع واقعاً ۵۰۰ گرم است.countsPerGram = (rawKnown - rawZero) / knownMassGrams
massGrams = (rawNow - rawZero) / countsPerGramضریب منفی الزاماً خطا نیست؛ جهت سیگنال پل میتواند علامت را عوض کند. ضریب نمونه اینترنتی را کپی نکنید. صفر کردن خطای ضریب را اصلاح نمیکند و کالیبراسیون تکنقطهای هم بهتنهایی خطیبودن کل بازه را اثبات نمیکند.
کتابخانه HX711 با نویسنده bogde را نصب کنید؛ نسخه مبنای این مثال ۰٫۷٫۵ است. Serial Monitor را روی ۱۱۵۲۰۰ و پایان خط Newline تنظیم کنید. برنامه عمداً در شروع خودکار صفر نمیکند تا بار موجود روی سینی پنهان نشود. ابتدا فقط عدد خام نمایش داده میشود.
#include <Arduino.h>
#include <HX711.h> // bogde/HX711 0.7.5
#include <math.h>
#include <stdlib.h>
#include <string.h>
#if defined(ARDUINO_ARCH_ESP32)
const uint8_t DATA_PIN = 19, CLOCK_PIN = 18;
#else
const uint8_t DATA_PIN = 2, CLOCK_PIN = 3; // Arduino Uno
#endif
HX711 adc;
double zeroRaw = 0, countsPerGram = 0;
bool zeroValid = false, calibrated = false;
char line[48];
uint8_t used = 0;
bool overflowed = false;
unsigned long lastReport = 0;
// Each sample has its own timeout. One task owns the ADC.
bool averageRaw(uint8_t count, double &value) {
int64_t sum = 0;
for (uint8_t i = 0; i < count; ++i) {
if (!adc.wait_ready_timeout(1200, 1)) {
Serial.println(F("ERROR: HX711 timeout; check power / DATA / SCK."));
return false;
}
long raw = adc.read();
if (raw == 8388607L || raw == -8388608L) {
Serial.println(F("ERROR: ADC saturated; check bridge wiring / load."));
return false;
}
sum += raw;
}
value = (double)sum / count;
return true;
}
void command(char *text) {
double raw;
if (strcmp(text, "t") == 0) {
Serial.println(F("Tare: keep platform empty and still..."));
if (averageRaw(20, raw)) {
zeroRaw = raw;
zeroValid = true;
Serial.print(F("zeroRaw=")); Serial.println(zeroRaw, 2);
}
} else if (text[0] == 'c' && text[1] == ' ') {
char *end;
double grams = strtod(text + 2, &end);
while (*end == ' ') ++end;
if (!zeroValid || end == text + 2 || *end != '\0' ||
!isfinite(grams) || grams <= 0) {
Serial.println(F("First send t empty; then c <known mass in grams>."));
return;
}
Serial.println(F("Calibration: keep known mass still..."));
if (!averageRaw(20, raw)) return;
double factor = (raw - zeroRaw) / grams;
if (!isfinite(factor) || fabs(factor) < 0.000001) {
Serial.println(F("ERROR: no useful span; check known load / wiring."));
return;
}
countsPerGram = factor; // Negative sign is legitimate.
calibrated = true;
Serial.print(F("countsPerGram=")); Serial.println(countsPerGram, 6);
Serial.println(F("Verify with OTHER known masses; not saved after reset."));
} else if (strcmp(text, "p") == 0) {
Serial.print(F("zeroRaw=")); Serial.println(zeroRaw, 2);
Serial.print(F("countsPerGram=")); Serial.println(countsPerGram, 6);
Serial.print(F("calibrated=")); Serial.println(calibrated);
} else {
Serial.println(F("Commands: t (empty tare), c 500 (known 500 g), p"));
}
}
void setup() {
Serial.begin(115200);
adc.begin(DATA_PIN, CLOCK_PIN, 128); // Channel A, gain 128
delay(500);
Serial.println(F("HX711: no automatic tare. Send t only with EMPTY platform."));
Serial.println(F("Then place a known mass and send c <grams>; newline enabled."));
}
void loop() {
while (Serial.available()) {
char ch = (char)Serial.read();
if (ch == '\r') continue;
if (ch == '\n') {
line[used] = '\0';
if (overflowed) Serial.println(F("ERROR: command too long."));
else if (used) command(line);
used = 0;
overflowed = false;
} else if (used < sizeof(line) - 1 && !overflowed) {
line[used++] = ch;
} else overflowed = true;
}
if (millis() - lastReport >= 1000) {
double raw;
if (averageRaw(5, raw)) {
Serial.print(F("raw=")); Serial.print(raw, 1);
if (calibrated) {
Serial.print(F(" grams="));
Serial.print((raw - zeroRaw) / countsPerGram, 1);
} else Serial.print(F(" NOT CALIBRATED"));
Serial.println();
}
lastReport = millis();
}
}فرمان p پارامترها را چاپ میکند. کالیبراسیون این مثال پس از ریست ذخیره نمیماند؛ در محصول نهایی ذخیرهسازی معتبر، کنترل بازه بار و سیاست صفر کردن لازم است. خواندن میانگین و فرمان کالیبراسیون چند تبدیل زمان میبرد؛ این حلقه برای کنترل بلادرنگ موتور طراحی نشده است.
دانلود مثال کامل و برنامه تشخیص داده خام. هر فایل ino را در پوشه مستقل همنام خودش قرار دهید. کدها برای Uno و ESP32 کامپایل شدهاند؛ آزمایش روی سختافزار فیزیکی انجام نشده است.
اول عدد خام را با سینی خالی، سپس با جرم ثابت ثبت کنید. دامنه تغییرات، برگشت به صفر و تفاوت قرائت در نقاط مختلف سینی را جداگانه یادداشت کنید. اگر جابهجایی محل بار نتیجه را عوض میکند، قبل از افزایش میانگینگیری، سراغ مکانیک بروید.
سیم سیگنال را کوتاه و دور از مسیر موتور، رله و مبدل سوئیچینگ نگه دارید. برای بررسی منشأ اختلال، مصرفکنندههای مزاحم را موقتاً از پروژه آزمایشی جدا و همان آزمون را تکرار کنید. میانگینگیری بیشتر ممکن است خروجی را آرامتر کند، اما پاسخ را کند میکند و خطای مکانیکی یا ضریب غلط را حذف نمیکند.
برای ثبت اثر دما میتوانید مسیر سنجش دما با DS18B20 را اضافه کنید؛ صرف ثبت دما به معنی جبرانسازی خودکار خطای وزن نیست.
| نشانه | بررسی اول | اقدام بعدی |
|---|---|---|
| TIMEOUT | تغذیه، زمین مشترک، شماره DATA/SCK | شماتیک برد و وضعیت کلاک را کنترل کنید؛ اسکن I2C کاربرد ندارد. |
| SATURATED | سیمهای پل، اتصال A و E، بار واردشده | بار را ایمن بردارید؛ اشباع را با ضریب نرمافزاری پنهان نکنید. |
| با بار عدد تغییر نمیکند | مسیر انتقال نیرو و اتصال پل | برنامه خام را اجرا کنید و تماس سینی با بدنه را بررسی کنید. |
| پس از برداشتن بار صفر برنمیگردد | گیر مکانیکی، ناپایداری و تغییر شرایط | چند چرخه بارگذاری کنترلشده ثبت کنید؛ دائماً صفر نکنید. |
| وزن چند برابر یا با علامت غلط است | واحد جرم مرجع و ضریب | کالیبراسیون را با جرم معلوم تکرار کنید؛ علامت را بیدلیل حذف نکنید. |
| اعشار زیاد ولی نتیجه ناپایدار | کیفیت نصب و دامنه نویز واقعی | تفکیک نمایش را متناسب با آزمون واقعی انتخاب کنید. |
بررسی در : دو صفحه زیر وضعیت موجود و امکان افزودن به سبد خرید نشان میدادند. موجودی ممکن است تغییر کند؛ مشخصات نسخه ارسالی، ولتاژ، پایهها و ظرفیت را هنگام سفارش دوباره کنترل کنید. قیمت ثابت درج نشده است.
برد مبدل برای خواندن لودسل؛ خود حسگر و سینی ترازو نیست. عکس ابتدای مقاله مربوط به نسخه Adafruit است، نه همین محصول.
گزینه مکمل برای بررسی پروژه سبک؛ ظرفیت مناسب شما به سینی، بار و شرایط مکانیکی بستگی دارد. پیش از انتخاب، ولتاژ تحریک توصیهشده و سیمبندی نسخه واقعی را از فروشنده بگیرید.
وجود سؤالهای خریداران در صفحه ماژول نشانه استفاده عملی است، اما تعداد نظرها را معادل تعداد فروش یا تضمین تقاضا نمیگیریم. توضیح پروتکل و مقادیر الکتریکی این آموزش از اسناد سازنده گرفته شده، نه متن تبلیغاتی فروشگاه.
خیر. رابط آن دو سیمه اختصاصی با DATA و PD_SCK است و آدرس I2C ندارد؛ اسکنر I2C برای پیدا کردن آن مناسب نیست.
خیر. دقت نهایی به لودسل، ظرفیت، مکانیک، نویز و کالیبراسیون وابسته است و باید با جرمهای مرجع آزمایش شود.
تنها اگر طراحی همان برد سطح دیجیتال سازگار با ۳٫۳ ولت را تضمین کند. خروجی DATA پنجولتی نباید مستقیم به ESP32 متصل شود.
جهت سیگنال پل میتواند ضریب را منفی کند. اگر جرمهای مرجع درست خوانده میشوند، منفی بودن ضریب بهتنهایی ایراد نیست.
در مثال این مقاله خیر. پس از ریست باید دوباره صفر و کالیبراسیون انجام شود؛ ذخیره پارامترها در حافظه پایدار مرحله توسعه جداگانه است.
برای ساخت سامانه ثبت چندحسگری، سنجش نور با BH1750 یا فشار هوا با BMP280 را به مسیر وزن اضافه کنید؛ تفاوت رابط این حسگرهای I2C با HX711 را حفظ کنید. مرور پایهها از راهنمای سنسورها و دوره سنسورها در ۷ قدم در دسترس است.
متن و مثال پروژه: LCENJ. کامپایل نرمافزاری جای آزمون فیزیکی، کالیبراسیون قابل ردیابی یا ارزیابی یک محصول نهایی را نمیگیرد.