Lately, I have been working on some software (Windows using Visual Studio in C# / .NET) to turn my laptop into a "brain" for the "Bittle with BiBoard" robot. To keep things simple, I want to use the well established bidirectional serial streaming communication of the Bluetooth Classic Serial Port Profile (SPP). By simple, I mean in contrast to the more complicated client-server option that is the Bluetooth Low Energy (BLE) with Generic Attribute (GATT) Profile. The ESP32 in the BiBoard supports both options and, I believe, the OpenCatEsp32 source code supports both as well (but see below).
Why is this simpler? Mainly because it is easier to code up a serial transceiver than to code up a GATT client. Also, it is a better fit to this use case because the SPP was designed for serial streaming whereas the GATT is meant for short message exchanges.
Here is the problem. I can only occasionally establish bidirectional Bluetooth communication between the Bittle with BiBoard and my Windows laptop using an SPP communication port. I do this by pairing the laptop with the Bittle and checking if I can send and receive over the Bluetooth SPP communication port that gets created. If I get a bidirectional communication, I move on to further coding work but if I only get unidirectional communication, I unpair, delete the com ports created and try pairing again. Eventually (1 out of 10 tries) I get a Bluetooth SPP bidirectional communication (but see below).
Before anyone asks, I am using the "Outgoing" port that specifically says "'ESP32SPP", meant for this SPP communication purpose, not the "Incoming" port that is also created. And also before anyone can ask this too, the "Incoming" port can only be initialized by the Bittle, not the laptop but this is not my use case, since I want the laptop to initiate and control the communication.
After getting a Bluetooth port that is bidirectional, I send Bittle commands and receive readbacks. However, after a short while (15 min) of back and fourth communication, the port becomes unidirectional. The laptop can send commands but not receive any readbacks.
So, has anyone tried such bidirectional Bluetooth Serial Port Profile (SPP) communication? If so, what kind of reliability have you experienced? More importantly, what might the source be of any unreliability in such communication? I have looked at the OpenCatEsp32 source code but have not found any obvious problems.
Is anyone interested in this capability and in combining efforts to search for the source (and fix) of this apparent (at least in my hands) unreliability?
BTW, I have searched the forum and the closest I found was this post by "Kevin KO" (and related comments): https://www.petoi.camp/forum/hardware/bluetooth-two-ways-communication-question. It sounds like my use case and that of the poster are similar, even though our software tools and Bittle robots (NyBoard with Bluetooth module vs BiBoard with built in Bluetooth) are different.
The solution turned out to be hiding in plain sight!
The function printToAllPorts(), in io.h of the OpenCatEsp32 source code, looks like this:
Line 117 is a conditional statement that guards the Bluetooth SPP object, SerialBT, using global variable BTconnected.
The problem is that the value of BTconnected is actually controlled by function BTAuthCompleteCallback(), also in io.h, shown next, to indicate Simple Serial Pairing (SSP) success or failure:
The function BTAuthCompleteCallback() is not routinely called so BTconnected is usually false.
So, BTconnected has nothing to do with the ability to use the SerialBT object. Since BTconnected is only used in the printToAllPorts() function, but should not be, I recommend that the BTconnected bool variable be deleted.
Well, how to guard this use of the SerialBT object? This object is an instance of the BluetoothSerial class which is derived from the Stream class which derives from the Print class, so there are quite a few methods available. General documentation on the methods unique to the BluetoothSerial class was hard to find so I consulted this Espressif Arduino ESP32 library source code and decided the isReady() method was the correct one to use. The guard condition on line 117 of io.h then becomes this:
if ( SerialBT.isReady() )
and problem solved!
Just for fun, below is a partial copy of the boot log, using USB serial communication, where I have sent runtime readbacks of the status for both "BTconnected " and for SerialBT.isReady(). As can be seen, once the BiBoard is paired and bonded with my laptop, the "BTconnected " bool is always false but SerialBT.isReady() behaves as expected, starting off false but then going true.
Start
BTconnected = 0
SerialBT.isReady() = 0
Bittle
Software version: BTconnected = 0
SerialBT.isReady() = 0
B02_240422
Buzzer volume: 1/10
Scanning I2C network...
- I2C device found at address 0x54 !
- I2C device found at address 0x68 !
- done
Initializing MPU...
OK
- Testing MPU connections...attempt 0
- MPU6050 connection successful
- Initializing DMP...
- Enabling DMP...
- Enabling interrupt detection (Arduino external interrupt 26)...
- DMP ready! Waiting for the first interrupt...
BLE: BittleA2_BLE
Waiting for a BLE client connection to notify...
SSP: BittleA2_SSP
The SSP device is started, now you can pair it with Bluetooth!
Setup ESP32 PWM servo driver...
Calibrated Zero Position
150 120 135 135 182 80 190 82 176 82 87 188
Build skill list...62
TaskQ
8
Init voice
Number of customized voice commands on the main board:
10
Turn on the audio response
T, L, D, I, U, G, M, A,
0, 0, 0, 0, 0, 0, 0, 1,
Ready!
BTconnected = 0
SerialBT.isReady() = 1
rest
Note:
Further testing confirmed that this small changes gives a reliable bidirectional Bluetooth Serial Port Profile (SPP) communication! 😎