思普瑞特,小票打印机和标签打印机java网络驱动设计与实现

2019/5/6 posted in  业务

网络打印走的socket连接,主流的java, php, c, c#等语言都支持socket,所以实现打印机的网络打印几乎是和语言无关的

打印机默认端口是9100
使用socket连接上打印机后,将打印内容按照打印机的指令进行编码后调用send就可以打印了

协议文档

小票打印机和标签打印机走的协议不同,所以各自都有一个协议,小票打印机使用的是esc协议,标签打印机使用的是tsc/tspl协议

小票打印机esc协议文档:

  1. http://sprinter.com.cn/img/up/data/1492073298104phpDo100.pdf
  2. https://blog.csdn.net/feng88724/article/details/17474351
  3. http://www.sprt.cc/img/up/data/1499134995151phpDo100.pdf

标签打印机tsc/tspl协议文档:

  1. http://sprinter.com.cn/img/up/data/1492073298104phpDo100.pdf

可以看到打印机支持的指令非常多,下面找到常用的指令实现我们的打印目的

小票打印机

小票打印机已经有java驱动了,参考:https://github.com/SubLuLu/thermal_printer
小票打印机指令使用10进制表示了,所以难以阅读,指令对应的意思请对照文档
核心代码如下

连接打印机

public NetEscPos(String ip, int port, String encoding) throws IOException {
    Socket socket = new Socket(ip, port);
    socketOut = socket.getOutputStream();
    socket.isClosed();
    NetEscPos.encoding = encoding;
    writer = new OutputStreamWriter(socketOut, encoding);
}

发送命令,比如发送切纸命令

private NetEscPos feedAndCut() throws IOException {
    writer.write(0x1D);
    writer.write(86);
    writer.write(65);
    writer.write(0);
    writer.flush();
    return this;
}

对应的文档为

查看打印效果
IMG_54061

标签打印机

参考实现:https://github.com/fintrace/tspl2-driver
核心指令如下

SIZE 40 mm, 30 mm
SPEED 5
DENSITY 7
DIRECTION 1
REFERENCE 0,0
OFFSET 0 mm
SHIFT 0
CLS
TEXT 20,60,"TSS24.BF2",0,2,2,"堂食   0001"
TEXT 20,120,"TSS24.BF2",0,1,1,"2019-03-22 10:05:51"
TEXT 20,150,"TSS24.BF2",0,1,1,"大鸡腿(1/3)"
TEXT 20,180,"TSS24.BF2",0,1,1,"加冰 标签备注"
PRINT 1

查看打印效果:
IMG_00401

对应的指令可以参考文档,比如第一行设置标签纸宽度和长度的SIZE指令

一些注意的点

可以使用redis的阻塞队列实现获取是否打印成功的状态,参考:基于List的 LPUSH+BRPOP 的实现

字符集需要设置成 gb2312

目前几乎所有的网络打印机都遵守这个打印协议,一般是可以通用的🎉

使用点阵图可以打印更清晰的图片
java参考:EscPos.java#L501
c#参考:https://blog.csdn.net/xiaoxian8023/article/details/8440625
核心代码如下

// 每行进行打印
for (int i = 0; i < bufImage.getHeight() / 24 + 1; i++) {
    write(escBmp);

    for (int j = 0; j < bufImage.getWidth(); j++) {
        for (int k = 0; k < 24; k++) {
            if (((i * 24) + k) < bufImage.getHeight()) {
                pixelColor = bufImage.getRGB(j, (i * 24) + k);
                if (pixelColor != -1) {
                    data[k / 8] += (byte) (128 >> (k % 8));
                }
            }
        }

        write(data);
        // 重置参数
        data[0] = (byte) 0x00;
        data[1] = (byte) 0x00;
        data[2] = (byte) 0x00;
    }
    //换行
    byte[] byteSend1 = new byte[2];
    byteSend1[0] = 0x0d;
    byteSend1[1] = 0x0a;
    write(byteSend1);
}

c#版如下

// data
for (int i = 0; i < (bmp.Height / 24) + 1; i++)
{
    socket.Send(escBmp, escBmp.Length, 0);
 
    for (int j = 0; j < bmp.Width; j++)
    {
        for (int k = 0; k < 24; k++)
        {
            if (((i * 24) + k) < bmp.Height)   // if within the BMP size
            {
                pixelColor = bmp.GetPixel(j, (i * 24) + k);
                if (pixelColor.R == 0)
                {
                    data[k / 8] += (byte)(128 >> (k % 8));
                }
            }
        }
 
        socket.Send(data, 3, 0);
        data[0] = (byte)'\x00';
        data[1] = (byte)'\x00';
        data[2] = (byte)'\x00';    // Clear to Zero.
    }
    
    byte_send = Encoding.GetEncoding("gb18030").GetBytes("\n");
 
    //发送测试信息
    socket.Send(byte_send, byte_send.Length, 0);
} // data

如果在编写驱动过程中遇到问题,可加作者微信探讨
wx