Page 1 of 1

Filtering BLE Advertisements with Custom AT Command in ESP-AT

Posted: Fri Sep 26, 2025 2:58 pm
by billsman
I’m using ESP-AT, but when I perform a BLE scan, a large amount of data is received, which increases the traffic over UART.
There are MAC and name filters available, but since it’s not always known in advance which devices will appear, these filters are not sufficient. I couldn’t find any other filtering options for this case.

Therefore, I decided to create a custom AT command.
My goal is to filter the incoming advertisement messages and send only the required data to the MCU via UART.
I created a custom AT command for this purpose.

When I run the scan command, the ble_gap_disc function returns success, and the corresponding event is triggered.
However, no actual data is received — the event->type value is always 0.

Does the IDF framework work properly inside a custom AT command, or am I making a mistake somewhere?
Any help would be greatly appreciated.
Thank you in advance!

Code: Select all

static int ble_gap_event_cb(struct ble_gap_event *event, void *arg)
{
    char rsp[250];
    struct ble_hs_adv_fields fields;
    ble_hs_adv_parse_fields(&fields, event->disc.data, event->disc.length_data);
    uint16_t len = sprintf(rsp, "--> Request Id: %d - Type: %d - Data Len: %d - Data: \r\n", ridcount++, event->type, event->disc.length_data, fields.svc_data_uuid128);
    esp_at_port_write_data((uint8_t *)rsp, len);

    switch (event->type)
    {

    // NimBLE event discovery
    case BLE_GAP_EVENT_DISC:
        esp_at_port_write_data((uint8_t *)"\r\n --> BLE_GAP_EVENT_DISC\r\n", 27);
        // ESP_LOGI("GAP", "GAP EVENT DISCOVERY");
        ble_hs_adv_parse_fields(&fields, event->disc.data, event->disc.length_data);
        
        len = sprintf(rsp, "Name: %d %s\n", fields.name_len, fields.name);
        if (fields.name_len > 0)
        {
            // printf("Name: %.*s\n", fields.name_len, fields.name);
            esp_at_port_write_data((uint8_t *)rsp, len);
        }
        break;

    case BLE_GAP_EVENT_DISC_COMPLETE:
        esp_at_port_write_data((uint8_t *)"\r\n --> BLE_GAP_EVENT_DISC_COMPLETE\r\n", 36);
        break;

    default:
        break;
    }
    return 0;
}

void ble_app_scan(void)
{
    esp_at_port_write_data((uint8_t *)"\r\n --> SCANNING...\r\n", 27);
    ble_hs_id_infer_auto(0, &ble_addr_type); // Determines the best address type automatically

    struct ble_gap_disc_params disc_params;
    disc_params.filter_duplicates = 0; // 1;
    disc_params.passive = 1;
    disc_params.itvl = 0x32;
    disc_params.window = 0x32;
    disc_params.filter_policy = BLE_HCI_SCAN_FILT_NO_WL;
    disc_params.limited = 0;

    int rc = ble_gap_disc(ble_addr_type, BLE_HS_FOREVER, &disc_params, ble_gap_event_cb, NULL);
    if (rc != 0)
    {
        char msg[64];
        sprintf(msg, "ble_gap_disc failed rc=%d\n", rc);
        esp_at_port_write_data((uint8_t *)msg, strlen(msg));
    }
    else
    {
        const uint8_t start_msg1[] = "AT+BILLSCAN\r\nSTART\r\n";
        esp_at_port_write_data((uint8_t *)start_msg1, strlen((char *)start_msg1));
    }
}