ESP32+WiFi+UART数据传输测试

刚开始使用ESP32芯片,摸索着实现了一个数据传输的功能,记录下来以免忘记。

实现功能

使用ESP32在服务器与下位机之间传输数据,整体的流程图如下所示。

示意图

如图所示,下位机与ESP通过串口连接,ESP32启动后直接和路由器连接,连接成功后就能在服务器与下位机之间传输数据了。

开发环境

硬件环境:下位机51单片机,乐鑫ESP32芯片,使用的是安信可的开发板。

软件环境:SDK为乐鑫ESP-IDF-V4.2,程序在Windows下面使用VScode编写,在Ubuntu下面编译。

SDK连接地址:https://www.espressif.com/zh-hans/support/download/sdks-demos?keys=&field_type_tid%5B%5D=13

环境搭建可以参考乐鑫官方教程:https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32/get-started/index.html

参考例程为ESP-IDF下面的两个例子分别为:①example/protocols/sockets/tcp_client/tcp_client.c   ②example/peripherals/uart/uart_echo/main/uart_echo_example_main.c

 参考的文档也是乐鑫的IDF快速入门指南。

下面是具体的代码,只实现了功能,没有考虑在WiFi不稳定的情况,后面准备加上一些错误处理再更新。刚开始弄,还请见谅!

/* BSD Socket API Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "freertos/queue.h"
#include"freertos/timers.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "addr_from_stdin.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include <stdio.h>
#include "driver/uart.h"
#include "driver/gpio.h"

#if defined(CONFIG_EXAMPLE_IPV4)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV4_ADDR
#elif defined(CONFIG_EXAMPLE_IPV6)
#define HOST_IP_ADDR CONFIG_EXAMPLE_IPV6_ADDR
#else
#define HOST_IP_ADDR ""
#endif


#define PORT CONFIG_EXAMPLE_PORT

#define ECHO_TEST_TXD  (GPIO_NUM_4)
#define ECHO_TEST_RXD  (GPIO_NUM_5)
#define ECHO_TEST_RTS  (UART_PIN_NO_CHANGE)
#define ECHO_TEST_CTS  (UART_PIN_NO_CHANGE)

#define BUF_SIZE (512)

//创建两个队列   xQueue1:发送队列        xQueue2:接收队列
QueueHandle_t xQueue1,xQueue2;

//队列项   队列中一个项目保存的数据,定义了两个变量,一个用于接收缓冲,一个用于发送缓冲
struct AMessage
{
    size_t datalength; //数据的长度  
    uint8_t ucData[512];//数据的具体内容
}sendMessage,recvMessage;//发送队列与接收队列

static const char *TAG = "example"; 
//static const char *payload = "Message from ESP32 ";
#define MAX_SIZE_BUF 512
// 传输数据命令协议

int sock;
TimerHandle_t socketTimer;
char host_ip[] = HOST_IP_ADDR;
int addr_family = 0;
int ip_protocol = 0;
uint8_t heartTimer=0;

// 串口收发任务,参考example下的UART例程
 static void echo_task(void *arg)
 {

    struct AMessage *pxMessage = &sendMessage;
    //发送缓冲器的指针  用于往串口发送数据
    struct AMessage *pUartSendMessage;
     //发送缓冲器的指针  用于取从串口接收来的数据
    struct AMessage *pTcpSendMessage;
    xQueue1 = xQueueCreate(5,sizeof(struct AMessage*));
    if(xQueue1==0)
    {
        printf("发送队列没有创建成功\n");
    }

    /* Configure parameters of an UART driver,
     * communication pins and install the driver */
    uart_config_t uart_config = {
        .baud_rate = 19200,
        .data_bits = UART_DATA_8_BITS,
        .parity    = UART_PARITY_DISABLE,
        .stop_bits = UART_STOP_BITS_1,
        .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
        .source_clk = UART_SCLK_APB,
    };
    uart_driver_install(UART_NUM_1, BUF_SIZE * 2, 0, 0, NULL, 0);
    uart_param_config(UART_NUM_1, &uart_config);
    uart_set_pin(UART_NUM_1, ECHO_TEST_TXD, ECHO_TEST_RXD, ECHO_TEST_RTS, ECHO_TEST_CTS);
    // Configure a temporary buffer for the incoming data
    uint8_t *data = (uint8_t *) malloc(BUF_SIZE);
    
    while (1) {
    //  vTaskDelay(1000 / portTICK_PERIOD_MS);
         // Write data to the UART
         if(xQueue2!=0)
         {
            // printf("串口发送数据\r\n");
            // printf("队列可用空间:%d\n",uxQueueSpacesAvailable(xQueue2));
                if(xQueueReceive(xQueue2,&(pUartSendMessage),(TickType_t)10))
                {
                    printf("进入Uart发送函数了,总长度为:%d\n",pUartSendMessage->datalength);
                    printf("队列可用空间:%d\n",uxQueueSpacesAvailable(xQueue2));
                    int sedlen=uart_write_bytes(UART_NUM_1, (const char*)pUartSendMessage->ucData,pUartSendMessage->datalength); 
                }
                vTaskDelay(1000 / portTICK_PERIOD_MS);
            }

        int len = uart_read_bytes(UART_NUM_1, data, BUF_SIZE, 40/ portTICK_RATE_MS);
        if(len>0)
        {
            int32_t   dtuTxNum=len;
            memcpy(&(pxMessage->ucData[3]),data,len);
            pxMessage->datalength = (size_t)dtuTxNum;
            xQueueSend(xQueue1,(void*)&pxMessage,(TickType_t)0);
            printf("串口接收队列已用空间:%d,组合数据的长度为:%d\n",uxQueueMessagesWaiting(xQueue1),pxMessage->datalength);
            for(int i=0;i<pxMessage->datalength;i++)
            {
                printf("%x ", pxMessage->ucData[i]);
            }
            printf("\n");
        }
                    if(xQueue1!=0) 
                    {
                        // printf("socket发送队列不为空\n");
                        if(xQueueReceive(xQueue1,&(pTcpSendMessage),(TickType_t)10))
                        {
                            printf("进入socket发送函数了,总长度为:%d\n",pTcpSendMessage->datalength);
                            printf("队列可用空间:%d\n",uxQueueSpacesAvailable(xQueue1));
                            int err = send(sock, &(pTcpSendMessage->ucData),pTcpSendMessage->datalength, 0);
                      if (err < 0) {
                                ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                                break;
                                }
                                vTaskDelay(1000 / portTICK_PERIOD_MS);
                        }
                       }
    }
}


// 定时器回调函数
 void vTimerCallback(int socketTimer)
 {
    //  printf("定时器执行了\r\n");   
    
 }
// 初始化socket参数
int socket_init()
{
    #if defined(CONFIG_EXAMPLE_IPV4)
                    struct sockaddr_in dest_addr;
                    dest_addr.sin_addr.s_addr = inet_addr(host_ip);
                    dest_addr.sin_family = AF_INET;
                    dest_addr.sin_port = htons(PORT);
                    addr_family = AF_INET;
                    ip_protocol = IPPROTO_IP;
            #elif defined(CONFIG_EXAMPLE_IPV6)
                    struct sockaddr_in6 dest_addr = { 0 };
                    inet6_aton(host_ip, &dest_addr.sin6_addr);
                    dest_addr.sin6_family = AF_INET6;
                    dest_addr.sin6_port = htons(PORT);
                    dest_addr.sin6_scope_id = esp_netif_get_netif_impl_index(EXAMPLE_INTERFACE);
                    addr_family = AF_INET6;
                    ip_protocol = IPPROTO_IPV6;
            #elif defined(CONFIG_EXAMPLE_SOCKET_IP_INPUT_STDIN)
                    struct sockaddr_in6 dest_addr = { 0 };
                    ESP_ERROR_CHECK(get_addr_from_stdin(PORT, SOCK_STREAM, &ip_protocol, &addr_family, &dest_addr));
            #endif
                    sock =  socket(addr_family, SOCK_STREAM, ip_protocol);
                    if (sock < 0) {
                        ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
                        return -1;
                        //break;
                    }
                    ESP_LOGI(TAG, "Socket created, connecting to %s:%d", host_ip, PORT);

                    int err = connect(sock, (struct sockaddr *)&dest_addr, sizeof(struct sockaddr_in6));
                    if (err != 0) {
                        ESP_LOGE(TAG, "Socket unable to connect: errno %d", errno);
                        return -1;
                        //break;
                    }
                    ESP_LOGI(TAG, "Successfully connected");
                    return 0;
}

// TCP_Client任务
static void tcp_client_task(void *pvParameters)
{
    // 接收缓存
    uint8_t rx_buffer[512];
    // socketTimer = xTimerCreate("socket_Timer",(TickType_t)100,true,0,vTimerCallback);
    // 创建定时器
    socketTimer = xTimerCreate("socket_Timer",pdMS_TO_TICKS(1000),true,0,vTimerCallback);
    // 初始化socket
    socket_init();
    // 启动定时器
    xTimerStart(socketTimer,10);
    struct AMessage *rxMessage = &recvMessage;
    // 创建接收队列
    xQueue2 = xQueueCreate(5,sizeof(struct AMessage*));
    if(xQueue2==0)
    {
        printf("接收队列没有创建成功\n");
    }
   // while (1) {
        while (1) {
            //进来后的第一步就是登录  根据标志登录
           
//队列中有数据才发送给服务器
//如果创建成功了,队列指针肯定不为空,这就能解释为啥每次都要进来执行这句话的问题,之前理解错误,以为是队列的内容不为空
                // if(xQueue1!=0) 
                //     {
                //         // printf("socket发送队列不为空\n");
                //         if(xQueueReceive(xQueue1,&(pTcpSendMessage),(TickType_t)10))
                //         {
                //             printf("进入socket发送函数了,总长度为:%d\n",pTcpSendMessage->datalength);
                //             printf("队列可用空间:%d\n",uxQueueSpacesAvailable(xQueue1));
                //             int err = send(sock, &(pTcpSendMessage->ucData),pTcpSendMessage->datalength, 0);
                //       if (err < 0) {
                //                 ESP_LOGE(TAG, "Error occurred during sending: errno %d", errno);
                //                 break;
                //                 }
                //                 vTaskDelay(1000 / portTICK_PERIOD_MS);
                //         }
                       
                //     }

                  //  int errheartdata = send(sock, heartdata,strlen(heartdata), 0);
                    int len = recv(sock, rx_buffer, sizeof(rx_buffer) - 1, 0);
                    // Error occurred during receiving
                    
                    if (len < 0) {
                        ESP_LOGE(TAG, "recv failed: errno %d", errno);
                        break;
                    }
                    // Data received
                    else {
                       // rx_buffer[len] = 0; // Null-terminate whatever we received and treat like a string
                        ESP_LOGI(TAG, "Received %d bytes from %s:", len, host_ip);
                        printf("接收到的数据总数:%d\n\r",len);
                        for(int i=0;i<len;i++)
                        {
                            printf("%x ",rx_buffer[i]);
                        }
                        printf("\r\n");
                  
                                    rxMessage->datalength = (size_t)(len);
                                    memcpy(rxMessage->ucData,&rx_buffer[0],rxMessage->datalength);
                                    xQueueSend(xQueue2,(void*)&rxMessage,(TickType_t)0);
                                    printf("接收队列已用空间:%d,读取的数据长度为:%d\n",uxQueueMessagesWaiting(xQueue2),rxMessage->datalength);
                                    for(int i=0;i<rxMessage->datalength;i++)
                                    {
                                        printf("%x ",rxMessage->ucData[i]);
                                    }
                                    printf("\n");
                                }
                            }
                        // }
                       
                    }
                    break;
                    }
                        //ESP_LOGI(TAG, "%s", rx_buffer);
                    }
                    //vTaskDelay(2000 / portTICK_PERIOD_MS);
                    // if(errheartdata < 0) {
                    //     ESP_LOGE(TAG, "Error occurred during sending heartdata: errno %d", errno);
                    //     break;
                    // }
        }
        if (sock != -1) {
            ESP_LOGE(TAG, "Shutting down socket and restarting...");
            shutdown(sock, 0);
            close(sock);
        }
    //}
    vTaskDelete(NULL);
}

// 启动任务函数  由主函数调用 
void app_main(void)
{
    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */
    ESP_ERROR_CHECK(example_connect());
    xTaskCreate(tcp_client_task, "tcp_client", 4096, NULL, 10, NULL);
    xTaskCreate(echo_task, "uart_echo_task", 4096, NULL, 9, NULL);
  }

我将串口实例里面程序拷贝到tcp_client例程里面,在使用Ubuntu环境中编译,在example/protocol/sockets/tcp_client/目录下面编译,编译前使用idf.py.menconfig命令在配置界面输入需要练级的WiFi名称和密码,保存后使用idf.py build命令编译,编译后使用idf.py -p /dev/ttyUSB0 flash 命令下载程序到ESP32板子中,其中/dev/ttyUSB0为Linux系统下面的串口设备文件,然后使用idf.py -p /dev/ttyUSB0 monitor命令,就可以查看打印信息的输出了。这里主要是记录ESP32收发数据的部分,没有贴出服务器的程序。

  • 9
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 13
    评论
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值