Browse Source

ci(tsens): add new test tsens phy coexist

Chen Yudong 2 năm trước cách đây
mục cha
commit
fe1b03308e

+ 82 - 0
.gitlab/ci/rules.yml

@@ -252,6 +252,10 @@
   - "components/app_update/**/*"
   - "components/esp_https_ota/**/*"
 
+# for jobs: custom_test*wifi*
+.patterns-custom_test-wifi: &patterns-custom_test-wifi
+  - "tools/test_apps/phy/**/*"
+
 # for jobs: example_test_pytest_sdio related
 .patterns-example_test-sdio: &patterns-example_test-sdio
   - "components/hal/sdio*.c"
@@ -967,6 +971,8 @@
       changes: *patterns-build_system
     - <<: *if-dev-push
       changes: *patterns-custom_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
     - <<: *if-dev-push
       changes: *patterns-downloadable-tools
     - <<: *if-dev-push
@@ -993,6 +999,8 @@
       changes: *patterns-build_system
     - <<: *if-dev-push
       changes: *patterns-custom_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
     - <<: *if-dev-push
       changes: *patterns-downloadable-tools
     - <<: *if-dev-push
@@ -1019,6 +1027,8 @@
       changes: *patterns-build_system
     - <<: *if-dev-push
       changes: *patterns-custom_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
     - <<: *if-dev-push
       changes: *patterns-downloadable-tools
     - <<: *if-dev-push
@@ -1045,6 +1055,8 @@
       changes: *patterns-build_system
     - <<: *if-dev-push
       changes: *patterns-custom_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
     - <<: *if-dev-push
       changes: *patterns-downloadable-tools
     - <<: *if-dev-push
@@ -1071,6 +1083,8 @@
       changes: *patterns-build_system
     - <<: *if-dev-push
       changes: *patterns-custom_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
     - <<: *if-dev-push
       changes: *patterns-downloadable-tools
     - <<: *if-dev-push
@@ -1097,6 +1111,8 @@
       changes: *patterns-build_system
     - <<: *if-dev-push
       changes: *patterns-custom_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
     - <<: *if-dev-push
       changes: *patterns-downloadable-tools
     - <<: *if-dev-push
@@ -1123,6 +1139,8 @@
       changes: *patterns-build_system
     - <<: *if-dev-push
       changes: *patterns-custom_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
     - <<: *if-dev-push
       changes: *patterns-downloadable-tools
     - <<: *if-dev-push
@@ -1149,6 +1167,8 @@
       changes: *patterns-build_system
     - <<: *if-dev-push
       changes: *patterns-custom_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
     - <<: *if-dev-push
       changes: *patterns-downloadable-tools
     - <<: *if-dev-push
@@ -1572,6 +1592,8 @@
       changes: *patterns-component_ut-usb
     - <<: *if-dev-push
       changes: *patterns-custom_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
     - <<: *if-dev-push
       changes: *patterns-downloadable-tools
     - <<: *if-dev-push
@@ -2287,6 +2309,21 @@
     - <<: *if-dev-push
       changes: *patterns-custom_test
 
+.rules:test:custom_test-esp32c2-wifi:
+  rules:
+    - <<: *if-revert-branch
+      when: never
+    - <<: *if-protected
+    - <<: *if-label-build-only
+      when: never
+    - <<: *if-label-custom_test
+    - <<: *if-label-custom_test_esp32c2
+    - <<: *if-label-target_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
+    - <<: *if-dev-push
+      changes: *patterns-target_test-wifi
+
 .rules:test:custom_test-esp32c3:
   rules:
     - <<: *if-revert-branch
@@ -2300,6 +2337,21 @@
     - <<: *if-dev-push
       changes: *patterns-custom_test
 
+.rules:test:custom_test-esp32c3-wifi:
+  rules:
+    - <<: *if-revert-branch
+      when: never
+    - <<: *if-protected
+    - <<: *if-label-build-only
+      when: never
+    - <<: *if-label-custom_test
+    - <<: *if-label-custom_test_esp32c3
+    - <<: *if-label-target_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
+    - <<: *if-dev-push
+      changes: *patterns-target_test-wifi
+
 .rules:test:custom_test-esp32c6:
   rules:
     - <<: *if-revert-branch
@@ -2339,6 +2391,21 @@
     - <<: *if-dev-push
       changes: *patterns-custom_test
 
+.rules:test:custom_test-esp32s2-wifi:
+  rules:
+    - <<: *if-revert-branch
+      when: never
+    - <<: *if-protected
+    - <<: *if-label-build-only
+      when: never
+    - <<: *if-label-custom_test
+    - <<: *if-label-custom_test_esp32s2
+    - <<: *if-label-target_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
+    - <<: *if-dev-push
+      changes: *patterns-target_test-wifi
+
 .rules:test:custom_test-esp32s3:
   rules:
     - <<: *if-revert-branch
@@ -2352,6 +2419,21 @@
     - <<: *if-dev-push
       changes: *patterns-custom_test
 
+.rules:test:custom_test-esp32s3-wifi:
+  rules:
+    - <<: *if-revert-branch
+      when: never
+    - <<: *if-protected
+    - <<: *if-label-build-only
+      when: never
+    - <<: *if-label-custom_test
+    - <<: *if-label-custom_test_esp32s3
+    - <<: *if-label-target_test
+    - <<: *if-dev-push
+      changes: *patterns-custom_test-wifi
+    - <<: *if-dev-push
+      changes: *patterns-target_test-wifi
+
 .rules:test:example_test-esp32:
   rules:
     - <<: *if-revert-branch

+ 32 - 0
.gitlab/ci/target-test.yml

@@ -1187,6 +1187,38 @@ pytest_test_apps_esp32s3_mspi_f4r4:
     - build_pytest_test_apps_esp32s3
   tags: [ esp32s3, MSPI_F4R4 ]
 
+pytest_test_apps_esp32s2_wifi_two_dut:
+  extends:
+    - .pytest_test_apps_dir_template
+    - .rules:test:custom_test-esp32s2-wifi
+  needs:
+    - build_pytest_test_apps_esp32s2
+  tags: [ esp32s2, wifi_two_dut ]
+
+pytest_test_apps_esp32s3_wifi_two_dut:
+  extends:
+    - .pytest_test_apps_dir_template
+    - .rules:test:custom_test-esp32s3-wifi
+  needs:
+    - build_pytest_test_apps_esp32s3
+  tags: [ esp32s3, wifi_two_dut ]
+
+pytest_test_apps_esp32c2_wifi_two_dut:
+  extends:
+    - .pytest_test_apps_dir_template
+    - .rules:test:custom_test-esp32c2-wifi
+  needs:
+    - build_pytest_test_apps_esp32c2
+  tags: [ esp32c2, wifi_two_dut, xtal_26mhz ]
+
+pytest_test_apps_esp32c3_wifi_two_dut:
+  extends:
+    - .pytest_test_apps_dir_template
+    - .rules:test:custom_test-esp32c3-wifi
+  needs:
+    - build_pytest_test_apps_esp32c3
+  tags: [ esp32c3, wifi_two_dut]
+
 # for parallel jobs, CI_JOB_NAME will be "job_name index/total" (for example, "IT_001 1/2")
 # we need to convert to pattern "job_name_index.yml"
 .define_config_file_name: &define_config_file_name |

+ 10 - 0
tools/test_apps/.build-test-rules.yml

@@ -41,6 +41,16 @@ tools/test_apps/phy/phy_multi_init_data_test:
   disable:
     - if: SOC_WIFI_SUPPORTED != 1
 
+tools/test_apps/phy/phy_tsens:
+  disable:
+    - if: SOC_WIFI_SUPPORTED != 1 or SOC_TEMP_SENSOR_SUPPORTED != 1
+  depends_components:
+    - hal
+    - driver
+    - esp_phy
+    - esp_hw_support
+    - esp_wifi
+
 tools/test_apps/protocols/esp_netif/build_config:
   enable:
     - if: IDF_TARGET in ["esp32", "esp32c2"]

+ 6 - 0
tools/test_apps/phy/phy_tsens/CMakeLists.txt

@@ -0,0 +1,6 @@
+#This is the project CMakeLists.txt file for the test subproject
+cmake_minimum_required(VERSION 3.16)
+
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+
+project(phy_tsens)

+ 2 - 0
tools/test_apps/phy/phy_tsens/README.md

@@ -0,0 +1,2 @@
+| Supported Targets | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
+| ----------------- | -------- | -------- | -------- | -------- | -------- |

+ 1 - 0
tools/test_apps/phy/phy_tsens/main/CMakeLists.txt

@@ -0,0 +1 @@
+idf_component_register(SRC_DIRS .)

+ 68 - 0
tools/test_apps/phy/phy_tsens/main/app_main.c

@@ -0,0 +1,68 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Unlicense OR CC0-1.0
+ */
+#include "esp_log.h"
+#include "esp_console.h"
+#include "esp_event.h"
+#include "nvs_flash.h"
+#include "esp_netif.h"
+#include "esp_wifi.h"
+
+
+extern void register_tsens_cmd(void);
+extern void register_wifi_cmd(void);
+
+
+static int restart(int argc, char **argv)
+{
+    ESP_LOGI("main", "Restarting");
+    esp_restart();
+}
+
+static void register_restart(void)
+{
+    const esp_console_cmd_t cmd = {
+        .command = "restart",
+        .help = "Restart the program",
+        .hint = NULL,
+        .func = &restart,
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
+}
+
+static void initialize_nvs(void)
+{
+    esp_err_t err = nvs_flash_init();
+    if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
+        ESP_ERROR_CHECK( nvs_flash_erase() );
+        err = nvs_flash_init();
+    }
+    ESP_ERROR_CHECK(err);
+}
+
+
+void app_main(void)
+{
+    esp_console_repl_t *repl = NULL;
+    esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
+    repl_config.prompt = "esp>";
+    repl_config.max_cmdline_length = 256;
+
+    initialize_nvs();
+    ESP_ERROR_CHECK(esp_netif_init());
+    ESP_ERROR_CHECK(esp_event_loop_create_default());
+    esp_netif_create_default_wifi_sta();
+    esp_netif_create_default_wifi_ap();
+    /* Register commands */
+    esp_console_register_help_command();
+    register_restart();
+    register_tsens_cmd();
+    register_wifi_cmd();
+
+    esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));
+
+    ESP_ERROR_CHECK(esp_console_start_repl(repl));
+}

+ 111 - 0
tools/test_apps/phy/phy_tsens/main/tsens_cmd.c

@@ -0,0 +1,111 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "esp_log.h"
+#include "esp_console.h"
+#include "linenoise/linenoise.h"
+#include "argtable3/argtable3.h"
+#include "driver/temperature_sensor.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+
+const static char* TAG = "TSENS";
+static temperature_sensor_handle_t s_temp_sensor = NULL;
+
+static int tsens_init(int argc, char **argv)
+{
+    ESP_LOGI(TAG, "Install temperature sensor, expected temp ranger range: 10~50 ℃");
+    temperature_sensor_config_t temp_sensor_config = TEMPERATURE_SENSOR_CONFIG_DEFAULT(10, 50);
+    ESP_ERROR_CHECK(temperature_sensor_install(&temp_sensor_config, &s_temp_sensor));
+    ESP_LOGI(TAG, "Enable temperature sensor");
+    ESP_ERROR_CHECK(temperature_sensor_enable(s_temp_sensor));
+    return 0;
+}
+
+static void register_tsens_init(void)
+{
+    const esp_console_cmd_t cmd = {
+        .command = "tsens_init",
+        .help = "Initialize temperature sensor",
+        .hint = NULL,
+        .func = &tsens_init,
+        .argtable = NULL
+    };
+    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
+}
+
+typedef struct {
+    struct arg_int *count;
+    struct arg_int *interval_ms;
+    struct arg_end *end;
+} tsens_read_args_t;
+static tsens_read_args_t tsens_args;
+
+typedef struct {
+    int count;
+    int interval_ms;
+} tsens_read_param_t;
+
+static void tsens_read_task(void *arg)
+{
+    tsens_read_param_t *param = (tsens_read_param_t*) arg;
+    int count = param->count;
+    int interval_ms = param->interval_ms;
+    free(arg);
+    float tsens_value;
+    while (count-- > 0) {
+        vTaskDelay(pdMS_TO_TICKS(interval_ms));
+        ESP_ERROR_CHECK(temperature_sensor_get_celsius(s_temp_sensor, &tsens_value));
+        ESP_LOGI(TAG, "Temperature value %.02f ℃", tsens_value);
+    }
+    vTaskDelete(NULL);
+}
+
+static int do_tsens_read(int argc, char **argv)
+{
+    int nerrors = arg_parse(argc, argv, (void **) &tsens_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, tsens_args.end, argv[0]);
+        return 1;
+    }
+
+    tsens_read_param_t* params = (tsens_read_param_t*)malloc(sizeof(tsens_read_param_t));
+    params->count = 1;
+    params->interval_ms = 100;
+
+    if (tsens_args.count->count > 0) {
+        params->count = tsens_args.count->ival[0];
+    }
+    if (tsens_args.interval_ms->count > 0) {
+        params->interval_ms = tsens_args.interval_ms->ival[0];
+    }
+    xTaskCreate(tsens_read_task, "tsens_read_task", 4096, (void*)params, 1, NULL);
+
+    return 0;
+}
+
+static void register_tsens_read(void)
+{
+    tsens_args.count = arg_int0("c", "count", "<count>", "read count");
+    tsens_args.interval_ms = arg_int0("i", "interval", "<interval>", "read interval in ms");
+    tsens_args.end = arg_end(2);
+    const esp_console_cmd_t cmd = {
+        .command = "tsens_read",
+        .help = "Temperature sensor read",
+        .hint = NULL,
+        .func = &do_tsens_read,
+        .argtable = &tsens_args
+    };
+    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
+}
+
+void register_tsens_cmd(void)
+{
+    register_tsens_init();
+    register_tsens_read();
+}

+ 268 - 0
tools/test_apps/phy/phy_tsens/main/wifi_cmd.c

@@ -0,0 +1,268 @@
+/*
+ * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "esp_log.h"
+#include "esp_wifi.h"
+#include "esp_console.h"
+#include "linenoise/linenoise.h"
+#include "argtable3/argtable3.h"
+#include "esp_pm.h"
+#include "esp_private/esp_clk.h"
+
+#define MAX_RECONNECT_TIMES (5)
+
+const static char *TAG = "WIFI";
+static int s_reconnect_times = 0;
+
+
+static void wifi_event_any_handler(void* arg, esp_event_base_t event_base,
+                                int32_t event_id, void* event_data)
+{
+    ESP_LOGI(TAG, "WiFi event: 0x%"PRIx32, event_id);
+}
+
+static void wifi_event_disconnected_handler(void* arg, esp_event_base_t event_base,
+                                int32_t event_id, void* event_data)
+{
+    ESP_LOGI(TAG, "WIFI_EVENT_STA_DISCONNECTED");
+    if (s_reconnect_times++ < MAX_RECONNECT_TIMES) {
+        esp_wifi_connect();
+    } else {
+        ESP_LOGE(TAG, "MAX RECONNECT TIME, STOP!");
+    }
+}
+
+static void wifi_event_connected_handler(void* arg, esp_event_base_t event_base,
+                                int32_t event_id, void* event_data)
+{
+    ESP_LOGI(TAG, "WIFI_EVENT_STA_CONNECTED");
+    s_reconnect_times = 0;
+}
+
+
+static void ip_event_got_ip_handler(void* arg, esp_event_base_t event_base,
+                                int32_t event_id, void* event_data)
+{
+    ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
+    ESP_LOGI(TAG, "STA_GOT_IP:" IPSTR, IP2STR(&event->ip_info.ip));
+}
+
+static int initialize_wifi(int argc, char **argv)
+{
+    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
+    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
+
+    esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_any_handler, NULL);
+    esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, wifi_event_disconnected_handler, NULL);
+    esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_CONNECTED, wifi_event_connected_handler, NULL);
+    esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, ip_event_got_ip_handler, NULL);
+
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
+    ESP_ERROR_CHECK(esp_wifi_start() );
+    /* always enable wifi sleep */
+    ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_MIN_MODEM));
+    ESP_LOGI(TAG, "initialize_wifi DONE.");
+    return 0;
+}
+
+static void register_wifi_init(void)
+{
+    const esp_console_cmd_t cmd = {
+        .command = "wifi_init",
+        .help = "Initialize WiFi",
+        .hint = NULL,
+        .func = &initialize_wifi,
+        .argtable = NULL
+    };
+    ESP_ERROR_CHECK(esp_console_cmd_register(&cmd));
+}
+
+typedef struct {
+    struct arg_str *ssid;
+    struct arg_str *password;
+    struct arg_int *authmode;
+    struct arg_int *channel;
+    struct arg_int *max_conn;
+    struct arg_end *end;
+} ap_set_args_t;
+static ap_set_args_t ap_set_args;
+
+static int cmd_do_ap_set(int argc, char **argv)
+{
+    int nerrors = arg_parse(argc, argv, (void **) &ap_set_args);
+
+    if (nerrors != 0) {
+        arg_print_errors(stderr, ap_set_args.end, argv[0]);
+        return 1;
+    }
+
+    wifi_config_t wifi_config = {};
+
+    const char *ssid = ap_set_args.ssid->sval[0];
+    strncpy((char *) wifi_config.ap.ssid, ssid, sizeof(wifi_config.ap.ssid));
+    const char *pass = ap_set_args.password->sval[0];
+    if (ap_set_args.password->count > 0) {
+        strncpy((char *) wifi_config.ap.password, pass, sizeof(wifi_config.ap.password));
+        wifi_config.ap.authmode = WIFI_AUTH_WPA2_PSK; // set default auth mode
+    }
+    if (ap_set_args.channel->count > 0) {
+        wifi_config.sta.channel = (uint8_t)(ap_set_args.channel->ival[0]);
+    }
+    if (ap_set_args.authmode->count > 0) {
+        wifi_config.ap.authmode = ap_set_args.authmode->ival[0];
+    }
+    if (ap_set_args.max_conn->count > 0) {
+        wifi_config.ap.max_connection = ap_set_args.max_conn->ival[0];
+    } else {
+        wifi_config.ap.max_connection = 2;
+    }
+
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP) );
+    esp_err_t err = esp_wifi_set_config(WIFI_IF_AP, &wifi_config);
+    if (err == ESP_OK) {
+        ESP_LOGI(TAG, "set ap config OK.");
+    } else {
+        ESP_LOGE(TAG, "set ap config Failed! (%s)", esp_err_to_name(err));
+    }
+    return 0;
+}
+
+
+static void register_ap_set(void)
+{
+    ap_set_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
+    ap_set_args.password = arg_str0(NULL, NULL, "<pass>", "password of AP");
+    ap_set_args.authmode = arg_int0("a", "authmode", "<authmode>", "wifi auth type (ie. open | wep| wpa2 | wpa2_enterprise)");
+    ap_set_args.channel = arg_int0("n", "channel", "<channel>", "channel of AP");
+    ap_set_args.max_conn = arg_int0("m", "max_conn", "<max_conn>", "Max station number, default: 2");
+    ap_set_args.end = arg_end(2);
+    const esp_console_cmd_t cmd = {
+        .command = "ap_set",
+        .help = "WiFi is ap mode, set ap config.",
+        .hint = NULL,
+        .func = &cmd_do_ap_set,
+        .argtable = &ap_set_args
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
+
+}
+
+typedef struct {
+    struct arg_str *ssid;
+    struct arg_str *password;
+    struct arg_int *channel;
+    struct arg_end *end;
+} sta_connect_args_t;
+static sta_connect_args_t connect_args;
+
+
+static int cmd_do_sta_connect(int argc, char **argv)
+{
+    int nerrors = arg_parse(argc, argv, (void **) &connect_args);
+
+    if (nerrors != 0) {
+        arg_print_errors(stderr, connect_args.end, argv[0]);
+        return 1;
+    }
+
+    wifi_config_t wifi_config = {
+        .sta = {
+            .scan_method = WIFI_ALL_CHANNEL_SCAN,
+            .sort_method = WIFI_CONNECT_AP_BY_SIGNAL,
+        },
+    };
+
+    const char *ssid = connect_args.ssid->sval[0];
+    memcpy((char *) wifi_config.sta.ssid, ssid, sizeof(wifi_config.sta.ssid));
+    const char *pass = connect_args.password->sval[0];
+    if (connect_args.password->count > 0) {
+        memcpy((char *) wifi_config.sta.password, pass, sizeof(wifi_config.sta.password));
+        wifi_config.sta.threshold.authmode = WIFI_AUTH_WEP;
+    }
+    if (connect_args.channel->count > 0) {
+        wifi_config.sta.channel = (uint8_t)(connect_args.channel->ival[0]);
+    }
+
+    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
+    ESP_LOGI(TAG, "Connecting to %s...", ssid);
+    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
+    s_reconnect_times = 0;
+    esp_err_t err = esp_wifi_connect();
+    ESP_LOGI(TAG, "WIFI_CONNECT_START, ret: 0x%x", err);
+    return 0;
+}
+
+static void register_sta_connect(void)
+{
+    connect_args.ssid = arg_str1(NULL, NULL, "<ssid>", "SSID of AP");
+    connect_args.password = arg_str0(NULL, NULL, "<pass>", "password of AP");
+    connect_args.channel = arg_int0("n", "channel", "<channel>", "channel of AP");
+    connect_args.end = arg_end(2);
+    const esp_console_cmd_t cmd = {
+        .command = "sta_connect",
+        .help = "WiFi is station mode, join specified soft-AP",
+        .hint = NULL,
+        .func = &cmd_do_sta_connect,
+        .argtable = &connect_args
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
+}
+
+
+typedef struct {
+    struct arg_str *type;
+    struct arg_end *end;
+} light_sleep_args_t;
+static light_sleep_args_t sleep_args;
+
+static int cmd_do_light_sleep(int argc, char **argv)
+{
+    int nerrors = arg_parse(argc, argv, (void **) &sleep_args);
+    if (nerrors != 0) {
+        arg_print_errors(stderr, sleep_args.end, argv[0]);
+        return 1;
+    }
+    const char *sleep_type = sleep_args.type->sval[0];
+    if (!strcmp(sleep_type, "enable")) {
+        esp_pm_config_t pm_config = {
+            .max_freq_mhz = CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ,
+            .min_freq_mhz = esp_clk_xtal_freq() / 1000000,
+            .light_sleep_enable = true,
+        };
+        ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
+        ESP_LOGI(TAG, "LIGHT_SLEEP_ENABLED,OK");
+    } else {
+        ESP_LOGE(TAG, "invaild arg!");
+        return 1;
+    }
+
+    return 0;
+}
+
+
+static void register_light_sleep(void)
+{
+    sleep_args.type = arg_str1(NULL, NULL, "<type>", "light sleep mode: enable");
+    sleep_args.end = arg_end(2);
+    const esp_console_cmd_t cmd = {
+        .command = "light_sleep",
+        .help = "Config light sleep mode",
+        .hint = NULL,
+        .func = &cmd_do_light_sleep,
+        .argtable = &sleep_args,
+    };
+    ESP_ERROR_CHECK( esp_console_cmd_register(&cmd) );
+}
+
+extern void register_wifi_cmd(void)
+{
+    register_wifi_init();
+    register_ap_set();
+    register_sta_connect();
+    register_light_sleep();
+}

+ 192 - 0
tools/test_apps/phy/phy_tsens/pytest_phy_tsens.py

@@ -0,0 +1,192 @@
+# SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
+# SPDX-License-Identifier: Apache-2.0
+
+import random
+import string
+from typing import Tuple
+
+import pexpect
+import pytest
+from pytest_embedded import Dut
+
+
+def run_phy_tsens_test(dut: Tuple[Dut, Dut]) -> None:
+    ap_dut = dut[0]
+    sta_dut = dut[1]
+
+    ap_dut.write('tsens_init')
+    sta_dut.write('tsens_init')
+    ap_dut.expect('Enable temperature sensor')
+    sta_dut.expect('Enable temperature sensor')
+
+    ap_dut.write('wifi_init')
+    sta_dut.write('wifi_init')
+    ap_dut.expect('initialize_wifi DONE')
+    sta_dut.expect('initialize_wifi DONE')
+
+    # check tsens read after wifi_init
+    for _dut in [ap_dut, sta_dut]:
+        _dut.write('tsens_read')
+        temp_val = _dut.expect(r'Temperature value ([\d.]+)[^\d.]')[1].decode()
+        assert 0 < float(temp_val) < 70
+        _dut.write('tsens_read')
+        temp_val2 = _dut.expect(r'Temperature value ([\d.]+)[^\d.]')[1].decode()
+        assert -5 < float(temp_val) - float(temp_val2) < 5
+
+    # start tsens read background task
+    for _dut in [ap_dut, sta_dut]:
+        _dut.write('tsens_read -c 100 -i 1000')  # 1000ms * 100
+        temp_val = _dut.expect(r'Temperature value ([\d.]+)[^\d.]')[1].decode()
+        assert 0 < float(temp_val) < 70
+
+    # check wifi connect
+    ssid = ''.join([random.choice(string.ascii_lowercase) for i in range(10)])
+    password = ''.join([random.choice(string.ascii_lowercase) for i in range(10)])
+    ap_dut.write(f'ap_set {ssid} {password}')
+    ap_dut.expect('set ap config OK')
+    sta_dut.write(f'sta_connect {ssid} {password}')
+    sta_dut.expect('STA_GOT_IP')
+
+    data = sta_dut.expect(pexpect.TIMEOUT, timeout=10).decode()
+    assert 'DISCONNECT' not in data
+    ap_dut.expect(pexpect.TIMEOUT, timeout=0.1)  # clear data
+
+    # check tsens read again after wifi connected
+    for _dut in [ap_dut, sta_dut]:
+        temp_val = _dut.expect(r'Temperature value ([\d.]+)[^\d.]')[1].decode()
+        assert 0 < float(temp_val) < 70
+
+
+def run_phy_tsens_test_init_wifi_first(dut: Tuple[Dut, Dut]) -> None:
+    ap_dut = dut[0]
+    sta_dut = dut[1]
+
+    ap_dut.write('tsens_init')
+    sta_dut.write('tsens_init')
+    ap_dut.expect('Enable temperature sensor')
+    sta_dut.expect('Enable temperature sensor')
+
+    ap_dut.write('wifi_init')
+    sta_dut.write('wifi_init')
+    ap_dut.expect('initialize_wifi DONE')
+    sta_dut.expect('initialize_wifi DONE')
+
+    # check tsens read after wifi_init
+    for _dut in [ap_dut, sta_dut]:
+        _dut.write('tsens_read')
+        temp_val = _dut.expect(r'Temperature value ([\d.]+)[^\d.]')[1].decode()
+        assert 0 < float(temp_val) < 70
+        _dut.write('tsens_read')
+        temp_val2 = _dut.expect(r'Temperature value ([\d.]+)[^\d.]')[1].decode()
+        assert -5 < float(temp_val) - float(temp_val2) < 5
+
+    # start tsens read background task
+    for _dut in [ap_dut, sta_dut]:
+        _dut.write('tsens_read -c 100 -i 1000')  # 1000ms * 100
+        temp_val = _dut.expect(r'Temperature value ([\d.]+)[^\d.]')[1].decode()
+        assert 0 < float(temp_val) < 70
+
+    # check wifi connect
+    ssid = ''.join([random.choice(string.ascii_lowercase) for i in range(10)])
+    password = ''.join([random.choice(string.ascii_lowercase) for i in range(10)])
+    ap_dut.write(f'ap_set {ssid} {password}')
+    ap_dut.expect('set ap config OK')
+    sta_dut.write(f'sta_connect {ssid} {password}')
+    sta_dut.expect('STA_GOT_IP')
+
+    data = sta_dut.expect(pexpect.TIMEOUT, timeout=10).decode()
+    assert 'DISCONNECT' not in data
+    ap_dut.expect(pexpect.TIMEOUT, timeout=0.1)  # clear data
+
+    # check tsens read again after wifi connected
+    for _dut in [ap_dut, sta_dut]:
+        temp_val = _dut.expect(r'Temperature value ([\d.]+)[^\d.]')[1].decode()
+        assert 0 < float(temp_val) < 70
+
+
+def run_phy_tsens_test_with_light_sleep(dut: Tuple[Dut, Dut]) -> None:
+    ap_dut = dut[0]
+    sta_dut = dut[1]
+
+    ap_dut.write('wifi_init')
+    sta_dut.write('wifi_init')
+    ap_dut.expect('initialize_wifi DONE')
+    sta_dut.expect('initialize_wifi DONE')
+
+    ap_dut.write('tsens_init')
+    sta_dut.write('tsens_init')
+    ap_dut.expect('Enable temperature sensor')
+    sta_dut.expect('Enable temperature sensor')
+
+    # start tsens read background task
+    for _dut in [ap_dut, sta_dut]:
+        _dut.write('tsens_read -c 100 -i 1000')  # 1000ms * 100
+        temp_val = _dut.expect(r'Temperature value ([\d.]+)[^\d.]')[1].decode()
+        assert 0 < float(temp_val) < 70
+
+    # check wifi connect
+    ssid = ''.join([random.choice(string.ascii_lowercase) for i in range(10)])
+    password = ''.join([random.choice(string.ascii_lowercase) for i in range(10)])
+    ap_dut.write(f'ap_set {ssid} {password}')
+    ap_dut.expect('set ap config OK')
+    sta_dut.write(f'sta_connect {ssid} {password}')
+    sta_dut.expect('STA_GOT_IP')
+
+    sta_dut.write('light_sleep enable')
+    data = sta_dut.expect(pexpect.TIMEOUT, timeout=30).decode(errors='ignore')
+    assert 'DISCONNECT' not in data
+    assert 'LIGHT_SLEEP_ENABLED,OK' in data
+    ap_dut.expect(pexpect.TIMEOUT, timeout=0.1)  # clear data
+
+    # check tsens read again after wifi connected
+    for _dut in [ap_dut, sta_dut]:
+        temp_val = _dut.expect(r'Temperature value ([\d.]+)[^\d.]')[1].decode()
+        assert 0 < float(temp_val) < 70
+
+
+@pytest.mark.esp32c3
+@pytest.mark.esp32c6
+@pytest.mark.esp32s2
+@pytest.mark.esp32s3
+@pytest.mark.wifi_two_dut
+@pytest.mark.parametrize('count', [2], indirect=True)
+def test_phy_tsens_coexist(dut: Tuple[Dut, Dut]) -> None:
+    for _dut in dut:
+        _dut.expect('esp>')
+    run_phy_tsens_test(dut)
+    for _dut in dut:
+        _dut.write('restart')
+        _dut.expect('boot:')
+        _dut.expect('esp>')
+    run_phy_tsens_test_init_wifi_first(dut)
+    for _dut in dut:
+        _dut.write('restart')
+        _dut.expect('boot:')
+        _dut.expect('esp>')
+    run_phy_tsens_test_with_light_sleep(dut)
+
+
+@pytest.mark.esp32c2
+@pytest.mark.wifi_two_dut
+@pytest.mark.xtal_26mhz
+@pytest.mark.parametrize(
+    'count, config, baud',
+    [
+        (2, 'c2_xtal26m', '74880'),
+    ],
+    indirect=True,
+)
+def test_phy_tsens_coexist_c2_xtal26m(dut: Tuple[Dut, Dut]) -> None:
+    for _dut in dut:
+        _dut.expect('esp>')
+    run_phy_tsens_test(dut)
+    for _dut in dut:
+        _dut.write('restart')
+        _dut.expect('boot:')
+        _dut.expect('esp>')
+    run_phy_tsens_test_init_wifi_first(dut)
+    for _dut in dut:
+        _dut.write('restart')
+        _dut.expect('boot:')
+        _dut.expect('esp>')
+    run_phy_tsens_test_with_light_sleep(dut)

+ 0 - 0
tools/test_apps/phy/phy_tsens/sdkconfig.ci


+ 2 - 0
tools/test_apps/phy/phy_tsens/sdkconfig.ci.c2_xtal26m

@@ -0,0 +1,2 @@
+CONFIG_IDF_TARGET="esp32c2"
+CONFIG_XTAL_FREQ_26=y

+ 4 - 0
tools/test_apps/phy/phy_tsens/sdkconfig.defaults

@@ -0,0 +1,4 @@
+CONFIG_PM_ENABLE=y
+CONFIG_PM_DFS_INIT_AUTO=y
+CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
+CONFIG_FREERTOS_HZ=1000