Waze ModWaze Mod
ESP32Tính năngẢnh thực tếTải xuốngCài đặtChangelogLưu ýFAQỦng hộ
Download

Waze Mod

Dành cho tài xế Việt Nam.

© 2026 Waze Mod. Waze Mod Extended không liên kết chính thức với Waze hoặc Google.

Hướng dẫn cài đặtFAQAdmin
Ví dụ BLEĐổi trang ↓
Tổng quanESP32 BLEClassic SPPVí dụ BLEVí dụ SPPHLP/1 protocolCấu hình thiết bịDuy trì kết nốiPhía AndroidPhần cứngXử lý lỗiKiểm thử

Pin map / tài liệu

Bắt đầu

Tổng quan

Kết nối

ESP32 BLEClassic SPP

Code mẫu

Ví dụ BLEVí dụ SPP

Giao thức

HLP/1 protocolCấu hình thiết bịDuy trì kết nối

Tham chiếu

Phía AndroidPhần cứngXử lý lỗiKiểm thử
ESP32HLP/1esp32-hlp-ble/

Code mẫu ESP32 BLE

Project ESP-IDF hoàn chỉnh dùng NimBLE, HLP GATT service và notification để nhận dữ liệu Waze.

Tải trang Markdown.MDTải bộ tài liệu cho AIDOCS + SOURCE

Ví dụ ESP-IDF 5.5.x này dành cho ESP32-C3/C6/H2/S3. main.c dựng HLP GATT service, nhận raw state từ Android qua TX write và in state hợp lệ trong serial monitor. Chiều RX notification chỉ dùng cho handshake dev/pong; project không kèm renderer màn hình.

Build và flash

HLP / source
cd esp32-hlp-ble
idf.py set-target esp32s3
idf.py build
idf.py -p COM5 flash monitor

Thay COM5 bằng cổng serial của board. Sau khi flash, chọn đúng transport và thiết bị trong Cài đặt Mod → Thiết bị → HUD Link.

Cấu trúc project

HLP / source
esp32-hlp-ble/CMakeLists.txt
esp32-hlp-ble/sdkconfig.defaults
esp32-hlp-ble/main/CMakeLists.txt
esp32-hlp-ble/main/main.c

CMakeLists.txt

HLP / source
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp32_hlp_ble)

sdkconfig.defaults

HLP / source
# ESP-IDF 5.5.x: enable the NimBLE host and BLE-only controller mode.
CONFIG_BT_ENABLED=y
CONFIG_BTDM_CTRL_MODE_BLE_ONLY=y
CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=n
CONFIG_BTDM_CTRL_MODE_BTDM=n
CONFIG_BT_BLUEDROID_ENABLED=n
CONFIG_BT_NIMBLE_ENABLED=y

main/CMakeLists.txt

HLP / source
idf_component_register(
    SRCS "main.c" "../../esp32-hlp-spp/shared/hlp_core.c"
         "../../esp32-hlp-spp/shared/hlp_messages.c"
         "../../esp32-hlp-spp/shared/hlp_device_config.c"
    INCLUDE_DIRS "." "../../esp32-hlp-spp/shared"
    REQUIRES bt nvs_flash json
)

main/main.c

HLP / source
#include "host/ble_att.h"
#include "host/ble_hs.h"
#include "host/ble_uuid.h"
#include "nimble/nimble_port.h"
#include "nimble/nimble_port_freertos.h"
#include "host/ble_hs_id.h"
#include "services/gap/ble_svc_gap.h"
#include "services/gatt/ble_svc_gatt.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"
#include "hlp_core.h"
#include "hlp_messages.h"
#include "hlp_device_config.h"
#include <string.h>

static const char *TAG = "hlp_ble";
static const ble_uuid128_t SERVICE =
    BLE_UUID128_INIT(0x01,0x00,0x4c,0x50,0x4c,0x48,0x9d,0x9a,
                     0x48,0x4c,0x6e,0x4d,0x01,0x00,0x7e,0x8a);
static const ble_uuid128_t TX =
    BLE_UUID128_INIT(0x01,0x00,0x4c,0x50,0x4c,0x48,0x9d,0x9a,
                     0x48,0x4c,0x6e,0x4d,0x02,0x00,0x7e,0x8a);
static const ble_uuid128_t RX =
    BLE_UUID128_INIT(0x01,0x00,0x4c,0x50,0x4c,0x48,0x9d,0x9a,
                     0x48,0x4c,0x6e,0x4d,0x03,0x00,0x7e,0x8a);
static const ble_uuid128_t CAPABILITIES =
    BLE_UUID128_INIT(0x01,0x00,0x4c,0x50,0x4c,0x48,0x9d,0x9a,
                     0x48,0x4c,0x6e,0x4d,0x04,0x00,0x7e,0x8a);

typedef struct { uint16_t length; uint8_t bytes[HLP_MAX_FRAME]; } ble_chunk_t;
static QueueHandle_t chunks;
static hlp_rx_t receiver;
static uint16_t connection = BLE_HS_CONN_HANDLE_NONE;
static uint16_t rx_value_handle;
static bool notify_enabled;
static volatile bool send_dev_pending;
static uint8_t own_addr_type;
static struct ble_gap_adv_params adv_params;

static void send_line(const char *line, void *user) {
    (void)user;
    if (!notify_enabled || connection == BLE_HS_CONN_HANDLE_NONE) return;
    size_t length = strlen(line);
    if (length + 1 > HLP_MAX_FRAME) return;
    char frame[HLP_MAX_FRAME];
    memcpy(frame, line, length);
    frame[length++] = '\n';
    uint16_t mtu = ble_att_mtu(connection);
    size_t payload = mtu > 3 ? mtu - 3 : BLE_ATT_MTU_DFLT - 3;
    for (size_t offset = 0; offset < length; offset += payload) {
        size_t chunk_length = length - offset;
        if (chunk_length > payload) chunk_length = payload;

        int rc = BLE_HS_ENOMEM;
        for (int attempt = 0; attempt < 3 && rc == BLE_HS_ENOMEM; ++attempt) {
            struct os_mbuf *om = ble_hs_mbuf_from_flat(frame + offset, chunk_length);
            if (!om) {
                rc = BLE_HS_ENOMEM;
            } else {
                rc = ble_gatts_notify_custom(connection, rx_value_handle, om);
            }
            if (rc == BLE_HS_ENOMEM) vTaskDelay(pdMS_TO_TICKS(5));
        }
        if (rc != 0) {
            ESP_LOGW(TAG, "RX notification failed: rc=%d", rc);
            return;
        }
    }
}

static void state_line(const char *line, size_t length, void *user) {
    (void)user;
    ESP_LOGI(TAG, "HLP state: %.*s", (int)length, line);
}

static void on_line(const char *line, size_t length, void *user) {
    (void)user;
    hlp_handle_line(line, length, send_line, state_line, NULL);
}

static int access_cb(uint16_t conn_handle, uint16_t attr_handle,
                     struct ble_gatt_access_ctxt *ctxt, void *arg) {
    (void)attr_handle; (void)arg;
    if (ctxt->op == BLE_GATT_ACCESS_OP_WRITE_CHR) {
        size_t length = OS_MBUF_PKTLEN(ctxt->om);
        if (length > HLP_MAX_FRAME) return BLE_ATT_ERR_INVALID_ATTR_VALUE_LEN;
        ble_chunk_t chunk = { .length = length };
        if (os_mbuf_copydata(ctxt->om, 0, length, chunk.bytes) != 0)
            return BLE_ATT_ERR_UNLIKELY;
        if (xQueueSend(chunks, &chunk, 0) != pdTRUE)
            return BLE_ATT_ERR_INSUFFICIENT_RES;
        return 0;
    }
    if (ctxt->op == BLE_GATT_ACCESS_OP_READ_CHR) {
        const char caps[] = "{\"v\":1,\"caps\":{\"transport\":\"ble\",\"maxFrame\":512}}\n";
        return os_mbuf_append(ctxt->om, caps, sizeof(caps) - 1) == 0
                ? 0 : BLE_ATT_ERR_INSUFFICIENT_RES;
    }
    (void)conn_handle;
    return BLE_ATT_ERR_UNLIKELY;
}

static struct ble_gatt_chr_def characteristics[] = {
    { .uuid = &TX.u, .access_cb = access_cb,
      .flags = BLE_GATT_CHR_F_WRITE },
    { .uuid = &RX.u, .val_handle = &rx_value_handle,
      .flags = BLE_GATT_CHR_F_NOTIFY },
    { .uuid = &CAPABILITIES.u, .access_cb = access_cb,
      .flags = BLE_GATT_CHR_F_READ },
    { 0 }
};
static const struct ble_gatt_svc_def services[] = {
    { .type = BLE_GATT_SVC_TYPE_PRIMARY, .uuid = &SERVICE.u,
      .characteristics = characteristics },
    { 0 }
};

static int gap_event(struct ble_gap_event *event, void *arg) {
    (void)arg;
    switch (event->type) {
        case BLE_GAP_EVENT_CONNECT:
            if (event->connect.status != 0) {
                connection = BLE_HS_CONN_HANDLE_NONE;
                ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
                                  &adv_params, gap_event, NULL);
            } else connection = event->connect.conn_handle;
            break;
        case BLE_GAP_EVENT_DISCONNECT:
            connection = BLE_HS_CONN_HANDLE_NONE;
            notify_enabled = false;
            ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
                              &adv_params, gap_event, NULL);
            break;
        case BLE_GAP_EVENT_SUBSCRIBE:
            if (event->subscribe.attr_handle == rx_value_handle) {
                notify_enabled = event->subscribe.cur_notify;
                if (notify_enabled) send_dev_pending = true;
            }
            break;
        default: break;
    }
    return 0;
}

static void advertise(void) {
    struct ble_hs_adv_fields fields = {0};
    fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
    fields.name = (uint8_t *)"WazeHUD";
    fields.name_len = 7;
    fields.name_is_complete = 1;
    fields.uuids128 = (ble_uuid128_t *)&SERVICE;
    fields.num_uuids128 = 1;
    fields.uuids128_is_complete = 1;
    int rc = ble_gap_adv_set_fields(&fields);
    if (rc != 0) {
        ESP_LOGE(TAG, "Cannot set advertising fields: rc=%d", rc);
        return;
    }
    adv_params = (struct ble_gap_adv_params){0};
    adv_params.conn_mode = BLE_GAP_CONN_MODE_UND;
    adv_params.disc_mode = BLE_GAP_DISC_MODE_GEN;
    rc = ble_gap_adv_start(own_addr_type, NULL, BLE_HS_FOREVER,
                           &adv_params, gap_event, NULL);
    if (rc != 0) ESP_LOGE(TAG, "Cannot start advertising: rc=%d", rc);
}

static void on_sync(void) {
    int rc = ble_hs_id_infer_auto(0, &own_addr_type);
    if (rc != 0) {
        ESP_LOGE(TAG, "Cannot infer address type: rc=%d", rc);
        return;
    }
    advertise();
}

static void host_task(void *arg) {
    (void)arg;
    nimble_port_run();
    nimble_port_freertos_deinit();
}

static void protocol_task(void *arg) {
    (void)arg;
    ble_chunk_t chunk;
    for (;;) {
        if (send_dev_pending) {
            send_dev_pending = false;
            hlp_send_dev(send_line, NULL, "ble", "ESP32-BLE", "1.0.0", 4);
        }
        if (xQueueReceive(chunks, &chunk, pdMS_TO_TICKS(100)) == pdTRUE
                && chunk.length > 0)
            hlp_rx_feed(&receiver, chunk.bytes, chunk.length);
    }
}

void app_main(void) {
    esp_err_t err = nvs_flash_init();
    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        err = nvs_flash_init();
    }
    ESP_ERROR_CHECK(err);
    hlp_device_config_init();

    chunks = xQueueCreate(16, sizeof(ble_chunk_t));
    if (!chunks) {
        ESP_LOGE(TAG, "Cannot create receive queue");
        return;
    }
    hlp_rx_init(&receiver, on_line, NULL);
    ESP_ERROR_CHECK(nimble_port_init());
    ble_svc_gap_init();
    ble_svc_gatt_init();
    int rc = ble_gatts_count_cfg(services);
    if (rc == 0) rc = ble_gatts_add_svcs(services);
    if (rc == 0) rc = ble_svc_gap_device_name_set("WazeHUD");
    if (rc != 0) {
        ESP_LOGE(TAG, "Cannot configure GATT server: rc=%d", rc);
        return;
    }
    ble_hs_cfg.sync_cb = on_sync;
    if (xTaskCreate(protocol_task, "hlp_protocol", 4096, NULL, 5, NULL) != pdPASS) {
        ESP_LOGE(TAG, "Cannot create protocol task");
        return;
    }
    nimble_port_freertos_init(host_task);
    ESP_LOGI(TAG, "HLP BLE service started");
}
TrướcClassic SPPTiếp Ví dụ SPP

Trên trang này

Build và flashCấu trúc projectCMakeLists.txtsdkconfig.defaultsmain/CMakeLists.txtmain/main.c