Xamarin Forms对接Niimbot蓝牙打印机连接成功但无法打印
问题根因
连接时打印机蜂鸣仅代表蓝牙底层链路建立成功,无法触发打印是代码存在几个核心错误:
- Niimbot便携标签打印机使用私有二进制通信协议,不会识别任意传入的纯文本内容,直接发送编码后的字符串属于无效数据,设备不会执行打印动作。
- 代码中使用
Encoding.UTF32编码文本完全不符合设备通信要求,这类热敏标签机的指令、打印内容均为原始字节流,不存在UTF32文本解析逻辑。 - 发送完数据立即关闭Socket,没有预留设备接收缓存、处理数据的时间,即使指令正确也可能因连接提前中断导致数据丢失。
- 方法定义了image入参但未实现任何图片转点阵逻辑:标签打印本质需要将内容转换为与标签尺寸匹配的1bit黑白点阵,按协议封包后传输,直接发送字符串或原始图片文件都无法被识别。
正确实现流程
- 连接建立后首先按协议格式发送握手/状态查询指令,待设备返回就绪响应后再传输打印数据,不要连接成功就直接发内容。Niimbot通用封包结构为:
包头(固定0x55 0x55) + 命令字 + 数据长度 + 数据内容 + 校验和。 - 打印内容预处理:不管是文本还是图片,都先转换为和所用标签纸尺寸匹配的1bit单色位图,每个像素用1位表示(黑色为1、白色为0),按行对齐凑整为字节数组。
- 按顺序发送配置与数据:先发送打印浓度、纸张类型、打印份数这类配置指令,再分包发送点阵数据(单包大小控制在128字节以内避免缓存溢出),所有点阵传输完成后再发送走纸、启动打印的指令。
- 所有指令发送完成后不要立刻断开连接,等待设备返回打印完成状态,或预留至少2秒处理时间,确保设备把缓存内的数据全部处理完毕。
- 连接前主动调用
bluetoothAdapter.CancelDiscovery()终止系统蓝牙扫描,避免扫描占用带宽导致连接不稳定。
参考修正代码
public async Task Print(string devName, Bitmap printImage) { BluetoothDevice device = (from bd in bluetoothAdapter?.BondedDevices where bd?.Name == devName select bd).FirstOrDefault(); BluetoothSocket bluetoothSocket = null; try { UUID sppUuid = UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"); bluetoothSocket = device.CreateRfcommSocketToServiceRecord(sppUuid); bluetoothAdapter.CancelDiscovery(); await bluetoothSocket.ConnectAsync(); var stream = bluetoothSocket.OutputStream; // 发送握手测试包,不同型号命令字、校验和存在差异,可抓官方APP通信包调整 byte[] handshakePacket = new byte[]{0x55, 0x55, 0x01, 0x00, 0x01, 0xF6}; await stream.WriteAsync(handshakePacket, 0, handshakePacket.Length); await stream.FlushAsync(); await Task.Delay(500); // 自行实现位图转1bit点阵逻辑,尺寸匹配所用标签纸,比如40*30mm标签对应320像素宽度 byte[] printDotData = ConvertBitmapTo1BitArray(printImage); // 发送打印配置:浓度3、打印1份 byte[] configPacket = BuildProtocolPacket(0x03, new byte[]{0x03, 0x01}); await stream.WriteAsync(configPacket, 0, configPacket.Length); await stream.FlushAsync(); // 分包发送点阵数据 int offset = 0; int maxPacketSize = 128; while(offset < printDotData.Length) { int sendLength = Math.Min(maxPacketSize, printDotData.Length - offset); byte[] dataPart = new byte[sendLength]; Array.Copy(printDotData, offset, dataPart, 0, sendLength); byte[] dataPacket = BuildProtocolPacket(0x04, dataPart); await stream.WriteAsync(dataPacket, 0, dataPacket.Length); await stream.FlushAsync(); await Task.Delay(10); offset += sendLength; } // 发送启动打印指令 byte[] startPrintPacket = BuildProtocolPacket(0x02, new byte[]{0x01}); await stream.WriteAsync(startPrintPacket, 0, startPrintPacket.Length); await stream.FlushAsync(); // 等待打印完成再关连接 await Task.Delay(2000); } catch (Exception e) { System.Diagnostics.Debug.WriteLine(e.Message); throw e; } finally { bluetoothSocket?.Close(); } } // 协议封包辅助方法 private byte[] BuildProtocolPacket(byte cmd, byte[] data) { List<byte> packet = new List<byte>(); packet.Add(0x55); packet.Add(0x55); packet.Add(cmd); packet.Add((byte)data.Length); packet.AddRange(data); // 计算校验和,不同型号算法略有差异,可根据抓包结果调整 byte checkSum = 0; for(int i = 2; i < packet.Count; i++) { checkSum ^= packet[i]; } packet.Add(checkSum); return packet.ToArray(); }
补充说明
不同型号的Niimbot打印机命令字、校验和计算逻辑存在细微差异,如果通用测试包无法触发打印,可抓取官方APP打印时的蓝牙通信流量,对照调整指令参数即可。不要使用任何文本编码处理打印数据,所有指令、点阵均按原始字节传输,需要打印文字时先把文字绘制到位图上再转换为点阵数据,不要直接发送字符串。
内容的提问来源于stack exchange,提问作者aqwaaa
相关产品推荐
相关产品推荐

