ARM-software / CMSIS-Driver

Repository of microcontroller peripheral driver implementing the CMSIS-Driver API specification

Home Page:https://arm-software.github.io/CMSIS-Driver/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ESP32 does not send more than 2048 bytes correctly

xennex22 opened this issue · comments

There is a problem sending more than 2048 bytes via TCP in ARM_WIFI_SocketSendTo

The logic to break the transmit data into chunks of 2048 bytes or less does not decrement to original data size.

There's a loop to send to data to the ESP32

while (rval == 0) {
if ((len == 0) && (AT_Send_GetFree() != 0)) {
break;
}

But nowhere does len get decremented. The inner loop counters do get decremented/incremented.

num += n;
cnt -= n;

So if I transmit 2050 bytes rather then sending 2048 bytes then 2 bytes the function just keeps trying to send 2048 bytes until eventually it fails.

The return value from ARM_WIFI_SocketSendTo has a problem too, as it checks if the last chunk size (max 2048) is equal to the total tx size (may be over 2048)

if (num == len) {
rval = (int32_t)num;
}

And this is done in the inner of two loops, so as soon as one chunk is tx then the rval is set, which exists the outer loop, which then compares the total tx with the rval and fails.

Hi,
implementation is a bit confusing due to its complexity, but what happens in line

if ((len == 0) && (AT_Send_GetFree() != 0)) {

is only a check when application called the driver with len argument equal to zero. According to ARM_WIFI_SocketSendTo documentation this is allowed to check (pool) if the socket is ready to send data.

Since ESP32 can only send 2048 bytes with a single AT+CIPSEND command, initial count is limited to 2048 bytes:

/* Determine the amount of data to send */
else if (sock->flags & SOCKET_FLAGS_NONBLOCK) {
/* Non-blocking socket sends as much as fits into tx buffer */
cnt = AT_Send_GetFree();
}
else {
/* Blocking socket can send max 2048 bytes with one send command */
cnt = 2048;
}

which are then loaded into ESP32 internal buffer:
/* Start sending actual data to device */
while (cnt != 0) {
/* Determine amount of data to put into tx buffer */

and the total number of byte transferred is incremented while loading buffer:

When the transfer is done and "SEND OK" is received from ESP32, the number of total bytes sent is checked:

if (num == len) {
rval = (int32_t)num;
}

If this check is not true, the whole process will be repeated. Any additional number of bytes left from 2048 is then sent with next AT+CIPSEND command.

If you can understand my pseudocode:

len = the total number of bytes to transmit
num = 0
while(len > 0) {
  cnt = the minimum of 2048 and the len
  send 'AT+CIPSEND' + cnt
  wait for 'OK'
  while (cnt != 0) {
    n = minimum of data to send and AT buffer size
    n = number of bytes sent
    num += n
    cnt -= n
  }
  wait for 'SEND OK'
  return number of bytes sent if (num == len)
}

So if we hand simulate with len = 2050

initialize:
len = 2050
num = 0

loop 1:
cnt = 2048
n = 2048
num = 2048
cnt = 0
*** len is unchanged at 2050 ***

loop 2:
cnt = 2048 *** not 2 since len is never decremented ***
n = 2048 *** buffer overflow ***
num = 4096
cnt = 0
num != len so we loop forever
...

You are absolutely right! Many thanks for insisting and providing additional explanation! For some reason I just didn't see it...
I'll fix that.

My fix:

uint32_t tx_size = len;
...
if (cnt > tx_size) { cnt = tx_size; }
...
tx_size -= n;

The check within the loop was modified to

if (cnt > (len - num)) {
cnt = (len - num);
}

It should do the job.

I've confirmed the fix works for me.