Close
0%
0%

Printing Bitmap Images on a QR204 Thermal Printer

Turn any monochrome image into a printable receipt using ESC/POS commands and Image2CPP

Similar projects worth following

Project Overview

Thermal printers are everywhere—from retail stores and ATMs to restaurant billing systems and ticket vending machines. Their speed, simplicity, and low operating cost make them one of the most popular printing technologies for embedded systems.

While printing plain text is relatively straightforward, printing graphics and logos is often much more challenging. Every printer interprets bitmap data differently, and many low-cost thermal printers have very little documentation regarding image printing commands. As a result, developers frequently encounter distorted images, random characters, inverted colors, or completely blank printouts.

In this project, we'll learn how to print high-quality bitmap images on a QR204 58 mm TTL Thermal Printer using the Arduino UNO R4 WiFi. We'll use the Image2CPP tool to convert images into monochrome bitmap arrays and send them directly to the printer using standard ESC/POS commands.

During the development process, one particularly interesting challenge emerged. Although the printer supported ESC/POS raster graphics, the generated images appeared scrambled and unreadable. After extensive testing and debugging, it was discovered that the QR204 printer expects every bitmap byte to be transmitted with its bits reversed before printing. Implementing this simple transformation completely resolved the issue and allowed images to print perfectly.

By the end of this tutorial, you'll have a reusable boilerplate that lets you print any monochrome image simply by replacing the bitmap array generated by Image2CPP.

Features

  • Print logos, icons, and graphics on a QR204 thermal printer
  • Works with the Arduino UNO R4 WiFi
  • Uses the standard ESC/POS raster image command
  • Compatible with Image2CPP-generated bitmap array
  • Supports full-width 384-pixel images
  • Lightweight implementation without additional printer libraries
  • Easy-to-use boilerplate for future projects
  • Perfect for receipts, attendance systems, IoT devices, POS systems, and embedded applications

Applications

This bitmap printing technique can be integrated into a wide range of embedded applications, including:

  • Smart School Attendance Systems
  • Point-of-Sale (POS) Receipts
  • Restaurant Billing Machines
  • Inventory Management Systems
  • RFID Access Control
  • Visitor Management Systems
  • Warehouse Label Printing
  • Medical Devices
  • Industrial Automation
  • Event Ticket Printing
  • IoT Dashboards
  • Embedded User Interfaces

Software Required

  • Arduino IDE
  • Image2CPP
  • Arduino UNO R4 Board Package
  • USB Cable

Why an External Power Supply is Important

One of the most common mistakes when using thermal printers is powering them directly from the Arduino's 5V output.

Although the printer may successfully print text, bitmap images require a significantly higher current because hundreds of tiny heating elements are activated simultaneously.

During image printing, the thermal head can draw more than 1.5 A, which is far beyond what the Arduino UNO R4 can safely provide.

For reliable operation:

  • Use a regulated 5V / 2A external power supply.
  • Connect the grounds of the Arduino and the external supply together.
  • Use short and thick power wires whenever possible.

This simple change dramatically improves print quality and eliminates random characters or incomplete images caused by voltage drops.

Wiring Diagram

The wiring is extremely simple.

Arduino UNO R4 WiFi QR204 Printer TX RX

RX TX

GND GND

External 5v Supply VCC

Important: The Arduino and the external power supply must share a common ground.

The final setup requires only a few connections and can be assembled in just a few minutes.

How ESC/POS Bitmap Printing Works

Most thermal printers communicate using the ESC/POS command set, originally developed for point-of-sale systems. These commands allow the printer to perform various operations such as:

  • Printing text
  • Changing font styles
  • Adjusting alignment
  • Feeding paper
  • Printing barcodes
  • Generating QR codes
  • Printing bitmap images

For this project, we use the GS...

Read more »

  • 1 × Arduino UNO R4 Wi-Fi
  • 1 × QR204 Thermal Printer
  • 1 × Jumper Wires

  • 1
    Resize the Image

    The QR204 thermal printer has a maximum printable width of 384 pixels. Any image wider than this will either be cropped or distorted.

    Recommended image size:

    Width : 384 pixels
    Height : Any value (280 pixels used in this project)

    Width  : 384 pixelsHeight : Any value (280 pixels used in this project)

    If your image is larger, resize it before importing it into Image2CPP.

  • 2
    Open Image2CPP

    Open Image2CPP and load your image.

    Configure the settings exactly as shown below.

    Image Settings

    Background

    White

    Color Mode

    Monochrome

    Brightness

    128 (default works well)

    Output Setting

    Select

    Horizontal - 1 bit per pixel

    Disable

    Compression

    Output Format

    Arduino Code

    Do not enable any byte swapping or special encoding options.

    Finally, click Generate Code.

    Image2CPP will produce something similar t

    const unsigned char epd_bitmap[] = {0xFF,0xFF,0xFF,...};

    Copy the complete array.

    Arduino Boilerplate

    The following sketch acts as a reusable template.

    The only thing you'll need to replace in future projects is the Image2CPP array.

    Simply paste the generated bitmap inside:

    const uint8_t epd_bitmap[] = {    // Paste Image2CPP output here};

    Then update

    #define IMAGE_WIDTH#define IMAGE_HEIGHT

    Everything else remains unchanged.

    Understanding the Printer Commands

    The QR204 printer communicates using ESC/POS commands.

    The first command initializes the printer.

    PRINTER.write(0x1B);PRINTER.write('@');

    This clears the printer's internal state before sending new data.

    Next comes the raster bitmap command.

    PRINTER.write(0x1D);PRINTER.write('v');PRINTER.write('0');PRINTER.write(0);

    This tells the printer that the following bytes represent a bitmap image.

    The printer then needs to know the dimensions of the image.

    First, the width is transmitted in bytes, not pixels.

    uint16_t bytesPerLine = width / 8;

    Since every byte represents eight pixels, a 384-pixel image becomes

    384 / 8 = 48 bytes

    Those bytes are then transmitted as

    PRINTER.write(bytesPerLine & 0xFF);PRINTER.write(bytesPerLine >> 8);

    The image height is transmitted in a similar manner.

    PRINTER.write(height & 0xFF);PRINTER.write(height >> 8);

    Once the printer knows the dimensions, it simply waits for

    Width × Height

    worth of bitmap data.

    The Interesting Discovery

    Initially, everything appeared correct.

    The ESC/POS command was valid.

    The printer received the correct image dimensions.

    The bitmap array generated by Image2CPP was also correct.

    Yet the printer produced:

    • Scrambled graphics
    • Thick black bars
    • Distorted logos
    • Random symbols

    At first, it looked like a communication problem.

    However, after testing different baud rates, power supplies, image sizes, and ESC/POS commands, the real issue turned out to be much simpler.

    The QR204 printer expects every bitmap byte to be transmitted with its bit order reversed.

    For example,

    Normal

    10010000

    must become

    00001001

    before sending it to the printer.

    This tiny detail completely changes the output quality.

    Reversing the Bit Order

    The following function reverses all eight bits inside a byte

    uint8_t reverseBits(uint8_t b){    b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;    b = (b & 0xCC) >> 2 | (b & 0x33) << 2;    b = (b & 0xAA) >> 1 | (b & 0x55) << 1;    return b;}

    Before transmitting every byte, simply execute

    uint8_t b = reverseBits(bmp[i]);PRINTER.write(b);

    This single line solved the bitmap rendering problem entirely.

    Printing the Bitmap

    The complete bitmap printing routine calculates the image size, initializes the printer, sends the raster command, transmits the dimensions, and finally streams the bitmap data byte by byte.

    Since Image2CPP already generates a perfectly formatted bitmap array, the Arduino only needs to iterate through every byte and send it to the printer after reversing its bit order.

    The result is a sharp, correctly aligned bitmap printed directly onto the thermal paper.

    First Successful Print

    After implementing the bit-reversal function, the QR204 immediately produced a clean bitmap with correct proportions and alignment.

    This approach works reliably for

    • Company logos
    • School logos
    • QR codes
    • Icons
    • Small graphics
    • Certificates
    • Attendance slips
    • Custom receipt

    Because the code uses a reusable boilerplate, printing a different image is as simple as replacing the Image2CPP array with a new one.

    Troubleshooting Guide

    One of the most interesting aspects of this project was the amount of debugging required before achieving a successful print. Although printing plain text worked immediately, printing bitmap graphics involved several challenges. This section summarizes the most common issues encountered during development and explains how each one was resolved.

    If your printer doesn't produce the expected output, the following troubleshooting steps will help you identify the problem quickly.

    Problem 1 – Random Characters Instead of an Image

    Typical output looked like:

    <B  B<X(Hello

    Cause

    The printer was interpreting the bitmap bytes as regular text instead of raster graphics.

    Possible reasons include:

    • Incorrect ESC/POS command sequence
    • Wrong baud rate
    • Corrupted serial communication

    Solution

    Verify that the printer is configured for 9600 baud and ensure the bitmap is transmitted using the GS v 0 raster image command.

    Problem 2 – Only Text Prints

    Sometimes the printer would print:

    END

    without printing the image.

    Cause

    The raster command was being accepted, but no valid bitmap data followed.

    Solution

    Check:

    • Image width
    • Image height
    • Total number of transmitted bytes

    The total bitmap size should always be:

    (width / 8) × height

    For a 384 × 280 image:

    48 × 280 = 13,440 bytes

    If fewer bytes are transmitted, the printer simply skips the image.

    Problem 3 – A Single Black Dot

    One of the earliest tests printed only a single black dot followed by normal text.

    Cause

    The raster command itself was correct.

    However, only one byte of image data was reaching the printer.

    Solution

    Increase the bitmap width and verify that the complete image buffer is transmitted.

    This test confirmed that the printer supported raster graphic

    Problem 4 – Thick Black Horizontal Line

    Increasing the transmitted data produced a thick black horizontal line.

    Although this wasn't the desired image, it actually confirmed something very important.

    It proved that:

    • Communication was working correctly.
    • ESC/POS raster commands were supported.
    • The printer was interpreting bitmap data successfully.

    The only remaining issue was the bitmap layout.

    Problem 5 – Completely Distorted Image

    After transmitting the full bitmap, the printer produced a heavily distorted image.

    At this stage it appeared as though the bitmap itself was incorrect.

    Several possibilities were investigated:

    • Wrong raster command
    • Incorrect width calculation
    • Wrong image orientation
    • Data alignment issues
    • ESC * graphics mode
    • GS v 0 raster mode
    • Image2CPP settings
    • Printer firmware differences

    None of these solved the issue.

    The Actual Solution

    After carefully analyzing the output, it became clear that the printer expected every byte with its bit order reversed.

    Instead of transmitting

    10010000

    the printer expected

    00001001

    The following function solved the problem completely.

    uint8_t reverseBits(uint8_t b){    b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;    b = (b & 0xCC) >> 2 | (b & 0x33) << 2;    b = (b & 0xAA) >> 1 | (b & 0x55) << 1;    return b;}

    Every transmitted byte is processed using:

    PRINTER.write(reverseBits(bmp[i]));

    After implementing this single change, the bitmap printed perfectly.

    Problem 6 – Black and White Colors Swapped

    Sometimes the printed image appeared as a photographic negative.

    White became black and black became white.

    Cause

    The bitmap bytes were inverted before transmission.

    ~b

    Solution

    Do not invert the data.

    Simply transmit

    reverseBits(b);

    This preserves the correct pixel polarity.

    Problem 7 – Printer Resets During Image Printing

    Printing text worked correctly.

    However, larger images caused incomplete prints or printer resets.

    Cause

    The printer was initially powered directly from the Arduino's 5V pin.

    While text printing consumes relatively little current, printing a bitmap activates hundreds of heating elements simultaneously, causing the current demand to increase significantly.

    Solution

    Use a dedicated:

    • 5V
    • 2A (or higher)

    regulated power supply.

    Also ensure that the Arduino ground and printer ground are connected together.

    After switching to an external supply, printing became stable and reliable.

    Testing Strategy

    Rather than immediately sending a large bitmap, smaller tests were used to verify each stage of the communication.

    The debugging sequence looked like this:

    • Print plain text.
    • Initialize the printer.
    • Print a single black pixel.
    • Print an 8 × 8 square.
    • Print a horizontal black strip.
    • Print a small bitmap.
    • Print the complete 384 × 280 image.

    This systematic approach made it much easier to identify exactly where the problem occurred.

    Performance Results

    After implementing the final solution, the printer successfully produced high-quality monochrome graphics.

    Test Platform

    Component

    Specification

    Microcontroller

    Arduino UNO R4 WiFi

    Printer

    QR204 58 mm TTL Thermal Printer

    Baud Rate

    9600

    Image Width

    384 pixels

    Image Height

    280 pixels

    Successful Tests

    Successful Tests

    • Company logos
    • School logos
    • QR Codes
    • Icons
    • Small graphics
    • Certificates
    • Attendance slips
    • Custom receipts

    Because the code uses a reusable boilerplate, printing a different image is as simple as replacing the Image2CPP array with a new one.

    • School logos
    • Custom graphics
    • Icons
    • QR codes
    • Receipt headers
    • Attendance system branding

    Each image printed cleanly with correct proportions and sharp edges.

    Real-World Applications

    With bitmap printing working correctly, the same boilerplate can now be integrated into numerous embedded systems.

    Some examples include:

    • Smart School Attendance System
    • RFID Access Control
    • Library Management Systems
    • Hospital Registration Kiosks
    • Restaurant Billing Systems
    • POS Machines
    • Smart Inventory Management
    • Visitor Management Systems
    • IoT Receipt Printing
    • Event Ticketing
    • Portable Label Printers
    • Industrial Data Logging

    Instead of printing plain text, these applications can now include company logos, QR codes, signatures, custom icons, and branded graphics, giving the final product a much more professional appearance.

View all instructions

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates