ESP-C3入门8. 连接WiFi并打印信息

news2025/2/22 12:36:00

ESP-C3入门8. 连接WiFi并打印信息

  • 一、ESP32 连接WiFi的基本操作流程
    • 1. 初始化nvs存储
    • 2. 配置WiFi工作模式
    • 3. 设置WiFi登陆信息
    • 4. 启动WiFi
    • 5. 开启连接
    • 6. 判断是否成功
  • 二、事件处理函数
    • 1. 定义事件处理函数
    • 2. 创建事件组
    • 3. 在事件处理函数中设置事件组位
    • 4. 在其他任务中等待事件组位
    • 5. 注册事件处理函数
    • 6. 如果有必要取消注册
  • 三、事件组的使用
    • 1. 创建事件组
    • 2. 向事件组中设置事件
    • 3. 在其它任务中等待事件
  • 四、实现过程
    • 1. WiFi初始化函数做以下事情
    • 2. WiFi Station模式初始化
    • 3. 事件回调
  • 五、完整代码

在这里插入图片描述

本文示例代码主要来自官方开源仓库:
https://gitee.com/EspressifSystems/book-esp32c3-iot-projects

一、ESP32 连接WiFi的基本操作流程

1. 初始化nvs存储

nvs_flash_init();
tcpip_adapter_init();

2. 配置WiFi工作模式

wifi_init_config_t wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
esp_wifi_init(&wifi_init_config);
esp_wifi_set_storage(WIFI_STORAGE_RAM);
esp_wifi_set_mode(WIFI_MODE_STA);

3. 设置WiFi登陆信息

wifi_config_t wifi_config = {
    .sta = {
        .ssid = "your_SSID",
        .password = "your_password",
    },
};
esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config);

4. 启动WiFi

esp_wifi_start();

5. 开启连接

esp_wifi_connect();

6. 判断是否成功

wifi_ap_record_t ap_info;
esp_wifi_sta_get_ap_info(&ap_info);
if (ap_info.authmode != WIFI_AUTH_OPEN) {
    // 连接成功
} else {
    // 连接失败
}

二、事件处理函数

在 ESP-IDF 中,一些关键的系统任务(比如,Wi-Fi连接、网络传输等)通常都是在事件驱动的模式下实现的,
系统会生成一些事件,然后由事件处理机制来进行处理。事件处理机制是用于接收并处理系统事件的主要机制,通过它,可以实现自定义的动作(比如,记录日志、进行状态更新等)。

在ESP-IDF中使用事件处理函数有以下步骤:

1. 定义事件处理函数

定义一个函数,用于处理特定的事件,如:

void event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
    // your code here
}

2. 创建事件组

使用 xEventGroupCreate 函数创建事件组。

3. 在事件处理函数中设置事件组位

在事件处理函数内部,使用 xEventGroupSetBits 函数设置事件组位,以标识事件已经发生。

4. 在其他任务中等待事件组位

使用 xEventGroupWaitBits 函数在其他任务中等待事件组位,以确认事件已经发生。

5. 注册事件处理函数

使用函数(如 esp_event_loop_init)注册事件处理函数,使其生效。

esp_event_handler_register(event_base, event_id, event_handler, arg);

其中:

  • event_base 是事件的基础,通常是一个枚举,它表示事件类别,比如 WIFI_EVENT。
  • event_id 是事件的 ID,通常也是一个枚举,它表示事件类型,比如 WIFI_EVENT_STA_DISCONNECTED。
  • event_handler 是事件处理函数,用来处理事件,从中读取事件数据。
  • arg 是一个用户指定的参数,在事件处理函数中可以读取。

6. 如果有必要取消注册

esp_event_handler_unregister(event_base, event_id, event_handler);

三、事件组的使用

ESP-IDF中的事件组是一种线程间同步机制,允许多个任务同时等待特定的事件发生。
在ESP-IDF中使用事件组的步骤是:

1. 创建事件组

EventGroupHandle_t s_event_group = xEventGroupCreate();

2. 向事件组中设置事件

xEventGroupSetBits(s_event_group, BIT0);

3. 在其它任务中等待事件

EventBits_t bits = xEventGroupWaitBits(s_event_group, BIT0, pdFALSE, pdFALSE, portMAX_DELAY);

四、实现过程

1. WiFi初始化函数做以下事情

  • 创建一个事件组,用来管理Wi-Fi连接事件。
  • 实始化协议栈
  • 创建默认事件循环
  • WiFi设置为sta模式
  • 注册事件处理器
static void wifi_initialize(void)
{
    // 创建一个事件组,用于管理Wi-Fi连接事件。
    s_wifi_event_group = xEventGroupCreate();

    // 初始化 TCP/IP 协议栈。
    ESP_ERROR_CHECK(esp_netif_init());

    // 创建默认事件循环。
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    // 创建默认的Wi-Fi网络接口。
    esp_netif_create_default_wifi_sta();
    // 设置 Wi-Fi 初始化配置为默认配置
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // 注册事件处理器,以处理 Wi-Fi 和 IP 相关事件
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
}

其中事件组的定义是:

static EventGroupHandle_t s_wifi_event_group = NULL;

2. WiFi Station模式初始化

  • 配置WiFi信息
  • WiFi工作模式设置为STA
  • 启动WiFi
  • 等待事件
static void wifi_station_initialize(void)
{
    // 配置WiFi的配置信息
    wifi_config_t wifi_config = {
            .sta = {
                    .ssid = LIGHT_ESP_WIFI_SSID,
                    .password = LIGHT_ESP_WIFI_PASS,
                    // 启用WPA2模式,常用的WiFi连接方式
                    .threshold.authmode = WIFI_AUTH_WPA2_PSK,

                    .pmf_cfg = {
                            .capable = true,
                            .required = false
                    },
            },
    };
    // WiFi工作模式设置为STA
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    // 设置WiFi工作模式
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    // 启动WiFi
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_station_initialize finished.");
    
	// 下面的代码不是必须的,但它演示了程序如何阻塞并等待连接成功
    /* 等待连接建立(WIFI_CONNECTED_BIT)或连接失败的次数达到最大值(WIFI_FAIL_BIT)。
     * 这些位通过 event_handler() 设置(详见上面)*/
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY);

    /* xEventGroupWaitBits() 返回调用前的 bits,因此我们可以测试实际发生了什么事件。 */
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", LIGHT_ESP_WIFI_SSID, LIGHT_ESP_WIFI_PASS);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", LIGHT_ESP_WIFI_SSID, LIGHT_ESP_WIFI_PASS);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }
}

xEventGroupWaitBits 函数在这里不是必须的。 它是一个阻塞函数,将等待一个或多个事件位被设置,直到返回时间结束。
本示例中,它等待 WIFI_CONNECTED_BIT 或 WIFI_FAIL_BIT 中的任何一个事件位被设置,并且等待的最长时间是 portMAX_DELAY。

相关参数:

  • s_wifi_event_group 是事件组变量,用于存储事件位。
  • WIFI_CONNECTED_BIT | WIFI_FAIL_BIT 是要等待的事件位。WIFI_CONNECTED_BIT 表示 Wi-Fi 连接成功的事件,WIFI_FAIL_BIT 表示 Wi-Fi 连接失败的事件。
  • pdFALSE 和 pdTRUE 是宏,分别表示布尔值的 false 和 true,此处代表不需要等待所有事件位(pdFALSE)和不清除事件位(pdFALSE)。
  • portMAX_DELAY 是一个常量,表示无限等待。
  • 返回值(EventBits_t bits)是一个事件位变量,表示返回的事件位。

该代码实现了在等待 Wi-Fi 连接结果的同时不占用 CPU 资源,并在连接成功或失败时记录事件位以作后续处理。

事件组允许每个事件有多个位,前面要定义:

#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

这里的BIT0 BIT1由开发者自行定义。

3. 事件回调

// 事件回调
static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    // 如果是Wi-Fi事件,并且事件ID是Wi-Fi事件STA_START
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        // 如果是Wi-Fi事件,并且事件ID是Wi-Fi事件STA_DISCONNECTED
        /* 如果重试次数小于最大重试次数 */
        if (s_retry_num < LIGHT_ESP_MAXIMUM_RETRY) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        } else {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG, "connect to the AP fail");
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        // 如果是IP事件,并且事件ID是IP事件STA_GOT_IP

        // 获取事件结果
        ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));

        // 将重试次数重置为 0;
        s_retry_num = 0;
        // 通过调用 xEventGroupSetBits 函数,将 WIFI_CONNECTED_BIT 设置到事件组中,表示成功连接到 AP
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

五、完整代码

#include <string.h>

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"

#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"


#define LIGHT_ESP_WIFI_SSID     "你的WIFI账号"
#define LIGHT_ESP_WIFI_PASS     "你的WiFi密码"
#define LIGHT_ESP_MAXIMUM_RETRY 5

// 事件组允许每个事件有多个位,这里只使用两个事件:
// 已经连接到AP并获得了IP
// 在最大重试次数后仍未连接
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

static const char *TAG = "wifi connection";

// FreeRTOS事件组,连接成功时发出信号
static EventGroupHandle_t s_wifi_event_group = NULL;
static int s_retry_num = 0;

// 事件回调
static void event_handler(void *arg, esp_event_base_t event_base,
                          int32_t event_id, void *event_data)
{
    // 如果是Wi-Fi事件,并且事件ID是Wi-Fi事件STA_START
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        // 如果是Wi-Fi事件,并且事件ID是Wi-Fi事件STA_DISCONNECTED
        /* 如果重试次数小于最大重试次数 */
        if (s_retry_num < LIGHT_ESP_MAXIMUM_RETRY) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        } else {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG, "connect to the AP fail");
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        // 如果是IP事件,并且事件ID是IP事件STA_GOT_IP

        // 获取事件结果
        ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));

        // 将重试次数重置为 0;
        s_retry_num = 0;
        // 通过调用 xEventGroupSetBits 函数,将 WIFI_CONNECTED_BIT 设置到事件组中,表示成功连接到 AP
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

static void wifi_initialize(void)
{
    // 创建一个事件组,用于管理Wi-Fi连接事件。
    s_wifi_event_group = xEventGroupCreate();

    // 初始化 TCP/IP 协议栈。
    ESP_ERROR_CHECK(esp_netif_init());

    // 创建默认事件循环。
    ESP_ERROR_CHECK(esp_event_loop_create_default());

    // 创建默认的Wi-Fi网络接口。
    esp_netif_create_default_wifi_sta();
    // 设置 Wi-Fi 初始化配置为默认配置
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    // 注册事件处理器,以处理 Wi-Fi 和 IP 相关事件
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL));
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL));
}

static void wifi_station_initialize(void)
{
    // 配置WiFi的配置信息
    wifi_config_t wifi_config = {
            .sta = {
                    .ssid = LIGHT_ESP_WIFI_SSID,
                    .password = LIGHT_ESP_WIFI_PASS,
                    // 启用WPA2模式,常用的WiFi连接方式
                    .threshold.authmode = WIFI_AUTH_WPA2_PSK,

                    .pmf_cfg = {
                            .capable = true,
                            .required = false
                    },
            },
    };
    // WiFi工作模式设置为STA
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    // 设置WiFi工作模式
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
    // 启动WiFi
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_station_initialize finished.");

    /* 等待连接建立(WIFI_CONNECTED_BIT)或连接失败的次数达到最大值(WIFI_FAIL_BIT)。
     * 这些位通过 event_handler() 设置(详见上面)*/
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group, WIFI_CONNECTED_BIT | WIFI_FAIL_BIT, pdFALSE, pdFALSE, portMAX_DELAY);

    /* xEventGroupWaitBits() 返回调用前的 bits,因此我们可以测试实际发生了什么事件。 */
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s", LIGHT_ESP_WIFI_SSID, LIGHT_ESP_WIFI_PASS);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s", LIGHT_ESP_WIFI_SSID, LIGHT_ESP_WIFI_PASS);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }
}

void app_main()
{
    int i = 0;
    ESP_LOGE(TAG, "app_main");
    // 初始化NVS存储区
    ESP_ERROR_CHECK(nvs_flash_init());

    // Wi-Fi初始化
    ESP_LOGI(TAG, "Wi-Fi initialization");
    wifi_initialize();

    // Wi-Fi Station初始化
    wifi_station_initialize();

    while (1) {
        ESP_LOGI(TAG, "[%02d] Hello world!", i++);
        vTaskDelay(pdMS_TO_TICKS(5000));
    }
}

运行结果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/344688.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Selenium+Python的web自动化测试框架

一、什么是Selenium&#xff1f; Selenium是一个基于浏览器的自动化测试工具&#xff0c;它提供了一种跨平台、跨浏览器的端到端的web自动化解决方案。Selenium主要包括三部分&#xff1a;Selenium IDE、Selenium WebDriver 和Selenium Grid。 Selenium IDE&#xff1a;Firefo…

【安全】Nginx负载均衡下上传Webshell

目录 负载均衡下上传webshell webshell简介 一、环境搭建 ①下载中国蚁剑&#xff0c;于github获取官方版&#xff1a; ②下载docker&docker-compose ③结合前面启动环境 ④验证 Ⅱ、负载均衡下webshell上传的几个问题 ①shell文件上传稳定节点问题 ②命令执行时的…

Windows CMD常用命令

目录 【打开CMD命令】 【网络测试命令】 ipconfig------查看本机网卡信息 ping------测试网络是否通畅 tracert------追踪路由&#xff0c;也可以用来查看网络连通性 telnet------查看目的主机ip的端口号是否开放 tcping------查看目的主机ip的端口号是否开放 【关于路…

【零基础入门前端系列】—无序列表、有序列表、定义列表(四)

一、HTML无序列表 无序列表是一个项目的列表&#xff0c;此列项目使用粗体圆点&#xff08;典型的小黑圆圈&#xff09;进行标记。 无序列表使用 <ul> 标签 <ul> <li>Coffee</li> <li>Milk</li> </ul>嵌套结构&#xff1a; <…

VSCode 的下载安装及基本使用

目录 一、VSCode 是什么&#xff1f; 二、VSCode 的下载和安装 2.1 - 下载 2.2 - 安装 2.3 - 安装汉化插件 三、MinGW-w64 的下载安装及配置 3.1 - 介绍 3.2 - 下载 3.3 - 解压安装 3.4 - 环境变量配置 3.5 - 验证配置是否成功 3.6 - 安装 C/C 插件 四、在 VSCode …

SD卡里的视频无法正常播放出来怎么办?

在日常工作和学习中经常会用到SD卡这个存储设备&#xff0c;它存储空间大&#xff0c;使用方便&#xff0c;很多设备上都使用SD卡作为存储工具。SD卡已经成为我们众多电子设备中重要的一员&#xff0c;为我们存储着相片、视频、文档、音频等数据。 SD卡为我们提供了巨大便利&a…

QT(51)-动态链接库-windows

1.qt- 调用win32 DLL 2.qt- 调用MFC DLL 0概述&#xff1a; 01.扩展DLL&#xff1a; 必须有一个DllMain()函数&#xff0c;且调用AfxInitExtensionModule()函数。 CRuntimeClass类-初始化函数CDynLinkLibrary。02.windows定位DLL文件&#xff1a; 1&#xff09…

第五章 在React中如何定义组件

一、安装react开发者工具 在开始之前&#xff0c;我们先做准备一下辅助工具&#xff0c;类似于Vue的开发者工具&#xff0c;React 开发者工具是一款浏览器扩展&#xff0c;可以帮助您在浏览器中调试 React 应用程序。 下面是如何安装 React 开发者工具的步骤&#xff1a; 打开…

常用类详解(三)StringBuilder

(1)一个可爱的字符序列。此类提供一个与StringBuffer兼容的API&#xff0c;但不保证同步(StringBuilder不是线程安全的)&#xff0c;该类被设计用作StringBuffer的一个简易替换&#xff0c;用在字符串缓冲区被单个线程使用的时候。如果可能&#xff0c;建议优先采用该类&#x…

保护品牌线上声誉的5种方法

我们如今生活在一个搜索便捷的世界&#xff0c;对于一个企业和个人来说&#xff0c;品牌的线上声誉也尤为重要。在客户考虑与您的公司开展业务之前&#xff0c;他们理所当然会先使用众多软件和平台搜索相关信息&#xff0c;以帮助他们了解和做决定。 因此&#xff0c;您的品牌…

2023最新整理软件测试面试题附答案

包含的模块&#xff1a; 本文分为十九个模块&#xff0c;分别是&#xff1a;软件测试 基础、liunx、MySQL、web测试、接口测试、APP测试 、管理工具、Python、性能测试、selenium、lordrunner、计算机网络、组成原理、数据结构与算法、逻辑题、人力资源需要的可以找我获取&…

预算砍砍砍,IT运维如何降本增效

疫情短暂过去&#xff0c;一个乐观的共识正在蔓延&#xff1a;2023年的互联网&#xff0c;绝对不会比2022年更差。 “降本”是过去一年许多公司的核心策略&#xff0c;营销大幅缩水、亏损业务大量撤裁&#xff0c;以及层出不穷的裁员消息。而2023年在可预期的经济复苏下&#…

ChatGPT走红| 微软ATP带你开启高校AI人才培育新时代

与传统搜索引擎不同的是&#xff0c;ChatGPT不是机械罗列出相关网页结果&#xff0c;而是将答案进行整理、优化&#xff0c;以对话形式呈现给用户&#xff0c;还能很快根据上下文互动&#xff01;甚至有人用它辅助自己写论文、完成文字性作业……▍抢跑早规划 入门AI不迷茫AI体…

【C++】类和对象(三)

目录 一、构造函数补充 1、初始化列表 1.1、初始化列表概念 1.2、初始化列表性质 2、explicit关键字 二、static成员 1、概念及使用 2、性质总结 三、友元 1、友元函数 2、友元类 四、内部类 五、拷贝对象时的一些编译器优化 一、构造函数补充 在《类和对象&#x…

GeoTools:FeatureShapefile之CRUD操作

之前在《GIS开源框架&#xff1a;ArcGIS文件地理数据库(GDB)解析与入库》中&#xff0c;从地理数据库的角度对Feature要素进行了解释&#xff0c;接下来&#xff0c;我们将从GeoTools库的角度&#xff0c;重新认识Feature要素&#xff0c;并通过GeoTools实现Shapefile文件在Fea…

产品、技术:如何编写有效的流程文档?

流程文档是指一系列的、连续的、有规律的活动过程&#xff0c;而这些活动以特定的方式进行&#xff0c;并导致特定结果&#xff08;创造价值&#xff09;的产生。流程梳理是指围绕企业的内部要素与外部要素&#xff0c;对整个企业的业务特点和现状进行深入细致的分析、整理、提…

Fiddler使用笔记(柠檬班)

Fiddler笔记 内部功能 Replay 重放请求。 号 移除请求&#xff0c;可以通过Shift Delete来选择要保存的请求&#xff0c;其他的都删掉。 Go 断点的时候使用&#xff0c;作用是转到下一步。 Stream 流模式&#xff0c;一般不用它。 Decode 用于解码信息。通常对响应…

Ambire AdEx 2023 年路线图

Ambire AdEx 是为简化 web3 显示广告而建立的&#xff0c;领先于时代。到 2023 年&#xff0c;它将专注于服务用户需求&#xff0c;同时保持其作为区块链隐私解决方案的核心&#xff0c;反对传统的数字广告模式。 回顾 2022 年 2022 年&#xff0c;AdEx 网络处理了超过 1 亿次展…

Hudi-并发控制

并发控制 Hudi支持的并发控制 MVCC Hudi的表操作&#xff0c;如压缩、清理、提交&#xff0c;hudi会利用多版本并发控制来提供多个表操作写入和查询之间的快照隔离。使用MVCC这种模型&#xff0c;Hudi支持并发任意数量的操作作业&#xff0c;并保证不会发生任何冲突。Hudi默…

小米电视安装 Plex 打造家庭影院

背景 最近突然想重温教父&#xff0c;本来想着直接投屏就可以&#xff0c;后来看了别人搭建的基于 NAS 的家庭影院很动心&#xff0c;也想依葫芦画瓢做一个&#xff0c;跟对象申请经费的时候被拒了&#xff0c;理由是有这钱还不如开个会员直接看。 我寻思不同电影在不同的平台…