ESP32: BT Serial, how to do handshaking from the receiver side?
Posted: Tue Nov 18, 2025 7:14 pm
I have made a device with on an ESP32 which has an RS232 serial port that's connected to a device (an old computer in this case). Serial connection with the old computer works great.
In the ESP32, I also offer a Bluetooth Serial (SPP) service, to which I can connect with a PC. In my ESP32, I then proxy the data received from SPP connection to the RS232 port, and vice versa.
This works fine in general, except for one thing. The old computer is very slow (4800 baud max), and does not have handshaking (not even xon/xoff).
So I am trying to pace the transfer by introducing a short pause after each received character. For every normal character, I pause 60ms, for every carriage return (CR, hex 0x0D), I pause 500ms. The old computer is programmed in BASIC, and needs extra time to process what it received, after it receives a CR.
However, on the Bluetooth side, the sender just keeps sending at its maximum rate. And after a while internal buffers start to overflow and the ESP32 starts to send garbage to the old computer.
I don't see any way in the SPP API to tell the Bluetooth sender to wait a while until my internal buffers are cleared again (i.e. handshaking with the sender). I have implemented queues with low water/high water marks, and try to send the Bluetooth sender the XOFF character when the high water mark is reached (80% of buffer is full), to have it pause until the low water mark is reached (20% buffer is full) and I send XON again. But it doesn't seem to work, the sender just continues sending. My queues are currently 1024 bytes
I might be doing something wrong, overcomplicating it. I do see the ESP_SPP_CONG_EVT, so I assume that the stack actually handles congestion itself. Am I using it wrong?
My code is actually using the BluetoothSerial library and registered for the callback. So basically my code always reads what is being sent by the sender, as it reads it on the ESP_SPP_DATA_IND_EVT event.
Maybe I should simply not register for that event and just read the data in a loop and stop reading when my internal buffers are full? I just need a bit of tips and pointers.
In the ESP32, I also offer a Bluetooth Serial (SPP) service, to which I can connect with a PC. In my ESP32, I then proxy the data received from SPP connection to the RS232 port, and vice versa.
This works fine in general, except for one thing. The old computer is very slow (4800 baud max), and does not have handshaking (not even xon/xoff).
So I am trying to pace the transfer by introducing a short pause after each received character. For every normal character, I pause 60ms, for every carriage return (CR, hex 0x0D), I pause 500ms. The old computer is programmed in BASIC, and needs extra time to process what it received, after it receives a CR.
However, on the Bluetooth side, the sender just keeps sending at its maximum rate. And after a while internal buffers start to overflow and the ESP32 starts to send garbage to the old computer.
I don't see any way in the SPP API to tell the Bluetooth sender to wait a while until my internal buffers are cleared again (i.e. handshaking with the sender). I have implemented queues with low water/high water marks, and try to send the Bluetooth sender the XOFF character when the high water mark is reached (80% of buffer is full), to have it pause until the low water mark is reached (20% buffer is full) and I send XON again. But it doesn't seem to work, the sender just continues sending. My queues are currently 1024 bytes
I might be doing something wrong, overcomplicating it. I do see the ESP_SPP_CONG_EVT, so I assume that the stack actually handles congestion itself. Am I using it wrong?
My code is actually using the BluetoothSerial library and registered for the callback. So basically my code always reads what is being sent by the sender, as it reads it on the ESP_SPP_DATA_IND_EVT event.
Maybe I should simply not register for that event and just read the data in a loop and stop reading when my internal buffers are full? I just need a bit of tips and pointers.