/* ble_at.c - HLK-B40 蓝牙透传模块 AT 配置 * * 接线:模块 PC5(功能按键输入) 接单片机 PB0。 * 进 AT 模式:PB0 拉低 1s 后释放(手册要求 0.5~3s); * 回透传模式:AT 模式下发送 AT+TS=1。 * 上电默认透传模式。 */ #include "ble_at.h" #include "uart.h" #include "log.h" #include "main.h" #include "system.h" #include "24c02.h" #include "stdio.h" #include "string.h" #define BLE_PC5_PORT GPIOB #define BLE_PC5_PIN GPIO_PIN_0 #define BLE_PC5_LOW() HAL_GPIO_WritePin(BLE_PC5_PORT, BLE_PC5_PIN, GPIO_PIN_RESET) #define BLE_PC5_HIGH() HAL_GPIO_WritePin(BLE_PC5_PORT, BLE_PC5_PIN, GPIO_PIN_SET) /* EEPROM 缓存区:已配置好的蓝牙名(132~157,26B,避开其它占用) */ #define BLE_CACHE_ADDR 132 #define BLE_CACHE_LEN 26 /* PB0 开漏输出:释放时由模块 PC5 内部上拉保持高电平 */ void ble_at_init(void) { GPIO_InitTypeDef g = {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); BLE_PC5_HIGH(); g.Pin = BLE_PC5_PIN; g.Mode = GPIO_MODE_OUTPUT_OD; g.Pull = GPIO_NOPULL; g.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(BLE_PC5_PORT, &g); BLE_PC5_HIGH(); } static void ble_delay(uint32_t ms) { while (ms >= 100) { HAL_Delay(100); IWDG_Feed(); ms -= 100; } if (ms) HAL_Delay(ms); } /* 清空 USART1 待处理行 */ static void ble_flush_rx(void) { while (USART1_GetCommand() != NULL) { } } /* 发送 AT 命令并等待期望应答(按行匹配子串),0=成功 */ static int ble_at_cmd(const char *cmd, const char *expect, int timeout_ms) { char line[64]; int n = snprintf(line, sizeof(line), "%s\r\n", cmd); ble_flush_rx(); HAL_UART_Transmit(&huart1, (uint8_t *)line, n, 500); int t = timeout_ms / 50; while (t-- > 0) { ble_delay(50); const char *resp = USART1_GetCommand(); if (resp) { if (strstr(resp, expect)) { log_info("> BLE AT: %s", resp); return 0; } if (strstr(resp, "ERROR")) { log_warn("> BLE AT 应答错误: %s", resp); return -1; } } } log_warn("> BLE AT 超时: %s", cmd); return -1; } /* 上电执行一次:进 AT 模式 → 蓝牙名称查询/设置为 BLE_NAME → 需要时重启模块生效。 * 先查询后设置:名字已正确则整个流程跳过(不留 AT 窗口,避免 APP 数据撞进 AT 命令) */ void ble_setup_name(void) { char cmd[48]; /* EEPROM 缓存命中则跳过整个 AT 流程(零窗口,蓝牙 APP 不断联) */ char cache[BLE_CACHE_LEN] = {0}; eepromReadData(BLE_CACHE_ADDR, cache, BLE_CACHE_LEN); cache[BLE_CACHE_LEN - 1] = 0; if (strcmp(cache, BLE_NAME) == 0) { log_info("> BLE: 配置已缓存(%s),跳过设置", BLE_NAME); return; } /* 查询当前名称 */ BLE_PC5_LOW(); ble_delay(1100); /* 拉低 1s 进 AT 模式 */ BLE_PC5_HIGH(); ble_delay(600); /* 等模块切换到 AT 模式 */ ble_flush_rx(); HAL_UART_Transmit(&huart1, (uint8_t *)"AT+NAME=?\r\n", 12, 500); const char *cur = NULL; int t = 30; while (t-- > 0 && !cur) { ble_delay(50); cur = USART1_GetCommand(); } if (cur && strstr(cur, BLE_NAME)) { /* 名称已正确:直接回透传,不动任何设置 */ log_info("> BLE: 名称已是 %s,无需设置", BLE_NAME); ble_at_cmd("AT+TS=1", "OK", 1500); return; } /* 需要设置:发 AT+NAME(最多重试 3 次)。 * 注意:本模块设置类参数重启后才生效,当场回查查到的仍是旧名, * 不能用作校验——改完直接重启,下次开机的查询才是真正的校验 */ int ok = -1; for (int i = 0; i < 3 && ok != 0; i++) { snprintf(cmd, sizeof(cmd), "AT+NAME=%s", BLE_NAME); ok = ble_at_cmd(cmd, "OK", 1500); } if (ok == 0) { #if BLE_UUID_CUSTOM /* 顺带设置透传 GATT UUID(FFE0/FFE1),通用 BLE 串口 APP 可连 */ snprintf(cmd, sizeof(cmd), "AT+UUIDS=%s", BLE_UUID_SERVICE); ble_at_cmd(cmd, "OK", 1500); snprintf(cmd, sizeof(cmd), "AT+UUIDR=%s", BLE_UUID_TX); ble_at_cmd(cmd, "OK", 1500); snprintf(cmd, sizeof(cmd), "AT+UUIDW=%s", BLE_UUID_RX); ble_at_cmd(cmd, "OK", 1500); log_info("> BLE: 名称与 UUID 已设置完成"); #else log_info("> BLE: 名称已设置为 %s", BLE_NAME); #endif /* 手册说明:设置类参数重启后才生效,重启模块使新名称生效; * 模块重启后默认就是透传模式,无需再发 AT+TS=1 */ for (int i = 0; i < 2; i++) { if (ble_at_cmd("AT+REBOOT=1", "OK", 1500) == 0) break; } eepromWriteData(BLE_CACHE_ADDR, (void *)BLE_NAME, strlen(BLE_NAME) + 1); } else { log_warn("> BLE: 设置名称失败(模块未接或应答异常)"); ble_at_cmd("AT+TS=1", "OK", 1500); /* 确保回到透传 */ } } /* 强制重新执行蓝牙设置(蓝牙 BLESET 命令触发):清缓存后重跑 */ void ble_force_setup(void) { eepromWriteData(BLE_CACHE_ADDR, "", 1); ble_setup_name(); }