ESP32+WiFi+UART数据传输测试

2023-05-16

刚开始使用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收发数据的部分,没有贴出服务器的程序。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ESP32+WiFi+UART数据传输测试 的相关文章

  • 多点连接 - 在 Swift 5 中获取文件传输(互联网)速度和文件大小

    我正在点对点传输照片 一切正常 但我无法获得照片 文件 传输速度 例如互联网速度 与MB 一样 文件也被传输 其次我想获取该文件的大小 我们使用数据格式传递照片MCSession 由于隐私原因 我无法在此处添加项目代码 但我将分享我关注的参
  • 无需连接WiFi即可获取MAC地址

    是否可以在未实际连接的情况下获取 WiFi MAC 地址 假设我有 Android 设备 A 我已经打开了 WiFi 因此我的 Android 设备现在能够检测到附近广播的 WiFi SSID 我附近有一些广播的 WiFi SSID 如下所
  • Android 2.2 wifi热点API

    我需要在 Android 2 2 Froyo 中进行什么 API 调用来创建 Wifi 热点 如网络共享和便携式热点设置项中所示 您可以致电 private boolean setWifiApEnabled WifiConfiguratio
  • 有没有办法在 python/opencv 中从单摄像头视频生成实时深度图?

    我试图将单个图像转换为其深度图 但我找不到任何有用的教程或文档 我想使用 opencv 但是如果您知道使用张量流等获取深度图的方法 我很高兴听到它 有很多关于立体视觉的教程 但我想让它更便宜 因为它是一个帮助盲人的项目 我目前正在使用 es
  • 在监控模式下使用 libpcap 嗅探 wifi

    问题陈述 Calling pcap activate 结果是PCAP ERR RFMON NOTSUP错误 即不支持 RF 监控模式 Context 我正在编写一个小型 C 程序 其工作是在监视器模式下监听我的笔记本电脑的 wifi 卡 该
  • Google iot MQTT - ESP32 首次连接,仅在 30m 后重新连接

    我正在使用 ESP32 与谷歌物联网云合作 我发送假值只是为了使用 MQTT 数据 PUB SUB 进行测试 显然我成功发布了值 有时 我无法重新连接到谷歌物联网 我不知道为什么它不断检查 wifi publishing 并且不检查 JWT
  • 如何使用Java连接wifi设备? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 如何在Java中在wifi范围内的设备之间建立连接 要通过网络 WiFi 电缆等 在两个设备之间建立连接 您需要编写 2 个软件 1
  • iOS从蓝牙接收数据,然后通过wi-fi将信息发送到另一台服务器上的数据库

    我应该查看哪些库来完成以下任务 我想通过蓝牙从另一台设备接收字符串 这需要查看附近设备的列表并选择正确的设备 然后我想通过 Wi fi 将字符串发送到数据库 这是我真正想做的事情的简单版本 但我需要首先知道如何做这些事情 此外 任何设计技巧
  • 检测连接到 Wifi 的 Android 设备

    我想制作一个连接到 Wifi 网络的 Android 应用程序 假设网络 SSID ABC 假设它已连接到 Wifi ABC 连接到 ABC 后 我希望我的应用程序显示连接到同一 wifi ABC 网络的所有 Android 设备的 ip
  • 使用 read(...) 时在换行符处停止

    我需要从通过 UART 连接的 GPS 读取 NMEA 语句 操作系统是Debian 语言必须是C 为此 我使用以下命令打开文件open 并读取一个字符串read 但是 这样我必须指定字符串长度 这会分解句子 相反 我想读到 NMEA 句子
  • 使用 C# 配置 Wifi 设置

    如何使用 C 紧凑框架 为 Win Mobile 应用程序配置 Wifi 设置 所以交易是 配置网络 隐藏APP的SSID 使用 TKIP 进行 WPA 身份验证 强制用户和密码 不会提示最终用户进行身份验证 看看智能设备框架 http w
  • 我可以枚举可用的 WLAN 吗?

    我想获取我周围可用的 WLAN 的信息 例如 SSID 这可能吗 Edit 可能是 通过市场认证 的意思 不 这在当前的 API 集中不可用 而且我认为 Mango 集中也没有提到它
  • 无法理解 PcapNG 文件中的 802.11 数据帧格式

    I have PcapNG由 Wireshark 创建的文件 我尝试用它来解析python pcapng However I cannot figure out how to reconcile the output I receive f
  • 如何在R中从串口读取数据

    我想绘制来自串行端口的实时数据 我认为 R 将是完成这项工作的好工具 我在尝试从串行端口 COM4 读取数据时遇到了困难 我已经验证数据是通过 terra term 传入的 并在尝试 R 之前关闭会话 但我似乎无法在 R 中获取任何内容 我
  • 在未越狱的 iOS 设备上启用/禁用 Wifi

    我的内部应用程序需要这个 我想在 ios 设备上切换 wifi 任何框架都可用 我尝试了以下代码 但它没有为我提供任何帮助 这不会改变我的 wifi 设置 Class BluetoothManager objc getClass Bluet
  • 通过 Wi-Fi 运行/安装/调试 Android 应用程序?

    我认为有一种方法可以通过 Wi Fi 测试开发中的应用程序 这可能吗 我希望能够摆脱手机的束缚并进行无线开发 参见论坛帖子 通过USB连接设备并确保调试正常 adb tcpip 5555 这使得设备开始侦听端口 5555 上的连接 查找设备
  • p2p0是android中WIFI DIRECT的无线接口吗? [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 当我打印 Android 设备中的所有接口时 03 10 19 50 15 484 I System out 22415 lo 03 10
  • 多久轮询一次 wifi 信号强度?

    理想情况下 我希望近乎实时地监控无线网络的信号强度 比如每 100 毫秒一次 但如此高的频率可能有点过大了 我正在使用托管无线网络 http managedwifi codeplex com 库轮询 RSSI 我实例化一个WlanClien
  • Android:扫描 Wifi 网络 + 可选列表

    我正在尝试创建一个应用程序 让您扫描可用的 wifi 网络 然后将它们显示在可选列表中 为此 我尝试使用以下代码 package android nacho WifiScan import java util List import and
  • Flutter IOS 使用连接或 wifi 插件读取 wifi 名称

    这个问题是类似的这个问题 https stackoverflow com questions 52498906 how to get the wifi namessid of the currently connected wifi in

随机推荐