blob: 9391ee586e3eb6baadb9263faf40fe90fb69e2f6 [file] [log] [blame]
/*
*
* Copyright (c) 2020 Project CHIP Authors
* Copyright (c) 2018 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file
* General utility methods for the ESP32 platform.
*/
/* this file behaves like a config.h, comes first */
#include <platform/internal/CHIPDeviceLayerInternal.h>
#include <lib/support/CodeUtils.h>
#include <lib/support/ErrorStr.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/ESP32/ESP32Utils.h>
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_netif_net_stack.h"
#include "esp_wifi.h"
#include "nvs.h"
using namespace ::chip::DeviceLayer::Internal;
using chip::DeviceLayer::Internal::DeviceNetworkInfo;
CHIP_ERROR ESP32Utils::IsAPEnabled(bool & apEnabled)
{
wifi_mode_t curWiFiMode;
esp_err_t err = esp_wifi_get_mode(&curWiFiMode);
if (err != ESP_OK)
{
ChipLogError(DeviceLayer, "esp_wifi_get_mode() failed: %s", esp_err_to_name(err));
return ESP32Utils::MapError(err);
}
apEnabled = (curWiFiMode == WIFI_MODE_AP || curWiFiMode == WIFI_MODE_APSTA);
return CHIP_NO_ERROR;
}
CHIP_ERROR ESP32Utils::IsStationEnabled(bool & staEnabled)
{
wifi_mode_t curWiFiMode;
esp_err_t err = esp_wifi_get_mode(&curWiFiMode);
if (err != ESP_OK)
{
ChipLogError(DeviceLayer, "esp_wifi_get_mode() failed: %s", esp_err_to_name(err));
return ESP32Utils::MapError(err);
}
staEnabled = (curWiFiMode == WIFI_MODE_STA || curWiFiMode == WIFI_MODE_APSTA);
return CHIP_NO_ERROR;
}
bool ESP32Utils::IsStationProvisioned(void)
{
wifi_config_t stationConfig;
return (esp_wifi_get_config(WIFI_IF_STA, &stationConfig) == ERR_OK && stationConfig.sta.ssid[0] != 0);
}
CHIP_ERROR ESP32Utils::IsStationConnected(bool & connected)
{
wifi_ap_record_t apInfo;
connected = (esp_wifi_sta_get_ap_info(&apInfo) == ESP_OK);
return CHIP_NO_ERROR;
}
CHIP_ERROR ESP32Utils::StartWiFiLayer(void)
{
int8_t ignored;
bool wifiStarted;
// There appears to be no direct way to ask the ESP WiFi layer if esp_wifi_start()
// has been called. So use the ESP_ERR_WIFI_NOT_STARTED error returned by
// esp_wifi_get_max_tx_power() to detect this.
esp_err_t err = esp_wifi_get_max_tx_power(&ignored);
switch (err)
{
case ESP_OK:
wifiStarted = true;
break;
case ESP_ERR_WIFI_NOT_STARTED:
wifiStarted = false;
break;
default:
return ESP32Utils::MapError(err);
}
if (!wifiStarted)
{
ChipLogProgress(DeviceLayer, "Starting ESP WiFi layer");
err = esp_wifi_start();
if (err != ESP_OK)
{
ChipLogError(DeviceLayer, "esp_wifi_start() failed: %s", esp_err_to_name(err));
return ESP32Utils::MapError(err);
}
}
return CHIP_NO_ERROR;
}
CHIP_ERROR ESP32Utils::EnableStationMode(void)
{
wifi_mode_t curWiFiMode;
// Get the current ESP WiFI mode.
esp_err_t err = esp_wifi_get_mode(&curWiFiMode);
if (err != ESP_OK)
{
ChipLogError(DeviceLayer, "esp_wifi_get_mode() failed: %s", esp_err_to_name(err));
return ESP32Utils::MapError(err);
}
// If station mode is not already enabled (implying the current mode is WIFI_MODE_AP), change
// the mode to WIFI_MODE_APSTA.
if (curWiFiMode == WIFI_MODE_AP)
{
ChipLogProgress(DeviceLayer, "Changing ESP WiFi mode: %s -> %s", WiFiModeToStr(WIFI_MODE_AP),
WiFiModeToStr(WIFI_MODE_APSTA));
err = esp_wifi_set_mode(WIFI_MODE_APSTA);
if (err != ESP_OK)
{
ChipLogError(DeviceLayer, "esp_wifi_set_mode() failed: %s", esp_err_to_name(err));
return ESP32Utils::MapError(err);
}
}
return CHIP_NO_ERROR;
}
CHIP_ERROR ESP32Utils::SetAPMode(bool enabled)
{
wifi_mode_t curWiFiMode, targetWiFiMode;
targetWiFiMode = (enabled) ? WIFI_MODE_APSTA : WIFI_MODE_STA;
// Get the current ESP WiFI mode.
esp_err_t err = esp_wifi_get_mode(&curWiFiMode);
if (err != ESP_OK)
{
ChipLogError(DeviceLayer, "esp_wifi_get_mode() failed: %s", esp_err_to_name(err));
return ESP32Utils::MapError(err);
}
// If station mode is not already enabled (implying the current mode is WIFI_MODE_AP), change
// the mode to WIFI_MODE_APSTA.
if (curWiFiMode != targetWiFiMode)
{
ChipLogProgress(DeviceLayer, "Changing ESP WiFi mode: %s -> %s", WiFiModeToStr(curWiFiMode), WiFiModeToStr(targetWiFiMode));
err = esp_wifi_set_mode(targetWiFiMode);
if (err != ESP_OK)
{
ChipLogError(DeviceLayer, "esp_wifi_set_mode() failed: %s", esp_err_to_name(err));
return ESP32Utils::MapError(err);
}
}
return CHIP_NO_ERROR;
}
int ESP32Utils::OrderScanResultsByRSSI(const void * _res1, const void * _res2)
{
const wifi_ap_record_t * res1 = (const wifi_ap_record_t *) _res1;
const wifi_ap_record_t * res2 = (const wifi_ap_record_t *) _res2;
if (res1->rssi > res2->rssi)
{
return -1;
}
if (res1->rssi < res2->rssi)
{
return 1;
}
return 0;
}
const char * ESP32Utils::WiFiModeToStr(wifi_mode_t wifiMode)
{
switch (wifiMode)
{
case WIFI_MODE_NULL:
return "NULL";
case WIFI_MODE_STA:
return "STA";
case WIFI_MODE_AP:
return "AP";
case WIFI_MODE_APSTA:
return "STA+AP";
default:
return "(unknown)";
}
}
struct netif * ESP32Utils::GetStationNetif(void)
{
return GetNetif("WIFI_STA_DEF");
}
struct netif * ESP32Utils::GetNetif(const char * ifKey)
{
struct netif * netif = NULL;
esp_netif_t * netif_handle = NULL;
netif_handle = esp_netif_get_handle_from_ifkey(ifKey);
netif = (struct netif *) esp_netif_get_netif_impl(netif_handle);
return netif;
}
bool ESP32Utils::IsInterfaceUp(const char * ifKey)
{
struct netif * netif = GetNetif(ifKey);
return netif != NULL && netif_is_up(netif);
}
bool ESP32Utils::HasIPv6LinkLocalAddress(const char * ifKey)
{
struct esp_ip6_addr if_ip6_unused;
return esp_netif_get_ip6_linklocal(esp_netif_get_handle_from_ifkey(ifKey), &if_ip6_unused) == ESP_OK;
}
CHIP_ERROR ESP32Utils::GetWiFiStationProvision(Internal::DeviceNetworkInfo & netInfo, bool includeCredentials)
{
wifi_config_t stationConfig;
esp_err_t err = esp_wifi_get_config(WIFI_IF_STA, &stationConfig);
if (err != ESP_OK)
{
return ESP32Utils::MapError(err);
}
VerifyOrReturnError(stationConfig.sta.ssid[0] != 0, CHIP_ERROR_INCORRECT_STATE);
netInfo.NetworkId = kWiFiStationNetworkId;
netInfo.FieldPresent.NetworkId = true;
memcpy(netInfo.WiFiSSID, stationConfig.sta.ssid,
min(strlen(reinterpret_cast<char *>(stationConfig.sta.ssid)) + 1, sizeof(netInfo.WiFiSSID)));
// Enforce that netInfo wifiSSID is null terminated
netInfo.WiFiSSID[kMaxWiFiSSIDLength] = '\0';
if (includeCredentials)
{
static_assert(sizeof(netInfo.WiFiKey) < 255, "Our min might not fit in netInfo.WiFiKeyLen");
netInfo.WiFiKeyLen = static_cast<uint8_t>(min(strlen((char *) stationConfig.sta.password), sizeof(netInfo.WiFiKey)));
memcpy(netInfo.WiFiKey, stationConfig.sta.password, netInfo.WiFiKeyLen);
}
return CHIP_NO_ERROR;
}
CHIP_ERROR ESP32Utils::SetWiFiStationProvision(const Internal::DeviceNetworkInfo & netInfo)
{
wifi_config_t wifiConfig;
char wifiSSID[kMaxWiFiSSIDLength + 1];
size_t netInfoSSIDLen = strlen(netInfo.WiFiSSID);
// Ensure that ESP station mode is enabled. This is required before esp_wifi_set_config(ESP_IF_WIFI_STA,...)
// can be called.
ReturnErrorOnFailure(ESP32Utils::EnableStationMode());
// Enforce that wifiSSID is null terminated before copying it
memcpy(wifiSSID, netInfo.WiFiSSID, min(netInfoSSIDLen + 1, sizeof(wifiSSID)));
if (netInfoSSIDLen + 1 < sizeof(wifiSSID))
{
wifiSSID[netInfoSSIDLen] = '\0';
}
else
{
wifiSSID[kMaxWiFiSSIDLength] = '\0';
}
// Initialize an ESP wifi_config_t structure based on the new provision information.
memset(&wifiConfig, 0, sizeof(wifiConfig));
memcpy(wifiConfig.sta.ssid, wifiSSID, min(strlen(wifiSSID) + 1, sizeof(wifiConfig.sta.ssid)));
memcpy(wifiConfig.sta.password, netInfo.WiFiKey, min((size_t) netInfo.WiFiKeyLen, sizeof(wifiConfig.sta.password)));
wifiConfig.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
wifiConfig.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL;
// Configure the ESP WiFi interface.
esp_err_t err = esp_wifi_set_config(WIFI_IF_STA, &wifiConfig);
if (err != ESP_OK)
{
ChipLogError(DeviceLayer, "esp_wifi_set_config() failed: %s", esp_err_to_name(err));
return ESP32Utils::MapError(err);
}
ChipLogProgress(DeviceLayer, "WiFi station provision set (SSID: %s)", netInfo.WiFiSSID);
return CHIP_NO_ERROR;
}
CHIP_ERROR ESP32Utils::ClearWiFiStationProvision(void)
{
wifi_config_t stationConfig;
// Clear the ESP WiFi station configuration.
memset(&stationConfig, 0, sizeof(stationConfig));
esp_wifi_set_config(WIFI_IF_STA, &stationConfig);
return CHIP_NO_ERROR;
}
CHIP_ERROR ESP32Utils::MapError(esp_err_t error)
{
if (error == ESP_OK)
{
return CHIP_NO_ERROR;
}
if (error == ESP_ERR_NVS_NOT_FOUND)
{
return CHIP_ERROR_PERSISTED_STORAGE_VALUE_NOT_FOUND;
}
if (error == ESP_ERR_NVS_INVALID_LENGTH)
{
return CHIP_ERROR_BUFFER_TOO_SMALL;
}
return CHIP_ERROR(ChipError::Range::kPlatform, error);
}
/**
* Given a CHIP error value that represents an ESP32 error, returns a
* human-readable NULL-terminated C string describing the error.
*
* @param[in] buf Buffer into which the error string will be placed.
* @param[in] bufSize Size of the supplied buffer in bytes.
* @param[in] err The error to be described.
*
* @return true If a description string was written into the supplied buffer.
* @return false If the supplied error was not an ESP32 error.
*
*/
bool ESP32Utils::FormatError(char * buf, uint16_t bufSize, CHIP_ERROR err)
{
if (!err.IsRange(ChipError::Range::kPlatform))
{
return false;
}
#if CHIP_CONFIG_SHORT_ERROR_STR
const char * desc = NULL;
#else // CHIP_CONFIG_SHORT_ERROR_STR
const char * desc = esp_err_to_name((esp_err_t) err.GetValue());
#endif // CHIP_CONFIG_SHORT_ERROR_STR
chip::FormatError(buf, bufSize, "ESP32", err, desc);
return true;
}
/**
* Register a text error formatter for ESP32 errors.
*/
void ESP32Utils::RegisterESP32ErrorFormatter()
{
static ErrorFormatter sErrorFormatter = { ESP32Utils::FormatError, NULL };
RegisterErrorFormatter(&sErrorFormatter);
}