diff --git a/assets/js/lunr/lunr-store.js b/assets/js/lunr/lunr-store.js index 78706c21..d2fd04e6 100644 --- a/assets/js/lunr/lunr-store.js +++ b/assets/js/lunr/lunr-store.js @@ -15,14 +15,14 @@ var store = [ }, { "title": "Tutorials: ", - "excerpt": "This set of tutorials is a series of sample scripts / files and accompanying .html walk-throughs to implement basic functionality to interact with a GoPro device using the following languages: - Python - Kotlin - More to come! The tutorials only support Open GoPro Version 2.0 and must be run on a [supported camera](/OpenGoPro/ble/index.htmlsupported-cameras). They will provide walk-throughs and sample code to use the relevant language / framework to exercise the Open GoPro Interface using Bluetooth Low Energy (BLE) and HTTP over WiFi. {% warning %} The tutorials are only tested on the latest camera / firmware combination. This is only an issue in cases where capabilities change between cameras such as setting options. {% endwarning %} The tutorials are meant as an introduction to the Open GoPro specification. They are not a substitute for the complete [BLE](/OpenGoPro/ble/index.html) and [HTTP](/http) specifications which will be your main source of reference after completing the tutorials. {% for tutorial in site.tutorials %} - [{{ tutorial.title }}]({{ tutorial.permalink | prepend: site.baseurl }}) {% endfor %}", + "excerpt": "This set of tutorials is a series of sample scripts / files and accompanying .html walk-throughs to implement basic functionality to interact with a GoPro device using the following languages: - Python - Kotlin - More to come! The tutorials only support Open GoPro Version 2.0 and must be run on a [supported camera](/ble/index.htmlsupported-cameras). They will provide walk-throughs and sample code to use the relevant language / framework to exercise the Open GoPro Interface using Bluetooth Low Energy (BLE) and HTTP over WiFi. {% warning %} The tutorials are only tested on the latest camera / firmware combination. This is only an issue in cases where capabilities change between cameras such as setting options. {% endwarning %} The tutorials are meant as an introduction to the Open GoPro specification. They are not a substitute for the complete [BLE](/ble/index.html) and [HTTP](/http) specifications which will be your main source of reference after completing the tutorials. {% for tutorial in site.tutorials %} - [{{ tutorial.title }}]({{ tutorial.permalink | prepend: site.baseurl }}) {% endfor %}", "categories": [], "tags": [], "url": "/OpenGoPro/tutorials/#" }, { "title": "Tutorial 1: Connect BLE: ", - "excerpt": "This tutorial will provide a walk-through to connect to the GoPro camera via Bluetooth Low Energy (BLE). Requirements Hardware A GoPro camera that is supported by Open GoPro python kotlin One of the following systems: Windows 10, version 16299 (Fall Creators Update) or greater Linux distribution with BlueZ >= 5.43 OS X/macOS support via Core Bluetooth API, from at least OS X version 10.11 An Android Device supporting SDK >= 33 Software python kotlin Python >= 3.9 and < 3.12 must be installed. See this Python installation guide. Android Studio >= 2022.1.1 (Electric Eel) Overview / Assumptions python kotlin This tutorial will use bleak to control the OS’s Bluetooth Low Energy (BLE). The Bleak BLE controller does not currently support autonomous pairing for the BlueZ backend. So if you are using BlueZ (i.e. Ubuntu, RaspberryPi, etc.), you need to first pair the camera from the command line. One way to do this is to use bluetoothctl. The bleak module is based on asyncio which means that its awaitable functions need to be called from an async coroutine. In order to do this, all of the code below should be running in an async function. We accomplish this in the tutorial scripts by making main async as such: import asyncio async def main() -> None: Put our code here if __name__ == \"__main__\": asyncio.run(main()) These are stripped down Python tutorials that are only meant to show the basics. For a complete Python SDK that uses bleak as the backend as well as a cross-platform WiFi backend to easily write Python apps that control the GoPro, see the Open GoPro Python SDK This tutorial will provide a set of Kotlin tutorials to demonstrate Open GoPro Functionality. The tutorials are provided as a single Android Studio project targeted to run on an Android device. The tutorials are only concerned with application-level demonstrations of the Open GoPro API and therefore do not prioritize the following: UI: The tutorial project only contains a minimal UI to select, implement, and view logs for each tutorial Android architecture / best practices: the project architecture is designed to encapsulate Kotlin functionality to easily display per-tutorial functionality Android-specific requirements: permission handling, adapter enabling, etc are implemented in the project but not documented in the tutorials BLE / Wifi (HTTP) functionality: A simple BLE API is included in the project and will be touched upon in the tutorials. However, the focus of the tutorials is not on how the BLE API is implemented as a real project would likely use a third-party library for this such as Kable See the Punchthrough tutorials for Android BLE-Specific tutorials These tutorials assume familiarity and a base level of competence with: Android Studio Bluetooth Low Energy JSON HTTP Setup python kotlin This set of tutorials is accompanied by a Python package consisting of scripts separated by tutorial module. These can be found on Github. Once the Github repo has been cloned or downloaded to your local machine, the package can be installed as follows: Enter the python tutorials directory at $INSTALL/demos/python/tutorial/ where $INSTALL is the top level of the Open GoPro repo where it exists on your local machine Use pip to install the package (in editable mode in case you want to test out some changes): pip install -e . While it is out of the scope of this tutorial to describe, it is recommended to install the package in to a virtual environment in order to isolate system dependencies. You can test that installation was successful by viewing the installed package’s information: $ pip show open-gopro-python-tutorials Name: open-gopro-python-tutorials Version: 0.0.3 Summary: Open GoPro Python Tutorials Home-page: https://github.com/gopro/OpenGoPro Author: Tim Camise Author-email: gopro.com License: MIT Location: c:\\users\\tim\\gopro\\opengopro\\demos\\python\\tutorial Requires: bleak, requests Required-by: This set of tutorials is accompanied by an Android Studio project consisting of, among other project infrastructure, Kotlin files separated by tutorial module. The project can be found on Github. Once the Github repo has been cloned or downloaded to your local machine, open the project in Android studio. At this point you should be able to build and load the project to your Android device. The project will not work on an emulated device since BLE can not be emulated. Just Show me the Demo!! python kotlin Each of the scripts for this tutorial can be found in the Tutorial 1 directory. Python >= 3.9 and < 3.12 must be used as specified in the requirements You can test connecting to your camera through BLE using the following script: python ble_connect.py See the help for parameter definitions: $ python ble_connect.py --help usage: ble_connect.py [-h] [-i IDENTIFIER] Connect to a GoPro camera, pair, then enable notifications. optional arguments: -h, --help show this help message and exit -i IDENTIFIER, --identifier IDENTIFIER Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to The Kotlin file for this tutorial can be found on Github. To perform the tutorial, run the Android Studio project, select “Tutorial 1” from the dropdown and click on “Perform.” Perform Tutorial 1 This will start the tutorial and log to the screen as it executes. When the tutorial is complete, click “Exit Tutorial” to return to the Tutorial selection screen. Basic BLE Tutorial This tutorial will walk through the process of connecting to a GoPro via BLE. This same connect functionality will be used as a foundation for all future BLE tutorials. Here is a summary of the sequence that will be described in detail in the following sections: GoProOpen GoPro user deviceGoProOpen GoPro user deviceScanningConnectedalt[If not Previously Paired]PairedReady to CommunicateAdvertisingAdvertisingConnectPair RequestPair ResponseEnable Notifications on Characteristic 1Enable Notifications on Characteristic 2Enable Notifications on Characteristic ..Enable Notifications on Characteristic N Advertise First, we need to ensure the camera is discoverable (i.e. it is advertising). Follow the per-camera steps here. The screen should appear as such: Camera is discoverable. Scan Next, we must scan to discover the advertising GoPro Camera. python kotlin We will do this using bleak. Let’s initialize an empty dict that will store discovered devices, indexed by name: Map of devices indexed by name devices: Dict[str, BleakDevice] = {} We’re then going to scan for all devices. We are passing a scan callback to bleak in order to also find non-connectable scan responses. We are keeping any devices that have a device name. Scan callback to also catch nonconnectable scan responses def _scan_callback(device: BleakDevice, _: Any) -> None: Add to the dict if not unknown if device.name and device.name != \"Unknown\": devices[device.name] = device Now discover and add connectable advertisements for device in await BleakScanner.discover(timeout=5, detection_callback=_scan_callback): if device.name != \"Unknown\" and device.name is not None: devices[device.name] = device Now we can search through the discovered devices to see if we found a GoPro. Any GoPro device name will be structured as GoPro XXXX where XXXX is the last four digits of your camera’s serial number. If you have renamed your GoPro to something other than the default, you will need to update the below steps accordingly. First, we define a regex which is either “GoPro “ followed by any four alphanumeric characters if no identifier was passed, or the identifier if it exists. In the demo ble_connect.py, the identifier is taken from the command-line arguments. token = re.compile(identifier or r\"GoPro [A-Z0-9]{4}\") Now we build a list of matched devices by checking if each device’s name includes the token regex. matched_devices: List[BleakDevice] = [] Now look for our matching device(s) matched_devices = [device for name, device in devices.items() if token.match(name)] Due to potential RF interference and the asynchronous nature of BLE advertising / scanning, it is possible that the advertising GoPro will not be discovered by the scanning PC in one scan. Therefore, you may need to redo the scan (as ble_connect.py does) until a GoPro is found. That is, matched_device must contain at least one device. Similarly, connection establishment can fail for reasons out of our control. Therefore, the connection process is also wrapped in retry logic. Here is an example of the log from ble_connect.py of scanning for devices. Note that this includes several rescans until the devices was found. $ python ble_connect.py INFO:root:Scanning for bluetooth devices... INFO:root: Discovered: INFO:root: Discovered: TR8600 seri INFO:root:Found 0 matching devices. INFO:root: Discovered: INFO:root: Discovered: TR8600 seri INFO:root: Discovered: GoPro Cam INFO:root: Discovered: GoPro 0456 INFO:root:Found 1 matching devices. Among other devices, you should see GoPro XXXX where XXXX is the last four digits of your camera’s serial number. First let’s define a filter that will be used to find GoPro device advertisements. We do this by filtering on the GoPro Service UUID that is included in all GoPro advertisements: private val scanFilters = listOf<ScanFilter>( ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(GOPRO_UUID)).build() ) We then send this to the BLE API and collect events from the SharedFlow that it returns. We take the first event emitted from this SharedFlow and notify (via a Channel) that a GoPro advertiser has been found, store the GoPro’s BLE address, and stop the scan. ble.startScan(scanFilters).onSuccess { scanResults -> val deviceChannel: Channel<BluetoothDevice> = Channel() // Collect scan results CoroutineScope(Dispatchers.IO).launch { scanResults.collect { scanResult -> // We will take the first discovered gopro deviceChannel.send(scanResult.device) } } // Wait to receive the scan result goproAddress = deviceChannel.receive().address ble.stopScan(scanResults) } At this point, the GoPro’s BLE address is stored (as a string) in goproAddress. Here is an example log output from this process: Scanning for GoPro's Received scan result: GoPro 0992 Found GoPro: GoPro 0992 Connect Now that we have discovered at least one GoPro device to connect to, the next step is to establish a BLE connection to the camera. python kotlin We're just taking the first device if there are multiple. device = matched_devices[0] client = BleakClient(device) await client.connect(timeout=15) An example output of this is shown here where we can see that the connection has successfully been established as well as the GoPro’s BLE MAC address.: INFO:root:Establishing BLE connection to EF:5A:F6:13:E6:5A: GoPro 0456... INFO:bleak.backends.dotnet.client:Services resolved for BleakClientDotNet (EF:5A:F6:13:E6:5A) INFO:root:BLE Connected! ble.connect(goproAddress) At this point, the BLE connection is established but there is more setup to be done before we are ready to communicate. Pair The GoPro has encryption-protected characteristics which require us to pair before writing to them. Therefore now that we are connected, we need to attempt to pair. python kotlin try: await client.pair() except NotImplementedError: This is expected on Mac pass Not all OS’s allow pairing (at this time) but some require it. Rather than checking for the OS, we are just catching the exception when it fails. Rather than explicitly request pairing, we rely on the fact that Android will automatically start the pairing process if you try to read a characteristic that requires encryption. To do this, we read the Wifi AP Password characteristic. First we discover all characteristics (this will also be needed later when enabling notifications): ble.discoverCharacteristics(goproAddress) This API will discover the characteristics over-the-air but not return them here. They are stored to the ble object for later access via the servicesOf method. Then we read the relevant characteristic to trigger pairing: ble.readCharacteristic(goproAddress, GoProUUID.WIFI_AP_PASSWORD.uuid) At this point a pairing popup should occur on the Android Device. Select “Allow Pairing” to continue. Here is an example log output from this process: Discovering characteristics Discovered 9 services for F7:5B:5D:81:64:1B Service 00001801-0000-1000-8000-00805f9b34fb Characteristics: |-- Service 00001800-0000-1000-8000-00805f9b34fb Characteristics: |--00002a00-0000-1000-8000-00805f9b34fb: READABLE |--00002a01-0000-1000-8000-00805f9b34fb: READABLE |--00002a04-0000-1000-8000-00805f9b34fb: READABLE ... |------00002902-0000-1000-8000-00805f9b34fb: EMPTY |--b5f90082-aa8d-11e3-9046-0002a5d5c51b: WRITABLE |--b5f90083-aa8d-11e3-9046-0002a5d5c51b: NOTIFIABLE |------00002902-0000-1000-8000-00805f9b34fb: EMPTY |--b5f90084-aa8d-11e3-9046-0002a5d5c51b: NOTIFIABLE |------00002902-0000-1000-8000-00805f9b34fb: EMPTY Service 00001804-0000-1000-8000-00805f9b34fb Characteristics: |--00002a07-0000-1000-8000-00805f9b34fb: READABLE Pairing Read characteristic b5f90003-aa8d-11e3-9046-0002a5d5c51b : value: 66:3F:54:2D:38:35:72:2D:4E:35:63 Once paired, the camera should beep and display “Connection Successful”. This pairing process only needs to be done once. On subsequent connections, the devices will automatically re-establish encryption using stored keys. That is, they are “bonded.” Enable Notifications As specified in the Open GoPRo BLE Spec, we must enable notifications for a given characteristic to receive responses from it. To enable notifications, we loop over each characteristic in each service and enable the characteristic for notification if it has notify properties: python kotlin It is necessary to define a notification handler to pass to the bleak start_notify method. Since we only care about connecting to the device in this tutorial (and not actually receiving data), we are just passing an empty function. A future tutorial will demonstrate how to use this meaningfully. for service in client.services: for char in service.characteristics: if \"notify\" in char.properties: await client.start_notify(char, notification_handler) In the following example output, we can see that notifications are enabled for each characteristic that is notifiable. INFO:root:Enabling notifications... INFO:root:Enabling notification on char 00002a19-0000-1000-8000-00805f9b34fb INFO:root:Enabling notification on char b5f90073-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90075-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90077-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90079-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90092-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90081-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90083-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90084-aa8d-11e3-9046-0002a5d5c51b INFO:root:Done enabling notifications INFO:root:BLE Connection is ready for communication. ble.servicesOf(goproAddress).onSuccess { services -> services.forEach { service -> service.characteristics.forEach { char -> if (char.isNotifiable()) { ble.enableNotification(goproAddress, char.uuid) } } } } Here is an example log output from this process: Enabling notifications Enabling notifications for 00002a19-0000-1000-8000-00805f9b34fb Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90073-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90075-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90077-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90079-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90092-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90081-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90083-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90084-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Bluetooth is ready for communication! The characteristics that correspond to each UUID listed in the log can be found in the Open GoPro API. These will be used in a future tutorial to send data. Once the notifications are enabled, the GoPro BLE initialization is complete and it is ready to communicate via BLE. Quiz time! 📚 ✏️ How often is it necessary to pair? A: Pairing must occur every time to ensure safe BLE communication. B: We never need to pair as the GoPro does not require it to communicate. C: Pairing only needs to occur once as the keys will be automatically re-used for future connections. Submit Answer Correct!! 😃 Incorrect!! 😭 The correct answer is C. Pairing is only needed once (assuming neither side deletes the keys). If the GoPro deletes the keys (via Connections->Reset Connections), the devices will need to re-pair. Troubleshooting Device not connecting If the connection is not starting, it is likely because the camera is not advertising. This can be due to either: The camera is not in pairing mode. Ensure that this is achieved as done in the advertise section. The devices never disconnected from the previous session so are thus already connected. If this is the case, perform the “Complete System Reset” shown below. Complete System Reset BLE is a fickle beast. If at any point it is impossible to discover or connect to the camera, perform the following. Reset the camera by choosing Connections –> Reset Connections Use your OS’s bluetooth settings GUI to remove / unpair the Gopro Restart the procedure detailed above Logs python kotlin The demo program has enabled bleak logs and is also using the default python logging module to write its own logs. To enable more bleak logs, follow bleak’s troubleshooting section. The demo program is using Timber. It is piping all log messages to the UI but they are also available in the logcat window and can be filtered using: package:mine tag:GP_. Good Job! Congratulations 🤙 You can now successfully connect to the GoPro via BLE and prepare it to receive / send data. To see how to send commands, you should advance to the next tutorial.", + "excerpt": "This tutorial will provide a walk-through to connect to the GoPro camera via Bluetooth Low Energy (BLE). Requirements Hardware A GoPro camera that is supported by Open GoPro python kotlin One of the following systems: Windows 10, version 16299 (Fall Creators Update) or greater Linux distribution with BlueZ >= 5.43 OS X/macOS support via Core Bluetooth API, from at least OS X version 10.11 An Android Device supporting SDK >= 33 Software python kotlin Python >= 3.9 and < 3.12 must be installed. See this Python installation guide. Android Studio >= 2022.1.1 (Electric Eel) Overview / Assumptions python kotlin This tutorial will use bleak to control the OS’s Bluetooth Low Energy (BLE). The Bleak BLE controller does not currently support autonomous pairing for the BlueZ backend. So if you are using BlueZ (i.e. Ubuntu, RaspberryPi, etc.), you need to first pair the camera from the command line as shown in the BlueZ tutorial. The bleak module is based on asyncio which means that its awaitable functions need to be called from an async coroutine. In order to do this, all of the code below should be running in an async function. We accomplish this in the tutorial scripts by making main async as such: import asyncio async def main() -> None: Put our code here if __name__ == \"__main__\": asyncio.run(main()) These are stripped down Python tutorials that are only meant to show the basics. For a complete Python SDK that uses bleak as the backend as well as a cross-platform WiFi backend to easily write Python apps that control the GoPro, see the Open GoPro Python SDK This tutorial will provide a set of Kotlin tutorials to demonstrate Open GoPro Functionality. The tutorials are provided as a single Android Studio project targeted to run on an Android device. The tutorials are only concerned with application-level demonstrations of the Open GoPro API and therefore do not prioritize the following: UI: The tutorial project only contains a minimal UI to select, implement, and view logs for each tutorial Android architecture / best practices: the project architecture is designed to encapsulate Kotlin functionality to easily display per-tutorial functionality Android-specific requirements: permission handling, adapter enabling, etc are implemented in the project but not documented in the tutorials BLE / Wifi (HTTP) functionality: A simple BLE API is included in the project and will be touched upon in the tutorials. However, the focus of the tutorials is not on how the BLE API is implemented as a real project would likely use a third-party library for this such as Kable See the Punchthrough tutorials for Android BLE-Specific tutorials These tutorials assume familiarity and a base level of competence with: Android Studio Bluetooth Low Energy JSON HTTP Setup python kotlin This set of tutorials is accompanied by a Python package consisting of scripts separated by tutorial module. These can be found on Github. Once the Github repo has been cloned or downloaded to your local machine, the package can be installed as follows: Enter the python tutorials directory at $INSTALL/demos/python/tutorial/ where $INSTALL is the top level of the Open GoPro repo where it exists on your local machine Use pip to install the package (in editable mode in case you want to test out some changes): pip install -e . While it is out of the scope of this tutorial to describe, it is recommended to install the package in to a virtual environment in order to isolate system dependencies. You can test that installation was successful by viewing the installed package’s information: $ pip show open-gopro-python-tutorials Name: open-gopro-python-tutorials Version: 0.0.3 Summary: Open GoPro Python Tutorials Home-page: https://github.com/gopro/OpenGoPro Author: Tim Camise Author-email: gopro.com License: MIT Location: c:\\users\\tim\\gopro\\opengopro\\demos\\python\\tutorial Requires: bleak, requests Required-by: This set of tutorials is accompanied by an Android Studio project consisting of, among other project infrastructure, Kotlin files separated by tutorial module. The project can be found on Github. Once the Github repo has been cloned or downloaded to your local machine, open the project in Android studio. At this point you should be able to build and load the project to your Android device. The project will not work on an emulated device since BLE can not be emulated. Just Show me the Demo!! python kotlin Each of the scripts for this tutorial can be found in the Tutorial 1 directory. Python >= 3.9 and < 3.12 must be used as specified in the requirements You can test connecting to your camera through BLE using the following script: python ble_connect.py See the help for parameter definitions: $ python ble_connect.py --help usage: ble_connect.py [-h] [-i IDENTIFIER] Connect to a GoPro camera, pair, then enable notifications. optional arguments: -h, --help show this help message and exit -i IDENTIFIER, --identifier IDENTIFIER Last 4 digits of GoPro serial number, which is the last 4 digits of the default camera SSID. If not used, first discovered GoPro will be connected to The Kotlin file for this tutorial can be found on Github. To perform the tutorial, run the Android Studio project, select “Tutorial 1” from the dropdown and click on “Perform.” Perform Tutorial 1 This will start the tutorial and log to the screen as it executes. When the tutorial is complete, click “Exit Tutorial” to return to the Tutorial selection screen. Basic BLE Tutorial This tutorial will walk through the process of connecting to a GoPro via BLE. This same connect functionality will be used as a foundation for all future BLE tutorials. Here is a summary of the sequence that will be described in detail in the following sections: GoProOpen GoPro user deviceGoProOpen GoPro user deviceScanningConnectedalt[If not Previously Paired]PairedReady to CommunicateAdvertisingAdvertisingConnectPair RequestPair ResponseEnable Notifications on Characteristic 1Enable Notifications on Characteristic 2Enable Notifications on Characteristic ..Enable Notifications on Characteristic N Advertise First, we need to ensure the camera is discoverable (i.e. it is advertising). Follow the per-camera steps here. The screen should appear as such: Camera is discoverable. Scan Next, we must scan to discover the advertising GoPro Camera. python kotlin We will do this using bleak. Let’s initialize an empty dict that will store discovered devices, indexed by name: Map of devices indexed by name devices: Dict[str, BleakDevice] = {} We’re then going to scan for all devices. We are passing a scan callback to bleak in order to also find non-connectable scan responses. We are keeping any devices that have a device name. Scan callback to also catch nonconnectable scan responses def _scan_callback(device: BleakDevice, _: Any) -> None: Add to the dict if not unknown if device.name and device.name != \"Unknown\": devices[device.name] = device Now discover and add connectable advertisements for device in await BleakScanner.discover(timeout=5, detection_callback=_scan_callback): if device.name != \"Unknown\" and device.name is not None: devices[device.name] = device Now we can search through the discovered devices to see if we found a GoPro. Any GoPro device name will be structured as GoPro XXXX where XXXX is the last four digits of your camera’s serial number. If you have renamed your GoPro to something other than the default, you will need to update the below steps accordingly. First, we define a regex which is either “GoPro “ followed by any four alphanumeric characters if no identifier was passed, or the identifier if it exists. In the demo ble_connect.py, the identifier is taken from the command-line arguments. token = re.compile(identifier or r\"GoPro [A-Z0-9]{4}\") Now we build a list of matched devices by checking if each device’s name includes the token regex. matched_devices: List[BleakDevice] = [] Now look for our matching device(s) matched_devices = [device for name, device in devices.items() if token.match(name)] Due to potential RF interference and the asynchronous nature of BLE advertising / scanning, it is possible that the advertising GoPro will not be discovered by the scanning PC in one scan. Therefore, you may need to redo the scan (as ble_connect.py does) until a GoPro is found. That is, matched_device must contain at least one device. Similarly, connection establishment can fail for reasons out of our control. Therefore, the connection process is also wrapped in retry logic. Here is an example of the log from ble_connect.py of scanning for devices. Note that this includes several rescans until the devices was found. $ python ble_connect.py INFO:root:Scanning for bluetooth devices... INFO:root: Discovered: INFO:root: Discovered: TR8600 seri INFO:root:Found 0 matching devices. INFO:root: Discovered: INFO:root: Discovered: TR8600 seri INFO:root: Discovered: GoPro Cam INFO:root: Discovered: GoPro 0456 INFO:root:Found 1 matching devices. Among other devices, you should see GoPro XXXX where XXXX is the last four digits of your camera’s serial number. First let’s define a filter that will be used to find GoPro device advertisements. We do this by filtering on the GoPro Service UUID that is included in all GoPro advertisements: private val scanFilters = listOf<ScanFilter>( ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString(GOPRO_UUID)).build() ) We then send this to the BLE API and collect events from the SharedFlow that it returns. We take the first event emitted from this SharedFlow and notify (via a Channel) that a GoPro advertiser has been found, store the GoPro’s BLE address, and stop the scan. ble.startScan(scanFilters).onSuccess { scanResults -> val deviceChannel: Channel<BluetoothDevice> = Channel() // Collect scan results CoroutineScope(Dispatchers.IO).launch { scanResults.collect { scanResult -> // We will take the first discovered gopro deviceChannel.send(scanResult.device) } } // Wait to receive the scan result goproAddress = deviceChannel.receive().address ble.stopScan(scanResults) } At this point, the GoPro’s BLE address is stored (as a string) in goproAddress. Here is an example log output from this process: Scanning for GoPro's Received scan result: GoPro 0992 Found GoPro: GoPro 0992 Connect Now that we have discovered at least one GoPro device to connect to, the next step is to establish a BLE connection to the camera. python kotlin We're just taking the first device if there are multiple. device = matched_devices[0] client = BleakClient(device) await client.connect(timeout=15) An example output of this is shown here where we can see that the connection has successfully been established as well as the GoPro’s BLE MAC address.: INFO:root:Establishing BLE connection to EF:5A:F6:13:E6:5A: GoPro 0456... INFO:bleak.backends.dotnet.client:Services resolved for BleakClientDotNet (EF:5A:F6:13:E6:5A) INFO:root:BLE Connected! ble.connect(goproAddress) At this point, the BLE connection is established but there is more setup to be done before we are ready to communicate. Pair The GoPro has encryption-protected characteristics which require us to pair before writing to them. Therefore now that we are connected, we need to attempt to pair. python kotlin try: await client.pair() except NotImplementedError: This is expected on Mac pass Not all OS’s allow pairing (at this time) but some require it. Rather than checking for the OS, we are just catching the exception when it fails. Rather than explicitly request pairing, we rely on the fact that Android will automatically start the pairing process if you try to read a characteristic that requires encryption. To do this, we read the Wifi AP Password characteristic. First we discover all characteristics (this will also be needed later when enabling notifications): ble.discoverCharacteristics(goproAddress) This API will discover the characteristics over-the-air but not return them here. They are stored to the ble object for later access via the servicesOf method. Then we read the relevant characteristic to trigger pairing: ble.readCharacteristic(goproAddress, GoProUUID.WIFI_AP_PASSWORD.uuid) At this point a pairing popup should occur on the Android Device. Select “Allow Pairing” to continue. Here is an example log output from this process: Discovering characteristics Discovered 9 services for F7:5B:5D:81:64:1B Service 00001801-0000-1000-8000-00805f9b34fb Characteristics: |-- Service 00001800-0000-1000-8000-00805f9b34fb Characteristics: |--00002a00-0000-1000-8000-00805f9b34fb: READABLE |--00002a01-0000-1000-8000-00805f9b34fb: READABLE |--00002a04-0000-1000-8000-00805f9b34fb: READABLE ... |------00002902-0000-1000-8000-00805f9b34fb: EMPTY |--b5f90082-aa8d-11e3-9046-0002a5d5c51b: WRITABLE |--b5f90083-aa8d-11e3-9046-0002a5d5c51b: NOTIFIABLE |------00002902-0000-1000-8000-00805f9b34fb: EMPTY |--b5f90084-aa8d-11e3-9046-0002a5d5c51b: NOTIFIABLE |------00002902-0000-1000-8000-00805f9b34fb: EMPTY Service 00001804-0000-1000-8000-00805f9b34fb Characteristics: |--00002a07-0000-1000-8000-00805f9b34fb: READABLE Pairing Read characteristic b5f90003-aa8d-11e3-9046-0002a5d5c51b : value: 66:3F:54:2D:38:35:72:2D:4E:35:63 Once paired, the camera should beep and display “Connection Successful”. This pairing process only needs to be done once. On subsequent connections, the devices will automatically re-establish encryption using stored keys. That is, they are “bonded.” Enable Notifications As specified in the Open GoPRo BLE Spec, we must enable notifications for a given characteristic to receive responses from it. To enable notifications, we loop over each characteristic in each service and enable the characteristic for notification if it has notify properties: python kotlin It is necessary to define a notification handler to pass to the bleak start_notify method. Since we only care about connecting to the device in this tutorial (and not actually receiving data), we are just passing an empty function. A future tutorial will demonstrate how to use this meaningfully. for service in client.services: for char in service.characteristics: if \"notify\" in char.properties: await client.start_notify(char, notification_handler) In the following example output, we can see that notifications are enabled for each characteristic that is notifiable. INFO:root:Enabling notifications... INFO:root:Enabling notification on char 00002a19-0000-1000-8000-00805f9b34fb INFO:root:Enabling notification on char b5f90073-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90075-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90077-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90079-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90092-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90081-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90083-aa8d-11e3-9046-0002a5d5c51b INFO:root:Enabling notification on char b5f90084-aa8d-11e3-9046-0002a5d5c51b INFO:root:Done enabling notifications INFO:root:BLE Connection is ready for communication. ble.servicesOf(goproAddress).onSuccess { services -> services.forEach { service -> service.characteristics.forEach { char -> if (char.isNotifiable()) { ble.enableNotification(goproAddress, char.uuid) } } } } Here is an example log output from this process: Enabling notifications Enabling notifications for 00002a19-0000-1000-8000-00805f9b34fb Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90073-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90075-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90077-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90079-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90092-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90081-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90083-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Enabling notifications for b5f90084-aa8d-11e3-9046-0002a5d5c51b Wrote to descriptor 00002902-0000-1000-8000-00805f9b34fb Bluetooth is ready for communication! The characteristics that correspond to each UUID listed in the log can be found in the Open GoPro API. These will be used in a future tutorial to send data. Once the notifications are enabled, the GoPro BLE initialization is complete and it is ready to communicate via BLE. Quiz time! 📚 ✏️ How often is it necessary to pair? A: Pairing must occur every time to ensure safe BLE communication. B: We never need to pair as the GoPro does not require it to communicate. C: Pairing only needs to occur once as the keys will be automatically re-used for future connections. Submit Answer Correct!! 😃 Incorrect!! 😭 The correct answer is C. Pairing is only needed once (assuming neither side deletes the keys). If the GoPro deletes the keys (via Connections->Reset Connections), the devices will need to re-pair. Troubleshooting Device not connecting If the connection is not starting, it is likely because the camera is not advertising. This can be due to either: The camera is not in pairing mode. Ensure that this is achieved as done in the advertise section. The devices never disconnected from the previous session so are thus already connected. If this is the case, perform the “Complete System Reset” shown below. Complete System Reset BLE is a fickle beast. If at any point it is impossible to discover or connect to the camera, perform the following. Reset the camera by choosing Connections –> Reset Connections Use your OS’s bluetooth settings GUI to remove / unpair the Gopro Restart the procedure detailed above Logs python kotlin The demo program has enabled bleak logs and is also using the default python logging module to write its own logs. To enable more bleak logs, follow bleak’s troubleshooting section. The demo program is using Timber. It is piping all log messages to the UI but they are also available in the logcat window and can be filtered using: package:mine tag:GP_. Good Job! Congratulations 🤙 You can now successfully connect to the GoPro via BLE and prepare it to receive / send data. To see how to send commands, you should advance to the next tutorial.", "categories": [], "tags": [], "url": "/OpenGoPro/tutorials/connect-ble#" diff --git a/contribution.html b/contribution.html index bac0ada4..bf944530 100644 --- a/contribution.html +++ b/contribution.html @@ -532,10 +532,10 @@

Quiz

%} -
+
-
What is the question?
-
+
What is the question?
+

@@ -548,10 +548,10 @@

Quiz

- - @@ -567,10 +567,10 @@

Quiz

%}
-
+
-
True or False?
-
+
True or False?
+

@@ -580,10 +580,10 @@

Quiz

- - @@ -606,7 +606,7 @@

Tabs

-
    +
    • tab1 @@ -621,7 +621,7 @@

      Tabs

    -
      +
      • This is the content of the first tab.

        diff --git a/feed.xml b/feed.xml index d3de316d..130befde 100644 --- a/feed.xml +++ b/feed.xml @@ -1 +1 @@ -Jekyll2024-04-19T14:06:52-07:00https://gopro.github.io/OpenGoPro/feed.xmlOpen GoProOpen Source GoPro InterfaceGoPro \ No newline at end of file +Jekyll2024-04-19T14:09:51-07:00https://gopro.github.io/OpenGoPro/feed.xmlOpen GoProOpen Source GoPro InterfaceGoPro \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 8eeb3b4a..1fb346da 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,39 +2,39 @@ https://gopro.github.io/OpenGoPro/tutorials/connect-ble -2024-04-19T14:06:52-07:00 +2024-04-19T14:09:51-07:00 https://gopro.github.io/OpenGoPro/tutorials/send-ble-commands -2024-04-19T14:06:52-07:00 +2024-04-19T14:09:51-07:00 https://gopro.github.io/OpenGoPro/tutorials/parse-ble-responses -2024-04-19T14:06:52-07:00 +2024-04-19T14:09:51-07:00 https://gopro.github.io/OpenGoPro/tutorials/ble-queries -2024-04-19T14:06:52-07:00 +2024-04-19T14:09:51-07:00 https://gopro.github.io/OpenGoPro/tutorials/ble-protobuf -2024-04-19T14:06:52-07:00 +2024-04-19T14:09:51-07:00 https://gopro.github.io/OpenGoPro/tutorials/connect-wifi -2024-04-19T14:06:52-07:00 +2024-04-19T14:09:51-07:00 https://gopro.github.io/OpenGoPro/tutorials/send-wifi-commands -2024-04-19T14:06:52-07:00 +2024-04-19T14:09:51-07:00 https://gopro.github.io/OpenGoPro/tutorials/camera-media-list -2024-04-19T14:06:52-07:00 +2024-04-19T14:09:51-07:00 https://gopro.github.io/OpenGoPro/tutorials/cohn -2024-04-19T14:06:52-07:00 +2024-04-19T14:09:51-07:00 https://gopro.github.io/OpenGoPro/contribution.html @@ -43,96 +43,17 @@ https://gopro.github.io/OpenGoPro/faq -https://gopro.github.io/OpenGoPro/http.html - - https://gopro.github.io/OpenGoPro/ https://gopro.github.io/OpenGoPro/tutorials/ -https://gopro.github.io/OpenGoPro/ble/features/access_points.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/features/cohn.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/features/control.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/features/hilights.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/features/live_streaming.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/features/presets.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/features/query.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/features/settings.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/features/statuses.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/genindex.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/ -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/operation-operation_index.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/protocol.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/protocol/ble_setup.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/protocol/data_protocol.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/protocol/id_tables.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/protocol/protobuf.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/protocol/state_management.html -2024-04-19T14:04:33-07:00 - - -https://gopro.github.io/OpenGoPro/ble/search.html -2024-04-19T14:04:33-07:00 - - https://gopro.github.io/OpenGoPro/ble_2_0.html -2024-04-19T14:00:41-07:00 +2024-04-19T14:08:08-07:00 https://gopro.github.io/OpenGoPro/http_2_0.html -2024-04-19T14:00:41-07:00 +2024-04-19T14:08:08-07:00 diff --git a/tutorials/ble-protobuf.html b/tutorials/ble-protobuf.html index 3fedce33..2ac5ef4a 100644 --- a/tutorials/ble-protobuf.html +++ b/tutorials/ble-protobuf.html @@ -34,7 +34,7 @@ - + @@ -438,7 +438,7 @@

        GoPro

        - +
        @@ -534,7 +534,7 @@

        Requirements

        Just Show me the Demo(s)!!

        -
          +
          • python @@ -545,7 +545,7 @@

            Just Show me the Demo(s)!!

          -
            +
            • Each of the scripts for this tutorial can be found in the Tutorial 5 @@ -688,7 +688,7 @@

              Protobuf Message Example

        -
          +
          • python @@ -699,7 +699,7 @@

            Protobuf Message Example

          -
            +
            • from tutorial_modules import proto
              @@ -733,7 +733,7 @@ 

              Protobuf Message Example

              ResponseGeneric object.

              -
                +
                • python @@ -744,7 +744,7 @@

                  Protobuf Message Example

                -
                  +
                  • response_bytes = proto.ResponseGeneric(result=proto.EnumResultGeneric.RESULT_SUCCESS).SerializeToString()
                    @@ -820,7 +820,7 @@ 

                    Protobuf Response Parser

                    -
                      +
                      • python @@ -831,7 +831,7 @@

                        Protobuf Response Parser

                      -
                        +
                        • @@ -868,7 +868,7 @@

                          Set Turbo Transfer

                          Feature ID, Action ID, and length bytes:

                          -
                            +
                            • python @@ -879,7 +879,7 @@

                              Set Turbo Transfer

                            -
                              +
                              • turbo_mode_request = bytearray(
                                @@ -906,7 +906,7 @@ 

                                Set Turbo Transfer

                                from the Set Turbo Mode Documentation: ResponseGeneric.

                                -
                                  +
                                  • python @@ -917,7 +917,7 @@

                                    Set Turbo Transfer

                                  -
                                    +
                                    • await client.write_gatt_char(request_uuid.value, turbo_mode_request, response=True)
                                      @@ -979,7 +979,7 @@ 

                                      Response Manager

                                      parsing of all response types:

                                      -
                                        +
                                        • python @@ -990,7 +990,7 @@

                                          Response Manager

                                        -
                                          +
                                          • @@ -1073,7 +1073,7 @@

                                            Examples of Each Response Type

                                            Now let’s perform operations that will demonstrate each response type:

                                            -
                                              +
                                              • python @@ -1084,7 +1084,7 @@

                                                Examples of Each Response Type

                                              -
                                                +
                                                • # TLV Command (Setting)
                                                  @@ -1167,7 +1167,7 @@ 

                                                  Good Job!

                                                  -

                                                  Updated:

                                                  +

                                                  Updated:

                                                  diff --git a/tutorials/ble-queries.html b/tutorials/ble-queries.html index 55127b79..321c659f 100644 --- a/tutorials/ble-queries.html +++ b/tutorials/ble-queries.html @@ -34,7 +34,7 @@ - + @@ -438,7 +438,7 @@

                                                  GoPro

                                                  - +
                                                  @@ -531,7 +531,7 @@

                                                  Requirements

                                                  Just Show me the Demo(s)!!

                                                  -
                                                    +
                                                    • python @@ -542,7 +542,7 @@

                                                      Just Show me the Demo(s)!!

                                                    -
                                                      +
                                                      • Each of the scripts for this tutorial can be found in the Tutorial 4 @@ -668,7 +668,7 @@

                                                        Setup

                                                        connecting BLE tutorial.

                                                        -
                                                          + -
                                                            +
                                                            • We have slightly updated the notification handler from the previous tutorial to handle a QueryResponse instead of @@ -830,7 +830,7 @@

                                                              Parsing a Query Response

                                                              storing each value in a hash map indexed by ID for later access.

                                                              -
                                                                +
                                                                • python @@ -841,7 +841,7 @@

                                                                  Parsing a Query Response

                                                                -
                                                                  +
                                                                  • class QueryResponse(TlvResponse):
                                                                    @@ -897,10 +897,10 @@ 

                                                                    Parsing a Query Response

                                                                    -
                                                                    +
                                                                    -
                                                                    How many packets are query responses?
                                                                    -
                                                                    +
                                                                    How many packets are query responses?
                                                                    +

                                                                    @@ -913,19 +913,19 @@

                                                                    Parsing a Query Response

                                                                    - - -
                                                                    +
                                                                    -
                                                                    Which field is not common to all TLV responses?
                                                                    -
                                                                    +
                                                                    Which field is not common to all TLV responses?
                                                                    +

                                                                    @@ -941,10 +941,10 @@

                                                                    Parsing a Query Response

                                                                    - - @@ -1000,7 +1000,7 @@

                                                                    Individual Query Poll

                                                                    First we send the query:

                                                                    -
                                                                      +
                                                                      • python @@ -1011,7 +1011,7 @@

                                                                        Individual Query Poll

                                                                      -
                                                                        +
                                                                        • @@ -1039,7 +1039,7 @@

                                                                          Individual Query Poll

                                                                          QueryResponse class and extract the new resolution value.

                                                                          -
                                                                            +
                                                                            • python @@ -1050,7 +1050,7 @@

                                                                              Individual Query Poll

                                                                            -
                                                                              +
                                                                              • # Wait to receive the notification response
                                                                                @@ -1097,7 +1097,7 @@ 

                                                                                Individual Query Poll

                                                                                has changed:

                                                                                -
                                                                                  +
                                                                                  • python @@ -1108,7 +1108,7 @@

                                                                                    Individual Query Poll

                                                                                  -
                                                                                    +
                                                                                    • while resolution is not target_resolution:
                                                                                      @@ -1178,7 +1178,7 @@ 

                                                                                      Multiple Simultaneous Query Polls

                                                                                      FOV.

                                                                                      -
                                                                                        +
                                                                                        • python @@ -1189,7 +1189,7 @@

                                                                                          Multiple Simultaneous Query Polls

                                                                                        -
                                                                                          +
                                                                                          • RESOLUTION_ID = 2
                                                                                            @@ -1217,7 +1217,7 @@ 

                                                                                            Multiple Simultaneous Query Polls

                                                                                            We are also parsing the response to get all 3 values:

                                                                                            -
                                                                                              +
                                                                                              • python @@ -1228,7 +1228,7 @@

                                                                                                Multiple Simultaneous Query Polls

                                                                                              -
                                                                                                +
                                                                                                • response.parse()
                                                                                                  @@ -1255,7 +1255,7 @@ 

                                                                                                  Multiple Simultaneous Query Polls

                                                                                                  They are then printed to the log which will look like the following:

                                                                                                  -
                                                                                                    +
                                                                                                    • python @@ -1266,7 +1266,7 @@

                                                                                                      Multiple Simultaneous Query Polls

                                                                                                    -
                                                                                                      +
                                                                                                      • Getting the current resolution, fps, and fov.
                                                                                                        @@ -1326,10 +1326,10 @@ 

                                                                                                        Query All

                                                                                                        Quiz time! 📚 ✏️

                                                                                                        -
                                                                                                        +
                                                                                                        -
                                                                                                        How can we poll the encoding status and the resolution setting using one query?
                                                                                                        -
                                                                                                        +
                                                                                                        How can we poll the encoding status and the resolution setting using one query?
                                                                                                        +

                                                                                                        @@ -1342,10 +1342,10 @@

                                                                                                        Query All

                                                                                                        - -