Skip to content

Commit

Permalink
Merge pull request #39 from scytacki/use-ble-mtu
Browse files Browse the repository at this point in the history
fix issue with the BLE MTU
  • Loading branch information
meatpiHQ authored May 11, 2023
2 parents 97c95a2 + 835d602 commit 47a2974
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 24 deletions.
117 changes: 93 additions & 24 deletions main/ble.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ static uint8_t adv_config_done = 0;
#define EXT_ADV_MAX_EVENTS 0

#define GATTS_DEMO_CHAR_VAL_LEN_MAX 0x40
#define BLE_SEND_BUF_SIZE 490

static uint8_t dev_name[32] = {0};
static uint8_t manufacturer[]="MeatPi";
Expand Down Expand Up @@ -208,8 +209,13 @@ static const uint8_t heart_measurement_ccc[2] = {0x00, 0x00};
static const uint8_t char_value[20] = {0x11, 0x22, 0x33, 0x44};
#define CHAR_DECLARATION_SIZE (sizeof(uint8_t))
#define SVC_INST_ID 0
static uint16_t spp_mtu_size = 23;
static uint16_t spp_conn_id = 0xffff;
static esp_gatt_if_t spp_gatts_if = 0xff;
// If the client sends a larger MTU size, the ble_max_data_size will be set to
// the minium of BLE_SEND_BUF_SIZE and (spp_mtu_size - 3).
// Since the default MTU size is 23 this is initially set to 20
static uint16_t ble_max_data_size = 20;
static bool is_connected = false;
static uint8_t test1[] = {0x66 ,0x33 ,0x22 ,0x11 ,0xBB ,0x00 ,0x00 ,0x00 ,0x11 ,0x00 ,0x00 ,0x00 ,0x33 ,0x00 ,0x00 ,0x00 ,0xA4 ,0x3C ,0xD9 ,0x49};
/* Full Database Description - Used to add attributes into the database */
Expand All @@ -225,6 +231,11 @@ static const esp_gatts_attr_db_t gatt_db[HRS_IDX_NB] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&character_declaration_uuid, ESP_GATT_PERM_READ,
CHAR_DECLARATION_SIZE, CHAR_DECLARATION_SIZE, (uint8_t *)&char_prop_read_write_notify}},

// NOTE: This is the notify characteristic used to send data to the client
// It currently uses GATTS_DEMO_CHAR_VAL_LEN_MAX which is set to 64 (0x40).
// However more bytes might be sent over this characteristic.
// In the ESP SPP demo code the characteristic size is 512 which seems to
// be the max MTU supported by BLE.
/* Characteristic Value */
[IDX_CHAR_VAL_A] =
{{ESP_GATT_AUTO_RSP}, {ESP_UUID_LEN_16, (uint8_t *)&GATTS_CHAR_UUID_TEST_A, ESP_GATT_PERM_READ | ESP_GATT_PERM_WRITE,
Expand Down Expand Up @@ -514,6 +525,23 @@ static void gatts_profile_event_handler(esp_gatts_cb_event_t event,
case ESP_GATTS_EXEC_WRITE_EVT:
break;
case ESP_GATTS_MTU_EVT:
// NOTE: The ESP32 documentation doesn't explain how MTU negotiation works.
// From the ESP SPP server demo, the characteristic is declared with a size of 512
// and then this event determines the actual MTU size.
// From experimentation the esp_ble_gatt_set_local_mtu function is related to this.
// It seems the value set by esp_ble_gatt_set_local_mtu is sent to the client during
// the connection. Then if the client supports it, it will send this ESP_GATTS_MTU_EVT
// with the MTU value the client can support.
//
// As noted in above the call to esp_ble_gatt_set_local_mtu is not sent by
// Car Scanner ELM OBD2 on Android. So it uses the default MTU of 23.
spp_mtu_size = param->mtu.mtu;
// Each BLE packet has 3 header bytes, so the actual amount of data that
// can be sent is (MTU - 3).
//
// set the max data size to the minimum of (spp_mtu_size - 3) and BLE_SEND_BUF_SIZE
ble_max_data_size = BLE_SEND_BUF_SIZE <= (spp_mtu_size - 3) ? BLE_SEND_BUF_SIZE : (spp_mtu_size -3);
ESP_LOGI(GATTS_TABLE_TAG, "ESP_GATTS_MTU_EVT: %d", spp_mtu_size);
break;
case ESP_GATTS_CONF_EVT:
break;
Expand Down Expand Up @@ -623,7 +651,7 @@ static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_
static void ble_task(void *pvParameters)
{
static xdev_buffer tx_buffer;
static uint8_t ble_send_buf[490];
static uint8_t ble_send_buf[BLE_SEND_BUF_SIZE];
static uint32_t ble_send_buf_len = 0;
static uint32_t num_msg = 0;
static int64_t time_old = 0;
Expand Down Expand Up @@ -660,40 +688,66 @@ static void ble_task(void *pvParameters)
{
while((xQueuePeek(*xBle_TX_Queue, ( void * ) &tx_buffer, 0) == pdTRUE))
{
if(ble_send_buf_len + tx_buffer.usLen < sizeof(ble_send_buf))
// figure out how many packets are needed to send this tx_buffer
int num_req_packets = ((ble_send_buf_len + tx_buffer.usLen) / ble_max_data_size);
// Round up. Only part of a packet might be needed and integer math rounds down.
if((ble_send_buf_len + tx_buffer.usLen) % ble_max_data_size) {
num_req_packets++;
}

if(free_packet < num_req_packets)
{
xQueueReceive(*xBle_TX_Queue, ( void * ) &tx_buffer, 0);
// We don't have enough free_packets to send this item
break;
}

memcpy(ble_send_buf+ble_send_buf_len, tx_buffer.ucElement, tx_buffer.usLen);
num_msg++;
ble_send_buf_len += tx_buffer.usLen;
if(esp_timer_get_time() - time_old > 1000*1000)
{
time_old = esp_timer_get_time();
xQueueReceive(*xBle_TX_Queue, ( void * ) &tx_buffer, 0);
num_msg++;
if(esp_timer_get_time() - time_old > 1000*1000)
{
time_old = esp_timer_get_time();

// ESP_LOGI(GATTS_TABLE_TAG, "msg %u/sec", num_msg);
num_msg = 0;
}
// ESP_LOGI(GATTS_TABLE_TAG, "msg %u/sec", num_msg);
num_msg = 0;
}
else
int tx_buffer_copied = 0;
while(tx_buffer_copied < tx_buffer.usLen)
{
// xEventGroupWaitBits(s_ble_event_group,
// BLE_CONNECTED_BIT,
// pdFALSE,
// pdFALSE,
// portMAX_DELAY);
// ESP_LOG_BUFFER_HEXDUMP(GATTS_TABLE_TAG, ble_send_buf, ble_send_buf_len, ESP_LOG_INFO);
// vTaskDelay(pdMS_TO_TICKS(30000));
ble_send(ble_send_buf, ble_send_buf_len);
ble_send_buf_len = 0;
if(--free_packet == 0)
int ble_send_buf_remaining = ble_max_data_size - ble_send_buf_len;
int tx_buffer_remaining = tx_buffer.usLen - tx_buffer_copied;
// only copy bytes that will fit in the ble_send_buf
int copy_len = tx_buffer_remaining >= ble_send_buf_remaining ? ble_send_buf_remaining : tx_buffer_remaining;
memcpy(ble_send_buf+ble_send_buf_len, tx_buffer.ucElement+tx_buffer_copied, copy_len);
ble_send_buf_len += copy_len;
tx_buffer_copied += copy_len;

// If we have a full ble_send_buf, send it. Otherwise wait to fill it with more bytes,
// If there are no more bytes to fill it with, then it will be sent at the end.
if(ble_send_buf_len == ble_max_data_size)
{
break;
// xEventGroupWaitBits(s_ble_event_group,
// BLE_CONNECTED_BIT,
// pdFALSE,
// pdFALSE,
// portMAX_DELAY);
// ESP_LOG_BUFFER_HEXDUMP(GATTS_TABLE_TAG, ble_send_buf, ble_send_buf_len, ESP_LOG_INFO);
// vTaskDelay(pdMS_TO_TICKS(30000));
ble_send(ble_send_buf, ble_send_buf_len);
ble_send_buf_len = 0;
if(--free_packet == 0 && tx_buffer_remaining > 0)
{
// We did a computation above to make sure we had a enough
// free_packets. If we are down to zero and there are still
// bytes to send, then something went wrong
ESP_LOGE(GATTS_TABLE_TAG, "Ran out of free_packets too soon");
break;
}
}
}
}
if(free_packet != 0 && ble_send_buf_len != 0)
{
// ESP_LOG_BUFFER_HEXDUMP(GATTS_TABLE_TAG, ble_send_buf, ble_send_buf_len, ESP_LOG_INFO);
ble_send(ble_send_buf, ble_send_buf_len);
ble_send_buf_len = 0;
}
Expand Down Expand Up @@ -743,6 +797,9 @@ void ble_send(uint8_t* buf, uint8_t buf_len)
if(ble_tx_ready())
{
esp_ble_gatts_send_indicate(spp_gatts_if, spp_conn_id, profile_handle_table[IDX_CHAR_VAL_A],buf_len, buf, false);
// The ESP SPP server demo adds a 20ms delay after each send.
// It doesn't seem like it is needed in the WiCAN case.
// vTaskDelay(20 / portTICK_PERIOD_MS);
}
}
static uint32_t ble_pass_key = 0;
Expand Down Expand Up @@ -816,6 +873,18 @@ void ble_init(QueueHandle_t *xTXp_Queue, QueueHandle_t *xRXp_Queue, uint8_t conn
return;
}

// The documentation on how this works is not clear. It is used in the ESP
// SPP server demo. From experimentation, the value set here is sometimes
// sent back to us via the ESP_GATTS_MTU_EVT. Only when it is sent back,
// does it mean the MTU has been increased from the default of 23. From
// looking at the ESP code its max value is 517.
//
// Tested configurations:
// - Realdash on Android: this value is sent back
// - ble-serial on MacOS: this value is sent back
// - Carscanner on Android: this value is not sent back. And based on
// experimentation the message length is capped at 20 bytes which is
// consistent with the default MTU setting of 23.
esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(517);
if (local_mtu_ret){
ESP_LOGE(GATTS_TABLE_TAG, "set local MTU failed, error code = %x", local_mtu_ret);
Expand Down
23 changes: 23 additions & 0 deletions main/elm327.c
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,9 @@ const xelm327_cmd_t elm327_commands[] = {

int8_t elm327_process_cmd(uint8_t *buf, uint8_t len, twai_message_t *frame, QueueHandle_t *q)
{
// Because the cmd_buffer and cmd_len are static they keep their value
// across multiple calls. So if a buf is an incomplete command the next
// call will keep add to the cmd_buffer until the ending CR is found.
static char cmd_buffer[128];
static uint8_t cmd_len = 0;
static char cmd_response[128];
Expand All @@ -571,6 +574,7 @@ int8_t elm327_process_cmd(uint8_t *buf, uint8_t len, twai_message_t *frame, Queu
{
if(buf[i] == '\r' || cmd_len > 126)
{
// ESP_LOGI(TAG, "end of command i: %d, cmd_len: %u", i, cmd_len);
cmd_buffer[cmd_len] = 0;
cmd_response[0] = 0;
cmd_found_flag = 0;
Expand Down Expand Up @@ -615,6 +619,25 @@ int8_t elm327_process_cmd(uint8_t *buf, uint8_t len, twai_message_t *frame, Queu
strcat(cmd_response, "\r>");

elm327_response((char*)cmd_response, 0, q);

if(cmd_buffer[2] == 'z')
{
// When the command is a reset ignore any other commands in the buffer.
// This approach fixes an issue seen in the Carscanner Android app.
// It might actually match a real ELM327 chip since the documentation
// for the chip say an ATZ is like a full reset of the chip. So it
// would make sense that fully reset would imply anything in the
// incoming serial buffer would be cleared.
//
// In the Carscanner app a reset (ATZ) and a echo off (ATE0) are sent
// in a single BLE message. Without the code below elm327_process_cmd
// would respond to the ATZ and the ATE0. When it does this
// Carscanner gets out of sync. The Carscanner log shows all following
// commands with a response from the previous command.
cmd_len = 0;
memset(cmd_response, 0, sizeof(cmd_response));
return 0;
}
}
else if(!strncmp(cmd_buffer, "vti", 3) || !strncmp(cmd_buffer, "sti", 3))
{
Expand Down

0 comments on commit 47a2974

Please sign in to comment.