Skip to content

Draft: Add Bluetooth SPP/RFCOMM driver - #427

Open
johnbeard wants to merge 3 commits into
michaelrsweet:masterfrom
johnbeard:bt_spp
Open

Draft: Add Bluetooth SPP/RFCOMM driver#427
johnbeard wants to merge 3 commits into
michaelrsweet:masterfrom
johnbeard:bt_spp

Conversation

@johnbeard

@johnbeard johnbeard commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Includes BlueZ for device discovery and an SPP (serial port profile) layer on top for devices such as the Orgbro X1.

Fixes: #421


This does work and I can print from an Orgbro X1 Bluetooth printer with a driver registration like:

{ "orgbro_x1",               "Orgbro X1 Label Printer",
  "MFG:Orgbro;MDL:X1;NAME:X1;", NULL },

This is a draft PR because I'm still working on tidying it up more and especially thinking about the matching method. For now, I've made a dangerous-feeling change around here just to get it working.

    if ((value = cupsGetOption(current->name, (cups_len_t)num_did, did)) == NULL)
    {
      // TODO: biggest questions in the BT patch...how do we handle this?
      // Key not present in device ID: skip, don't disqualify.
      // This allows a driver to declare multiple matching keys
      // (e.g., MFG+MDL for USB and NAME for Bluetooth) without
      // requiring all of them to be present in every device.
      continue;
    }

But @emolitor said they had time this weekend to mess with their printers, so here it is :-D. But I don't expect any review of it as I have just squashed several weeks worth of messy refactors, dead ends, and so on into a big blobby commit. (Of course I also won't mind if someone just takes it and beats it into shape, or makes any comments!)

I've also only tried it on Linux and don't have any other OSs I can try.

Other questions that occur at this point:

  • I put my name in the copyright line along with Michaal Sweet. If that's not the done thing, let me know, It's more about not putting only someone else's name on code they didn't write.
  • If a blobby commit is too much to handle, what would be the ideal way to chunk down for easier review.

Full disclosure: I have used Deepseek v4 Pro to help with this, but it's not just vibe-coded!

Includes BlueZ for device discovery and an SPP (serial port
profile) layer on top for devices such as the Orgbro X1.

Fixes: michaelrsweet#421
Assisted-by: DeepSeek:v4-pro
@emolitor

emolitor commented Jul 18, 2026

Copy link
Copy Markdown

~HI works and got back the version information.
Sent a hand crafted ZPL label (raw ZPL) and it works and prints.
Used papplDeviceWrite to send 16K to make sure a larger write succeeds which worked as well.

label-test

@emolitor

emolitor commented Jul 18, 2026

Copy link
Copy Markdown

One reconnect issue turned up while testing with an RP425.

An initial open/write/close succeeded, but an immediate reopen could fail with EBUSY. In another delayed reconnect, poll() signalled completion and getsockopt(SO_ERROR) returned ECONNRESET. The current error path closes the socket without assigning that value to errno, so the caller can retain and report the earlier EINPROGRESS.

The tested patch:

  • preserves errors across every failure path in pappl_bt_spp_rfcomm_connect(), including assigning SO_ERROR to errno
  • treats failure to set or restore the original socket flags as a connection failure
  • makes at most one retry with a fresh socket: after 1500ms for EBUSY, or 1000ms for ECONNRESET

I deliberately limited this to one retry. During testing, repeated rapid reconnect attempts left the RP425 unresponsive over Bluetooth until it was power-cycled.

After restarting the printer:

  • three sequential ~HI open/query/close jobs with no caller-side delay succeeded, 3/3
  • two separate ZPL senders launched back-to-back with no caller-side delay both completed their writes, and both labels physically printed in order
  • BlueZ reported Connected: no afterward
  • the patched tree rebuilt with ASan enabled, and the targeted Bluetooth tests passed, including retry-policy coverage for six error values

The supporting test changes also keep the pure Bluetooth address and URI tests active when BlueZ/D-Bus support is unavailable. The broader local test run reaches an unrelated Avahi avahi_domain_browser_free assertion in this environment.

@johnbeard your scrappy prototype code is pretty close IMO to getting all the basics working. Printing a single page worked without any changes, but more than one failed until I did the clean up in the suggested changes. This also adds a simple retry mechanism which is probably worth having.

The replacement review contains ten smaller, non-overlapping suggestions. They form one patch and should be applied as a batch.

Linux CI follow-up

This file is unchanged in the MR, so GitHub cannot attach an inline suggestion to it.

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -21 +21 @@ jobs:
-      run: sudo apt-get install -y avahi-daemon cppcheck libavahi-client-dev libjpeg-dev libpam-dev libpng-dev libssl-dev libusb-1.0-0-dev zlib1g-dev
+      run: sudo apt-get install -y avahi-daemon cppcheck libavahi-client-dev libbluetooth-dev libdbus-1-dev libjpeg-dev libpam-dev libpng-dev libssl-dev libusb-1.0-0-dev zlib1g-dev
@@ -35 +35 @@ jobs:
-      run: ./configure --enable-debug --enable-debug-printfs --enable-maintainer --with-sanitizer=address
+      run: ./configure --enable-bluetooth --enable-debug --enable-debug-printfs --enable-maintainer --enable-sanitizer

@emolitor

Copy link
Copy Markdown

Per @michaelrsweet instructions I did use GPT 5.6-sol in High mode to review my changes and to help write the summary but all the debug, initial coding and tests were by hand. The printer I tested with for this round was a Rongta RP425 running v1.11 firmware.

@emolitor emolitor left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The original broad suggestions were superseded by the ten smaller, re-anchored suggestions in the later review.

@emolitor emolitor left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These ten replacement suggestions form one tested patch and should be applied as a batch. The Linux CI follow-up is included in the existing conversation comment because that file is unchanged in this MR.

Comment thread pappl/device-bt-spp.c
Comment thread pappl/bt-private.h
Comment thread pappl/device-bt-spp.c
Comment thread pappl/device-bt-spp.c
Comment thread pappl/device-bt-spp.c
Comment thread pappl/device-bt-spp.c
Comment thread pappl/device-bt-spp.c
Comment thread testsuite/testbluetooth.c Outdated
Comment thread testsuite/testbluetooth.c
Comment thread testsuite/testbluetooth.c Outdated
@michaelrsweet michaelrsweet self-assigned this Jul 18, 2026
@michaelrsweet

michaelrsweet commented Jul 18, 2026

Copy link
Copy Markdown
Owner

(Updated) So some quick feedback:

  • I'm generally good with the direction of this code; obviously I'll want to do some performance testing
  • Longer term I'll want to break this out into separate PAPPL_DEVTYPE_BT_xxx constants.
  • I'd also like to see HCRP (near term) and BPP (longer term) support to cover a wider range of BT printers
  • SPP doesn't provide a 1284 device ID but Core's device address (BD_ADDR) has a manufacturer prefix that could be used to provide a make, with (maybe) the service name providing a model? Also if the device supports DIS (Device Information Service) we can get make and model from that...

johnbeard and others added 2 commits July 19, 2026 16:04
Co-authored-by: Eric Molitor <534583+emolitor@users.noreply.github.com>
@johnbeard

Copy link
Copy Markdown
Contributor Author

These ten replacement suggestions form one tested patch and should be applied as a batch. The Linux CI follow-up is included in the existing conversation comment because that file is unchanged in this MR.

Thank you very much - I'm sure it's pretty obvious that I basically only got as far as printing one label at a time! To be frank there's something specifically about socket syscall loops that gives me an instant headache, so I'm very grateful for any help!

I also applied the CI fixes as a separate commit as suggested.

@johnbeard

johnbeard commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

(Updated) So some quick feedback:

Thank you very much for the feedback. I'm really pleased I'm not completely off-piste here! I'll be really interested to know about performance specifics. Not being super familiar with this code (or BT in general, TBH) I don't really know where I'd look, so it's interesting to me.

  • For PAPPL_DEVTYPE_BT_xxx - I could do that now if you like. I just chose PAPPL_DEVTYPE_CUSTOM_NETWORK as a placeholder.
  • I'd like to see more types too, but for now I don't have any other printers to try. I'll try to keep an eye open for particularly cheap HCRP/BPP devices.
  • For the OUI bytes, as it turns out, the Orgbro X1 (the only printer I have to hand for now) has a MAC of 06:08:BC:xx:yy::zz. 06:08:BC doesn't come up on an OUI search engine that I tried. Were you thinking of having it as something like OUI=06:08:BC; in the 1284 string? Or would there a be a little DB of OUI triplets to a name string?
  • As for DIS, I don't think this device has it as that's a BLE GATT - all I get is:
Device 06:08:BC:A5:4B:B5 (public)
        Name: X1
        Alias: X1
        Class: 0x00040680 (263808)
        Icon: printer
        Paired: yes
        Bonded: yes
        Trusted: no
        Blocked: no
        Connected: no
        LegacyPairing: no
        CablePairing: no
        UUID: SDP                       (00000001-0000-1000-8000-00805f9b34fb)
        UUID: RFCOMM                    (00000003-0000-1000-8000-00805f9b34fb)
        UUID: L2CAP                     (00000100-0000-1000-8000-00805f9b34fb)
        UUID: Serial Port               (00001101-0000-1000-8000-00805f9b34fb)
        UUID: PnP Information           (00001200-0000-1000-8000-00805f9b34fb)
        UUID: Vendor specific           (ffcacade-afde-cade-defa-cade00000000)
        Modalias: usb:v05ACp0239d0644

And:

$ sudo cat /var/lib/bluetooth/04:7B:CB:BB:0D:DD/06:08:BC:A5:4B:B5/info
[General]
Name=X1
Class=0x040680
SupportedTechnologies=BR/EDR;
Trusted=false
Blocked=false
CablePairing=false
Services=00000001-0000-1000-8000-00805f9b34fb;00000003-0000-1000-8000-00805f9b34fb;00000100-0000-1000-8000-00805f9b34fb;00001101-0000-1000-8000-00805f9b34fb;00001200-0000-1000-8000-00805f9b34fb;ffcacade-afde-cade-defa-cade00000000;

[LinkKey]
Key=BADB09B282C400135CF2FD4A3A1A7D6F
Type=5
PINLength=0

[DeviceID]
Source=2
Vendor=1452
Product=569
Version=1604

/var/lib/bluetooth/04:7B:CB:BB:0D:DD/06:08:BC:A5:4B:B5/attributes is 0-length.

So that looks promising, except that 1452 = 0x5AC seem to be:

  - value: 0x05AC
    name: 'GoerTek Dynaudio Co., Ltd.'

But then none of the BT devices I have paired seem to agree with https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/company_identifiers/company_identifiers.yaml so maybe that's the wrong mapping to use.

But I think because this is in the cache that we can get the VID/PID (even if they might not be the expected company...?) without having to actively connect and query the service.

@emolitor

emolitor commented Aug 2, 2026

Copy link
Copy Markdown

I have some time this week to work a bit more on this @johnbeard or @michaelrsweet, anything in particular you want me to dig into? I was going to look at testing this a bit more and taking a look at how hard it would be to add BLE / GATT / L11 support for some of the cheap BLE printers I have.

@johnbeard

johnbeard commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

Hi @emolitor sorry it took a while to get back to you. Something has "come up" in real life so I might not be able to engage too much with this in the next handful of weeks. Basically what I'm not least happy with is the matching system.

There's not a lot of stuff to match on: looks like we get to see, for the Orgbro X1 at least:

  • The name string: X1
  • The OUI triplet from the MAC which seems suspect (I'm not sure if it's random, or self-allocated, or if I'm looking in the wrong place for the lookup)
  • The Vendor/product ID, again not clear to me if these are formal assignments or some kind of self-allocation.

Assuming these are stable, perhaps an IEEE string like VID=0x05AC;PID=0x0239 may be adequate?

Other than that, I don't really have anything else to dig into at the moment. I'm more than happy for you to take this branch and run with it if you have time and printers to dig in deeper right now.

I will try to remain available for testing/etc too, but I can't promise too much I'm afraid, and I don't want to bog down your progress!

@emolitor

emolitor commented Sep 9, 2026

Copy link
Copy Markdown

Work got intense over the past few weeks so only now just coming back to this. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bluetooth printer support

3 participants