ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

C#直连TSC打印机:TSPL指令实现二维码标签打印攻略

C#直连TSC打印机:TSPL指令实现二维码标签打印攻略 简介这是一份由达摩老生出品的C#源码项目用于驱动TSC标签打印机打印二维码标签解决标签打印功能开发中的指令调用与界面交互问题。项目以ShenMaPrint为核心包含主窗体及Form1至Form6在内的多个功能窗体、程序入口、辅助类同时集成log4net日志记录、INI文件读取、资源图片管理等配套设施代码逻辑完整可直接运行也便于二次扩展。压缩包共79个文件主要包含23个cs源代码、11个DLL依赖库、7个resources和7个resx资源文件以及exe、config、txt等配置与可执行程序整体体积仅2.12MB目录结构清晰方便按模块查阅学习。源码经过作者亲测校正从界面布局到二维码打印指令调用均有完整实现适合新手入门学习也可供有一定经验的开发人员快速理解TSC打印机的工作原理并移植到实际项目中。目前已有1218人学习下载对于需要对接TSC标签打印机、开发仓库或商品标签打印功能的技术人员是一份值得参考的实用资料。1. 这次直接用C#把TSC标签打印机跑起来从逐条指令到二维码标签落地很多C#上位机项目做到一半就会遇到一个绕不开的需求用TSC标签打印机打二维码。要么是固定资产贴着二维码做盘点要么是仓储拣货单上打一个含货号信息的二维码要么是小料盒上打一个唯一批次码。网上关于TSC打印的代码很多但大多只贴一句“调用驱动打印图片”或者只给出一个PRINT指令让人自己拼根本没法直接落到产线。这一篇我按自己常用的工程做法把链路完整拆开用TSPL指令直接直发到TSC打印机在C#里生成二维码、排版、连续打印并附上能直接抄的通信类和避坑记录。选择TSPL直发而不是Windows驱动原因很直接驱动打印是走操作系统打印队列和GDI标签内容稍有变化就要重新渲染速度慢、位置容易偏移TSPL是指令级打印机收到原始命令后自己解析、自己栅格化连打印机都能看到打印内容重启后还能继续打。产线上要求每张标签都稳、准、快指令直发是更可控的方案。别被“程序源码”四个字吓到真正写起来不到一百行核心代码难点全在坐标换算和几条容易反直觉的指令上。2. 先看TSPL指令是怎么工作的为什么直发指令比驱动打印更稳2.1 TSPL指令的最小完整流程初始化、定位、画图、打印TSC标签打印机虽然也有Windows驱动和SDK但只要走TSPL它就是一个“文本命令解析器”。你发过去一串ASCII字符它按命令执行最终把内容输出到标签纸上。一个最精简的TSPL打印流程只有四行核心指令SIZE 50 mm,30 mm GAP 3 mm,0 CLS QRCODE 10,10,H,4,0,2,https://example.com PRINT 1,1先用SIZE告诉打印机标签纸物理尺寸是50mm宽、30mm高再用GAP说明两张标签中间间隙为3mm第二个参数是偏移量连续纸或圆形标签场景会用到CLS清空图片缓冲区避免上一次打印内容残留然后QRCODE在坐标(10,10)处生成一个二维码最后PRINT 1,1表示打印1份、从当前缓冲取第1份内容。这四步就是TSPL的最小闭环所有标签打印都围绕它展开。SIZE和GAP是打印机走纸校准的根基。很多新手第一张标签打出来是歪的不是代码写错而是这两个参数和实际标签纸不一致。GAP告诉打印机两纸之间的间隙长度打印机用反射传感器去找这个间隙找对了才能避免打印头和标签错位。如果标签纸没有间隙比如连续热敏纸就写GAP 0如果是黑标纸TSPL里要改用专用的BLINE命令。CLS这个指令最容易漏。生产环境里上一条指令发送的数据会留在图片缓冲区如果不清理下一张标签会把旧内容和新内容叠在一起。打印头扫描一遍旧图还在缓冲区里结果是整张标签出现半透明的重影。所以每次打印新内容前必须先CLS再发图元命令最后PRINT。2.2 二维码的两种生成路线打印机原生QRCODE指令 vs 上位机生成位图同样是打二维码工程上有两条路线。第一条就是我上面用QRCODE命令让打印机自己生成二维码。TSC的固件里内置了QR码生成器只要给它坐标、纠错等级、模块宽度、方向、数据它就在打印缓存里画好二维码。优点是不占上位机CPU不依赖任何二维码类库打印速度极快。同时因为二维码是打印机自己的渲染引擎画出来的锯齿控制和点阵对齐都针对打印头做过优化扫描识别率通常更高。第二条路线是上位机先用QRCoder或ZXing生成二维码位图再通过TSPL的BITMAP命令把图片点阵数据发给打印机。这样做的灵活性大可以在二维码中央加Logo、换颜色、调整边距样式甚至把二维码和图片、表格合成一张整图。缺点是打印前要把位图转换成打印机认识的单色点阵数据转换逻辑稍有不慎就会得到一张全黑或反白的标签而且数据量比一行指令大得多网络传输和打印机解析都更耗时。对比项打印机原生QRCODE上位机生成位图上位机依赖无仅拼字符串需要二维码库和图像处理打印速度快指令短慢图片数据量大样式定制只能控制基础参数可加Logo、改配色抗锯齿打印机硬件优化取决于位图转换算法适合场景产线、批量、物联网需要品牌定制的内外标签我的选择原则是没有Logo需求的场景一律用原生QRCODE指令只有像产品包装标签这种要对二维码做视觉定制时才用上位机生成位图。后面第3章的源码会同时给出这两条路线的实现生产上可以按需切换。2.3 打印机连接方式网口、串口、USB哪个适合你的工位TSPL指令发到打印机首先要打通物理连接。TSC标签打印机常见三种接口网口RJ45通常监听9100端口、串口RS232、USB。网口是现在工位环境的首选一台打印机配一个固定IP同一局域网里多台C#上位机都能连它走TCP协议命令发送简单还能把打印状态通过打印机自己的网络状态页查出来。串口在老旧设备或纺织车间里还能见到波特率从9600到115200不等TSC打印机一般默认9600或38400。用串口的好处是抗干扰强、实时性高但一根串口线只能连一台打印机而且C#里串口写命令要注意Handshake和DtrEnable的设置否则会出现偶尔发不出去的情况。USB接口在桌面级TSC型号上最常见Windows驱动装完后会映射成虚拟串口也可以用TSC提供的专用通讯库但如果你没有二次开发SDK直接用串口API会发现命令发送和网口没有本质区别。实际项目里我一般优先要求设备组提供网口标签打印机因为后续维护IP比维护串口USB线容易得多。如果现场只有USB那就把TSC的虚拟串口号找出来在C#里按串口驱动来写代码上只需要替换发送底层上层TSPL字符串完全不用变。3. C#源码拆解一个可直接使用的TscQrLabel类3.1 先搭通信层用Socket把命令发给打印机先写最底层的通信类。假设你用的是网口打印机IP是192.168.1.200端口默认9100。TSPL命令本质上是字符串C#里只要有一个能发送ASCII字符串的通道就够了。我习惯把通信逻辑封装成一个TscPrinter类后续所有打印方法都复用它。using System.Net.Sockets; using System.Text; public class TscPrinter { private readonly string _host; private readonly int _port; private Socket _socket; public TscPrinter(string host, int port 9100) { _host host; _port port; } public void Connect() { _socket new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _socket.Connect(_host, _port); _socket.ReceiveTimeout 3000; } public void Send(string command) { byte[] data Encoding.ASCII.GetBytes(command); _socket.Send(data, SocketFlags.None); } public void SendWithCrLf(string command) { Send(command \r\n); } public void Close() { if (_socket ! null _socket.Connected) { _socket.Close(); } } }这里有个很容易翻车的点Socket.Send在TCP协议下有时不会一次性把完整的字节流发送出去尤其是在上位机CPU繁忙或网络延迟波动时。命令被截断打印机收到半条指令就会一直等待剩余字节表现为“打印机没反应”或“偶尔打印出乱码”。稳妥做法是加一个循环发送直到把整个data数组发完或者改用NetworkStream.Write再Flush。上面的示例为了可读性做了简化我实际写生产代码时会这样做int sent 0; while (sent data.Length) { sent _socket.Send(data, sent, data.Length - sent, SocketFlags.None); }发送TSPL命令还要注意行尾符。TSPL协议规定每条指令以CRLF\r\n结尾不是\n也不是空格。我用SendWithCrLf方法统一拼接避免整个代码里到处手动加\r\n导致遗漏。3.2 用打印机原生QRCODE指令生成二维码版本、纠错和坐标通信层打通以后先实现最省事的二维码打印让打印机自己生成。TSPL指令里QRCODE的完整参数不算复杂但容易记混。我封装了一个方法把坐标和数据传进去它负责拼指令并发送。public void PrintQrCode(string data, int x, int y, string ecc H, int cellSize 4) { string safeData data.Replace(\, \\); string command $QRCODE {x},{y},{ecc},{cellSize},0,2,\{safeData}\\r\n; Send(command); }参数从左到右分别是二维码左上角X坐标、左上角Y坐标、纠错等级、模块宽度点、旋转角度、QR码版本、数据。ecc用L、M、Q、H四个等级我默认用H因为标签打印环境常有脏污和褶皱高纠错能扛住一部分物理损伤。cellSize是二维码每个黑色模块占用的点阵大小203dpi打印机上4个点约等于0.5mm大小适中。如果二维码内容特别长比如超过200个字符建议把cellSize调到5或6否则打印机生成的二维码过密扫描枪很难识别。注意数据里的双引号必须转义。TSPL里字符串是用双引号包裹的如果二维码内容本身含有双引号比如JSON串里的sku:A-003需要写成\否则打印机解析命令时会把后面参数当成数据的一部分导致整条指令报错。这个边界只在特定业务场景会踩到但踩一次就能耗掉半小时排查。还有方向参数。QRCODE的第四个参数如果传0就是正常方向90是顺时针旋转多用在竖版标签上。旋转后坐标原点还是标签左上角只是二维码从左上角往旋转后的方向生长设计版面时最好先出一次实物样张再决定坐标。3.3 中文和Logo打印TEXT指令不做中文字库必须转成图片有读者可能觉得既然能发ASCII指令那TSPL里的TEXT指令是不是也能打印中文很遗憾多数TSC打印机的固件里并没有GBK或UTF-8字库。TEXT指令只能打印数字、英文和部分内置符号中文内容直接塞进命令里要么被当成乱码要么被丢弃。产线上最常见的商品名称、工单描述基本都是中文所以必须走另一条路在C#里把中文渲染成图片再通过TSPL的BITMAP命令点阵输出。下面是一个把Bitmap转换成TSPL位图并发送的最小实现。我把它放在同一个TscPrinter类里。using System.Drawing; using System.Drawing.Imaging; public void PrintBitmap(Bitmap image, int x, int y, bool invert false) { // TSC打印头是单色热敏/热转印CMYK没有意义这里强制转成1位索引 Bitmap mono image.Clone(new Rectangle(0, 0, image.Width, image.Height), PixelFormat.Format1bppIndexed); int width image.Width; int height image.Height; int rowBytes (width 7) / 8; StringBuilder sb new StringBuilder(); sb.Append($BITMAP {x},{y},{width},{height},0,); BitmapData bmpData mono.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadOnly, mono.PixelFormat); int stride Math.Abs(bmpData.Stride); byte[] pixels new byte[stride * height]; System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, pixels, 0, pixels.Length); mono.UnlockBits(bmpData); for (int row 0; row height; row) { for (int col 0; col rowBytes; col) { byte b pixels[row * stride col]; if (invert) b (byte)~b; sb.Append(b.ToString(X2)); } } Send(sb.ToString()); mono.Dispose(); }这段代码的关键是把Windows的Bitmap数据按打印机需要的“逐行字节流”拼成十六进制字符串。打印机中当像素值为1时表示黑色或者是反色视固件而定所以invert参数用来调节黑白逻辑。不同型号的TSC打印机对BITMAP命令的字节对齐要求略有差异但大多数是按“每行补到整字节”来解析上面代码里rowBytes就是做这个补位。打印中文时先用Graphics绘制一行文字到一个透明位图上再调用PrintBitmappublic void PrintChineseText(string text, int x, int y, int fontSize) { using (Bitmap bmp new Bitmap(400, 40)) { using (Graphics g Graphics.FromImage(bmp)) { g.Clear(Color.White); using (Font f new Font(微软雅黑, fontSize, FontStyle.Regular, GraphicsUnit.Pixel)) { g.DrawString(text, f, Brushes.Black, 0, 0); } } PrintBitmap(bmp, x, y); } }中文字体渲染后一定要先Clear成白色背景否则Bitmap默认黑色透明区域在转单色时会变成大块黑斑。GraphicsUnit.Pixel用来保证字体大小和打印机点阵一致不会出现逻辑英寸和实体像素不一致导致的尺寸偏移。3.4 批量打印循环发CLS、内容和PRINT别一次塞大量数据批量打印是产线最常见场景比如连续打印50张资产标签编号依次递增。核心逻辑不复杂但有一条规矩必须遵守每一张标签都要重新发CLS然后发这张的内容最后发PRINT三部分顺序不能乱。public void PrintSerialLabels(int startNumber, int count) { for (int i 0; i count; i) { string serial $AT-{startNumber i:D6}; string qrData $asset:{serial};loc:WH-03; SendWithCrLf(CLS); // 二维码用打印机原生指令 SendWithCrLf($QRCODE 15,15,H,4,0,2,\{qrData}\); // 序列号文本用TEXT只支持英文数字这里没问题 SendWithCrLf($TEXT 15,115,\3\,0,1,1,\{serial}\); // 打印1张 SendWithCrLf(PRINT 1,1); } }把SIZE和GAP的初始化放在批量的第一张之前发一次即可不要在循环里重复发否则打印机每次都要重新校准标签纸走纸时间会翻倍。TEXT指令里\3\表示使用内置字体3号最后一个参数是放大倍数1就是按基准大小打印。如果文字太长可以放在二维码下方另起一行用不同的TEXT指令调整坐标不要试图在一条指令里塞多个位置。C#里循环发50张的速度大约在1到2秒内完成但实际出纸速度由打印机SPEED指令决定。如果标签很小打印机机械结构来不及出完一张下一张指令就可能积压在打印机缓存里。对于高速连续打印建议每发10张加一个短暂的Thread.Sleep(50)让走纸机构喘口气。4. 标签尺寸与坐标换算别把毫米当成像素4.1 DPI、标签宽高、点位换算公式TSPL里所有坐标都是按“点”dot算的而标签尺寸通常用毫米。打印机203dpi和300dpi是两种最常见的分辨率很多C#工程师把50mm直接写成50结果标签只打了四分之一就出纸了。正确换算是点数 毫米 × DPI ÷ 25.4203dpi打印机上1mm约等于8个点300dpi上是1mm约等于12个点。例如一张50mm×30mm的标签203dpi下就应该是400点×240点。我在代码里不会手算而是写一个换算函数private int MmToDots(float mm, int dpi) { return (int)Math.Round(mm * dpi / 25.4); }SIZE指令可以直接接受以mm为单位的写法但后续所有坐标计算仍然以“点”为单位。如果坐标不从毫米换算成点就很容易出现“二维码打在标签外面”的诡异情况。还有一个常见错觉标签纸打印区域并不是从物理边缘算起的打印头左边缘有2到3mm的不可印区所以最小X坐标不要小于1mm否则二维码会被裁掉。4.2 用DiagTool确认打印机分辨率与固件状态写代码前最好先用TSC官方的DiagTool检查一下打印机实际配置。这个名字在热词里也出现过它不仅是诊断工具还能直接发送TSPL命令、查看打印机固件版本、传感器状态和当前分辨率。第一次连接一台陌生打印机时我会先打开DiagTool读取它的当前分辨率确认是203dpi还是300dpi再写代码。很多工程为了节省成本会混合购买两种分辨率的TSC机器同一个上位机程序跑在不同机器上如果不做配置化处理标签上二维码的密度和位置都会不一致。DiagTool还有一个用途测试单条命令。当你怀疑C#发送的命令有语法问题时可以把指令原样粘贴到DiagTool的命令行窗口手动执行。如果能正常打印就是上位机通信层的问题如果同样不打印就是TSPL指令本身写错了。这一步能帮你把“代码问题”和“打印机问题”快速二分。4.3 标签间隙与传感器GAP不同会走纸跑偏标签之间要么有间隙要么有黑标要么是连续纸。GAP指令的第一参数是间隙长度TSC打印机通过传感器透射或反射检测这个间隙用于定位标签起始点。如果实际间隙是2mm你在GAP里写了3mm打印机每次都会多走一点标签纸越走越偏到最后一张二维码可能直接压在对折线中间。热敏标签纸上如果有一道定位黑标就不能用GAP而要用BLINE指令。黑标传感器反射黑标和白色底之间的光学差异实现定位。TSPL里黑标定位的基准命令是BLINE x,yx是标间距y是偏移量。我在一个冷链项目中遇到过标签纸没有间隙但有很小的定位缺口用GAP怎么调都偏后来发现是缺口裁切不干净反射传感器识别不稳定只能换回间隙纸。位置偏移不仅影响美观还会让打印头把内容打在上一张的间隙里导致碳带浪费和打印头不均匀磨损。所以换任何一家标签纸供应商都先打一张测试标签用尺子量一下内容和标签边界的距离。如果差了1mm以上就去校准GAP和SIZE不要相信供应商提供的“绝对标准”参数。5. 避开这些打印的坑从三盒标签换来的血泪经验5.1 现象二维码扫不出来手机相机能对到焦但解码失败原因打印机要么打印浓度太高二维码黑块粘连成一片要么浓度太低浅色点阵断线。尤其是热转印打印机碳带和质量差的标签纸组合不当时个别点阵会印不上。解决先调整DENSITY指令TSC的浓度范围是0到15。我习惯从DENSITY 6起步打印一张灰度测试二维码用手机扫扫不出来就调高一档再试。如果调到8还是不行检查打印机速度和碳带张力。速度过高时热转印加热时间不足墨粉来不及转印把SPEED 2降到SPEED 1再试。如果仍然不行考虑二维码内容是否过长导致点阵过密把cellSize从3调到4。5.2 现象中文字符在标签上变成“口口口”或空白原因TEXT指令缺少中文字库支持命令里直接拼了中文打印机按内置英文点阵去解析无法映射到字符。解决不要用TEXT打印中文改为在C#侧用Graphics绘制中文到Bitmap然后用BITMAP输出。如果实在不想走图片可以提前把中文字体刷写进打印机Flash存储器但那需要专门的字体下载工具和对应字库授权我建议除非有强制要求否则直接转图片最省事。另外记得转图片时背景要白色清空字体文件要选微软雅黑或SimHei等无衬线字体在203dpi下更清晰。5.3 现象连续打印50张前半段正常后半段每张向上偏移2mm原因打印头持续发热后标签纸受热略微伸长更常见的是标签纸卷本身张力不稳定打印机在连续走纸时间隙检测出现过漂移。解决在批量前打印几张缓冲让走纸机构稳定。批量循环里给每10张发一次GAP校准指令。TSPL没有主动“重新校准”的指令实际做法是把GAP原参数再发一遍打印机会重新读取传感器状态。也可以用SET GAPLESS关闭间隙检测但会导致打印停止位置不精确通常不推荐。5.4 现象C#程序连接打印机超时发送命令无反应但DiagTool能连上原因打印机网络模块一次只允许一个Socket会话。上位机连接后没有正常释放打印机侧的会话还处于打开状态后面的连接被拒绝。解决程序每次打印完成后必须调用Close()释放Socket不能依赖GC。如果已经出现这种情况重开打印机电源或者在DiagTool里清空会话。代码里还要加上重连逻辑打印异常时关闭旧Socket再重新Connect因为打印机在缺纸或卡纸后会自动断开TCP连接。5.5 现象二维码打印在标签最底边缘被裁掉一半原因坐标计算没有考虑标签的高度原点。TSPL原点在标签左上角但PRINT时还要考虑标签的高度和打印头到标签起始点的物理距离。如果你把二维码放在Y220点的位置标签只有240点高二维码本身高40点那么底部10点会被裁掉。解决坐标设计时给所有元素留下安全边距底部至少留5mm。计算时要先确定标签物理高对应多少点再在此基础上摆放内容。批量打印前打印一张“边框测试标签”用一个矩形命令把整个标签可打印区画出来看矩形的四条边是否都完整出现在标签上如果底边缺失就是Y坐标或SIZE高度不对。6. 把打印质量做成可验收的流程单张验证、解码测试、微调浓度二维码标签不像普通文本打出来肉眼看着清晰不一定能扫出来。我的习惯是建立一套“打印-扫描-微调”的流程而不是打完一批直接送去贴。第一步把打印机的SIZE、GAP、DENSITY、SPEED设置好之后不要直接发送二维码内容。先打一个测试块用BARCODE指令打一个Code128条码再打一个黑白网格观察水平和垂直方向的线条是不是均匀。如果网格线有歪斜说明标签纸走纸不正先解决问题再继续。第二步正式打出一张二维码不要只用眼睛看。用带解码功能的扫码枪或者手机扫描确认解码出的字符串与上位机发送的数据完全一致。有些热转印打印机用的碳带是蜡基的在光滑铜版纸上附着力不够二维码字符可能被搓掉肉眼又很难发现。这个流程必须做到“每次换碳带或换标签纸供应商时都执行一次”。第三步如果解码失败先动DENSITY再动SPEED最后才动大小。因为二维码的模块尺寸是由cellSize决定的改它会影响排版其他一系列坐标都要跟着迁移。相对地浓度和速度只影响印出质量不改变二维码在标签上的位置。批量生产时如果发现同一台打印机批间颜色变浅往往是环境温度变化导致碳带转印效率下降此时把浓度提高一级比重启打印机更有效。第四步建立一个“批次首件确认”制度。每次打印任务开始后先让程序只发一张等这张标签出了打印机人工确认二维码可扫、文字不偏、边距正确再让程序继续发剩余99张。这看起来损失了10秒效率但比起打完一整卷发现全部作废这点检查成本完全值得。这套流程说穿了就是把打印机当成一个需要校准的外设而不是当成一个“黑匣子”。我早期在项目里直接批量输出结果一整个班次打了三百张扫出来只有一半能用后面挨个排查才知道是碳带没安装到位、张力不均导致的点阵缺失。从那以后我再也没有跳过单张验证。换纸、换碳带、换打印机都必须重新校准一遍哪怕同一型号不同机器也可能有微小差异。希望这篇能让你的TSC二维码打印少走几趟弯路。本文还有配套的精品资源点击获取
返回列表