
简介基于51单片机的蓝牙电子秤设计资料是一份面向电子类学生、课程设计与竞赛选手的完整项目方案覆盖从硬件搭建、单片机编程到手机端App联调的整个流程。整套资源共47个文件压缩包约12.95MB文件类型较为齐全其中pdf与sch为原理图文档便于查看与修改c和h为51单片机源程序hex可直接烧录apk为手机端蓝牙称重App图片和doc/pdf文档则提供实物效果、开题报告与元件清单。已有2514人学习/下载适合单片机爱好者与毕业生作为参考模板。内容按“原理图-源程序-App-文档”组织读者既能依据电路设计完成称重传感器与蓝牙模块的接线也能通过源码理解数据采集、按键标定与蓝牙透传逻辑再借助配套App观察手机实时重量显示。相比网上零散资料这套资源结构清晰特别适合快速复用与二次开发。1. 基于51单片机蓝牙电子秤到底在做什么放在桌上的电子秤撕开外壳后你会看到核心无非三样东西受力变形的金属体、负责放大的模拟电路、把模拟电压变成数字的ADC。而基于51单片机蓝牙电子秤就是把这三样拆开用一颗STC89C52或AT89S52这样的老芯片加上HC-05蓝牙模块再配一个手机APP组成一套可以无线上位机看读数的完整系统。它解决的痛点很具体带显示的秤不能自动记录带串口的秤又拖着线有了蓝牙单片机把重量数据通过串口发给模块手机APP搜到模块就能实时显示、保存历史。适合做课程设计、毕设也适合想自己攒一台物联网秤的入门工程师。这套东西的难点不在于单片机本身而在于三件事HX711的时序能不能读准、标定系数怎么换算成真正的克数、APP端蓝牙socket的数据流怎么切干净。2. 原理图与硬件选型称重传感器到HC-05的每一根线2.1 称重传感器的量程、激励电压与接线方式电子秤的核心是电阻应变式称重传感器常见的是四臂惠斯通电桥。它的输出是差分信号满量程时典型灵敏度在1mV/V到2mV/V之间。比如一个5kg量程的传感器激励电压5V时满量程输出只有5mV到10mV这个信号直接进51单片机的ADC根本不够看所以必须经过专用ADC芯片放大和数字化。选择量程时有个经验让日常称重范围落在传感器满量程的20%到80%之间。做厨房秤就选5kg做物流秤选50kg想兼容人体秤就选100kg但注意量程越大小重量段的线性度和分辨率越差。原理图上传感器一般留4个接线端子E、E-接激励电源S、S-接信号输出。我一般会在E和E-之间加一个10uF电解电容并联0.1uF瓷片电容滤掉电源纹波S和S-之间加104电容滤高频干扰。这些细节在原理图里看起来不起眼但实际读数稳定度全靠它们。2.2 HX711的接口参数与原理图关键连接HX711是24位高精度ADC内置可编程增益放大器增益可选32、64、128。电子秤常用增益128对应的满量程差分输入范围大约±20mV。它的输出接口是两线PD_SCK时钟和DOUT数据单片机把SCK拉低时DOUT输出高表示数据准备好读取时每给一个上升沿DOUT移出一个位24个脉冲后读完一次数据。第25个脉冲会把增益切换位也移出来用于选择下一次转换的通道和增益。在原理图上HX711的AVDD一般接5VVSUP接5VVFB和BASE引脚用于外部稳压可以悬空或按数据手册接。RATE引脚接地时输出速率10Hz接VCC时80Hz。电子秤这种静态称重场景用10Hz足够而且抗工频干扰更好。单片机只需要两根普通IO口我通常选P1.0和P1.1但不建议和蜂鸣器、按键共用一组IO因为重载时数字噪声会影响读取时序。2.3 HC-05蓝牙模块的工作模式与电平转换HC-05是经典蓝牙串口透传模块默认波特率9600工作角色分主从电子秤场景里它做从机手机做主机。模块上有6个引脚VCC、GND、TXD、RXD、STATE、EN/KEY。其中TXD接单片机的RXDP3.0RXD接单片机的TXDP3.1。需要特别注意HC-05的RXD电平虽然是3.3V逻辑但大部分模块做了3.3V到5V的电平兼容可以直接接51单片机的TXD口。如果模块是纯3.3V版本就得加一个1k串联电阻或者用三极管做电平转换否则长期使用容易烧模块。原理图上要给HC-05留一个排针或杜邦线接口方便断开后用USB转TTL单独给模块发AT指令。EN引脚接高电平时上电进入AT指令模式此时模块的波特率固定为38400可以用来修改名称、配对密码、串口波特率。我的习惯是在模块旁边加一个跳线帽一端接3.3V一端接EN调试时插上跳线帽进AT模式正常使用时拔掉。下面是我常用的引脚连接表画原理图时直接照抄即可单片机引脚连接器件器件引脚说明P1.0HX711PD_SCK时钟输出默认低P1.1HX711DOUT数据输入查询方式读取P3.1HC-05RXD单片机TXD到模块RXDP3.0HC-05TXD模块TXD到单片机RXDVCC5VHX711AVDD/VSUP模拟电源与数字电源分开去耦GNDHX711/HC-05GND共地最好单点接地原理图上还要画出51最小系统11.0592MHz晶振、两个30pF负载电容、上电复位电路10uF电解电容加10k电阻、P0口上拉排阻。这些属于标配但很多人画PCB时把晶振放在单片机旁边是常识却忽略了HX711的模拟电源和数字电源之间要用0欧电阻或磁珠隔离。如果原理图上只有一颗1117给整板供电ADC读数的噪声会明显变大。推荐的做法是5V电源进入后先经过一个10uF电容去耦再分两路一路直接给数字部分另一路串联一个22欧电阻给HX711的模拟电源引脚这样可以把数字噪声对模拟部分的影响压到最低。3. 51单片机源程序HX711读取、标定与蓝牙发送3.1 HX711驱动时序的C语言实现HX711的读取时序不复杂但有两个容易写错的地方一是数据准备好后必须在25个时钟内读完否则会丢失二是每次读完24位数据后第25个脉冲会改变增益如果下一个读取周期要维持增益128需要连续给25个脉冲。我习惯写一个读函数一次读24个数据位放弃了通道B的扩展这样最简单。#include reg51.h #include intrins.h sbit HX711_SCK P1^0; sbit HX711_DOUT P1^1; unsigned long HX711_Read(void) { unsigned long value 0; unsigned char i; HX711_DOUT 1; HX711_SCK 0; // 等待DOUT变低表示数据准备完毕 while(HX711_DOUT); for(i 0; i 24; i) { HX711_SCK 1; _nop_(); // 短延时满足时序要求 value value 1; HX711_SCK 0; if(HX711_DOUT) value; } // 第25个脉冲恢复增益128 HX711_SCK 1; value value ^ 0x800000; // 转换为无符号数处理补码 _nop_(); HX711_SCK 0; return value; }这个函数的关键是DOUT拉高后如果SCK为低DOUT会在转换完成后自动变低表示数据就绪。所以读取前先让SCK保持低然后循环等待DOUT变低。循环里每个SCK上升沿移出一个位高位在前所以每读一位后把value左移一位。24位读出后原始值是二进制补码与0x800000异或可以把负数部分转换为从0到16777215的无符号数后续做减法标定更方便。参数上_nop_()产生的延时大约1us11.0592MHz晶振下足够满足HX711的时钟要求最大10MHz。如果单片机频率更低比如6MHz晶振这里可以不加延时因为SCK高电平时间会更长HX711要求SCK高电平最小0.2us低电平最小0.2us6MHz下半个机器周期就已经达标了。3.2 零点漂移、标定系数与重量换算公式HX711读出的数不是重量而是ADC原始码。要变成克数必须做两步第一步测零点第二步用已知重量求系数。零点就是秤盘上什么也不放时的读数会因为传感器预紧力、温度、应变片不对称产生一个偏移。标定系数就是单位重量对应的ADC读数增量。权重换算公式一般是weight (current_value - zero_value) / scalescale的单位是ADC读数每克。获取scale的方法很直接放一个标准砝码比如500g读当前值然后scale (value_500g - zero_value) / 500。如果秤跳字厉害可以读十次取平均再算后面会讲滤波。下面是主程序中执行标定和发送的代码unsigned long zero_value 0; unsigned long scale 0; unsigned long raw_value 0; int weight 0; unsigned long temp_sum 0; unsigned char i; // 零点标定空秤时连续读10次取平均 for(i 0; i 10; i) { temp_sum HX711_Read(); } zero_value temp_sum / 10; // 放500g砝码后再次读取 temp_sum 0; for(i 0; i 10; i) { temp_sum HX711_Read(); } // 计算标定系数500是砝码质量克 scale (temp_sum / 10 - zero_value) / 500;标定系数scale在同一个传感器、同一放大倍数下是固定的可以提前算好存到单片机的EEPROM里比如STC89C52内部集成了EEPROM通过ISP下载程序时就能设置。如果每次上电都重新标定那传感器必须在上电时处于空载状态否则零点就错了。我在实际项目中会在按键按下时触发标定而不是上电自动标定这样更灵活。3.3 串口蓝牙发送协议的设计与代码蓝牙透传模块对传输内容不识别它只负责把单片机从串口发出的字节原封不动转发给手机。所以发送协议完全由自己定义。我的常用格式是文本行88.5kg\n或者更通用一点用W:885\n表示重量885克方便手机端解析。用文本协议的最大好处是调试容易串口助手直接能看不依赖蓝牙也能测。发送代码写在定时器中断外每500ms发送一次当前重量。注意不要在中断里调用串口发送函数因为HX711读取需要阻塞等待占了中断会很麻烦。下面是把重量转换为字符串并通过串口发送的代码#include string.h void UART_Init(void) { TMOD 0x20; // 定时器1模式28位自动重装 TH1 0xFD; // 11.0592MHz下9600波特率重装值 TL1 0xFD; SCON 0x50; // 串口模式1允许接收 TR1 1; } void SendStr(char *s) { while(*s) { SBUF *s; while(!TI); TI 0; } } void SendWeight(unsigned int w) { char buf[10]; // 重量以克为单位输出字符串如 W:0885\n buf[0] W; buf[1] :; buf[2] 0 (w / 1000) % 10; buf[3] 0 (w / 100) % 10; buf[4] 0 (w / 10) % 10; buf[5] 0 w % 10; buf[6] \n; buf[7] \0; SendStr(buf); }波特率计算11.0592MHz晶振定时器1模式2TH10xFD时波特率11.0592MHz/((256-0xFD)1216) 9600。这个晶振选型就是为了让串口波特率精确换成12MHz晶振后9600的误差会到2%以上蓝牙模块在9600下还能容忍但到了115200就极容易乱码。所以只要在这个项目里用串口和蓝牙我就坚持用11.0592MHz晶振。协议里每行末尾的\n是给手机端做数据结束标志。手机APP接收时按行解析取到W:后面四个数字再转成int。这个协议虽然简单但已经能支持连续上报和断线重连后的重同步。如果以后想加电池电压、时间戳只需要扩展字段比如W:0885,V:4080\n手机端按逗号切分即可。4. 手机APP的蓝牙通信与数据解析实现4.1 Android经典蓝牙的权限、扫描与连接手机APP这块最常见的技术栈是Android原生加蓝牙经典BluetoothAdapter。为什么不用BLE因为HC-05是经典蓝牙Bluetooth Classic走的是RFCOMM协议连接方式和BLE完全两套。如果用BLE去扫HC-05连设备都看不见。Android端做经典蓝牙需要关注三个权限BLUETOOTH、BLUETOOTH_ADMIN、ACCESS_FINE_LOCATION。定位权限是Android 6.0以后扫描蓝牙设备的硬性要求很多人漏了这个导致扫描列表为空。扫描和连接的核心代码如下BluetoothAdapter adapter BluetoothAdapter.getDefaultAdapter(); if (adapter ! null !adapter.isEnabled()) { adapter.enable(); // 需要用户确认权限 return; } // 开始扫描需要先注册BroadcastReceiver IntentFilter filter new IntentFilter(BluetoothDevice.ACTION_FOUND); registerReceiver(receiver, filter); adapter.startDiscovery(); // 连接指定设备 BluetoothDevice device adapter.getRemoteDevice(00:15:83:00:XX:XX); BluetoothSocket socket null; try { socket device.createRfcommSocketToServiceRecord( UUID.fromString(00001101-0000-1000-8000-00805F9B34FB)); socket.connect(); // 连接成功后从socket获取输入输出流 InputStream input socket.getInputStream(); OutputStream output socket.getOutputStream(); } catch (IOException e) { e.printStackTrace(); }这里用到的一定是SDP服务的UUID00001101-0000-1000-8000-00805F9B34FB它是串口透传Profile的标准UUIDHC-05模块默认支持。如果你用createRfcommSocketToServiceRecord(SERIAL_PORT_UUID)连不上另一个常见workaround是用反射方法device.createRfcommSocket(1)但建议先确认模块是否处于配对状态、波特率是否和单片机一致。4.2 数据接收线程与重量值解析蓝牙Socket的数据读取必须放在子线程里因为input.read()是阻塞的放在主线程会导致界面无响应。读取的buffer大小不固定蓝牙模块按字节流发过来可能一次只有几字节也可能几十字节粘在一起。所以解析要认真处理半包和粘包。下面是一个典型的读取与解析实现private void startReadThread(InputStream input) { new Thread(() - { byte[] buffer new byte[1024]; StringBuilder line new StringBuilder(); int bytes; try { while ((bytes input.read(buffer)) ! -1) { String chunk new String(buffer, 0, bytes, UTF-8); for (char c : chunk.toCharArray()) { if (c \n) { String lineStr line.toString().trim(); parseWeightLine(lineStr); line.setLength(0); } else { line.append(c); } } } } catch (IOException e) { // 连接断开提示重连 } }).start(); } private void parseWeightLine(String line) { if (line.startsWith(W:)) { try { int weight Integer.parseInt(line.substring(2, 6)); runOnUiThread(() - weightView.setText(weight g)); } catch (NumberFormatException e) { // 忽略错误格式 } } }每次读到数据后先把字节拼接成字符串然后逐个字符检查换行符。遇到\n就把之前累积的内容当成一行来处理这样即使半包也不乱。粘包的情况更好解决一行行切出来就行。解析时用substring取第四到第六个字符前提是单片机端固定输出4位数字比如W:0085。如果单片机发送的是带小数的浮点字符串解析时改为Float.parseFloat即可。4.3 APP不同权限、设备兼容性处理手机APP的坑不在逻辑而在Android版本碎片化。Android 10以下和以上的权限模型不一样Android 12开始对蓝牙权限有了新要求BLUETOOTH_SCAN和BLUETOOTH_CONNECT是运行时权限而且需要请求用户授权同时还要在AndroidManifest.xml中声明android:usesPermissionFlagsneverForLocation否则会被系统以定位用途拒绝。我的建议是在MainActivity里统一做运行时权限申请在onRequestPermissionsResult回调中再启动蓝牙扫描。另外很多安卓手机对经典蓝牙扫描有缓存改了HC-05模块的名称后旧名字可能还在系统蓝牙缓存里导致扫描重复出现两个设备。及时清理系统蓝牙缓存或忽略旧设备即可。iOS端对经典蓝牙的支持很麻烦蓝牙SCO音频以外iOS系统不允许第三方APP通过RFCOMM协议连接传统蓝牙串口设备所以如果你要面向iOS用户要么换BLE模块要么走MFi认证。这就是为什么很多电子秤项目直接用BLE模块而不是HC-05BLE是手机端的更优选择但51单片机驱动BLE会稍微复杂一点因为BLE的广播、连接和数据通道都有额外状态机。5. 标定技巧、抗干扰滤波与远程调秤的进阶玩法5.1 用串口助手代替手机APP验证数据链路拿到硬件后第一步不是连手机而是用USB转TTL线直接接HC-05的TXD和RXD模块处于透传模式打开串口助手波特率9600。如果能看到稳定的W:xxxx数据流说明单片机、HX711、标定系数都对了。如果看不到先用万用表量HX711的DOUT引脚看空载时是否有0到5V的脉冲变化再用示波器看SCK有没有时钟输出。这种分步排查法能快速把问题定位在传感器、ADC还是单片机上。5.2 滑动平均滤波的工程实现HX711的原始数据即使硬件调好了也会因为机械振动和电磁干扰有±5克左右的跳动。最简单的算法是滑动平均维护一个长度为8的数组每次新读数覆盖最旧的然后求平均值。这样能把随机噪声压到±1克又不会明显增加响应时间。注意不要用指数平均因为指数平均在重量突变时会产生拖尾滞后。#define FILTER_N 8 unsigned long filter_buf[FILTER_N]; unsigned char filter_index 0; unsigned long Filter_Avg(unsigned long new_value) { unsigned char i; unsigned long sum 0; filter_buf[filter_index] new_value; if(filter_index FILTER_N) filter_index 0; for(i 0; i FILTER_N; i) sum filter_buf[i]; return sum / FILTER_N; }调用时只需要把每次读到的ADC值传入在标定流程中同样先用滤波函数处理再参与计算。这个数组长度取8是平衡了响应速度和平滑效果如果秤的承重台机械结构软可以增加到16。5.3 标定系数的批量自动化方案批量生产或反复校准时手动放砝码容易出错。进阶做法是设计一个串口指令上位机发送CAL:500单片机收到后读取当前值并自动计算scale存到EEPROM。下面给出指令处理代码框架unsigned char uart_buffer[16]; unsigned char uart_len 0; void UART_ISR(void) interrupt 4 { if(RI) { RI 0; uart_buffer[uart_len] SBUF; if(uart_buffer[uart_len-1] \n || uart_len 15) { // 收到完整指令解析 if(strncmp(uart_buffer, CAL:, 4) 0) { int weight_std atoi(uart_buffer[4]); // 读取当前滤波值扣零点计算scale并存入EEPROM // 然后回复OK\n } uart_len 0; } } }这样配合一个Windows或手机端工具就能实现桌上放砝码、刷卡自动校秤的流水线操作。5.4 掉电存储与低功耗模式如果电子秤用电池供电HC-05是耗电大户空载时电流约30mA加上单片机、传感器整机可能40mA左右。用3节AA电池撑不久所以在不用时要让HC-05进入休眠。方法是把HC-05的STATE引脚接到单片机P2.0检测到STATE输出低表示未连接此时可以延时后单片机关断HC-05的电源用MOS管或者让单片机进入掉电模式用外部按键唤醒。存储标定系数使用STC单片机的EEPROM时注意写入前要先擦除扇区而且EEPROM写寿命只有10万次如果每500ms写一次会很快报废。正确做法是只在标定指令触发时写一次运行过程中使用RAM中的缓存值。验证整机时可以把手机APP界面截图、重量曲线截图一起保留这是调试记录也是项目交付的一部分。最后一招在手机APP里加一个远程调零按钮发送Z指令给单片机单片机收到后重新读取零点可以解决秤使用一段时间后零点漂移的问题这也是让这套蓝牙电子秤更接近商品化的实用改进。本文还有配套的精品资源点击获取