#一:简介
WiFi 设备工作在 2.4G 频段或 5G 频段,由于无线信号的开放性,不同的无线设备间会存在相互干扰。因此,不同国家对 WiFi 设备都有一定的要求和限制。主要体现在频谱、射频功率、安全性及其他方面。因此,在不同国家销售的 WiFi 设备应遵循该国家或地区的要求。在 ESP8266 和 ESP32 中,国家码主要就是针对不同国家可使用的频率不同而设计的。因为 WiFi 协议中将可发射的频率划分为不同的信道,所以也可以理解为国家码就是针对不同国家的信道要求而设计。因为 ESP8266 和 ESP32 目前只支持 2.4G 频段,所以本文就以 2.4G 频段中的信道为例来说明。
国家码
下图是主要国家对于 2.4G 频段的信道要求,不同的国家对信道要求不同,如中国和欧洲支持的信道为 1-13(所以欧洲的路由器在中国是可以直接使用的),而北美只支持 1-11,日本则支持 1-14。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Beps6dwZ-1614058840601)(https://i.imgur.com/2mP3o7v.jpg)]
因为国内支持的信道为 1-13,所以在使用国内的路由器时,WiFi 信道只能在 1-13 之间任选;
美国所支持的信道是 1-11,那么路由器中只可以使用 1-11,12 和 13 即为非法信道。如果将国内的路由器带到美国,并设置信道为 12,那么用美国的手机将扫描不到这个路由器的信息。所以如果不能自由转换国家码,必然会对售往不同国家的 WiFi 设备产生影响。
#二:国家码接口
在 ESP32 中,国家码相关的接口主要包含设置国家码和获取国家码
/**
* @brief configure country info
*
* @attention 1. The default country is {.cc="CN", .schan=1, .nchan=13, policy=WIFI_COUNTRY_POLICY_AUTO}
*
* @param wifi_country_t *country: the configured country info
*
* @return
* - ESP_OK: succeed
* - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by esp_wifi_init
* - ESP_ERR_WIFI_ARG: invalid argument
*/
esp_err_t esp_wifi_set_country(const wifi_country_t *country);
/**
* @brief get the current country info
*
* @param country country info
*
* @return
* - ESP_OK: succeed
* - ESP_ERR_WIFI_NOT_INIT: WiFi is not initialized by esp_wifi_init
* - ESP_ERR_WIFI_ARG: invalid argument
*/
esp_err_t esp_wifi_get_country(wifi_country_t *country);
其中 esp_wifi_set_country (ESP8266 中称为 wifi_set_country) 用来设置国家码信息,esp_wifi_get_country (ESP8266 中称为 wifi_get_country) 用来获取当前的国家码信息。国家码参数信息 wifi_country_t 主要由以下几部分组成:
typedef struct {
char cc[3]; /**< country code string */
uint8_t schan; /**< start channel */
uint8_t nchan; /**< total channel number */
uint8_t policy; /**< country policy */
} wifi_country_t;
其中 cc 为国家代码,如中国的国家代码为 CN,美国为 US;schan 为起始信道,nchan 为总信道数,如美国的起始信道为 1,信道数为 11,policy 为国家码选项,目前只支持如下两种:
typedef enum {
WIFI_COUNTRY_POLICY_AUTO, /**< Country policy is auto, use the country info of AP to which the station is connected */
WIFI_COUNTRY_POLICY_MANUAL, /**< Country policy is manual, always use the configured country info */
} WIFI_COUNTRY_POLICY;
如果选择 auto 那么将会使用所连接 AP 的国家码信息,而 manual 表示由用户配置信息决定。
#三:国家码使用
3.1 STATION 模式
当国家码 policy 为 auto 时,STA 的国家码信息由所连接的 AP 决定,如果 AP 中不含国家码信
息,则使用默认值 {“CN”, 1, 13}。比如国家码设置为 {“US”, 1, 11},连接中国的路由器后,其国家码会被自动更新为 {“CN”, 1, 13}。
当国家码 policy 为 manual 时,STA 的国家码信息由 esp_wifi_set_country 中配置信息决定,
仅扫描国家码规定的信道。
3.2 SOFTAP 模式
在 AP 模式下,国家码 policy 不会生效,可以省略,如果配置了国家为 {“US”, 1, 11},则AP可配置的信道范围为 1~11。默认的国家码信息为 {“CN”, 1, 13}。
需要注意的是配置的国家码信息会对 AP 参数的配置有影响,例如在 ESP32 中,esp_wifi_set_config 能够配置的信道范围就是由配置的国家码决定的。
3.3 AP + STATION 模式
当国家码 policy 为 auto 时,AP 的国家码信息跟随 STA 所连接的外部 AP 的国家码信息,此时
AP 会动态更新 beacon/probe response 中的国家码信息。当 STA 连接的外部 AP 的国家码信息
与 AP 配置的国家码信息不同时,会打出一条 Warning 信息,如果 STA 与连接的 AP 断开连接,
国家码依然使用最近连接过的 AP 的国家码。
当国家码 policy 为 manual 时,AP 的国家码信息由配置所决定
四:运行分析
本文以ESP8266 RTOS 的 AP + STATION 模式下的 一个 ConnectAP 的 Demo (Conn_AP_DEMO)来分析国家码的具体实现。
根据这个 demo,主要修改代码如下:
void wifi_handle_event_cb(System_Event_t *evt)
{
switch (evt->event_id) {
case EVENT_STAMODE_CONNECTED:
printf("connect to ssid %s, channel %d\n", evt->event_info.connected.ssid,
evt->event_info.connected.channel);
wifi_country_t config_country;
wifi_get_country(&config_country);
printf("Country info:CC:%c%c,SC:%d,NC:%d\n",config_country.cc[0],config_country.cc[1],config_country.schan,config_country.nchan);
break;
case EVENT_STAMODE_DISCONNECTED:
printf("disconnect from ssid %s, reason %d\n", evt->event_info.disconnected.ssid,
evt->event_info.disconnected.reason);
break;
......
}
}
void conn_ap_init(void)
{
// wifi_country_t config_country = {.cc="US", .schan=1, .nchan=11, WIFI_COUNTRY_POLICY_AUTO};
wifi_country_t config_country = {.cc="US", .schan=1, .nchan=11, WIFI_COUNTRY_POLICY_MANUAL};
wifi_set_country(&config_country);
wifi_set_opmode(STATIONAP_MODE);
struct station_config config;
memset(&config, 0, sizeof(config)); //set value of config from address of &config to width of size to be value '0'
sprintf(config.ssid, DEMO_AP_SSID);
sprintf(config.password, DEMO_AP_PASSWORD);
wifi_station_set_config(&config);
wifi_set_event_handler_cb(wifi_handle_event_cb);
wifi_station_connect();
}
代码中设置的国家码为美国,policy 为 manual,如果路由器的信道为 11,运行程序,程序可以正常连接到路由,并打印如下信息:
可以看到此时通过 wifi_get_country 获得的国家码为代码中设置的国家码。使用 OmniPeak 抓包,发现 ESP32 上 AP 的国家码信息也会设置为美国。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yKhxRGuO-1614058840615)(https://i.imgur.com/ZPUp11T.jpg)]
如果将路由器的信道更改为 12,此时重新运行程序,会发现无法连接到此路由器,串口会打印如下信息:
因为程序根据设置的国家码要求在信道 1-11 扫描,但路由器是在信道 12 上广播的,所以没有办法扫描到 SSID。
如果将国家码 policy 从 WIFI_COUNTRY_POLICY_MANUAL 设置为 WIFI_COUNTRY_POLICY_AUTO,重新运行程序,会发现即使路由器信道改为 12 依然可以连接上,在 wifi_get_country 中打印的信息为路由器中的国家码信息。
此时使用OmniPeek抓取空中包,发现 ESP32 上的 softap 回复的 Probe Rsp 包中的国家码信息已经变成路由器中的国家码信息。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bi8Kb88D-1614058840627)(https://i.imgur.com/QO3AJeh.jpg)]