注:对于ESP32开源技术感兴趣的可以加群,我们一起探索交流学习,群号:782473783。群名:ESP32开源技术交流群。
这篇文章的目的是解释如何开始使用ESP32的WiFi功能,更精确地说,如何扫描周围的WiFi网络以及如何连接到特定的WiFi网络。所有在这里进行的测试都是在DFRobot的ESP-WROOM-32 模块上完成的,集成在ESP32 FireBeetle板上。
介绍
这篇文章的目的是解释如何开始使用ESP32的WiFi功能,更精确地说,如何扫描周围的WiFi网络以及如何连接到特定的WiFi网络。
这篇文章还将介绍一些参数,例如连接到WiFi网络时ESP32的本地IP地址,以及它的MAC地址。我们还将介绍如何从WiFi网络断开连接。
请注意,本教程位于以前的一些教程之上,涵盖了使用ESP32连接到WiFi网络的一些方面。尽管如此,这篇文章还包含了更广泛的功能,它的目标是作为ESP32上WiFi的入门指南,因为它涵盖了我们开始使用设备时通常执行的大部分功能。
设置功能
我们要做的第一件事就是包含WiFi.h库,这将允许我们连接到WiFi网络以及许多其他功能。你可以在这里查看头文件的定义 和实现文件。
对于那些有使用ESP8266背景的人来说,请注意这次头文件通常称为WiFi,而不是ESP8266库,命名为ESP8266WiFi.h。
#include
需要注意的一个重点是在头文件中定义了一个名为WiFi的外部变量。这是WiFi类的变量,我们将会看到,我们将使用它来访问大部分WiFi功能。
接下来,我们将声明两个全局变量来保存我们的WiFi网络凭证,更准确地说是网络名称(SSID)和密码。这样,我们可以轻松地访问和修改这些变量。请注意,您需要更改适用于您的网络的值。
const
char* ssid ="yourNetworkName";
const
char* password ="yourNetworkPassword";
现在我们将进入Arduino设置功能,在那里我们将运行所有剩余的代码。由于这是更多的网络配置教程,我们将不需要使用主循环。
在我们的设置功能的第一行,我们将打开一个串口连接,所以我们可以将我们的程序的结果输出到Arduino IDE串口监视器。为此,我们只需要调用Serial对象的begin函数。我们将使用波特率115200的值。考虑到这个值是非常重要的,因为我们需要在使用Arduino IDE的串口监视器建立到ESP32的串口连接时需要匹配它。
Serial.begin(115200);
之后,我们将调用我们将在下一节中定义的两个函数。这样,我们将代码封装在不同的可重用函数中,所以在其他项目中读取或使用起来更容易。
第一个功能函数叫做scanNetworks,将扫描周围可用的WiFi网络并打印一些关于它们的信息。第二个函数称为connectToNetwork,将设备连接到WiFi网络。
scanNetworks();
connectToNetwork();
现在我们将把这个功能当作黑匣子,并且假定它们会工作。连接到网络后,我们的ESP32应该有一个本地IP分配。为了得到它,我们只需要调用 WiFi extern变量的localIP方法。
Serial.println(WiFi.localIP());
需要注意的是,本地IP将取决于我们所连接的网络,并可能在不同的连接上发生变化。
其他有趣的价值,我们可以得到的是MAC。所以,要获得ESP32的MAC地址,我们只需要调用macAddress方法,再次调用WiFi extern变量。
Serial.println(WiFi.macAddress());
请注意,此值来自ESP32的网络接口,即使未连接到WiFi网络,也不会更改,并且可以恢复。
要完成设置功能,我们将断开与WiFi网络的连接。为此,我们只需要调用 WiFi变量的断开方法。请注意,此方法接收作为输入的标志,允许禁用站操作模式,通过在其实现中调用此方法。
虽然这个参数值默认设置为false,但我们会传递true值来禁用WiFi。之后,我们将调用方法再次获取IP,以确认我们从网络断开连接,并且不再分配IP。
WiFi.disconnect(true);
Serial.println(WiFi.localIP());
下面可以看到设置功能的完整代码。请记住更改全局变量,即您的WiFi网络凭据。请注意,将代码分解为函数使我们有了更清晰的设置函数,从而可以轻松理解我们正在执行的操作,而无需查看函数的实现。
void setup()
{
Serial.begin(115200);
scanNetworks();
connectToNetwork();
Serial.println(WiFi.macAddress());
Serial.println(WiFi.localIP());
WiFi.disconnect(true);
Serial.println(WiFi.localIP());
}
扫描WiFi网络
在连接到WiFi网络之前,我们将扫描周围的网络。与此同时,我们将打印这些网络的一些参数,如网络名称(SSID),信号强度,MAC和加密类型。如前所述,这将在一个名为scanNetworks的函数中实现。
要开始执行网络扫描,我们只需调用 前面提到的WiFi外部变量的scanNetworks功能即可。此操作将启动扫描并返回成功执行时发现的网络数量。
请注意,此函数接收两个布尔参数,它们指示扫描是否应以异步模式执行,并且是否应显示隐藏网络。但是请注意,在实现函数的类的头文件中,两个参数的默认值均为false,因此我们可以在不传递任何输入参数的情况下在代码中调用该函数。为了简单起见,这就是我们要做的。
int
numberOfNetworks = WiFi.scanNetworks();
请注意,我们存储了在变量中找到的网络数量。迭代将存储关于这些网络的信息的数据结构时,这将是需要的。
因此,在执行扫描后,我们可以使用此处显示的功能访问每个网络的参数。请注意,它们全部以参数形式接收网络编号的整数,从0到检测到的网络总数减1。
在我们的代码中,我们将获得并打印网络名称(SSID),MAC地址,信号强度(RSSI)和加密类型。但是请注意,还有功能来检索网络的频道。
但在继续之前,我们需要考虑到加密类型是作为枚举返回的,这里定义了枚举。所以,我们将定义一个辅助函数,它将接收这个枚举的值并返回一个文本描述来指示加密类型。这样,我们得到一个人类可读的结果,而不是一个整数。
所以,这个函数接收一个类型为wifi_auth_mode_t的变量作为输入 ,它是前面提到的enum,并且简单地做一个switch case来返回每个可能值的文本描述。你可以检查下面的实现。
StringtranslateEncryptionType(wifi_auth_mode_t encryptionType)
{
switch (encryptionType){
case (WIFI_AUTH_OPEN):
return "Open";
case (WIFI_AUTH_WEP):
return "WEP";
case (WIFI_AUTH_WPA_PSK):
return "WPA_PSK";
case (WIFI_AUTH_WPA2_PSK):
return "WPA2_PSK";
case (WIFI_AUTH_WPA_WPA2_PSK):
return "WPA_WPA2_PSK";
case (WIFI_AUTH_WPA2_ENTERPRISE):
return "WPA2_ENTERPRISE";
}
}
既然我们有一个允许将加密类型转换为字符串的函数,我们可以继续迭代数据。因此,我们使用我们的网络变量数作为停止条件并调用前面提到的函数来获取每个扫描网络的信息。
还要注意对之前定义的translateEncryptionType函数的调用。
for (int i = 0; i< numberOfNetworks; i++)
{
Serial.print("Networkname: ");
Serial.println(WiFi.SSID(i));
Serial.print("Signalstrength: ");
Serial.println(WiFi.RSSI(i));
Serial.print("MACaddress: ");
Serial.println(WiFi.BSSIDstr(i));
Serial.print("Encryptiontype: ");
String encryptionTypeDescription = translateEncryptionType(WiFi.encryptionType(i));
Serial.println(encryptionTypeDescription);
Serial.println("-----------------------");
}
请注意,为了使输出更完整,还有一些额外的打印。
void scanNetworks()
{
int numberOfNetworks= WiFi.scanNetworks();
Serial.print("Numberof networks found: ");
Serial.println(numberOfNetworks);
for (int i = 0; i< numberOfNetworks; i++) {
Serial.print("Networkname: ");
Serial.println(WiFi.SSID(i));
Serial.print("Signalstrength: ");
Serial.println(WiFi.RSSI(i));
Serial.print("MACaddress: ");
Serial.println(WiFi.BSSIDstr(i));
Serial.print("Encryptiontype: ");
String encryptionTypeDescription = translateEncryptionType(WiFi.encryptionType(i));
Serial.println(encryptionTypeDescription);
Serial.println("-----------------------");
}
}
连接到WiFi网络
现在我们要实现 connectToNetwork函数,它将负责处理ESP32到指定网络的连接。
请注意,代码中将不会与前一次扫描中获得的网络建立链接,但预计我们要连接的网络已在前一次调用中列出。否则,这很可能意味着它不在ESP32搜索范围内。
因此,启动连接,我们只需在前面提到的WiFi类中调用begin方法,将全局变量中定义的网络名称和密码作为输入传递。
尽管为了简单起见,我们直接访问全局变量,因此将函数的可重用性作为connectToNetwork的输入来传递这些值将会更加合适。
WiFi.begin(ssid, password);
由于连接可能需要一段时间,我们接下来将轮询WiFi变量以检查何时完成。为此,我们调用status方法,该方法将返回在此定义的wl_status_t类型的结构。我们要检查的值是 WL_CONNECTED。
代码如下所示。请注意,我们正在循环的每次迭代之间添加一个延迟,以避免不断检查变量。此外,为了简单起见,这段代码将无限期地查询连接,直到成功。当然,为了更强大的解决方案,您可以创建某种最大轮询尝试机制。
while (WiFi.status()!= WL_CONNECTED) {
delay(1000);
Serial.println("Establishingconnection to WiFi..");
}
整个函数的最终代码可以在下面看到。
void connectToNetwork()
{
WiFi.begin(ssid,password);
while (WiFi.status()!= WL_CONNECTED) {
delay(1000);
Serial.println("Establishingconnection to WiFi..");
}
Serial.println("Connectedto network");
}
最终的代码
您可以查看完整的源代码,以便轻松复制和粘贴。不要忘了使用WiFi网络的凭据来更改全局变量的值。还要注意,正如前一节所述,我们不打算使用主循环函数,所以我们可以将它留空。
#include
const char* ssid ="yourNetworkName";
const char*password = "yourNetworkPassword";
String translateEncryptionType(wifi_auth_mode_t encryptionType) {
switch (encryptionType){
case (WIFI_AUTH_OPEN):
return "Open";
case (WIFI_AUTH_WEP):
return "WEP";
case (WIFI_AUTH_WPA_PSK):
return "WPA_PSK";
case (WIFI_AUTH_WPA2_PSK):
return "WPA2_PSK";
case (WIFI_AUTH_WPA_WPA2_PSK):
return "WPA_WPA2_PSK";
case (WIFI_AUTH_WPA2_ENTERPRISE):
return "WPA2_ENTERPRISE";
}
}
void scanNetworks(){
int numberOfNetworks= WiFi.scanNetworks();
Serial.print("Numberof networks found: ");
Serial.println(numberOfNetworks);
for (int i = 0; i< numberOfNetworks; i++) {
Serial.print("Networkname: ");
Serial.println(WiFi.SSID(i));
Serial.print("Signalstrength: ");
Serial.println(WiFi.RSSI(i));
Serial.print("MACaddress: ");
Serial.println(WiFi.BSSIDstr(i));
Serial.print("Encryptiontype: ");
String encryptionTypeDescription = translateEncryptionType(WiFi.encryptionType(i));
Serial.println(encryptionTypeDescription);
Serial.println("-----------------------");
}
}
void connectToNetwork(){
WiFi.begin(ssid,password);
while (WiFi.status()!= WL_CONNECTED) {
delay(1000);
Serial.println("Establishingconnection to WiFi..");
}
Serial.println("Connectedto network");
}
void setup() {
Serial.begin(115200);
scanNetworks();
connectToNetwork();
Serial.println(WiFi.macAddress());
Serial.println(WiFi.localIP());
WiFi.disconnect(true);
Serial.println(WiFi.localIP());
}
void loop() {
}
测试代码
为了测试代码,我们只需要使用Arduino IDE进行编译和上传,然后打开串口监视器来检查结果。不要忘记从工具 - >主板菜单中选择正确的主板。就我而言,我需要选择FireBeetle ESP32。
另外,在串口监视器上,使用设置功能中定义的波特率115200,以便串行连接正常工作。
在串行控制台中获得的结果如图1所示,你应该是相似的。我的ESP32检测到3个WiFi网络,每个网络的参数(MAC地址,网络名称,信号强度和加密类型)都按预期显示。
之后,连接成功,我们的设备获得由路由器分配的本地IP。然后,在将ESP32从WiFi网络断开连接之后,如预期的那样,先前分配的本地IP丢失。
另外,我们的ESP32的MAC地址按照代码中的定义进行打印。为了好奇,MAC地址包含有关供应商的信息。所以,如果你把你在这个查找网站上获得的MAC ,它应该返回Espressif,ESP32的制造商。
图1 - ESP32程序的输出。
关于本地IP
如前所述,当我们将ESP32连接到WiFi网络时,我们将获得一个本地IP。请注意,此IP只在该网络内有效,不能用它从网络外部到达ESP32。
为此,您需要将您的路由器端口转向并与其公共IP进行交互。路由器然后将数据包转发到ESP32。但是请注意,端口转发过程可能不是微不足道的,它取决于所使用的路由器。
此外,端口转发路由器可能会使您的网络面临安全问题,所以如果您打算尝试,请小心行事。
与网络外部的实体需要使用路由器的相同公共IP访问ESP32,从ESP32发送到网络外部的所有数据将具有网络的公共IP而不是我们获得的私有IP(在前面的代码中)。
当然,这只是网络工作的一个简化,并且更详细地解释它超出了本文的范围。不过,我建议您对该主题进行一点研究,以便更好地理解事情的工作方式,这对开发更复杂的应用程序非常有用,这些应用程序涉及在ESP32和其他实体之间更改数据。在这篇文章中,我们只涉及连接到网络而不是交换数据,所以程序的架构非常简单。
相关内容
§ ESP-WROOM-32产品页面
§ FireBeetle ESP32产品页面
§ Espressif网站
§ MAC供应商查找
§ 什么是MAC地址?
§ 什么是本地IP地址?
§ 什么是端口转发?