From f83fd97ca843bcd0db2cf9c64c602c062d71bbcd Mon Sep 17 00:00:00 2001 From: Siwat Sirichai Date: Fri, 7 Mar 2025 11:35:30 +0700 Subject: [PATCH] ibeacon implementation --- lib/esp_ibeacon/esp_ibeacon_api.cpp | 68 ++++++++++++++++++++++ lib/esp_ibeacon/esp_ibeacon_api.hpp | 71 +++++++++++++++++++++++ partitions_custom.csv | 6 ++ platformio.ini | 2 + src/config.hpp | 10 +++- src/main.cpp | 89 +++++++++++++++++++++++++++++ src/main.hpp | 5 ++ 7 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 lib/esp_ibeacon/esp_ibeacon_api.cpp create mode 100644 lib/esp_ibeacon/esp_ibeacon_api.hpp create mode 100644 partitions_custom.csv diff --git a/lib/esp_ibeacon/esp_ibeacon_api.cpp b/lib/esp_ibeacon/esp_ibeacon_api.cpp new file mode 100644 index 0000000..0f58782 --- /dev/null +++ b/lib/esp_ibeacon/esp_ibeacon_api.cpp @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + + + +/**************************************************************************** +* +* This file is for iBeacon APIs. It supports both iBeacon encode and decode. +* +* iBeacon is a trademark of Apple Inc. Before building devices which use iBeacon technology, +* visit https://developer.apple.com/ibeacon/ to obtain a license. +* +****************************************************************************/ + +#include +#include +#include +#include + +#include "esp_gap_ble_api.h" +#include "esp_ibeacon_api.hpp" + + +const uint8_t uuid_zeros[ESP_UUID_LEN_128] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +/* For iBeacon packet format, please refer to Apple "Proximity Beacon Specification" doc */ +/* Constant part of iBeacon data */ +esp_ble_ibeacon_head_t ibeacon_common_head = { + .flags = {0x02, 0x01, 0x06}, + .length = 0x1A, + .type = 0xFF, + .company_id = 0x004C, + .beacon_type = 0x1502 +}; + +/* Vendor part of iBeacon data*/ +esp_ble_ibeacon_vendor_t vendor_config = { + .proximity_uuid = ESP_UUID, + .major = ENDIAN_CHANGE_U16(ESP_MAJOR), //Major=ESP_MAJOR + .minor = ENDIAN_CHANGE_U16(ESP_MINOR), //Minor=ESP_MINOR + .measured_power = static_cast(0xC5) +}; + +bool esp_ble_is_ibeacon_packet (uint8_t *adv_data, uint8_t adv_data_len){ + bool result = false; + + if ((adv_data != NULL) && (adv_data_len == 0x1E)){ + if (!memcmp(adv_data, (uint8_t*)&ibeacon_common_head, sizeof(ibeacon_common_head))){ + result = true; + } + } + + return result; +} + +esp_err_t esp_ble_config_ibeacon_data (esp_ble_ibeacon_vendor_t *vendor_config, esp_ble_ibeacon_t *ibeacon_adv_data){ + if ((vendor_config == NULL) || (ibeacon_adv_data == NULL) || (!memcmp(vendor_config->proximity_uuid, uuid_zeros, sizeof(uuid_zeros)))){ + return ESP_ERR_INVALID_ARG; + } + + memcpy(&ibeacon_adv_data->ibeacon_head, &ibeacon_common_head, sizeof(esp_ble_ibeacon_head_t)); + memcpy(&ibeacon_adv_data->ibeacon_vendor, vendor_config, sizeof(esp_ble_ibeacon_vendor_t)); + + return ESP_OK; +} diff --git a/lib/esp_ibeacon/esp_ibeacon_api.hpp b/lib/esp_ibeacon/esp_ibeacon_api.hpp new file mode 100644 index 0000000..c4ce409 --- /dev/null +++ b/lib/esp_ibeacon/esp_ibeacon_api.hpp @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + + +/**************************************************************************** +* +* This file is for iBeacon definitions. It supports both iBeacon sender and receiver +* which is distinguished by macros IBEACON_SENDER and IBEACON_RECEIVER, +* +* iBeacon is a trademark of Apple Inc. Before building devices which use iBeacon technology, +* visit https://developer.apple.com/ibeacon/ to obtain a license. +* +****************************************************************************/ + +#include +#include +#include +#include + +#include "esp_gap_ble_api.h" +#include "esp_gattc_api.h" + +#define IBEACON_SENDER 0 +#define IBEACON_RECEIVER 1 +#define IBEACON_MODE CONFIG_IBEACON_MODE + +/* Major and Minor part are stored in big endian mode in iBeacon packet, + * need to use this macro to transfer while creating or processing + * iBeacon data */ +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) + +/* Espressif WeChat official account can be found using WeChat "Yao Yi Yao Zhou Bian", + * if device advertises using ESP defined UUID. + * Please refer to http://zb.weixin.qq.com for further information. */ +#define ESP_UUID {0xFD, 0xA5, 0x06, 0x93, 0xA4, 0xE2, 0x4F, 0xB1, 0xAF, 0xCF, 0xC6, 0xEB, 0x07, 0x64, 0x78, 0x25} +#define ESP_MAJOR 10167 +#define ESP_MINOR 61958 + + +typedef struct { + uint8_t flags[3]; + uint8_t length; + uint8_t type; + uint16_t company_id; + uint16_t beacon_type; +}__attribute__((packed)) esp_ble_ibeacon_head_t; + +typedef struct { + uint8_t proximity_uuid[16]; + uint16_t major; + uint16_t minor; + int8_t measured_power; +}__attribute__((packed)) esp_ble_ibeacon_vendor_t; + + +typedef struct { + esp_ble_ibeacon_head_t ibeacon_head; + esp_ble_ibeacon_vendor_t ibeacon_vendor; +}__attribute__((packed)) esp_ble_ibeacon_t; + + +/* For iBeacon packet format, please refer to Apple "Proximity Beacon Specification" doc */ +/* Constant part of iBeacon data */ +extern esp_ble_ibeacon_head_t ibeacon_common_head; + +bool esp_ble_is_ibeacon_packet (uint8_t *adv_data, uint8_t adv_data_len); + +esp_err_t esp_ble_config_ibeacon_data (esp_ble_ibeacon_vendor_t *vendor_config, esp_ble_ibeacon_t *ibeacon_adv_data); diff --git a/partitions_custom.csv b/partitions_custom.csv new file mode 100644 index 0000000..ecf102f --- /dev/null +++ b/partitions_custom.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 0x5000, +otadata, data, ota, 0xe000, 0x2000, +app0, app, factory, 0x10000, 0x1E0000, +app1, app, ota_0, 0x1F0000, 0x1E0000, +spiffs, data, spiffs, 0x3D0000, 0x20000, \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index b9dcab6..d941a3e 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,6 +15,7 @@ board = wt32-eth01 framework = arduino lib_deps = siwats/ESPMegaPROR3@^2.9.4 monitor_speed = 115200 +board_build.partitions = partitions_custom.csv build_flags = -DCORE_DEBUG_LEVEL=0 -DSW_VERSION='"cud-3.0.5-duct"' -DBOARD_MODEL='"ESPMegaPRO R3.3c"' -DAC_TYPE=AC_TYPE_DUCTED ; Ceiling A/C Variant (and newer ducted models) @@ -24,4 +25,5 @@ board = wt32-eth01 framework = arduino lib_deps = siwats/ESPMegaPROR3@^2.9.4 monitor_speed = 115200 +board_build.partitions = partitions_custom.csv build_flags = -DCORE_DEBUG_LEVEL=0 -DSW_VERSION='"cud-3.0.5-ceiling"' -DBOARD_MODEL='"ESPMegaPRO R3.3c"' -DAC_TYPE=AC_TYPE_CEILING \ No newline at end of file diff --git a/src/config.hpp b/src/config.hpp index 26e4348..f680ba7 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -25,6 +25,14 @@ #define MOSQUITO_ZAPPER_PIN 8 // There are one power relay for the socket #define SOCKET_CONTACTOR_PIN 9 + +/*********************************************** + * iBeacon Settings * + ***********************************************/ +#define ESP_MAJOR 10167 +#define ESP_MINOR 61958 +// UUID is now dynamically generated per device from MAC address + /*********************************************** * Air Conditioner * ***********************************************/ @@ -88,4 +96,4 @@ #define AQI_REQUEST_TOPIC "/iqair/request" #define AC_LOCK_RELATIVE_TOPIC "/ac/lock" #define AC_TEMP_LOWER_BOUND_RELATIVE_TOPIC "/ac/temp_bound/lower" -#define AC_TEMP_UPPER_BOUND_RELATIVE_TOPIC "/ac/temp_bound/upper" +#define AC_TEMP_UPPER_BOUND_RELATIVE_TOPIC "/ac/temp_bound/upper" \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 1521d55..9c0a1d2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -36,10 +36,46 @@ CUDDisplay cudDisplay = CUDDisplay(&cud_display_conf); ESPMegaDisplayOTA cudDisplayOTA = ESPMegaDisplayOTA(); ESPMegaDisplayOTA internalDisplayOTA = ESPMegaDisplayOTA(); +// iBeacon configuration +static esp_ble_adv_params_t ble_adv_params = { + .adv_int_min = 0x20, + .adv_int_max = 0x40, + .adv_type = ADV_TYPE_NONCONN_IND, + .own_addr_type = BLE_ADDR_TYPE_PUBLIC, + .channel_map = ADV_CHNL_ALL, + .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY, +}; + +esp_ble_ibeacon_vendor_t vendor_config_custom; + /************************************************ * End of Global Variables * ************************************************/ +/** + * Generate a unique UUID based on ESP32's chip ID + * @param uuid Output buffer for the UUID (16 bytes) + */ +void generate_unique_uuid(uint8_t *uuid) { + // Get ESP32 chip ID (6 bytes) + uint8_t mac[6]; + esp_efuse_mac_get_default(mac); + + // Use a fixed prefix (8 bytes) + static const uint8_t prefix[8] = {0xFD, 0xA5, 0x06, 0x93, 0xA4, 0xE2, 0x4F, 0xB1}; + + // Copy prefix to first 8 bytes of UUID + memcpy(uuid, prefix, 8); + + // Copy MAC address to next 6 bytes + memcpy(uuid + 8, mac, 6); + + // Fill the remaining 2 bytes with a hash of the MAC + uint16_t hash = mac[0] + (mac[1] << 8) + mac[2] + (mac[3] << 8) + mac[4] + (mac[5] << 8); + uuid[14] = hash & 0xFF; + uuid[15] = (hash >> 8) & 0xFF; +} + void setup() { // Initialize both displayports @@ -122,6 +158,30 @@ void setup() espmega.iot->registerMqttCallback(handle_mqtt_message); cudDisplayOTA.begin("/cuddisp", &cudDisplay, espmega.webServer); internalDisplayOTA.begin("/intdisp", espmega.display, espmega.webServer); + + // Initialize iBeacon with unique UUID + ESP_LOGV("CUD IoT OS", "Initializing iBeacon"); + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + esp_bt_controller_init(&bt_cfg); + esp_bt_controller_enable(ESP_BT_MODE_BLE); + esp_bluedroid_init(); + esp_bluedroid_enable(); + esp_ble_gap_register_callback(esp_gap_cb); + + // Generate unique UUID for this device + uint8_t uuid[16]; + generate_unique_uuid(uuid); + + // Configure iBeacon with unique UUID + memcpy(vendor_config_custom.proximity_uuid, uuid, sizeof(uuid)); + vendor_config_custom.major = ENDIAN_CHANGE_U16(ESP_MAJOR); + vendor_config_custom.minor = ENDIAN_CHANGE_U16(ESP_MINOR); + vendor_config_custom.measured_power = static_cast(0xC5); + + esp_ble_ibeacon_t ibeacon_adv_data; + esp_ble_config_ibeacon_data(&vendor_config_custom, &ibeacon_adv_data); + esp_ble_gap_config_adv_data_raw((uint8_t *)&ibeacon_adv_data, sizeof(ibeacon_adv_data)); + #ifdef TAMPER_DETECTION espmega.display->registerPageChangeCallback(pageChangeCallback); espmega.display->registerTouchCallback(touchCallback); @@ -156,6 +216,35 @@ void handle_mqtt_message(char *topic, char *payload) ESP_LOGD("CUD IoT OS", "MQTT Message Received: %s, %s", topic, payload); } +static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) +{ + esp_err_t err; + + switch (event) { + case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT: { + esp_ble_gap_start_advertising(&ble_adv_params); + break; + } + case ESP_GAP_BLE_ADV_START_COMPLETE_EVT: + // adv start complete event to indicate adv start successfully or failed + if ((err = param->adv_start_cmpl.status) != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE("iBeacon", "Advertising start failed, error %s", esp_err_to_name(err)); + } else { + ESP_LOGI("iBeacon", "Advertising start successfully"); + } + break; + case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT: + if ((err = param->adv_stop_cmpl.status) != ESP_BT_STATUS_SUCCESS) { + ESP_LOGE("iBeacon", "Advertising stop failed, error %s", esp_err_to_name(err)); + } else { + ESP_LOGI("iBeacon", "Advertising stop successfully"); + } + break; + default: + break; + } +} + #ifdef TAMPER_DETECTION void pageChangeCallback(uint8_t page) { diff --git a/src/main.hpp b/src/main.hpp index 3495415..5ed5d98 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -14,6 +14,10 @@ #include #include #include +#include +#include +#include +#include #include "lcd_elements.hpp" #include "ir_codes.hpp" #include "display.hpp" @@ -27,6 +31,7 @@ void loop(); void send_stop_bytes(HardwareSerial &uart); void handle_input_change(uint8_t pin, bool state); void handle_mqtt_message(char *topic, char *payload); +static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param); #ifdef TAMPER_DETECTION void pageChangeCallback(uint8_t page);