Notes on Bluetooth - tiredboffin/fffw GitHub Wiki
The article is not complete.
Note: Below is a brief overview of the BLE implementation on Fujifilm cameras. A proof-of-concept implementation for both the "client" (app) and "server" (camera) written in python is planned to be published on ffbt eventually.
If you're curious about Fujifilm's BLE and the high-level overview below doesn’t cover what you need, feel free to ping me on the Fujihack server Your interest might just give me the motivation to wrap it up.
Pairing Mode
Fujifilm cameras use standard Bluetooth Low Energy (BLE) to communicate with mobile apps, but they do not utilize BLE security features such as pairing, bonding, or encryption. Instead, they implement a process called "Pairing Registration" (as described in the user guide), which resembles the BLE "Just Works" association model from the user's perspective. During this process, the camera registers a specific instance of the mobile app and provides it with what can be described as a pairing token. This token is then used by the app in subsequent sessions with the camera, functioning similarly to an authentication token in API-based systems. However, there is minimal effort to protect or secure this token—it is highly predictable, can be easily eavesdropped, and in Standby State the camera simply broadcasts it.
Pairing Advertisements
When in pairing mode, the Camera periodically broadcasts Bluetooth advertisements that include the Camera Advertised Name (referred to as the "Complete Local Name" in Bluetooth Core terminology), the Camera Remote UUID, and Manufacturer Data, which contains the Pairing Token.
- Camera Remote UUID:
117c4142-edd4-4c77-8696-dd18eebb770a - Camera Advertised Name: Constructed by appending the camera model name (e.g.,
X-T4) to the last four hexadecimal digits of the Camera’s Unique Number. For example:2A09X-T42A09. This name is fixed and cannot be changed via the user interface. - Manufacturer Data: Includes a data element identified by Fujifilm’s Bluetooth SIG Registered Company ID (
0x04D8). This element is 5 bytes long:- The first byte is always
0x02 - The remaining four bytes represent the Pairing Token
- The Pairing Token is a pseudo-random value generated using a simple variant of a Galois Linear Feedback Shift Register (LFSR), seeded with the camera’s "age" in seconds. This LFSR is easily reversible (see, for example, ...).
- The first byte is always
- Cameras that support XApp also broadcast XApp UUID:
af854c2e-b214-458e-97e2-912c4ecf2cb8. Both types of advertisements are sent intermittently, making the newer camera discoverable by both Camera Remote and XApp applications. All current cameras still support Camera Remote. - The Pairing Token value also differs between advertisements with the XApp UUID and those with the Camera Remote UUID.
Pairing Connect
- When the app connects to the camera, it sends back the Pairing Token obtained from the advertisement. The camera compares this value with the one it advertised. For cameras that support both XApp and Camera Remote, the token must match one of the two advertised values, allowing the camera to distinguish whether it is pairing with XApp or Camera Remote.
- See Connection for more details. The only notable difference I think is that the camera immediately requests a datetime sync from XApp. Otherwise, the camera and app proceed as usual and are ready for normal operation.
On Successful Pairing:
- The App creates a new profile and:
- Stores the Camera Advertised Name and Pairing Token. The camera’s BLE MAC address is not random but is still not used.
- Reads and stores the Camera Unique Number (exchanged as a Serial Number String GATT charc 0x2a25, also matches the last 18 digits of USB Serial Number).
- Reads and stores the Camera Name, which is editable from both the camera and the app. This name is used for display in the UI and as the Wi-Fi AP SSID. The
FUJIFILMprefix is fixed and cannot be changed via the UI, though a non-native app may override it. - If the Camera Unique Number matches an existing profile, XApp updates that profile instead of creating a new one.
- The Camera creates a new profile on each pairing and:
- Uses the app name (received during connection) as the profile’s display name in the UI.
- Stores the Pairing Token; the phone's BLE MAC address is not stored.
- On newer models, also records which app (Camera Remote or XApp) was used for pairing.
- Stores the app name for UI display purposes.
- Sets the newly created profile as the active one (pairing destination profile).
- Supports up to 7 profiles; when an 8th profile is created, the oldest one is deleted.
Standby State
When the Connect While Powered Off option is enabled on the camera (available on most newer models), the camera enters Standby Mode when powered off. In this mode, it broadcasts a Bluetooth advertisement approximately every 15 seconds using a special standby UUID: 731893f9-744e-4899-b7e3-174106ff2b82. This advertisement includes the previously stored Pairing Token from the current pairing destination profile.
Note: This makes it possible for unpaired apps to intercept the token and attempt to connect.
In Standby Mode, initial connection attempts from XApp are immediately rejected, resulting in a timeout on the app side. After this rejection, the camera briefly switches to broadcasting using the standard XApp UUID: af854c2e-b214-458e-97e2-912c4ecf2cb8, allowing XApp to rediscover the camera and retry the connection.
Note: This behavior is not currently emulated by
ffbt-camera, as the BlueZ API does not expose a way to reject Bluetooth connections. (TBC)
Once connected in Standby Mode, the camera screen remains off, and it expects XApp to immediately issue a command (e.g., Save Backup). If no command is received within a short window, the camera terminates the connection and resumes Standby advertising.
Once the command completes, XApp requests the camera to terminate the connection, after which the camera returns to Standby Mode.
Advertising State
- When BLE is enabled and a pairing destination profile is selected on the Camera, it continuously broadcasts advertisements using:
- The Advertisement Name from the selected profile
- The UUID associated with the paired application (e.g., XApp or Camera Remote; see above)
- The app listens (using Passive Scanning) for advertisements filtered by its specific UUID and matches the Camera Advertisement Name stored in its profile.
- When a match is found, the Camera proceeds to establish a connection.
Note: If multiple application instances are registered with the camera—such as on two or more mobile devices—each will attempt to connect upon detecting the camera's advertisements, leading to a connection collision. All but one instance will fail due to a pairing token mismatch (see Connection).
Connection
- The camera establishes a BLE connection and discovers all available services and characteristics using the standard GATT discovery process. For a complete list of the discovered services and characteristics, along with their descriptions, refer to [documentation link].
- The camera requests MTU update. The value is fixed and depends on the camera model
- The app reads the Camera Unique Number to ensure it matches the expected profile. If there is no match, the app disconnects.
-
Note: Interestingly, the unique number appears to be used to associate some camera-related data (e.g., backups). If two cameras share the same unique number (which can only occur with an emulator), the app essentially 'shares' data (like backups) between the two cameras.
- The app then sends the Pairing Token to the camera for validation. If the token does not match the value stored in the pairing destination profile, the camera returns an error. If it matches, the connection is successfully established.
- The app reads the firmware version of the camera and appears to check it against an internal compatibility table. If the model name/version is not listed in the table, the app refuses to proceed.
- The app reads the camera’s name and updates the UI to reflect any changes made to the name on the camera itself. The app sends the client (phone) name to the camera and the camera updates the profile name accordingly.
- The app reads various data elements, some seemingly unused data (e.g., camera body serial number, lens serial number, lens name, etc.). It also sets up notification handlers for specific shared settings, which are linked to the Shared Settings Sync process.
- For complete details refer to the source code at ffbt (TBP)
Functions
Shutter release
APP sends shutter S1 (half) and S2 (full) press/release commands to the camera. For details see for e.g. furble and ffbt (TBP)
Shared settings sync
Some settings can be modified on both the Camera and XApp sides. One example is the "Resize Images" option allows the camera to further compress JPEG images during transfer to XApp for faster transmissions. When one of these 'shared' settings is changed in XApp, the app writes the updated value to the camera. Conversely, when the setting is changed on the camera, it immediately sends a BLE update to XApp, which in turn reflects the change in the UI.
Time and Location sync
Both XApp and the Camera Remote (excluding the very first versions) support synchronization of time and geolocation data. However, the exact implementation varies depending on the app.
XApp: the geolocation update interval can be configured via the app settings (starting from 10 seconds). The app then updates this interval on the camera.
Note: In current firmware versions, there is no option to change the geolocation interval from the camera's UI. (To be confirmed)
Once geolocation sync is enabled—whether from the app or camera side (as this is one of the Shared settings)—the camera begins regularly polling XApp for updated location data.
The location data includes:
- Latitude
- Longitude
- Altitude
- Elevation
- Estimated accuracy
- Timestamp
Time synchronization can also be requested by the camera (see Pairing Connect) or XApp may send time updates unsolicited. XApp uses a newer date-time format that includes UTC time, time zone, and DST (Daylight Saving Time).
Camera Remote: todo: need to double check
For more details, refer to the furble and ffbt repositories. (To be published)
File operations
- XApp uses the following file operations over BLE:
- Backup save
- Backup restore
- Activity Logs reading
- Camera Vitals (statistics) reading - supported on the latest cameras only
- All file operations use the same underlying file transfer protocol.
- Backup files are compressed using zlib, while activity logs use gzip.
- Activity logs are human-readable CSV files.
Note: The backup file format and activity logs are the subject of ongoing research (see activity samples).
- Camera Vitals is a ~6KB JSON file that contains various statistics, such as the shutter count. Some of this data isn't visible in the camera’s user interface—for example, "lens_connect_count", "command_dial_push_count", and many other values (see samples).
Note: The original file also includes an error log, which has always been empty in my testing but still occupies a significant portion (~85%) of the file and is deleted from the sample
File transfer protocol
The files are transferred using the same file transfer protocol over GATT. The exact protocol appears to be proprietary, as I was unable to identify a standard equivalent.
Both the client and server sides of protocol are implemented in ffbt-app and ffbt-camera applications. For more details, see [link]. TBP
Operations over Wifi
XApp and Camera Remote support several operations over a PTP/IP Wi-Fi connection:
- Get and Take pictures
- Auto image transfer
- Firmware Updates
Note: For details on PTP/IP implementation on Fujifilm cameras see also fudge
When XApp needs to perform one of these operations, it sends a corresponding command to the camera, specifying the requested action or "PTP mode".
For firmware updates, the application first sends the name and size of the firmware DAT file (for the camera or lens) before issuing the firmware update command.
Upon receiving the command, the camera enables its Wi-Fi Access Point (AP), using the camera name as the SSID.
XApp retrieves the SSID from the camera and also obtains the AP's BSSID (i.e., its MAC address). This BSSID is used to uniquely identify the correct AP, since the camera name (SSID) is user-configurable and may not be unique.
Note: When running the
ffbt-camera --app xappthe emulator should be configured with both the SSID and BSSID of the PTP emulator operating on or behind the access point (AP). Alternatively, it can pass these values as parameters to a (to be) integrated PTP emulator vcam.