instax_api icon indicating copy to clipboard operation
instax_api copied to clipboard

Instax Mini Link support placeholder

Open vitorio opened this issue 4 years ago • 41 comments

Launched last month, the Instax Mini Link: https://instax.com/mini_link/en/

Same mini film at 800x600 resolution as the SP-2, but prints over Bluetooth instead of wifi, and uses a different Instax Mini Link app.

vitorio avatar Nov 28 '19 02:11 vitorio

Oooh, this is basically what I wished the instax was when it first launched! I've not got any reason to buy one for myself sadly, but happy to help support someone else adding it to this library.

At a guess, I would imagine that Fujifilm will have likely used the same communications protocol under the covers, but have switched from WiFi to bluetooth. If this is the case, then it should make it pretty easy to adapt this library to work with it.

jpwsutton avatar May 27 '20 09:05 jpwsutton

Does instax mini link use the same communications protocol as sp-2? If the answer is true, I gonna buy a mini link instead of sp-2.

caichunjian520 avatar Dec 23 '20 02:12 caichunjian520

dmesg:

[27085.469143] usb 3-2.1: USB disconnect, device number 8
[28991.907737] usb 3-2.1: new full-speed USB device number 9 using xhci_hcd
[28992.044195] usb 3-2.1: New USB device found, idVendor=04cb, idProduct=5019, bcdDevice= 2.00
[28992.044200] usb 3-2.1: New USB device strings: Mfr=1, Product=2, SerialNumber=3
[28992.044202] usb 3-2.1: Product: instax mini Link1
[28992.044205] usb 3-2.1: Manufacturer: FUJIFILM
[28992.044207] usb 3-2.1: SerialNumber: 00000000001A
[28992.100359] cdc_acm 3-2.1:1.0: ttyACM0: USB ACM device`

ok -> CDC ACM (serial port over USB)

Bluetooth find 2 devices:

INSTAX-10669469(ANDROID) typ printer but can not connect
INSTAX-10669469(IOS) typ unkown  it is possible to connect 
[INSTAX-10669469(IOS)]# info
Device FA:AB:BC:4D:1A:FA (random)
	Name: INSTAX-10669469(IOS)
	Alias: INSTAX-10669469(IOS)
	Paired: yes
	Trusted: yes
	Blocked: no
	Connected: yes
	LegacyPairing: no
	UUID: Generic Access Profile    (00001800-0000-1000-8000-00805f9b34fb)
	UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
	UUID: Device Information        (0000180a-0000-1000-8000-00805f9b34fb)
	UUID: Vendor specific           (0000e0ff-3c17-d293-8e48-14fe2e4da212)
	UUID: Vendor specific           (70954782-2d83-473d-9e5f-81e1d02d5273)
	Modalias: bluetooth:v005Dp0000d0100
	ManufacturerData Key: 0x04d8
	ManufacturerData Value:
  01 00                                            ..              
	RSSI: -46
	TxPower: 0

netzwanze avatar Dec 23 '20 23:12 netzwanze

Last year, I successfully translate this project to iOS project with swift language. So I purchased a mini link to test. There are three characteristics can read or write 0xFFE1 (Write Notify) 0xFFE9 (Write) 0xFFEA (Notify)

When I tried to sendPrePrintCommand with cmdNumber from 1 to 8, it gets no response at all. So I think they have changed communications protocol.

caichunjian520 avatar Dec 24 '20 10:12 caichunjian520

I suspect that you're right @caichunjian520, the jump to bluetooth would have probably have instigated more changes deeper down the stack too. However, this actually should be pretty easy to reverse engineer compared to the original instax devices. If anyone has an instax mini and an android device, they can enable the Bluetooth HCI snoop log, which I have a feeling will let you log all of your bluetooth traffic and then analyse it in WireShark on your computer (https://support.honeywellaidc.com/s/article/How-to-capture-Bluetooth-traffic-from-and-to-an-Android-Device)

If we could get a complete set of interactions between the android app and the mini this way, we should be able to figure out what's going on.

I'm unlikely to buy another instax printer, but if we can get the wireshark logs uploaded, I'd be happy to go through and see if I can match them to this API.

jpwsutton avatar Dec 28 '20 09:12 jpwsutton

Hi will try it with my Moto G5 with LineageOS.I find the developer options. Now i have to find the log.

Am Montag, den 28.12.2020, 01:39 -0800 schrieb James Sutton:

I suspect that you're right @caichunjian520, the jump to bluetooth would have probably have instigated more changes deeper down the stack too. However, this actually should be pretty easy to reverse engineer compared to the original instax devices.

If anyone has an instax mini and an android device, they can enable the Bluetooth HCI snoop log, which I have a feeling will let you log all of your bluetooth traffic and then analyse it in WireShark on your computer ( https://support.honeywellaidc.com/s/article/How-to-capture-Bluetooth-traffic-from-and-to-an-Android-Device ) If we could get a complete set of interactions between the android app and the mini this way, we should be able to figure out what's going on. I'm unlikely to buy another instax printer, but if we can get the wireshark logs uploaded, I'd be happy to go through and see if I can match them to this API.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

netzwanze avatar Dec 28 '20 10:12 netzwanze

I just try to get the BLE HCI log file. There are two files when printing a photo with mini link printer. One file with extension .log, another with extension .last

In order to get the smallest data file. I just switch on the HCI log option, select image, and print, then switch off the log option. So it would be quicker to analyse.

I print two photos, each has 2 files. So there are total 4 files to analyse.

Image a https://www.dropbox.com/s/z282i2fbl9ldo7p/btsnoop_hci_image_a.log?dl=0 https://www.dropbox.com/s/22uen29t8s0fhv4/btsnoop_hci.log_image_a.last?dl=0

Image b https://www.dropbox.com/s/2h05637o8nhnime/btsnoop_hci_image_b.log?dl=0 https://www.dropbox.com/s/13qwm7lz2rq85f5/btsnoop_hci.log_image_b.last?dl=0

caichunjian520 avatar Dec 28 '20 10:12 caichunjian520

So, it was not so easy for me. I found it on my G5 with LineageOS here ./data/misc/bluetooth/logs/btsnoop_hci.log I printed one file. + 2. file :-) https://www.dropbox.com/sh/dwe22pvy0gs4zy0/AAD536KOrH6jkHpJj78yMxw3a?dl=0

Please contact me via my email. netzwanze(at)gmx.net

netzwanze avatar Dec 29 '20 19:12 netzwanze

Ok, These logs look perfect! For reference, I'm viewing them in wireshark with the btspp filter on as these look like the actual commands / responses between the device and the phone.

Screenshot 2020-12-31 at 11 22 29

Some first observations:

  1. Command Packets start with 0x41 0x62 and Response packets start with 0x61 0x42
  2. The next two bytes appear to be the total length of the packet.
  3. The final byte is a two's complement checksum - 1 for some reason

I'm starting to write some parsing code for the log files you've provided me with, will update here as I find more 👍🏻

jpwsutton avatar Dec 31 '20 13:12 jpwsutton

Ok getting a bit further now, here are what I think some of the commands / responses are: (I'm stripping the headers out for the payloads to make things tidier below.

Command Payload Response Payload Notes
0007 0000 0010 0000 0001 0000 0000 0000 00
0008 0001 00 0012 0001 0000 0846 554a 4946 494c 4d ASCII for FUJIFILM, byte 6 is string length, 8
0008 0001 01 000e 0001 0001 0453 502d 34 ASCII for SP-4, byte 6 is length, 8
0008 0001 02 0012 0001 0002 0831 3034 3538 3634 37 ASCII for 10458647 (Serial Number) byte 6 is length, 8
0008 0001 03 000e 0001 0003 0430 3030 30 ASCII for 0000 byte 6 length, 4
0008 0001 04 000e 0001 0004 0430 3130 32 ASCII for 0102 byte 6 length, 4
0008 0001 05 000e 0001 0005 0431 2e30 30 ASCII for 1.00
0008 0002 00 0013 0002 0000 0258 0320 0200 0002 5800
0007 2010 000a 2010 0000 00
0009 3002 0203 0009 3002 0003
0137...... 0008 3001 00 This is a big command, not big enough for image data thought
0008 0002 02 0011 0002 0002 2600 000d 0000 0000
0008 0002 03 0011 0002 0003 0000 0004 0000 0001
0008 0002 01 000d 0002 0001 0232 0010
0007 3000 000f 3000 0000 10ff f0fb f000
038f...... 000c 1001 0000 0000 02 Lots of these, and huge, definitely the image being sent.

There are a few more, but I'll pause there for now..

This Protocol, definitely seems to be a step-change from the previous one used on the SP-1,2 and 3. Not surprising given the new form factor and communication method (Bluetooth rather than WiFi). From what I've seen already, it would appear that they've had some time to reflect and improve on the old one (always a good thing, but just means a bit more work for us!)

The Plan!

[ ] Create a new mini-link branch [ ] Build out a packet library for the mini link / sp-4 [ ] Build out the test harness and appropriate bluetooth serial code [ ] Test with App & test harness [ ] Flesh out Packet Library [ ] Test with real device [ ] Merge mini-link branch back to master

I can probably get away with doing most of the steps above, and am more than happy to. But will definitely need some help in testing with the real device eventually!

jpwsutton avatar Dec 31 '20 16:12 jpwsutton

Great. I will test with my mini link.

netzwanze avatar Jan 01 '21 09:01 netzwanze

I have a question. I worked on several BLE projects on iOS as well as Android. All of them need to set notify for listening data change from peripheral device. Or send byte array to characteristic to send data.

Based on the log file. I can't figure out which characteristic I should write data. does it work with RFCOMM protocol?

caichunjian520 avatar Jan 01 '21 10:01 caichunjian520

ASCII for 10458647 (SN? or version?) byte 6 is length, 8

10458647 is my instax mini link serial number string. I think every device is different.

caichunjian520 avatar Jan 03 '21 08:01 caichunjian520

@caichunjian520 Thanks, I've updated the table to confirm that it's the SN In regards to the BLE, I've never worked with it before so I wouldn't know where to start. Once we've got some very basic commands implemented in the packet library we can start trying to work out how to initiate and manage the BLE connection. I believe it is using RFCOMM though as that's what wireshark is telling me...

jpwsutton avatar Jan 04 '21 11:01 jpwsutton

I have a question. I worked on several BLE projects on iOS as well as Android. All of them need to set notify for listening data change from peripheral device. Or send byte array to characteristic to send data.

Based on the log file. I can't figure out which characteristic I should write data. does it work with RFCOMM protocol?

It's a pretty basic protocol and basically just implements a UART using GATT. The printer advertises a the UART "service" ( "70954782-2d83-473d-9e5f-81e1d02d5273"), which contains a characteristic where the client registers notifications ( "70954784-2d83-473d-9e5f-81e1d02d5273") and another which the client writes to ("70954783-2d83-473d-9e5f-81e1d02d5273"). There are some MTU limitations in BLE, so it seems like there are cases where an entire request/response frame won't always fit in a single GATT write or notification. It seems like the printer and the official client just performs multiple writes/notifications in that case.

I believe Android uses the exact same protocol over RFCOMM instead of BLE. There might be some subtle differences to "reserve" the connection in the BLE case (there is a command that only seems to be used over BLE), but I don't think that is important.

andysan avatar Jan 31 '21 22:01 andysan

I have played around with one of these printers and made the following observations:

  • Requests start with a 2 byte opcode followed by request-specific data.
  • Responses start with a 2 byte opcode, a 1 byte result code, followed by response-specific data.
  • The first byte in the opcode is a seems to be a command group.
  • The image is always(?) transfered as a JPEG.
  • Integers are always big endian.

Interesting opcode:

  • 0x0001: Query device information such as model number (returns ASCII). Most of this information is available using standard GATT characteristics. The first byte selects the value to query.
  • 0x0002: Query device capabilities and state (print resolution, prints left, battery, etc.). The first byte determines which function to query.
  • 0x1000: Begin image
  • 0x1001: Image data block (32-bit block index followed by data)
  • 0x1002: End of image
  • 0x1080: Print uploaded image
  • 0x3000: Query accelerometer
  • 0x3001: Set LED state (it's possible to specify a short animation)

Images are broken into blocks of data and the last block is padded to be the length of a full block. The start command contains the size of the image and the response contains the requested block size.

andysan avatar Jan 31 '21 22:01 andysan

Just a heads up that Fujifilm has a new partnership with Nintendo and a new app that supports printing Nintendo Switch screenshots, a new colorway of the Mini Link printer (white/red/blue), a Pikachu silicon case, might ultimately drive some additional traffic to this repo: https://instax.com/instaxyourhero/en/

vitorio avatar Apr 28 '21 21:04 vitorio

I have this Mini Link because of the new app lol but not the Nintendo version, but maybe reversing the app might show additional information about how it connects and the like.

therealprocyon avatar Jun 05 '21 14:06 therealprocyon

I will have a look at this later either this week or the next. As I have multiple things on the planning today etc.

therealprocyon avatar Jun 09 '21 06:06 therealprocyon

recent update to their mini-link app completely destroyed print quality when image resizing is required. I have images to show for comparison, it's absurd. The nintendo app also prints to the mini link and did not get the update.

however, even before the update, the downsizing led to weird but consistent artifacts between prints. I even bought a second printer to verify, and used the mini link app across different versions on different phones to determine the cause.

I'd love to use this repo to print to my mini-link. not very familiar with the hardware side, but can contribute to resizing / compression if using this doesn't already fix the problem of artifacting I am noticing. Please let me know how I might be able to help (happy to do so, especially on the testing side).

mathematicalmichael avatar Aug 03 '21 05:08 mathematicalmichael

did we ever get to the point of actually printing ? I have a bunch of those around :D and would love to thinker (there's also the wide printer which I believe is the same protocol)

emudojo avatar Jan 18 '22 17:01 emudojo

Anyone had luck with this?

chicoe avatar Sep 12 '22 20:09 chicoe

For some reason I wasn't able to find this repo before. Great to see other users wanting to liberate the Link Mini from its app!

Edit: after reading this thread more thoroughly I see my info was a bit redundant, but I'll leave it up anyway ;)

Below is a small dump of my attempts at reverse engineering this device. I'll be sure to further check everything that has been said in this thread. I wasn't able to get the BLE snoop logs to work on my Pixel 3a, so thanks for your upload @netzwanze! And for the note on USB. Turns out I was using one of those cursed microusb-with-just-power-no-data cables..

My experience with BLE devices is very limited but I've been able to figure out you can indeed register for notifications with 70954784-2d83-473d-9e5f-81e1d02d5273 like @andysan said. After registering I get the following response: b'\x02\t\xf7\x00\x11\x01\x00\x80\x84\x1e\x00'. Not been able to decode this yet (but also didn't really try).

But checking the bt_stack logs on my Android device also seem to suggest it uses rfcomm?

# bt_stack: [INFO:port_api.cc(234)] RFCOMM_CreateConnection: bd_addr=88:b4:36:xx:xx:xx, scn=6, is_server=0, mtu=990, uuid=0x1101, dlci=12, signal_state=0x0b, p_port=0x72a45da9c8

I've been able to open a connection using rfcomm on the commandline, but I don't know how to send my bytearray that way.

I've been going trough the Android code, and from what I can see in the code, at least some of the packets you send to the device are ordered like this:

'Ab' // two bytes, some header
packetsize // two bytes, default is 7
event ID // byte, integer
eventValue // byte, integer
checksum // byte

The checksum is calculated by summing all items and generating a value from 0 to 255 which gets added to the packet as the last byte: return (255 - (sum(bytearray) & 255)) & 255;

Those event ID and value seem to match the ones in this list, but I'm not completely sure if these are things you can send to the device, or types of info you can get back from the device (there are a total of 64 of these):

SUPPORT_FUNCTION_AND_VERSION_INFO(0, 0), 
DEVICE_INFO_SERVICE(0, 1), 
SUPPORT_FUNCTION_INFO(0, 2), 
IDENTIFY_INFORMATION(0, 16), 
SHUT_DOWN(1, 0), 
RESET(1, 1), 
AUTO_SLEEP_SETTINGS(1, 2), 
BLE_CONNECT(1, 3), 
PRINT_IMAGE_DOWNLOAD_START(16, 0), 

Fun fact: running logcat on the Nintendo version of the app (# adb logcat "InstaxApplication:D *:S") returns the x, y and z value of the accelerometer on std.out. Kind of a weird thing for the devs to leave in there.

javl avatar Dec 12 '22 21:12 javl

This is a small bare-bones script that allows you to talk to the printer. It connects to the first Instax-... (IOS) device it sees, registers to the notify endpoint and sends a packet to the write endpoint. It prints out any response and checks if it contains a valid checksum.

So next step: figure out what to send exactly ;)

import asyncio
from bleak import BleakScanner, BleakClient
from bleak.backends.characteristic import BleakGATTCharacteristic
from struct import pack, unpack_from

writeUUID = '70954783-2d83-473d-9e5f-81e1d02d5273'
notifyUUID = '70954784-2d83-473d-9e5f-81e1d02d5273'

def prettify_bytearray(value):
  return ' '.join([f'{x:02x}' for x in value])

def createChecksum(bytearray):
    return (255 - (sum(bytearray) & 255)) & 255;

def createPacket(payload):
    start = b'\x41\x62'  # Ab from client, server responds with Ba
    packetSize = pack('>H', 5 + len(payload))
    packet = start + packetSize + payload
    packet += pack('B', createChecksum(packet))
    print(f"Sending packet: {packet}, (length: {len(packet)})")
    print(f"  {prettify_bytearray(packet)} (length: {len(packet)}")
    return packet

def validate_checksum(data):
    return (sum(data) & 255) == 255

def notification_handler(characteristic: BleakGATTCharacteristic, packet: bytearray):
    if len(packet) < 8:
        print(f"Error: response packet size should be >= 8 (was {len(packet)})")
        raise
    elif not validate_checksum(packet):
        print("Checksum validation failed")
        raise

    data = packet[4:-1]  # the packet without header and checksum
    print(f'Response data: {data} (length: {len(data)}, checksum OK)')
    print(f'  {prettify_bytearray(data)}')


async def main():
    dev = None
    devices = await BleakScanner.discover()
    for d in devices:
        if d.name.startswith('INSTAX-')  and d.name.endswith('(IOS)'):
            dev = d
            break
    if not dev:
        print("No printer found")
        exit()

    print(f'Found instax printer {dev.name} at {dev.address}')

    async with BleakClient(dev.address) as client:
        print('connected')
        await client.start_notify(notifyUUID, notification_handler)

        packet = createPacket(b'\x00\x01\x02') # Request serial number
        await client.write_gatt_char(writeUUID, packet)

        await asyncio.sleep(5.0)

asyncio.run(main())

javl avatar Dec 14 '22 08:12 javl

Hi Javl! Glad to read that you got a connection. It would be a burner to get an image printed over bluetooth at the mini link directly from an own app. Please keep on going!!

hermanneduard avatar Dec 14 '22 09:12 hermanneduard

Sorry for my poor English and lack of knowledge on bluetooth data.

I just print a photo and get the log

The image data split into 69 blocks, 900 bytes image data each block

41 62 / 03 8F / 10 01 / 00 00 00 n / image block bytes / checksum

0x41, 0x62 header 0x03, 0x8F length
0x10, 0x01 upload image 0x00, 0x00, 0x00, n upload block index, ~the last n range: 0x00 - 0x44~ image block bytes 900 bytes

But there are two questions I don't understand:

  1. I tried to print two different images, but the first image sent with 69 blocks and the second with 60 blocks.
  2. If I use 69 blocks, 900 bytes * 69 = 62100 bytes, and 3 bytes per pixel for a color image, so there is only 20700 pixels? But the image should be 600 * 800 * 3 = 1440000 pixels.
  3. At the final image block (index 0x44), most of the image data is 0x00, which is totally different from previous block.

caichunjian520 avatar Dec 15 '22 07:12 caichunjian520

I'm keeping track of my attempts over at my InstaxBLE repo. It might be nice to integrate it with instax_api at some point but for now I like to be able to store it somewhere.

As a demo the script currently gets the device's serialnumber, and then prints out the accelerometer info, 2 times per second. Events are defined as EventType.<eventname> for easy access. For example, requesting the serialnumber can be done using:

packet = createPacket(EventType.DEVICE_INFO_SERVICE, b'\x02')  # \x02 means serialnumber
await client.write_gatt_char(writeUUID, packet)

Events are listed over here with parsers for each type here. Not the most optimized code, but it's progress ;)

javl avatar Dec 15 '22 08:12 javl

I think I've got it; I wrote a working parser for the logs provided by @netzwanze and @caichunjian520! (the blue blobs are for privacy)

I've also been able to send commands to control the LEDs, so in theory I should be able to combine the two and make an actual print! I hope to be able to check this tonight.

For the LED control the packet turns out to use the following:

  1. the number of frames in your animation
  2. the frame duration (higher is a slower animation)
  3. a RGB color per frame
  4. when to run (immediately, on print, on print complete or 'pattern switch', not sure what that is)
  5. how many times to repeat the animation (0 plays once, 1 plays two times (repeats once), etc.. up to 255 which makes it repeat forever.

image image

javl avatar Dec 16 '22 16:12 javl

This is awesome folks! Sorry I've not been more involved, work has been mad and I still don't have a bluetooth version of the printer so can't do much myself at the moment. I need to do some general improvements to the existing library though, so will have a think about how the bluetooth components could be merged in.

jpwsutton avatar Dec 16 '22 17:12 jpwsutton

I was able to print one of the above photos from my Python code! I didn't manage to print my own image yet; there are still some values I'm not sure about which I need to calculate per image. But very close!

javl avatar Dec 16 '22 18:12 javl