ESP8266使用AT指令回传判断思路(STM32上位机)

前言

        ESP8266是由安信可科技开发的一款低成本、高性能 Wi-Fi 模块,上位机可以通过使用串口发送AT指令配置 Wi-Fi 连接、TCP/UDP 通信等以接入互联网,但是ESP8266返回的内容十分不规范,上位机判断ESP8266返回的内容还是有点难度的。

最初的回传判断方法

        最初判断ESP8266返回的内容使用的是最简单的办法,就是上位机通过串口给ESP8266发送AT指令之后,直接延时等待ESP8266返回所有内容在进行回传内容的判断。

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
   if (huart->Instance == USART2) {
        extern uint8_t esp_rx_buf[ESP8266_RX_BUFFER_LEN];
        extern uint16_t volatile esp_rx_write_pos;
        extern uint32_t volatile esp_prev_rx_tick;

        esp_prev_rx_tick = HAL_GetTick();
        esp_rx_write_pos++;
        esp_rx_write_pos %= ESP8266_RX_BUFFER_LEN;
        HAL_UART_Receive_IT(&huart2, esp_rx_buf + esp_rx_write_pos, 1);
    }
}

int8_t ESP8266_SendCmd(const char *const cmd, const char *const expected_ack,
                       uint32_t wait_ticks) {
    HAL_UART_AbortReceive_IT(&huart2);
    memset(esp_rx_buf, 0, esp_rx_write_pos);
    esp_rx_write_pos = 0;
    esp_prev_rx_tick = osKernelGetTickCount();
    HAL_UART_Receive_IT(&huart2, esp_rx_buf, 1);
    HAL_UART_Transmit(&huart2, (uint8_t *)cmd, strlen(cmd), HAL_MAX_DELAY);
    HAL_Delay(wait_ticks);
    HAL_UART_AbortReceive_IT(&huart2);
    esp_rx_buf[esp_rx_write_pos] = '\0';

    if (expected_ack == NULL) {
        return 0;
    }
    else {
        if (strstr((char *)esp_rx_buf, expected_ack) == NULL) {
            return -1;
        }
        else {
            return 0;
        }
    }
    return 0;
}

        仅在发送AT指令时开启串口中断(这是为了防止在发送AT指令函数执行之外ESP8266的相应内容对接下来回传判断的影响),并且延时等待ESP8266响应。这种方法无论AT指令是否发送成功,ESP8266是否响应,在调用函数时必须确定一个延时时间并且在函数内干等着,而且这种方法如果执行一条AT指令出错的话,要么因为延时太短错过ESP8266回传的错误报告,要么就是已经错误了程序还处在delay循环中。

        但是这种方法又难以确定延时时间,因为ESP8266响应受指令复杂度、网络环境等多重因素影响,延时短了可能导致数据截断,对回传内容的判断可能会失败,延时长了又是干等着,即浪费系统资源,又降低执行效率,这对多任务系统可能影响稍微不是很明显,但是对裸机环境就是一个巨大的资源浪费。

目前的判断方法

        目前的判断方法则取消了发送函数的延时操作,无论ESP8266是否响应,函数在发送完成之后都会立即退出。原先只会在发送AT指令期间开启串口中断接收,现在则改成了随时接收ESP8266返回的数据,避免错过ESP8266返回的消息。当程序要想检查ESP8266回传的内容时,改成调用ESP8266_WaitForResponse是否正确响应,而且不再使用延时而改为超时时间,这样的好处是当ESP8266返回程序所期待的内容时,ESP8266_WaitForResponse函数会立刻返回而不是阻塞,这样ESP8266正确执行AT指令的话,使用timeout对程序的执行效率影响较小。

static void *_ESP8266_Memmem(char *haystack, size_t hlen, char *needle,
                             size_t nlen);

int8_t ESP8266_SendCmd(const char *cmd) {
    return HAL_UART_Transmit(&huart2, (const uint8_t *)cmd, strlen(cmd),
                             HAL_MAX_DELAY);
}

int8_t ESP8266_WaitForResponse(const char *resp, uint32_t timeout) {
    uint32_t tick_start = HAL_GetTick();
    while (HAL_GetTick() - tick_start < timeout) {
        if (_ESP8266_Memmem((char *)esp_rx_buf, ESP8266_RX_BUFFER_LEN,
                            (char *)resp, strlen(resp))
            != NULL) {
            return 0;
        }
    }
    return -1;
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
    if (huart->Instance == USART2) {
        esp_rx_buf_write_pos++;
        if (esp_rx_buf_write_pos >= ESP8266_RX_BUFFER_LEN) {
            esp_rx_buf_write_pos = 0;
        }
        HAL_UART_Receive_IT(&huart2, esp_rx_buf + esp_rx_buf_write_pos, 1);
    }
}

static void *_ESP8266_Memmem(char *haystack, size_t hlen, char *needle,
                             size_t nlen) {
    if (haystack == NULL || needle == NULL || nlen == 0 || needle[0] == '\0'
        || hlen < nlen) {
        return NULL;
    }

    size_t last_possible = hlen - nlen + 1;
    for (size_t i = 0; i < last_possible; i++) {
        if ((haystack + i)[0] == needle[0]
            && memcmp(haystack + i, needle, nlen) == 0) {
            return haystack + i;
        }
    }

    return NULL;
}

        ESP8266_WaitForResponse在ESP8266接收缓冲区中查找程序期待的字符串,由于串口中断随时开启,ESP8266执行上一条AT指令留下来的内容可能会影响本次回传判断,所以在执行ESP8266_WaitForResponse前还要清空一次缓冲区。

void ESP8266_ClearBuffer() {
    HAL_UART_AbortReceive_IT(&huart2);
    esp_rx_buf_write_pos = 0;
    memset(esp_rx_buf, 0, ESP8266_RX_BUFFER_LEN);
    HAL_UART_Receive_IT(&huart2, esp_rx_buf, 1);
}

总结

        目前的回传判断方法仅是我的一点点小思路,也不是最优的,大家如果有什么更好的思路欢迎分享!

你可能感兴趣的:(单片机,嵌入式硬件,stm32,esp8266)