1、app_main 中新增 ble_init() 调用,启动 BLE GATT Server(设备名: MY-BLE) 2、移除 temp 模块:CMakeLists.txt、main.c、pages.c 中的引用和 temp_test() 函数 3、精简 ble.c:移除 SENSOR_SERVICE(温湿度服务)、TEMP/HUMI 特征、READ_EVT 处理,仅保留图片传输服务 4、新增项目性能分析报告(N16R8 模组资源消耗详细分析) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
327 lines
12 KiB
C
327 lines
12 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "esp_log.h"
|
|
#include "esp_bt.h"
|
|
#include "esp_gap_ble_api.h"
|
|
#include "esp_gatts_api.h"
|
|
#include "esp_bt_main.h"
|
|
#include "esp_bt_device.h"
|
|
#include "esp_gatt_common_api.h"
|
|
#include "esp_mac.h"
|
|
#include "fatfs.h"
|
|
#include "pages.h"
|
|
|
|
|
|
#define APP_ID_PLACEHOLDER 0
|
|
|
|
#define IMAGE_SERVICE_INSTID 0x0B
|
|
#define IMAGE_SERVICE_UUID 0x0B00
|
|
#define IMAGE_WRITE_UUID 0x0B01
|
|
#define IMAGE_EDIT_UUID 0x0B02
|
|
|
|
static uint16_t image_service_handle = 0;
|
|
static uint16_t image_write_handle = 0;
|
|
static uint16_t image_edit_handle = 0;
|
|
|
|
static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
|
|
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param);
|
|
|
|
|
|
static const char *CONN_TAG = "CONN_BLE";
|
|
static const char device_name[] = "MY-BLE";
|
|
|
|
static uint16_t conn_id;
|
|
|
|
static char *filepath;
|
|
|
|
typedef struct
|
|
{
|
|
uint8_t type;
|
|
char filename[23];
|
|
uint32_t len;
|
|
} Megtype;
|
|
|
|
typedef struct{
|
|
bool isSend;
|
|
uint32_t port;
|
|
} MegStatus;
|
|
|
|
Megtype firstMeg;
|
|
MegStatus SendStatus = {false,0};
|
|
|
|
uint8_t *img_data = 0;
|
|
FILE *file_img;
|
|
|
|
static uint8_t attr_value_write[512] = {0};
|
|
static uint8_t attr_value_edit[20] = {0};
|
|
|
|
static esp_attr_value_t char_val_image_write = {
|
|
.attr_max_len = 512,
|
|
.attr_len = 512,
|
|
.attr_value = attr_value_write
|
|
} ;
|
|
static esp_attr_value_t char_val_image_edit = {
|
|
.attr_max_len = 20,
|
|
.attr_len = 20,
|
|
.attr_value = attr_value_edit
|
|
} ;
|
|
|
|
static esp_attr_control_t control_image_write = {
|
|
.auto_rsp = ESP_GATT_AUTO_RSP
|
|
};
|
|
static esp_attr_control_t control_image_edit = {
|
|
.auto_rsp = ESP_GATT_AUTO_RSP
|
|
};
|
|
|
|
// 图片传输服务
|
|
static esp_gatt_srvc_id_t server_id_image = {
|
|
.id.uuid.uuid.uuid16 = IMAGE_SERVICE_UUID,
|
|
.id.inst_id = IMAGE_SERVICE_INSTID,
|
|
.is_primary = true,
|
|
};
|
|
static esp_bt_uuid_t image_write_uuid = {
|
|
.len = ESP_UUID_LEN_16,
|
|
.uuid.uuid16 = IMAGE_WRITE_UUID,
|
|
};
|
|
static esp_bt_uuid_t image_edit_uuid = {
|
|
.len = ESP_UUID_LEN_16,
|
|
.uuid.uuid16 = IMAGE_EDIT_UUID,
|
|
};
|
|
|
|
|
|
static esp_ble_adv_params_t adv_params = {
|
|
.adv_int_min = 0x20,
|
|
.adv_int_max = 0x20,
|
|
.adv_type = ADV_TYPE_IND,
|
|
.own_addr_type = BLE_ADDR_TYPE_PUBLIC,
|
|
.channel_map = ADV_CHNL_ALL,
|
|
.adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
|
|
};
|
|
|
|
|
|
static uint8_t adv_raw_data[] = {
|
|
0x02, ESP_BLE_AD_TYPE_FLAG, 0x06,
|
|
0x07, ESP_BLE_AD_TYPE_NAME_CMPL, 'M','Y','-','B','L','E',
|
|
0x02, ESP_BLE_AD_TYPE_TX_PWR, 0x09,
|
|
0x03, ESP_BLE_AD_TYPE_16SRV_CMPL, 0xB0, 0x00,
|
|
0x07, ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE,0x4C,0x44,0x64,0x7A,0x62,0x6A
|
|
};
|
|
|
|
|
|
void ble_init(void)
|
|
{
|
|
esp_err_t ret;
|
|
ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
|
|
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
|
|
ret = esp_bt_controller_init(&bt_cfg);
|
|
if (ret) {
|
|
ESP_LOGE(CONN_TAG, "%s initialize controller failed: %s", __func__, esp_err_to_name(ret));
|
|
return;
|
|
}
|
|
ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
|
|
if (ret) {
|
|
ESP_LOGE(CONN_TAG, "%s enable controller failed: %s", __func__, esp_err_to_name(ret));
|
|
return;
|
|
}
|
|
ret = esp_bluedroid_init();
|
|
if (ret) {
|
|
ESP_LOGE(CONN_TAG, "%s init bluetooth failed: %s", __func__, esp_err_to_name(ret));
|
|
return;
|
|
}
|
|
ret = esp_bluedroid_enable();
|
|
if (ret) {
|
|
ESP_LOGE(CONN_TAG, "%s enable bluetooth failed: %s", __func__, esp_err_to_name(ret));
|
|
return;
|
|
}
|
|
ret = esp_ble_gap_register_callback(esp_gap_cb);
|
|
if (ret) {
|
|
ESP_LOGE(CONN_TAG, "%s gap register failed, error code = %x", __func__, ret);
|
|
return;
|
|
}
|
|
|
|
ret = esp_ble_gatts_register_callback(gatts_event_handler);
|
|
if (ret) {
|
|
ESP_LOGE(CONN_TAG, "%s gatts register failed, error code = %x", __func__, ret);
|
|
return;
|
|
}
|
|
ret = esp_ble_gatts_app_register(APP_ID_PLACEHOLDER);
|
|
if (ret) {
|
|
ESP_LOGE(CONN_TAG, "%s gatts app register failed, error code = %x", __func__, ret);
|
|
return;
|
|
}
|
|
ret = esp_ble_gatt_set_local_mtu(512);
|
|
if (ret) {
|
|
ESP_LOGE(CONN_TAG, "set local MTU failed, error code = %x", ret);
|
|
return;
|
|
}
|
|
ret = esp_ble_gap_set_device_name(device_name);
|
|
if (ret) {
|
|
ESP_LOGE(CONN_TAG, "set device name failed, error code = %x", ret);
|
|
return;
|
|
}
|
|
ret = esp_ble_gap_config_adv_data_raw(adv_raw_data, sizeof(adv_raw_data));
|
|
if (ret) {
|
|
ESP_LOGE(CONN_TAG, "config adv data failed, error code = %x", ret);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
|
|
{
|
|
switch (event) {
|
|
case ESP_GAP_BLE_ADV_DATA_RAW_SET_COMPLETE_EVT:
|
|
ESP_LOGI(CONN_TAG, "Advertising data set, status %d", param->adv_data_raw_cmpl.status);
|
|
esp_ble_gap_start_advertising(&adv_params);
|
|
break;
|
|
case ESP_GAP_BLE_ADV_START_COMPLETE_EVT:
|
|
if (param->adv_start_cmpl.status != ESP_BT_STATUS_SUCCESS) {
|
|
ESP_LOGE(CONN_TAG, "Advertising start failed, status %d", param->adv_start_cmpl.status);
|
|
break;
|
|
}
|
|
ESP_LOGI(CONN_TAG, "Advertising start successfully");
|
|
break;
|
|
case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
|
|
if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS) {
|
|
ESP_LOGE(CONN_TAG, "Advertising stop failed, status %d", param->adv_stop_cmpl.status);
|
|
}
|
|
ESP_LOGI(CONN_TAG, "Advertising stop successfully");
|
|
break;
|
|
case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
|
|
ESP_LOGI(CONN_TAG, "Connection params update, status %d, conn_int %d, latency %d, timeout %d",
|
|
param->update_conn_params.status,
|
|
param->update_conn_params.conn_int,
|
|
param->update_conn_params.latency,
|
|
param->update_conn_params.timeout);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// GATT服务器事件处理函数
|
|
static void gatts_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param)
|
|
{
|
|
switch (event) {
|
|
case ESP_GATTS_REG_EVT:
|
|
ESP_LOGI(CONN_TAG, "GATT server register, status %d, app_id %d",param->reg.status, param->reg.app_id);
|
|
// 创建图片传输服务
|
|
esp_ble_gatts_create_service(gatts_if,&server_id_image,10);
|
|
break;
|
|
case ESP_GATTS_CREATE_EVT:
|
|
if (param->create.status == ESP_GATT_OK) {
|
|
image_service_handle = param->create.service_handle;
|
|
esp_ble_gatts_add_char(
|
|
image_service_handle,
|
|
&image_write_uuid,
|
|
ESP_GATT_PERM_WRITE,
|
|
ESP_GATT_CHAR_PROP_BIT_WRITE,
|
|
&char_val_image_write,
|
|
&control_image_write
|
|
);
|
|
esp_ble_gatts_add_char(
|
|
image_service_handle,
|
|
&image_edit_uuid,
|
|
ESP_GATT_PERM_WRITE,
|
|
ESP_GATT_CHAR_PROP_BIT_WRITE,
|
|
&char_val_image_edit,
|
|
&control_image_edit
|
|
);
|
|
ESP_LOGI(CONN_TAG, "图片传输服务创建成功,句柄: %x", image_service_handle);
|
|
} else {
|
|
ESP_LOGE(CONN_TAG, "服务创建失败,状态: %d", param->create.status);
|
|
}
|
|
break;
|
|
|
|
case ESP_GATTS_ADD_CHAR_EVT:
|
|
if (param->add_char.status == ESP_GATT_OK) {
|
|
if (param->add_char.char_uuid.uuid.uuid16 == (uint16_t)IMAGE_WRITE_UUID) {
|
|
image_write_handle = param->add_char.attr_handle;
|
|
ESP_LOGI(CONN_TAG, "图片写入特征创建成功,句柄: %d", image_write_handle);
|
|
} else if (param->add_char.char_uuid.uuid.uuid16 == (uint16_t)IMAGE_EDIT_UUID) {
|
|
image_edit_handle = param->add_char.attr_handle;
|
|
ESP_LOGI(CONN_TAG, "图片编辑特征创建成功,句柄: %d", image_edit_handle);
|
|
esp_ble_gatts_start_service(image_service_handle);
|
|
}
|
|
} else {
|
|
ESP_LOGE(CONN_TAG, "特征创建失败,状态: %d", param->add_char.status);
|
|
}
|
|
break;
|
|
case ESP_GATTS_WRITE_EVT:
|
|
if(param->write.handle == image_write_handle){
|
|
uint8_t *value = param->write.value;
|
|
if(!SendStatus.isSend){
|
|
ESP_LOGI(CONN_TAG, "处理前序数据");
|
|
firstMeg.type = value[0];
|
|
memcpy(firstMeg.filename, value + 1, 22);
|
|
firstMeg.filename[22] = '\0';
|
|
firstMeg.len = (value[23] << 16) | (value[24] << 8) | value[25];
|
|
ESP_LOGI(CONN_TAG, "图片数据长度:%d",(int)firstMeg.len);
|
|
if(firstMeg.type == 0xfd){
|
|
SendStatus.isSend = true;
|
|
img_data = malloc((int)firstMeg.len);
|
|
filepath = malloc(sizeof(char) * 33);
|
|
sprintf(filepath,"/spiflash/%s",firstMeg.filename);
|
|
file_img = fopen(filepath,"w");
|
|
ESP_LOGI(CONN_TAG,"传输通道建立成功,数据指针:%p,文件名称:%s,文件大小:%d",img_data,firstMeg.filename,(int)firstMeg.len);
|
|
}
|
|
}else if(SendStatus.isSend){
|
|
ESP_LOGI(CONN_TAG, "获取到数据:第:%d包,是否结束:%d",*value+1,*(value+1));
|
|
uint8_t isEnd = *(value + 1);
|
|
uint8_t port = *(value);
|
|
uint8_t *data = value + 2;
|
|
memcpy(img_data + SendStatus.port,data,(int)param->write.len-2);
|
|
SendStatus.port += param->write.len-2;
|
|
if(isEnd){
|
|
fwrite(img_data,sizeof(uint8_t),firstMeg.len,file_img);
|
|
fclose(file_img);
|
|
SendStatus.isSend = false;
|
|
SendStatus.port = 0;
|
|
ESP_LOGI(CONN_TAG,"图片接收成功");
|
|
nvs_change_img(firstMeg.filename);
|
|
app_img_change(firstMeg.filename);
|
|
free(img_data);
|
|
free(filepath);
|
|
}
|
|
}
|
|
}// 图片编辑特征写入事件
|
|
else if(param->write.handle == image_edit_handle){
|
|
uint8_t *value = param->write.value;
|
|
char imgName[23];
|
|
uint8_t type = *(value + param->write.len - 1);
|
|
memcpy(imgName, value, 23);
|
|
if(type == 0xff){
|
|
nvs_change_img(imgName);
|
|
app_img_change(imgName);
|
|
}else if(type == 0xF1){
|
|
remove(filepath);
|
|
SendStatus.isSend = false;
|
|
SendStatus.port = 0;
|
|
free(img_data);
|
|
free(filepath);
|
|
}
|
|
}
|
|
break;
|
|
case ESP_GATTS_CONNECT_EVT:
|
|
esp_ble_conn_update_params_t conn_params = {0};
|
|
memcpy(conn_params.bda, param->connect.remote_bda, sizeof(esp_bd_addr_t));
|
|
conn_params.latency = 0;
|
|
conn_params.max_int = 32;
|
|
conn_params.min_int = 16;
|
|
conn_params.timeout = 400;
|
|
conn_id = param->connect.conn_id;
|
|
ESP_LOGI(CONN_TAG, "Connected, conn_id %u, remote "ESP_BD_ADDR_STR"",
|
|
param->connect.conn_id, ESP_BD_ADDR_HEX(param->connect.remote_bda));
|
|
esp_ble_gap_update_conn_params(&conn_params);
|
|
break;
|
|
case ESP_GATTS_DISCONNECT_EVT:
|
|
ESP_LOGI(CONN_TAG, "Disconnected, remote "ESP_BD_ADDR_STR", reason 0x%02x",
|
|
ESP_BD_ADDR_HEX(param->disconnect.remote_bda), param->disconnect.reason);
|
|
esp_ble_gap_start_advertising(&adv_params);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|