Printing labels on a Brother QL-810W from PowerShell over the network, no driver installed

Printing Labels on a Brother QL-810W from PowerShell – No Driver, Just the Network

I needed a label printer for a small production workflow. Every batch gets a label: batch number on top, SKU below it, date at the bottom. Nothing fancy.

The interesting part is where it had to run from. The goal was an Azure Automation Runbook on a Hybrid Worker, taking three input parameters and printing a label. No desktop, nobody logged on, no locally installed printer driver — just the Hybrid Worker talking to the printer over the network.

I picked a Brother QL-810W. Here is how it went, including the parts that did not work. And there were a few.

Part One: Just Getting the Thing to Talk

Before any PowerShell, I lost an evening to the hardware. If you are doing this yourself, these are the things that cost me time:

The driver install failed. Windows found the printer, I ran the “Installation compatible printer” path, and it errored out. The Brother downloads are all installers, not raw driver packages, and they do not unpack cleanly if you try.

The firmware tool refused to work. My plan was to update the firmware over USB, because the WLAN was so unreliable I did not trust it. The firmware tool threw an error and would not see the device.

At one point the printer was simply switched off. I will leave that here as a reminder that it is always worth checking the obvious thing first. It appeared immediately afterwards.

The WiFi was genuinely bad. Two metres from the access point and still dropping. What fixed it in the end: moving off DHCP and giving the printer a fixed IP. Since then it has been stable. If your QL-810W keeps falling off the network, try that before you buy a different printer.

Also useful to know: the power button LED is a status display. Blinking means it wants something — wrong media, cover open, out of labels. A red blink is not a crash, it is a complaint.

No Driver, Just a Socket

Once the network was stable, the actual question was the interesting one: can I print without installing the printer at all?

Yes. The QL series speaks Brother Raster directly on TCP port 9100. You build the raster data yourself, open a socket, write the bytes, done. No spooler, no driver, no printer object on the machine. Which is exactly what you want on a Hybrid Worker.

The documentation you need is the Brother Raster Command Reference for the QL series. It is a PDF on the Brother support site, and it is the only reason any of this worked.

Building the Label as a Bitmap

The printer wants a raster image, so the label gets drawn in memory first with System.Drawing. For the DK-11201 (29 × 90 mm) at 300 dpi that is 991 × 306 pixels.

$Width  = 991
$Height = 306

$bitmap = [System.Drawing.Bitmap]::new($Width, $Height)
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
$graphics.Clear([System.Drawing.Color]::White)

Three lines of text, each in its own band, the batch number bold and large:

Draw-Line $BatchNumber 15 115 76 $true
Draw-Line $SKU         135 80  50 $false
Draw-Line $Date        220 65  36 $false

One small thing that turned out to matter in practice: the font size shrinks automatically until the text fits. A long SKU would otherwise silently run off the edge of the label, and you would only notice on the printed sticker.

$measured = $graphics.MeasureString($Text, $font)
if ($measured.Width -le ($Width - 60) -and $measured.Height -le $H) { break }
$Size -= 2

The Raster Commands

Then the command sequence. Reset, raster mode, media description, cut behaviour, and off we go:

# Reset and initialize
Write-Bytes ([byte[]]::new(400))
Write-Bytes ([byte[]](0x1B, 0x40))

# Raster mode
Write-Bytes ([byte[]](0x1B, 0x69, 0x61, 0x01))

# Print information: die-cut, 29 x 90 mm
Write-Bytes ([byte[]](0x1B, 0x69, 0x7A, 0x0E, 0x0B, 0x1D, 0x5A))

# Auto cut
Write-Bytes ([byte[]](0x1B, 0x69, 0x4D, 0x40))
Write-Bytes ([byte[]](0x1B, 0x69, 0x41, 0x01))

# No compression
Write-Bytes ([byte[]](0x4D, 0x00))

Each raster row is 90 bytes, because the print head has 720 dots. The label only uses a slice of those.

The Bug That Cost Me the Most Time

This is the part worth reading, because everything else is just following the manual.

The format was right. The printer took the job. The script even counted the black pixels it had generated — and it found plenty. And the label came out of the printer completely blank.

The cause: I had the margins the wrong way round.

The Brother manual states that the first transmitted byte sits on the side of the right margin. For a 29 mm label the order is therefore 6 margin dots → 306 printable dots → 408 margin dots. I had put the 408 dots before the image data.

So every single black pixel was pushed outside the printable area. The data was fine, the pixel counter was fine, the printer did exactly what it was told — and the label was empty. One line:

# wrong
$pin = 408 + $y

# right
$pin = 6 + $y

Checked against synthetic image data: with the old offset, 0 of 22,450 black test pixels landed in the printable area. With the corrected offset, all 22,450 did.

If you ever get a blank label out of a QL printer while your code insists it sent black pixels — look at the margin offset first.

Printing Over the Network

The last piece is unremarkable, which is the point:

$client = [System.Net.Sockets.TcpClient]::new()
$async = $client.BeginConnect($PrinterIP, 9100, $null, $null)
if (-not $async.AsyncWaitHandle.WaitOne(5000)) { throw "Printer connection timeout." }
$client.EndConnect($async)

$stream = $client.GetStream()
$stream.Write($data, 0, $data.Length)
$stream.Flush()

Explicit timeouts on connect and send, because a Hybrid Worker job that hangs on a socket forever is nobody’s friend.

Summary

  • Driverless printing works — raster commands straight to TCP 9100, no printer installed on the machine. Exactly what a Hybrid Worker needs.
  • Give the printer a fixed IP. The WLAN problems disappeared with that one change.
  • The margin offset is the trap. The first byte is on the right margin side. Get that wrong and you print perfect, empty labels.
  • Count your black pixels and fail loudly if there are none. It will not catch the offset bug, but it catches everything else.
  • Still open: the script confirms that the data was transmitted, not that a label actually came out. A status query back from the printer is the next thing I want to add before this runs unattended in production.

That last point matters for the Azure Automation part. A Runbook that reports success because a socket accepted bytes is not telling you the truth about the physical world.

GitHub

The full script is on GitHub, including the parameters for batch number, SKU and date:

Repo: Seidlm/BROTHER-PS
File: Print.ps1

Michael Seidl aka Techguy
au2mate everything

Leave a Comment

Your email address will not be published. Required fields are marked *

*