Back in 2020, I published a project to make a Wi-Fi camera remote out of my favorite dev board; WeMos D1 Mini. That project won a Most Practical award in 2020 China-US Young Maker Competition. If you're not familiar with that project, don't worry, this one is similar.
The bad news: I lost my GoPro Hero 3 in a tragic skydiving accident.
The good news: I got a GoPro Hero 8 instead.
The better news: Infineon sent me PSoC™ 4100S Pioneer Kit.
How is it different?The major difference is the wireless technology used to communicate with the camera. While older GoPro cameras like Hero 3 used Wi-Fi, the newer ones like Hero 8 use Bluetooth Low Energy (BLE). It was easier to communicate with the camera via Wi-Fi, but battery life was not great. Also, with older cameras we had many solutions from several makers to choose from to build a remote.
There are GoPro remotes commercially available, but they are expensive and not customizable. Even though GoPro Hero 8 is water-resistant without the case, the back screen will not respond to touch when wet. That's another reason to use CAPSENSE™.
Game PlanWe needed to be able to turn the camera on and off as well as start and stop recording with two buttons only. We also needed to get camera's battery level. In this project we referred to Open GoPro to find out how to communicate with the camera. That page did not tell the whole story, but it was a great place to start. Hero 8 is not one of the supported cameras, but it works. We may even be able to make it work with Hero 7.
We had to use Service UUID FEA6 for Control & Query and write Command Characteristic UUID will be b5f90072-aa8d-11e3-9046-0002a5d5c51b. We found out later that our code does not use that format, so we had to convert it using the following Python code:
# Make {0x1bu, 0xc5u, 0xd5u, 0xa5u, 0x02u, 0x00u, 0x46u, 0x90u, 0xe3u, 0x11u, 0x8du, 0xaau, 0x72u, 0x00u, 0xf9u, 0xb5u}
s = 'b5f90072-aa8d-11e3-9046-0002a5d5c51b'
s = s.replace('-', '')
d = ''
for i in range(len(s)):
if i % 2 == 0:
d = '0x' + str(s[i]) + str(s[i+1]) + 'u, ' + d
print('{'+d[:-2]+'}')
As for reading a battery level we got that from the BLE standard Assigned Numbers Document because it is not unique to GoPro.
HardwarePSoC™ 4100S Pioneer Kit has two chips that we can program independently:
- PRoC: Programmable Radio-on-Chip
- PSoC: Programmable System-on-Chip
It is recommended to unplug the device and turn it off before switching between the two chips using SW5 switch.
All board documentations are available at CY8CKIT-041-41XX.
PRoC and PSoC communicate via I2C. We added OLED display on the same bus to show camera status and battery level.
We used PSoC Creator 4.4 to program PRoC and PSoC. One workspace contains both the PRoC project which we started from an example, and the PSoC project which we started from blank template.
Our remote needs to connect to a camera that advertises BLE. therefore, the best example to start with is Day036_I2C_BLE_Bridge_Client from Infineon/PSoC-4-BLE (github.com). That is probably the same example as I2C_BLE_Central from Infineon/EZ-BLE_PRoC_Module (github.com). Few changes needed to be done to that project to make it work with GoPro. All changes to PRoC code had to be tested with Bridge Control Panel since we don't have code on PSoC yet. We also used nRF Connect for Mobile to see what GoPro received.
This is how the workspace, and first project should look like.
We started by selecting the correct device.
Then, updated the project with new components.
We don't need to save backup.
Project is now up to date.
We removed timeout from BLE.
We selected bonding as requirement.
We changed data rate to 100 kbps.
Selected the correct pins for I2C. According to the schematic, other pins are not connected so assignment does not have any effect.
Clean and build to generate code before editing.
We added KeepConnection to main.h so we can use it in app_I2C.c and app_BLE.c
In app_I2C.h we uncommented "#define RESET_I2C_READ_DATA"
In app_I2C.c we added "uint32 LoopCounter = 0;" to keep track of how many times we handled I2C traffic.
We also added
if (CyBle_GetState() != CYBLE_STATE_CONNECTED && wrBuf[0]==0x0F)
{
KeepConnection = true;
CyBle_GapcStartScan(CYBLE_SCANNING_FAST);
}
and
if (wrBuf[0] == 0x01 && wrBuf[1] == 0x05) KeepConnection = false;
before handling I2C write.
After handling I2C traffic we added
if (LoopCounter> 500 && CyBle_GattGetBusStatus() != CYBLE_STACK_STATE_BUSY)
{
LoopCounter=0;
apiResult = CyBle_GattcReadCharacteristicValue(cyBle_connHandle, I2CReadDataCharHandle);
}
LoopCounter++;
In app_Ble.c we replaced read and write service and characteristic UUIDs with the ones we need to work with GoPro. We replaced 16 with 1 in:
if(0 == memcmp((uint8 *)&(readResponse->attrData.attrValue[5]), (uint8 *)I2CReadDataUUID, 16))
We commented out "CyBle_GapcStartScan(CYBLE_SCANNING_FAST);" from CYBLE_STATE_DISCONNECTED because we don't want GoPro to auto restart when we turn it off. We also commented that out from CYBLE_EVT_STACK_ON because we want to turn on GoPro only when we press a button. The example code was written to make the device search for I2C device. To make our remote search for GoPro instead of I2C device we made this change:
if((advReport->eventType == CYBLE_GAPC_SCAN_RSP) && strstr((char*)advReport->data,"GoPro") != NULL)
To reconnect if we didn't ask for connection to be terminated, we added this line:
if (KeepConnection) CyBle_GapcStartScan(CYBLE_SCANNING_FAST);
We added a case CYBLE_EVT_GATTC_READ_RSP to get read data using:
rdBuf[0]=((CYBLE_GATTC_READ_RSP_PARAM_T *)eventParam)->value.val[0];
The last change we had to make is the most important one. This code:
CyBle_GapAuthReq(cyBle_connHandle.bdHandle, &cyBle_authInfo);
will allow the device to pair with GoPro which is a required step for communication.
Now the easy part. We disconnected the device and flipped SW5 switch found behind the back cover. In PSoC Creator a new project has been added.
We populated the project afterwards.
We named the project.
We needed a CapSense component for our buttons.
We also needed I2C for OLED and to communicate with PRoC.
We added a clock to drive the Counter.
We added a 1 second counter.
Pin assignment is important.
We replaced main.c and copied font.h, ssd1306.h, and ssd1306.c to the same location. Finally, we added the files to the project in PSoC Creator then uploaded the code.
Comments
Please log in or sign up to comment.