Skip to main content

TWIL 21 2026

·5 mins

In the previous TWIL, we’ve learned about Thermal printers, their components, communication interfaces & Command language. In this TWIL, we’ll try to communicate with one Label Printer using its command language which is TSPL.

Talking to a thermal printer using TSPL #

In the previous TWIL, I explored how thermal printers work internally, identified my printer, and successfully established a Bluetooth connection to it using Python.

Now that the connection works, the next step is learning how to actually communicate with the printer & make it print something intentionally.

The interesting part is that thermal printers are not “normal printers” in the way most desktop applications think about printing.

Most thermal printers are actually small embedded systems that receive textual commands written in a dedicated printer language.

My printer supports TSPL.

TSPL stands for TSC Printer Language.

Instead of sending a PDF or an image directly to the printer, we send a sequence of textual instructions describing:

  • label size
  • positioning
  • fonts
  • barcodes
  • QR codes
  • graphics
  • print count

The printer firmware interprets these commands & drives the motors and thermal print head accordingly.

Understanding how TSPL works #

A TSPL program is simply a text document.

For example:

SIZE 100 mm,100 mm
GAP 2 mm,0 mm
CLS
TEXT 100,100,"3",0,2,2,"HELLO WORLD"
PRINT 1

The printer receives this text over USB, Bluetooth, Wi-Fi, or Serial communication.

Let’s understand it line by line.

Defining the label size #

SIZE 100 mm,100 mm

This defines the physical dimensions of the label.

My labels are 100x100 mm shipping labels.

The printer needs this information so it knows the printable area boundaries.

Defining the label gap #

GAP 2 mm,0 mm

Thermal label rolls usually contain small gaps between labels.

The printer uses internal sensors to detect these gaps so it knows where one label ends & the next one begins.

Without proper gap configuration, labels may become misaligned.

Clearing the image buffer #

CLS

This clears the printer image memory before drawing anything new.

Think of it like clearing a canvas before painting.

Writing text #

TEXT 100,100,"3",0,2,2,"HELLO WORLD"

This command draws text on the label.

Parameters:

ParameterMeaning
100,100X/Y position
"3"Font ID
0Rotation
2,2X/Y scaling
"HELLO WORLD"Actual text

Thermal printers work using a coordinate system.

Everything is positioned precisely using X/Y coordinates.

Printing the label #

PRINT 1

This tells the printer to physically print one copy.

Until this command is received, nothing comes out.

Sending TSPL commands over Bluetooth #

Now let’s send these commands from Python.

I’m using the bleak library for Bluetooth Low Energy communication.

Discovering printer services #

Before writing data, we first need to inspect the printer Bluetooth services & characteristics.

from bleak import BleakClient
import asyncio

PRINTER_MAC = "EDA0AA8E-****-****-****-****"

async def main():
    async with BleakClient(PRINTER_MAC) as client:

        for service in client.services:
            print(service)

            for char in service.characteristics:
                print("   ", char)

asyncio.run(main())

This displays all BLE services exposed by the printer.

We are mainly interested in a writable characteristic.

On my printer, one characteristic supports writing binary data to the printer.

Sending a simple TSPL program #

Once we identify the writable characteristic UUID, printing becomes surprisingly simple.

from bleak import BleakClient
import asyncio

PRINTER_MAC = "EDA0AA8E-****-****-****-****"

WRITE_UUID = "0000AE01-xxxx-xxx-xxxx-xxxxx"

TSPL_COMMAND = """
SIZE 100 mm,100 mm
GAP 2 mm,0 mm
CLS
TEXT 100,100,"3",0,2,2,"HELLO WORLD"
PRINT 1
"""

async def main():

    async with BleakClient(PRINTER_MAC) as client:

        await client.write_gatt_char(
            WRITE_UUID,
            TSPL_COMMAND.encode()
        )

        print("Label sent successfully")

asyncio.run(main())

And suddenly…

The printer wakes up.

The motor starts moving.

The thermal head heats tiny dots line by line.

A label comes out with HELLO WORLD printed on it.

Thermal printers are tiny plotters #

What surprised me the most is that thermal printers are much closer to plotters than traditional printers.

You are not sending “documents”.

You are sending drawing instructions.

Every text, barcode, QR code, or image becomes a sequence of commands interpreted by the firmware.

For example:

BARCODE 100,100,"128",100,1,0,2,2,"123456789"

creates a barcode.

And:

QRCODE 100,100,L,5,A,0,"https://khalid9assi.com"

creates a QR code.

The printer itself generates the final graphical representation internally.

Why this is better than suspicious vendor software #

The original seller recommended a random proprietary Windows application from an unknown source.

I really did not want to install that on my machine.

After understanding the protocol, I realized I do not actually need vendor software at all.

The printer is simply accepting textual commands.

This means I can:

  • generate labels programmatically
  • automate printing workflows
  • integrate printing into my own applications
  • print from Linux/macOS
  • avoid proprietary software entirely

Printing images #

Of course, real labels usually contain logos, icons, or shipping layouts.

Thermal printers cannot directly print PNG or JPG files.

Images must first be converted into monochrome bitmap data compatible with the printer language.

The workflow becomes:

Image
Resize
Convert to black & white
Transform into printer bitmap bytes
Send using TSPL BITMAP command

This is where things become much more interesting.

Especially because thermal printers have strict memory limitations and DPI constraints.

What I learned #

A thermal printer is essentially:

  • a tiny embedded computer
  • a motor controller
  • a heating element array
  • a protocol interpreter

Once you understand the command language, the printer becomes extremely programmable.

References #