ESP32 Wi-Fi讲解,station连接热点案例_esp32sta模式与手机热点链接-程序员宅基地

技术标签: ESP32 IDF  ESP32 WIFI  ESP32连接热点  ESP32 开发板  ESP32连接AP  ESP32 Station  

零. 声明


本专栏文章我们会以连载的方式持续更新,本专栏计划更新内容如下:

第一篇:ESP-IDF基本介绍,主要会涉及模组,芯片,开发板的介绍,环境搭建,程序编译下载,启动流程等一些基本的操作,让你对ESP-IDF开发有一个总体的认识,比我们后续学习打下基础!

第二篇:ESP32-IDF外设驱动介绍,主要会根据esp-idf现有的driver,提供各个外设的驱动,比如LED,OLED,SPI LCD,TOUCH,红外,Codec ic等等,在这一篇中,我们不仅仅来做外设驱动,还会对常用的外设总线做一个介绍,让大家知其然又知其所以然!

第三篇:目前比较火热的GUI LVGL介绍,主要会设计LVGL7.1,LVGL8的移植介绍,并且也会介绍各个组件,知道原理后,最后,我们会推出一款组态软件来构建我们的GUI,来提升我们的效率!

第四篇:ESP32-蓝牙,熟悉我的,应该都知道,我即使从事蓝牙协议栈的开发的,所以这个是我们独有的优势,在这一篇章,我们会提供不仅仅是蓝牙应用方法的知识,也会应用结合蓝牙底层协议栈的理论,让你彻底从上到下打通蓝牙任督二脉!

第五篇:Wi-Fi介绍,熟悉我的,应该也知道,我们也做过一款sdio wifi的驱动教程板子,所以在wifi这方面我们也是有独有的优势,在这一篇章,我们同样不仅仅提供Wi-Fi应用方面的知识,也会结合底层理论,让你对Wi-Fi有一个清晰的认知!

第六篇:FreeRTOS介绍,主要介绍下FreeRTOS各个功能(任务管理/消息队列/信号量/互斥量/事件/软件定时器/任务通知/内存管理/中断管理等)的使用以及运作机制。

第七篇:Arduino介绍,主要介绍ESP32 Arduino的基本操作(环境搭建,烧录,下载等开发流程),以及介绍下基于Arduino的外设,蓝牙,wifi的使用。

第八篇:Demo,此篇章是融会贯通以上章节,做一些综合性的demo,让你巩固以上篇章的同时,还能学到实际项目!!

另外,我们的教程包括但是不局限于以上篇章,为了给你一个更好的导航,以下信息尤其重要,请详细查看!!

------------------------------------------------------------------------------------------------------------------------------------------

购买开发板(点击我)

文档目录(点击我)

Github代码仓库(点击我)

蓝牙交流扣扣群:539357317

微信公众号↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

------------------------------------------------------------------------------------------------------------------------------------------

一.Wi-Fi station的使用步骤

下图为 station 模式下的宏观场景,其中包含不同阶段的具体描述:

1.Wi-Fi/LwIP 初始化阶段

  • s1.1:主任务通过调用函数 esp_netif_init() 创建一个 LwIP 核心任务,并初始化 LwIP 相关工作。
  • s1.2:主任务通过调用函数 esp_event_loop_create() 创建一个系统事件任务,并初始化应用程序事件的回调函数。在此情况下,该回调函数唯一的动作就是将事件中继到应用程序任务中。
  • s1.3:主任务通过调用函数 esp_netif_create_default_wifi_sta() 创建有 TCP/IP 堆栈的默认网络接口实例绑定 station 或 AP。
  • s1.4:主任务通过调用函数 esp_wifi_init() 创建 Wi-Fi 驱动程序任务,并初始化 Wi-Fi 驱动程序。
  • s1.5:主任务通过调用 OS API 创建应用程序任务。

推荐按照 s1.1 ~ s1.5 的步骤顺序针对基于 Wi-Fi/LwIP 的应用程序进行初始化。但这一顺序 并非 强制,您可以在第 s1.1 步创建应用程序任务,然后在该应用程序任务中进行所有其它初始化操作。不过,如果您的应用程序任务依赖套接字,那么在初始化阶段创建应用程序任务可能并不适用。此时,您可以在接收到 IP 后再进行任务创建。

ESP_ERROR_CHECK(esp_netif_init());

ESP_ERROR_CHECK(esp_event_loop_create_default());
esp_netif_create_default_wifi_sta();

wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));

2.Wi-Fi配置阶段

Wi-Fi 驱动程序初始化成功后,可以进入到配置阶段。该场景下,Wi-Fi 驱动程序处于 station 模式。因此,首先您需调用函数 esp_wifi_set_mode() (WIFI_MODE_STA) 将 Wi-Fi 模式配置为 station 模式。可通过调用其它 esp_wifi_set_xxx API 进行更多设置,例如:协议模式、国家代码、带宽等。请参阅 ESP32 Wi-Fi 配置

一般情况下,我们会在建立 Wi-Fi 连接之前配置 Wi-Fi 驱动程序,但这 并非 强制要求。也就是说,只要 Wi-Fi 驱动程序已成功初始化,您可以在任意阶段进行配置。但是,如果您的 Wi-Fi 在建立连接后不需要更改配置,则应先在此阶段完成配置。因为调用配置 API(例如 esp_wifi_set_protocol())将会导致 Wi-Fi 连接断开,为您的操作带来不便。

如果 menuconfig 已使能 Wi-Fi NVS flash,则不论当前阶段还是后续的 Wi-Fi 配置信息都将被存储至该 flash 中。那么,当主板上电/重新启动时,就不需从头开始配置 Wi-Fi 驱动程序。您只需调用函数 esp_wifi_get_xxx API 获取之前存储的配置信息。当然,如果不想使用之前的配置,您依然可以重新配置 Wi-Fi 驱动程序。

#define EXAMPLE_ESP_WIFI_SSID      "wireless link"
#define EXAMPLE_ESP_WIFI_PASS      "12345678"
#define EXAMPLE_ESP_MAXIMUM_RETRY  5

wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS,
            /* Setting a password implies station will connect to all security modes including WEP/WPA.
             * However these modes are deprecated and not advisable to be used. Incase your Access point
             * doesn't support WPA2, these mode can be enabled by commenting below line */
	     .threshold.authmode = WIFI_AUTH_WPA2_PSK,

            .pmf_cfg = {
                .capable = true,
                .required = false
            },
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );

3.Wi-Fi启动阶段

  • s3.1:调用函数 esp_wifi_start() 启动 Wi-Fi 驱动程序。
  • s3.2:Wi-Fi 驱动程序将事件 WIFI_EVENT_STA_START 发布到事件任务中,然后,事件任务将执行一些正常操作并调用应用程序的事件回调函数。
  • s3.3:应用程序的事件回调函数将事件 WIFI_EVENT_STA_START 中继到应用程序任务中。推荐您此时调用函数 esp_wifi_connect() 进行 Wi-Fi 连接。当然,您也可以等待在 WIFI_EVENT_STA_START 事件发生后的其它阶段再调用此函数。
ESP_ERROR_CHECK(esp_wifi_start() );
ESP_LOGI(TAG, "wifi_init_sta finished.");

4.Wi-Fi 连接阶段

  • s4.1:调用函数 esp_wifi_connect() 后,Wi-Fi 驱动程序将启动内部扫描/连接过程。
  • s4.2:如果内部扫描/连接过程成功,将产生 WIFI_EVENT_STA_CONNECTED 事件。然后,事件任务将启动 DHCP 客户端服务,最终触发 DHCP 程序。
  • s4.3:在此情况下,应用程序的事件回调函数会将 WIFI_EVENT_STA_CONNECTED 事件中继到应用程序任务中。通常,应用程序不需进行操作,而您可以执行任何动作,例如:打印日志等。

步骤 s4.2 中 Wi-Fi 连接可能会由于某些原因而失败,例如:密码错误、未找到 AP 等。这种情况下,将引发 WIFI_EVENT_STA_DISCONNECTED 事件并提示连接错误原因。

if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
}

5.Wi-Fi 获取 IP 阶段

  • s5.1:一旦步骤 4.2 中的 DHCP 客户端初始化完成,Wi-Fi 驱动程序将进入 获取 IP 阶段。
  • s5.2:如果 Wi-Fi 成功从 DHCP 服务器接收到 IP 地址,则将引发 IP_EVENT_STA_GOT_IP事件,事件任务将执行正常处理。
  • s5.3:应用程序的事件回调函数将事件 IP_EVENT_STA_GOT_IP 中继到应用程序任务中。对于那些基于 LwIP 构建的应用程序,此事件较为特殊,因为它意味着应用程序已准备就绪,可以开始任务,例如:创建 TCP/UDP 套接字等。此时较为容易犯的一个错误就是在接收到 IP_EVENT_STA_GOT_IP 事件之前就初始化套接字。切忌在接收到 IP 之前启动任何套接字相关操作。
else if (event_base == IP_EVENT && event_id == IP_EVENT_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));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }

6.Wi-Fi 断开阶段

  • s6.1:当 Wi-Fi 因为某些原因(例如:AP 掉电、RSSI 较弱等)连接中断时,将产生 WIFI_EVENT_STA_DISCONNECTED 事件。此事件也可能在上文阶段 3 中发生。在这里,事件任务将通知 LwIP 任务清除/移除所有 UDP/TCP 连接。然后,所有应用程序套接字都将处于错误状态。也就是说,WIFI_EVENT_STA_DISCONNECTED 事件发生时,任何套接字都无法正常工作。
  • s6.2:上述情况下,应用程序的事件回调函数会将 WIFI_EVENT_STA_DISCONNECTED 事件中继到应用程序任务中。推荐您调用函数 esp_wifi_connect() 重新连接 Wi-Fi,关闭所有套接字,并在必要时重新创建套接字。

7.Wi-Fi IP 更改阶段

  • s7.1:如果 IP 地址发生更改,将引发 IP_EVENT_STA_GOT_IP 事件,其中 “ip_change” 被置为 “true”。
  • s7.2:此事件对应用程序至关重要。这一事件发生时,适合关闭所有已创建的套接字并进行重新创建。

8.Wi-Fi 清理阶段

  • s8.1:调用函数 esp_wifi_disconnect() 断开 Wi-Fi 连接。
  • s8.2:调用函数 esp_wifi_stop() 终止 Wi-Fi 驱动程序。
  • s8.3:调用函数 esp_wifi_deinit() 清理 Wi-Fi 驱动程序。

二.代码

/* WiFi station 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 "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"

#include "lwip/err.h"
#include "lwip/sys.h"

/* The examples use WiFi configuration that you can set via project configuration menu

   If you'd rather not, just change the below entries to strings with
   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_ESP_WIFI_SSID      "wireless link"
#define EXAMPLE_ESP_WIFI_PASS      "12345678"
#define EXAMPLE_ESP_MAXIMUM_RETRY  5

/* FreeRTOS event group to signal when we are connected*/
static EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event, but we only care about two events:
 * - we are connected to the AP with an IP
 * - we failed to connect after the maximum amount of retries */
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT      BIT1

static const char *TAG = "wifi station";

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)
{
    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) {
        if (s_retry_num < EXAMPLE_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_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

void wifi_init_sta(void)
{
    s_wifi_event_group = xEventGroupCreate();

    ESP_ERROR_CHECK(esp_netif_init());

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    esp_event_handler_instance_t instance_any_id;
    esp_event_handler_instance_t instance_got_ip;
    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_any_id));
    ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                        IP_EVENT_STA_GOT_IP,
                                                        &event_handler,
                                                        NULL,
                                                        &instance_got_ip));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS,
            /* Setting a password implies station will connect to all security modes including WEP/WPA.
             * However these modes are deprecated and not advisable to be used. Incase your Access point
             * doesn't support WPA2, these mode can be enabled by commenting below line */
	     .threshold.authmode = WIFI_AUTH_WPA2_PSK,

            .pmf_cfg = {
                .capable = true,
                .required = false
            },
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start() );

    ESP_LOGI(TAG, "wifi_init_sta finished.");

    /* Waiting until either the connection is established (WIFI_CONNECTED_BIT) or connection failed for the maximum
     * number of re-tries (WIFI_FAIL_BIT). The bits are set by event_handler() (see above) */
    EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

    /* xEventGroupWaitBits() returns the bits before the call returned, hence we can test which event actually
     * happened. */
    if (bits & WIFI_CONNECTED_BIT) {
        ESP_LOGI(TAG, "connected to ap SSID:%s password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else if (bits & WIFI_FAIL_BIT) {
        ESP_LOGI(TAG, "Failed to connect to SSID:%s, password:%s",
                 EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
    } else {
        ESP_LOGE(TAG, "UNEXPECTED EVENT");
    }

    /* The event will not be processed after unregister */
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, instance_got_ip));
    ESP_ERROR_CHECK(esp_event_handler_instance_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, instance_any_id));
    vEventGroupDelete(s_wifi_event_group);
}

void app_main(void)
{
    //Initialize NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);

    ESP_LOGI(TAG, "ESP_WIFI_MODE_STA");
    wifi_init_sta();
}

整份代码就是我们第一小节的内容,所以大致自己看下就可以了

三.效果演示

log如下:

我们可以看到已经连接成功,并且分配的IP地址是:192.168.61.27

此时我们看热点的连接设备列表,可以看到有设备连接进来,如下图:

此时我们用另外一台STA,也就是我们的PC去ping一下esp32的板子,发现能ping通

PC(sta) <-------> AP热点 <-------> ESP32开发板

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/124391887

智能推荐

rmd文件怎么转换html文件,提取.Rmd文件的html依赖项(包含htmlwidgets)-程序员宅基地

文章浏览阅读790次。题我怎样才能创建一个将.Rmd文件(包含htmlwidgets代码)作为输入的函数,并输出一个包含其JavaScript / CSS依赖项的html文件?具体来说,当渲染为html时,临时文件rmarkdown为pandoc的–include-in-header参数生成.细节示例 – myfile.Rmd:This is some text```{r}library(dygraphs)dygrap..._怎么把rmd保存为html

QT designer 如何快速查看效果_qt designer 怎样查看界面效果-程序员宅基地

文章浏览阅读793次。Ctrl+R_qt designer 怎样查看界面效果

华为AI盘古大模型研究框架(华为产业链深度系列研究)PPT_华为ai大模型产业链-程序员宅基地

文章浏览阅读494次,点赞9次,收藏9次。鲲鹏芯片:鲲鹏920作为低功耗、 高性能的Arm处理器, 为鲲鹏服务器主板及整机产品提供芯片支撑, 使鲲鹏生态发展壮大 的核心所在, 在此基础上, 华为进一步开启自主研发芯片, 为鲲鹏生态发展奠定坚实基础。:昇腾AI处理器作为基础, 通过模块、 标卡、 小站、 服务器等丰富的产品形态, 打造面向 “端、 边、 云 “的全栈解 决方案, 为整个昇腾AI产业的底层核心支撑。、行业应用:华为以行业聚合应用, 通过平台和生态双轮驱动, 形成行业应用矩阵, 为众多行业客户提供解决方案。历经多年的技术创新,_华为ai大模型产业链

兴业银行银企直联查转账手续费和退票流水(C#代码篇)_c# 对接银企直联-程序员宅基地

文章浏览阅读1.7k次。前几天写了在兴业银行的银企直联中,如何查询手续费和退票流水,但没有完整的代码展示,所以这里再完整的提供下查询相关的代码。封装代码不涉及任何外部业务,如果你也正在接入兴业银行,且使用的开发语言是NET,那么你完全可以发挥拿来主义,完全不需要你修改一行代码!首先为了在转账时将企业内部系统业务Id作为PURPOSE,我在这里定义了一个ICIBTransactionPurposeBuilder接口,该接..._c# 对接银企直联

JVM菜鸟进阶高手之路十(基础知识开场白)_jvm必备知识-程序员宅基地

文章浏览阅读4.8k次,点赞6次,收藏14次。最近没有什么实战,准备把JVM知识梳理一遍,先以开发人员的交流来谈谈jvm这块的知识以及重要性,依稀记得2、3年前用solr的时候老是经常oom,提到oom大家应该都不陌生,那个时候也并没有从根本仅仅oom,由于对jvm不熟悉,只是去百度,到处都是配置jvm参数的,那个时候啥不懂,直接粘贴,但是并没有解决问题,通过这个就告诉我们作为开发人员也需要对jvm很熟悉才行,问题来了,很多人会说我的代码并没_jvm必备知识

解决undefined reference to symbol ‘LZ4_decompress_safe‘问题_对‘lz4_decompress_safe’未定义的引用-程序员宅基地

文章浏览阅读2.6k次,点赞10次,收藏6次。文章目录问题描述解决方法参考资源问题描述make时碰到如下问题/usr/bin/ld: CMakeFiles/nearest_neighbors.dir/nearest_neighbors.cpp.o: undefined reference to symbol 'LZ4_decompress_safe'解决方法(不推荐)降低flann版本到1.7及以下(推荐)使用whereis liblz4.so找到对应的地址, 一般是/usr/lib/x86_64-linux-gnu/liblz4.s_对‘lz4_decompress_safe’未定义的引用

随便推点

srilm 阅读文档6_intervalheap-程序员宅基地

文章浏览阅读437次。IntervalHeap.h IntervalHeap.cc文档作者:jianzhu创立时间:08.08.30--------------------------------------1、基本类-------------------------------------- 这两个文件主要以模板方式定义了一个区间堆(IntervalHeap)。该区间堆是一个最大值堆和最小值堆的结合。通过在堆的每个节点

200 万年薪!西交大 2 位计算机博士入选华为天才少年-程序员宅基地

文章浏览阅读238次。点击上方“码农突围”,马上关注这里是码农充电第一站,回复“666”,获取一份专属大礼包真爱,请设置“星标”或点个“在看”转自 | 新智元编辑 | 桃子 拉燕2019年,华为发布全球招募令,计划在全世界招募20-30名华为天才少年。至今,已经有20位天才少年加入华为。近日,又有2名来自西安交通大学的博士成功入选。谁将有幸入选今年的华为天才少年呢?前几天,华为刚刚发布了新一...

Oracle select into,fetch into,returning into, bulk collect into limit, forall in 批量效率的读取游标数据 提高性能_fetch bulk collect into limit-程序员宅基地

文章浏览阅读2.3k次。博客园 | 首页 | 新随笔 | 联系 | 订阅 | 管理ORACLE FETCH BULK COLLECT INTO LIMITDECLARE TYPE rr IS REF CURSOR; TYPE r_emp IS RECORD( empno NUMBER(4), ename VARCHAR2(10), job VARCHAR..._fetch bulk collect into limit

SENET与 CBAM 注意力机制_如果把senet和cbam结合成为双重注意力机制,插入到backbone网络中,那么他的输出特-程序员宅基地

文章浏览阅读1.3k次,点赞2次,收藏13次。原文链接:https://blog.csdn.net/xu380393916/article/details/109304082一、SENETSENET是2017年的世界冠军,SE全称Squeeze-and-Excitation是一个模块,将现有的网络嵌入SE模块的话,那么该网络就是SENet,它几乎可以嵌入当前流行的任何网络,那么为什么会引出这个东西呢,来看下图:SE结构一个SEblock的过程分为 Squeeze(压缩) 和 Excitation(激发) 两个步骤:Squeeze(压缩) 通_如果把senet和cbam结合成为双重注意力机制,插入到backbone网络中,那么他的输出特

Android 9.0 TV版导入GMS组件以及Google Play_google play tv版-程序员宅基地

文章浏览阅读9.3k次,点赞2次,收藏17次。Android 9.0 TV版导入GMS组件以及Google Play一、Android P之后的差异参照phone版本整理出一版适合电视或者电视盒子相关GMS框架导入方法可查看GMS P Initial Release Notes 了解:1、P上增加了一些core app :AndroidPlatformServices、GoogleRestore、Device Health Services (Turbo)2、GoogleContacts is Non-privileged ,其实2_google play tv版

Windows的Mysql5.7安装及使用_window mysql5.7 区分大小写并安装-程序员宅基地

文章浏览阅读1k次。mysql5.7_window mysql5.7 区分大小写并安装

推荐文章

热门文章

相关标签