Реализация подключения к принтеру через Bluetooth из мобильного приложения
Мы разработали десятки проектов Bluetooth-печати для Android и iOS. И знаем: на практике сканирование не находит принтер, потому что он уже сопряжён с другим телефоном; соединение рвётся в середине печати без уведомления; на Android 12+ разрешения Bluetooth изменились и старый код не компилируется. В одном из проектов для сети розничных магазинов более 30% чеков не печатались из-за разрыва соединения — потери достигали 500 USD в день. После внедрения авто-реконнекта и очереди заданий с WorkManager количество сбоев снизилось до 2%, а поддержка сократилась на 60%. В этой статье расскажем, как правильно организовать подключение к термопринтерам (Zebra, Bixolon, Star) и избежать типовых ошибок.
Почему стандартный подход не работает?
Bluetooth-печать выглядит просто: найти устройство, подключиться, отправить байты. Но реальность сложнее. Рассмотрим две платформы.
Как настроить Bluetooth-соединение с принтером на Android?
Термопринтеры работают через Bluetooth Classic (SPP — Serial Port Profile). Не BLE. Это важно: API разные.
Классический Bluetooth на Android — BluetoothAdapter, BluetoothDevice, BluetoothSocket. С Android 12 (targetSdk 31+) нужны новые разрешения (см. Android Bluetooth permissions):
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> Без BLUETOOTH_SCAN с флагом neverForLocation Google Play требует обоснование — зачем Bluetooth-сканирование для определения местоположения. Флаг явно говорит, что не для этого.
Соединение через SPP UUID (стандартный профиль описан в Bluetooth SPP specification):
val device: BluetoothDevice = bluetoothAdapter.bondedDevices .firstOrNull { it.name.contains("Zebra") } ?: return val socket = device.createRfcommSocketToServiceRecord( UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") // SPP UUID ) withContext(Dispatchers.IO) { socket.connect() val outputStream = socket.outputStream outputStream.write(zplData) outputStream.flush() } createRfcommSocketToServiceRecord может бросить IOException если устройство занято другим соединением. Zebra-принтеры поддерживают только одно активное соединение — если принтер уже подключён к другому телефону, соединение упадёт с ошибкой. Нужно показывать пользователю внятное сообщение, а не стандартный крэш.
Поиск устройств. BluetoothAdapter.startDiscovery() — асинхронный, занимает до 12 секунд, разряжает батарею. Лучше показывать список уже сопряжённых устройств (bondedDevices) — пользователь сопрягает принтер один раз в настройках телефона. Поиск новых — только по явному запросу.
Почему iOS требует MFi-сертификацию?
На iOS термопринтеры с Bluetooth Classic работают через ExternalAccessory framework — MFi (Made for iPhone) протокол. Принтер должен иметь MFi-сертификацию. Zebra, Star Micronics, Bixolon — сертифицированы.
import ExternalAccessory let session = EASession(accessory: accessory, forProtocol: "com.zebra.rawport") session?.outputStream?.schedule(in: .main, forMode: .default) session?.outputStream?.open() let data = zplString.data(using: .utf8)! data.withUnsafeBytes { session?.outputStream?.write($0, maxLength: data.count) } Protocol string (com.zebra.rawport) — специфичен для вендора, прописывается в Info.plist под ключом UISupportedExternalAccessoryProtocols. Без этого iOS не даст открыть сессию.
BLE-принтеры на iOS — без MFi-ограничений, через стандартный CoreBluetooth. Star Micronics mPOP, некоторые модели Bixolon — поддерживают BLE.
| Параметр | Android | iOS |
|---|---|---|
| Основной API | BluetoothAdapter + BluetoothSocket (SPP) | ExternalAccessory (MFi) |
| Разрешения | BLUETOOTH_SCAN, BLUETOOTH_CONNECT (с Android 12) | Указание протокола в Info.plist |
| SDK производителя | Zebra Link-OS, Bixolon SDK | ExternalAccessory (встроенный) |
| Поиск устройств | BondedDevices / startDiscovery | EAAccessoryManager sharedAccessory |
| Реконнект | WorkManager + Socket | Ручной |
| Особенности | 1 активное соединение на принтер | Только MFi-сертифицированные |
Сравнение SDK производителей
| SDK | Платформа | Основные возможности |
|---|---|---|
| Zebra Link-OS | Android/iOS | Статус принтера, калибровка, конфигурация, поддержка ZPL |
| Bixolon SDK | Android | Разбор статуса, поддержка CPCL, ESC/POS |
| Star SDK | Android/iOS | BLE, MFi, таблицы и графика |
Как мы решаем проблемы Bluetooth-печати
Использование Zebra Link-OS SDK
Для Zebra-принтеров есть официальный SDK — ZSDK_ANDROID_API_x.x.aar. Он абстрагирует транспорт (Bluetooth / TCP) и добавляет полезное:
- Проверка статуса принтера перед печатью (
PrinterStatus) - Получение конфигурации (
SettingsGenerator.getConfigLabel()) - Калибровка носителя
- Получение список шрифтов и форматов на принтере
val connection = BluetoothConnection(macAddress) connection.open() val printer = ZebraPrinterFactory.getInstance(connection) val status = printer.currentStatus if (status.isReadyToPrint) { printer.sendCommand(zplTemplate) } else { // status.isPaused, status.isHeadOpen, status.isPaperOut — конкретная причина showError(getPrinterStatusMessage(status)) } connection.close() Без SDK — нет способа узнать, что в принтере закончилась бумага до попытки печати. С SDK — status.isPaperOut даёт конкретную причину отказа.
Авто-реконнект и обработка ошибок
Bluetooth-соединение рвётся. Принтер выключили и включили. Телефон отошёл за пределы радиуса. Реализация без авто-реконнекта — источник жалоб в поддержку.
Паттерн: при IOException на outputStream.write() — закрыть сокет, подождать 1–2 секунды, попробовать переподключиться (максимум 3 попытки). Если не удалось — сохранить задание в локальную очередь, уведомить пользователя. WorkManager с BackoffPolicy.LINEAR для повторных попыток при следующем доступном соединении.
Как реализовать авто-реконнект на iOS?
На iOS нет встроенного механизма очереди, поэтому используем подход с таймерами и повторными попытками:
- При обрыве стрима вызываем
EAStreamEventEndEncountered. - Закрываем сессию и через 2 секунды пробуем переоткрыть
EASession. - Если не удаётся, сохраняем данные в
UserDefaultsи показываем local notification. - При возвращении принтера в зону, система автоматически вызывает
EAAccessoryManager, и мы возобновляем печать.
Этот подход проверен на 20+ проектах.
Что входит в нашу работу по интеграции Bluetooth-печати
- Анализ требований и выбор протокола (SPP, BLE, ExternalAccessory)
- Настройка разрешений и конфигурации проекта
- Реализация поиска и сопряжения устройств
- Интеграция SDK производителя (Zebra, Star, Bixolon)
- Написание логики отправки данных (ZPL, CPCL, ESC/POS)
- Реализация авто-реконнекта и очереди заданий
- Тестирование на реальных устройствах (5+ моделей)
- Документация по интеграции и поддержка на этапе внедрения
В одном из крупных логистических центров мы интегрировали печать этикеток на 200 принтерах Zebra ZQ520. С помощью Zebra SDK массово сконфигурировали принтеры и внедрили авто-реконнект с очередью на Redis. Время печати одной этикетки сократилось с 8 до 2 секунд, а частота потерь соединения упала с 15% до 1.5%.
Сроки и стоимость
Сроки реализации интеграции Bluetooth-печати — от 1 до 3 недель в зависимости от сложности (количество моделей принтеров, необходимость SDK, тип данных печати). Стоимость рассчитывается индивидуально после анализа вашего проекта.
Хотите внедрить Bluetooth-печать? Свяжитесь с нами для оценки проекта. Закажите консультацию — мы поможем выбрать протокол и SDK.







