Een Bluetooth-printerdriver vanaf nul – van Python op de laptop naar C++ op een ESP32-C3

Quick Overview & Specifications

AttributeDetails
PublicationVinjo Engineering & Technology
CategoryHardware & IoT
AuthorVinay
Article TypeTechnical Tutorial & Case Study
Published2026-10-20
Last Updated2026-10-20
License / AccessOpen Educational Resource
Core Engineering Highlights:
  1. Verified hardware architecture, schematics, and pinout diagrams.
  2. Comprehensive firmware routines, protocol benchmarks, and build steps.
  3. Production design rules, tolerances, and thermal considerations.
Related Topic Taxonomy:
  • #Embedded Systems
  • #Bluetooth BLE
  • #ESP32
  • #Python
  • #Reverse Engineering

Een Bluetooth-printerdriver vanaf nul – van Python op de laptop naar C++ op een ESP32-C3

Summary: This project explores reverse-engineering a compact B23a thermal printer to operate without smartphone dependencies. By initial prototyping in Python using Bleak and then porting flow control and canvas rendering into C++ on an ESP32-C3 microcontroller, a fully standalone, embedded printing solution was constructed for IoT alerts.

Key Takeaways

  • Prototyping Strategy: Developing an initial Bluetooth Low Energy (BLE) driver in Python provides a flexible testbed for analyzing GATT attributes, custom printer commands, and raster data formatting before writing embedded C++ firmware.
  • Flow Control Mechanics: Thermal print speed and BLE buffers require precise data pacing—using printer notifications (01 01 return bytes) and breaking data streams into smaller chunks prevents buffer overflow and incomplete prints.
  • Standalone Graphical Output: Utilizing Adafruit_GFX on an ESP32-C3 enables real-time canvas rendering for icons, graphs, and QR codes locally on a microcontroller without relying on cloud services or external computers.

Architecture Comparison: Python vs. Embedded C++

Feature / Dimension Python Prototype (Laptop) C++ Firmware (ESP32-C3)
BLE Stack Bleak (Asyncio) NimBLE-Arduino
Execution Environment Desktop OS (Linux/macOS) Bare-metal FreeRTOS Microcontroller
Canvas Rendering Pillow (PIL) Adafruit_GFX in SRAM buffer
Flow Control Event-driven GATT notifications Non-blocking chunk pacing with return byte checking
Deployment Footprint Python runtime & OS dependencies Autonomous standalone hardware (<100 mA draw)

Detailed Overview

Hardware Inspection & Teardown

The project centers on a small B23a wireless thermal printer (Hi.22.052 Pocket Printer), which typically relies on the AiYin mobile application via Bluetooth. Opening the device reveals its inner components: a thermal printhead, a paper feed transport mechanism driven by a small motor, a 3.7 V Li-ion battery, and a compact control board.

Het uitgangspunt: een eenvoudige Hi.22.052 Pocket Printer Het uitgangspunt: een eenvoudige Hi.22.052 Pocket Printer die normaal via een app op de telefoon wordt aangestuurd.

Printer Teardown Onderzoeken wat er daadwerkelijk in het doosje zit. Printkop, papiertransport, accu en besturing zijn bijzonder compact opgebouwd.

Under a microscope, the flexible printed circuits (FPC) connecting the thermal printhead and drive motor to the main board reveal the trace routing and core integrated circuits.

Mechanism and Controller Board Het printermechanisme en de besturingsprint naast elkaar. De thermische printkop en motor zijn via flexprints met de elektronica verbonden.

PCB Inspection Onder de microscoop bekijk ik de componenten en printbanen op de besturingsprint.

Analyzing BLE Services and GATT Structure

Without an official SDK or protocol document, the Bluetooth communicaton served as the foundation for reverse engineering. Using a BLE scanner app (such as nRF Connect), the device advertises with the following parameters:

  • Advertised Name: B23a_7F36_BLE
  • Firmware Version: V2.0.4
  • MAC Address: CC:99:17:57:7F:36
  • Target GATT Service (FF00): 0000ff00-0000-1000-8000-00805f9b34fb
  • Write Characteristic (FF02): 0000ff02-0000-1000-8000-00805f9b34fb (used to send commands & raster data)
  • Notification Characteristic (FF03): 0000ff03-0000-1000-8000-00805f9b34fb (used to receive status responses)

Device info in app De printer meldt zich als B23a_7F36_BLE. In de originele software zijn onder andere serienummer en firmwareversie V2.0.4 zichtbaar.

nRF Connect BLE browser Met een BLE-browser onderzoek ik de GATT-structuur en zoek ik naar services, write-characteristics en notifications.


Deep Dive & Technical Insights

Python Driver Prototype

To rapidly validate communication before targeting microcontrollers, a Python script using bleak was created to test data transmission over BLE and register response handlers.

import asyncio
from bleak import BleakClient

PRINTER = "CC:99:17:57:7F:36"

FF02 = "0000ff02-0000-1000-8000-00805f9b34fb"
FF03 = "0000ff03-0000-1000-8000-00805f9b34fb"

ready = asyncio.Event()

def notification_handler(sender, data):
    if data == bytes([0x01, 0x01]):
        ready.set()

async def send(client, data):
    ready.clear()

    await client.write_gatt_char(
        FF02, data, response=False
    )

    try:
        await asyncio.wait_for(
            ready.wait(), timeout=0.15
        )
    except asyncio.TimeoutError:
        pass

Python Testing from Laptop De eerste experimenten vinden bewust nog vanaf de laptop plaats. Met Python test ik stap voor stap welke BLE-commando’s de printer accepteert.

Raster Specification & Flow Control

The thermal printhead is 384 pixels wide. Since 1 bit corresponds to 1 pixel, each horizontal line requires 384 / 8 = 48 bytes. Raster line transmission commands follow the header prefix: 1D 76 30 00, followed by width, height, and raw bitmap data.

To avoid overflowing the printer's input buffer during image transfers, flow control is synchronized via notifications sent from FF03. The printer emits 01 01 when it is ready for the next payload segment. Data streams are chunked into max 120-byte payloads with a fallback 150 ms timeout.

ESP32-C3                         B23a
   │                               │
   │── data ───────► FF02 ────────►│
   │                               │
   │◄──────── FF03: 01 01 ─────────│
   │                               │
   │── volgende data ─────────────►│

Porting to C++ on ESP32-C3

Once protocol logic was verified on Python, the implementation was ported to C++ for the ESP32-C3 microcontroller using NimBLE-Arduino and Adafruit_GFX.

#include <Arduino.h>
#include <NimBLEDevice.h>
#include <Adafruit_GFX.h>
#include <qrcode.h>
bool sendChunk(const uint8_t* data, size_t length) {
    flowReady = false;

    bool result = ff02->writeValue(
        data, length, false
    );

    if (!result) return false;

    unsigned long start = millis();

    while (!flowReady) {
        delay(1);

        if (millis() - start > 150) {
            break;
        }
    }

    return true;
}

ESP32-C3 Driving Printer directly Een belangrijke mijlpaal: de printer wordt nu rechtstreeks vanuit C++ op de ESP32-C3 aangestuurd. De laptop kan uit de keten.

Local Canvas Ticket Rendering

Using GFXcanvas1 from Adafruit_GFX, the ESP32-C3 renders monochromatic alerts directly into memory:

GFXcanvas1 canvas(
    PRINTER_WIDTH,
    CANVAS_HEIGHT
);

The system draws lines (drawLine), solid blocks (fillRect), icons (drawTriangle, drawCircle), status text, KPI metrics, trend charts, and a generated QR code (via qrcode.h). The buffer is then processed in horizontal strips of 32 pixels high (each strip containing 1536 raster bytes + 8 header bytes = 1544 bytes) and transmitted over BLE in chunks.

Predictive Maintenance Ticket Result Het eindresultaat: een complete Predictive Maintenance-alert met machinegegevens, KPI’s, trendgrafiek en QR-code, zelfstandig gerenderd door de ESP32-C3 en via BLE naar de printer gestuurd.


Conclusion & Resources

  • Standalone Integration: By removing the vendor app dependency, the portable printer becomes an edge output terminal that can connect directly to MQTT, REST interfaces, or local industrial sensors via WiFi/BLE.
  • Embedded Capabilities: Using structured software design patterns (Python prototyping leading to embedded C++ flow control), resource-constrained chips like the ESP32-C3 can perform real-time graphics rendering, QR generation, and hardware protocol handling.
  • Actionable Maintenance Alerts: The final implementation outputs complete "Predictive Maintenance" job tickets locally on thermal paper—featuring machine IDs, anomaly scores, KPI graphs, and actionable repair QR links.