feat: Initial esp32-hacking project with firmware sources and docs
This commit is contained in:
78
get-started/README.md
Normal file
78
get-started/README.md
Normal file
@@ -0,0 +1,78 @@
|
||||
# Get Started Examples
|
||||
[[中文]](./README_cn.md)
|
||||
|
||||
This example demonstrates how to obtain CSI data through communication between two espressif chips, and uses a graphical interface to display real-time data of CSI subcarriers
|
||||
|
||||
## hardware
|
||||
|
||||
You need to prepare two development boards for espressif chips, one as the sender and the other as the receiver
|
||||
|
||||

|
||||
|
||||
1. Use ESP32-C5 / ESP32-C6: ESP32-C5 supports dual-band Wi-Fi communication and is one of the best RF chips available. ESP32-C6 is the best RF chip among the currently released models.
|
||||
2. Use an external antenna: The PCB antenna has poor directivity and is easily interfered with by the motherboard.
|
||||
3. The distance between the two devices should be greater than 1 meter.
|
||||
|
||||
## Binding
|
||||
|
||||
1. Burn the firmware of `csi_recv` and `csi_send` on two development boards respectively
|
||||
|
||||

|
||||
|
||||
```shell
|
||||
# csi_send
|
||||
cd esp-csi/examples/get-started/csi_send
|
||||
idf.py set-target esp32c3
|
||||
idf.py flash -b 921600 -p /dev/ttyUSB0 monitor
|
||||
|
||||
# csi_recv
|
||||
cd esp-csi/examples/get-started/csi_recv
|
||||
idf.py set-target esp32c3
|
||||
idf.py flash -b 921600 -p /dev/ttyUSB1
|
||||
```
|
||||
|
||||
2. Run `csi_data_read_parse.py` in `csi_recv` for data analysis. Please close `idf.py monitor` before running
|
||||
|
||||
```shell
|
||||
cd esp-csi-gitlab/examples/get-started/tools
|
||||
|
||||
# Install python related dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Graphical display
|
||||
python csi_data_read_parse.py -p /dev/ttyUSB1
|
||||
```
|
||||
|
||||
## CSI Data Format
|
||||
|
||||
Taking a line of CSI raw data as an example:
|
||||
|
||||
> type,id,mac,rssi,rate,sig_mode,mcs,bandwidth,smoothing,not_sounding,aggregation,stbc,fec_coding,sgi,noise_floor,ampdu_cnt,channel,secondary_channel,local_timestamp,ant,sig_len,rx_state,len,first_word,data
|
||||
CSI_DATA,0,94:d9:b3:80:8c:81,-30,11,1,6,1,0,1,0,1,0,0,-93,0,13,2,2751923,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,5,0,20,1,20,1,19,0,17,1,16,2,15,2,14,1,12,0,12,-1,12,-3,12,-4,13,-6,15,-7,16,-8,16,-8,16,-8,16,-6,15,-5,15,-4,14,-4,13,-4,12,-4,11,-4,10,-4,9,-5,8,-6,4,-4,8,-9,9,-10,9,-10,10,-11,11,-10,11,-10,12,-9,11,-8,11,-7,10,-6,9,-6,7,-6,6,-7,5,-7,5,-8,5,-9,5,-10,5,-11,5,-11,6,-11,7,-11,8,-11,9,-10,9,-9,8,-8,8,-7,1,-2,0,0,0,0,0,0,0,0]"
|
||||
|
||||
*ESP32-C5 and ESP32-C6 are:*
|
||||
>type,seq,mac,rssi,rate,noise_floor,fft_gain,agc_gain,channel,local_timestamp,sig_len,rx_state,len,first_word,data
|
||||
CSI_DATA,7,1a:00:00:00:00:00,-23,11,-96,32,4,11,372852,47,0,256,0,"[0,0,0,0,0,0,0,0,0,0,0,0,-6,-13,-6,-14,-3,-15,-2,-16,-2,-18,-3,-17,-1,-18,2,-19,0,-21,3,-21,1,-20,4,-21,4,-23,6,-22,7,-21,8,-23,9,-23,10,-21,10,-22,11,-20,12,-19,11,-25,13,-22,12,-23,14,-23,14,-22,14,-21,13,-21,13,-19,14,-22,14,-19,14,-23,16,-22,14,-22,13,-22,14,-21,13,-22,13,-23,13,-23,12,-26,13,-24,13,-24,12,-25,14,-29,13,-26,14,-26,15,-26,13,-27,15,-28,16,-27,14,-30,15,-28,16,-28,18,-27,16,-31,18,-31,19,-31,0,0,0,0,0,0,-29,-23,-28,-21,-30,-18,-26,-20,-30,-21,-25,-23,-26,-21,-25,-22,-26,-19,-22,-22,-24,-19,-22,-20,-24,-20,-24,-18,-23,-18,-22,-18,-25,-17,-23,-18,-21,-18,-21,-17,-24,-14,-22,-16,-21,-14,-22,-15,-21,-19,-23,-16,-22,-17,-23,-13,-23,-16,-25,-15,-21,-17,-22,-15,-21,-17,-23,-16,-20,-16,-21,-20,-21,-19,-21,-19,-19,-20,-17,-20,-18,-20,-16,-21,-15,-21,-15,-20,-13,-21,-11,-20,-10,-20,-11,-19,-9,-20,-8,-22,-6,-19,-7,-20,-4,-19,-2,-18,-2,-18,1,-17,4,-18,0,0,0,0,0,0,0,0,0,0]"
|
||||
|
||||
- **Metadata Fields**: Including type, id, mac, rssi, rate, ... len, first_word, etc.
|
||||
- **CSI Data**: Stored in the last item data array, enclosed in [...]. It contains the channel state information for each subcarrier. For detailed structure, refer to the Long Training Field (LTF) section of the [ESP-WIFI-CSI Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-channel-state-information). For each subcarrier, the imaginary part is stored first, followed by the real part (i.e., [Imaginary part of subcarrier 1, Real part of subcarrier 1, Imaginary part of subcarrier 2, Real part of subcarrier 2, Imaginary part of subcarrier 3, Real part of subcarrier 3, ...]).
|
||||
The order of LTF is: LLTF, HT-LTF, STBC-HT-LTF. Depending on the channel and grouping information, not all 3 LTFs may appear.
|
||||
|
||||
## A&Q
|
||||
|
||||
### 1. `csi_send` prints no memory
|
||||
- **Phenomenon**: The following log appears on the serial port:
|
||||
```shell
|
||||
W (510693) csi_send: <ESP_ERR_ESPNOW_NO_MEM> ESP-NOW send error
|
||||
````
|
||||
- **Reason**: The current channel is congested causing congestion in sending packets, so that the ESP-NOW buffer space is full
|
||||
- **Solution**: Change the Wi-Fi channel or change to a place with a better network environment
|
||||
|
||||
### 2. `csi_data_read_parse.py` Serial port printing exception
|
||||
- **Phenomenon**: The following log appears on the serial port:
|
||||
```shell
|
||||
element number is not equal
|
||||
data is not incomplete
|
||||
````
|
||||
- **Reason**: PYQT takes up a lot of CPU when drawing, causing the PC to be unable to read the serial port buffer queue in time, resulting in data confusion
|
||||
- **Solution**: Advance the baud rate of the serial port
|
||||
83
get-started/README_cn.md
Normal file
83
get-started/README_cn.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Get Started Examples
|
||||
[[English]](./README.md)
|
||||
|
||||
本示例演示了如何通过两个 ESP32 芯片之间的通信获取 CSI 数据,并使用图形界面显示 CSI 子载波的实时数据。
|
||||
|
||||
## 硬件
|
||||
|
||||
您需要准备两块开发板,一块作为发送方,一块作为接收方。
|
||||
|
||||

|
||||
|
||||
为了保证 CSI 的感知效果,请尽量满足以下要求:
|
||||
|
||||
1. 使用 ESP32-C5 / ESP32-C6:ESP32-C5 支持双频WIFI通讯,是目前最好的射频芯片,ESP32-C6是目前已上市芯片中最好的射频芯片。
|
||||
2. 使用外部天线:PCB 天线的方向性较差,容易受到主板干扰。
|
||||
3. 两个设备之间的距离应大于 1 米。
|
||||
|
||||
## 绑定
|
||||
|
||||
1. 分别烧录 `csi_recv` and `csi_send` 到两块 ESP32 上。
|
||||
|
||||

|
||||
|
||||
```shell
|
||||
# csi_send
|
||||
cd esp-csi/examples/get-started/csi_send
|
||||
idf.py set-target esp32c3
|
||||
idf.py flash -b 921600 -p /dev/ttyUSB0 monitor
|
||||
|
||||
# csi_recv
|
||||
cd esp-csi/examples/get-started/csi_recv
|
||||
idf.py set-target esp32c3
|
||||
idf.py flash -b 921600 -p /dev/ttyUSB1
|
||||
```
|
||||
|
||||
2. 在 `csi_recv` 里运行 `csi_data_read_parse.py` 做数据分析。 在运行前请关闭 `idf.py monitor` 。
|
||||
|
||||
```shell
|
||||
cd esp-csi-gitlab/examples/get-started/tools
|
||||
|
||||
# Install python related dependencies
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Graphical display
|
||||
python csi_data_read_parse.py -p /dev/ttyUSB1
|
||||
```
|
||||
|
||||
## CSI 数据格式
|
||||
|
||||
以一行 CSI raw data 为例:
|
||||
|
||||
> type,id,mac,rssi,rate,sig_mode,mcs,bandwidth,smoothing,not_sounding,aggregation,stbc,fec_coding,sgi,noise_floor,ampdu_cnt,channel,secondary_channel,local_timestamp,ant,sig_len,rx_state,len,first_word,data
|
||||
CSI_DATA,0,94:d9:b3:80:8c:81,-30,11,1,6,1,0,1,0,1,0,0,-93,0,13,2,2751923,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,5,0,20,1,20,1,19,0,17,1,16,2,15,2,14,1,12,0,12,-1,12,-3,12,-4,13,-6,15,-7,16,-8,16,-8,16,-8,16,-6,15,-5,15,-4,14,-4,13,-4,12,-4,11,-4,10,-4,9,-5,8,-6,4,-4,8,-9,9,-10,9,-10,10,-11,11,-10,11,-10,12,-9,11,-8,11,-7,10,-6,9,-6,7,-6,6,-7,5,-7,5,-8,5,-9,5,-10,5,-11,5,-11,6,-11,7,-11,8,-11,9,-10,9,-9,8,-8,8,-7,1,-2,0,0,0,0,0,0,0,0]"
|
||||
|
||||
*esp32-c5 和 esp32-c6 为:*
|
||||
>type,seq,mac,rssi,rate,noise_floor,fft_gain,agc_gain,channel,local_timestamp,sig_len,rx_state,len,first_word,data
|
||||
CSI_DATA,7,1a:00:00:00:00:00,-23,11,-96,32,4,11,372852,47,0,256,0,"[0,0,0,0,0,0,0,0,0,0,0,0,-6,-13,-6,-14,-3,-15,-2,-16,-2,-18,-3,-17,-1,-18,2,-19,0,-21,3,-21,1,-20,4,-21,4,-23,6,-22,7,-21,8,-23,9,-23,10,-21,10,-22,11,-20,12,-19,11,-25,13,-22,12,-23,14,-23,14,-22,14,-21,13,-21,13,-19,14,-22,14,-19,14,-23,16,-22,14,-22,13,-22,14,-21,13,-22,13,-23,13,-23,12,-26,13,-24,13,-24,12,-25,14,-29,13,-26,14,-26,15,-26,13,-27,15,-28,16,-27,14,-30,15,-28,16,-28,18,-27,16,-31,18,-31,19,-31,0,0,0,0,0,0,-29,-23,-28,-21,-30,-18,-26,-20,-30,-21,-25,-23,-26,-21,-25,-22,-26,-19,-22,-22,-24,-19,-22,-20,-24,-20,-24,-18,-23,-18,-22,-18,-25,-17,-23,-18,-21,-18,-21,-17,-24,-14,-22,-16,-21,-14,-22,-15,-21,-19,-23,-16,-22,-17,-23,-13,-23,-16,-25,-15,-21,-17,-22,-15,-21,-17,-23,-16,-20,-16,-21,-20,-21,-19,-21,-19,-19,-20,-17,-20,-18,-20,-16,-21,-15,-21,-15,-20,-13,-21,-11,-20,-10,-20,-11,-19,-9,-20,-8,-22,-6,-19,-7,-20,-4,-19,-2,-18,-2,-18,1,-17,4,-18,0,0,0,0,0,0,0,0,0,0]"
|
||||
|
||||
- **元数据字段**:包括 type, id, mac, ... first_word等。
|
||||
- **CSI 数据**:由最后一项 data 数组存储,用 [...] 框起。包含每个子载波的信道状态信息。具体结构可参考 [ESP-WIFI-CSI Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/wifi.html#wi-fi-channel-state-information) 的 Long Training Field (LTF) 部分。对于每一个子载波,先储存虚数部分,再储存实数部分(即:[子载波1的虚部,子载波1的实部,子载波2的虚部,子载波2的实部,子载波3的虚部,子载波3的实部,...])。
|
||||
|
||||
LTF的顺序为:LLTF、HT-LTF、STBC-HT-LTF。根据通道和分组信息,可能不会出现所有3个LTF。
|
||||
he order of LTF is: LLTF, HT-LTF, STBC-HT-LTF. Depending on the channel and grouping information, not all 3 LTFs may appear.
|
||||
|
||||
## A&Q
|
||||
|
||||
### 1. csi_send 打印无内存
|
||||
|
||||
- **现象**:串口上出现以下日志:
|
||||
```shell
|
||||
W (510693) csi_send: ESP-NOW 发送错误
|
||||
```
|
||||
- **原因**:当前信道拥堵,导致发送数据包时出现拥堵,ESP-NOW 缓冲区空间满
|
||||
- **解决方案**:更换 Wi-Fi 信道或更换到网络环境更好的地方
|
||||
### 2. csi_data_read_parse.py 串口打印异常
|
||||
|
||||
- **现象**:串口上出现以下日志:
|
||||
```shell
|
||||
element number is not equal
|
||||
data is not incomplete
|
||||
```
|
||||
- **原因**:绘图时 PYQT 占用大量 CPU,导致 PC 无法及时读取串口缓冲队列,造成数据混乱
|
||||
- **解决方案**:提高串口的波特率
|
||||
15
get-started/csi_recv/CMakeLists.txt
Normal file
15
get-started/csi_recv/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctlycmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
add_compile_options(-fdiagnostics-color=always)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
message("EXTRA_COMPONENT_DIRS: " ${EXTRA_COMPONENT_DIRS})
|
||||
|
||||
string(REGEX REPLACE ".*/\(.*\)" "\\1" CURDIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
project(${CURDIR})
|
||||
|
||||
git_describe(PROJECT_VERSION ${COMPONENT_DIR})
|
||||
message("Project commit: " ${PROJECT_VERSION})
|
||||
1
get-started/csi_recv/README.md
Normal file
1
get-started/csi_recv/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# CSI_RECV
|
||||
2
get-started/csi_recv/main/CMakeLists.txt
Normal file
2
get-started/csi_recv/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
INCLUDE_DIRS ".")
|
||||
297
get-started/csi_recv/main/app_main.c
Normal file
297
get-started/csi_recv/main/app_main.c
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
/* Get Start 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#include "esp_mac.h"
|
||||
#include "rom/ets_sys.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_now.h"
|
||||
#include "esp_csi_gain_ctrl.h"
|
||||
|
||||
#define CONFIG_LESS_INTERFERENCE_CHANNEL 11
|
||||
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 || (CONFIG_IDF_TARGET_ESP32C6 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0))
|
||||
#define CONFIG_WIFI_BAND_MODE WIFI_BAND_MODE_2G_ONLY
|
||||
#define CONFIG_WIFI_2G_BANDWIDTHS WIFI_BW_HT40
|
||||
#define CONFIG_WIFI_5G_BANDWIDTHS WIFI_BW_HT40
|
||||
#define CONFIG_WIFI_2G_PROTOCOL WIFI_PROTOCOL_11N
|
||||
#define CONFIG_WIFI_5G_PROTOCOL WIFI_PROTOCOL_11N
|
||||
#else
|
||||
#define CONFIG_WIFI_BANDWIDTH WIFI_BW_HT40
|
||||
#endif
|
||||
|
||||
#define CONFIG_ESP_NOW_PHYMODE WIFI_PHY_MODE_HT40
|
||||
#define CONFIG_ESP_NOW_RATE WIFI_PHY_RATE_MCS0_LGI
|
||||
#define CONFIG_FORCE_GAIN 0
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
|
||||
#define CSI_FORCE_LLTF 0
|
||||
#endif
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61
|
||||
#define CONFIG_GAIN_CONTROL 1
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
|
||||
#define ESP_IF_WIFI_STA ESP_MAC_WIFI_STA
|
||||
#endif
|
||||
|
||||
static const uint8_t CONFIG_CSI_SEND_MAC[] = {0x1a, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
static const char *TAG = "csi_recv";
|
||||
|
||||
static void wifi_init()
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C5
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
esp_wifi_set_band_mode(CONFIG_WIFI_BAND_MODE);
|
||||
wifi_protocols_t protocols = {
|
||||
.ghz_2g = CONFIG_WIFI_2G_PROTOCOL,
|
||||
.ghz_5g = CONFIG_WIFI_5G_PROTOCOL
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_wifi_set_protocols(ESP_IF_WIFI_STA, &protocols));
|
||||
wifi_bandwidths_t bandwidth = {
|
||||
.ghz_2g = CONFIG_WIFI_2G_BANDWIDTHS,
|
||||
.ghz_5g = CONFIG_WIFI_5G_BANDWIDTHS
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_wifi_set_bandwidths(ESP_IF_WIFI_STA, &bandwidth));
|
||||
#elif (CONFIG_IDF_TARGET_ESP32C6 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)) || CONFIG_IDF_TARGET_ESP32C61
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
esp_wifi_set_band_mode(CONFIG_WIFI_BAND_MODE);
|
||||
wifi_protocols_t protocols = {
|
||||
.ghz_2g = CONFIG_WIFI_2G_PROTOCOL,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_wifi_set_protocols(ESP_IF_WIFI_STA, &protocols));
|
||||
wifi_bandwidths_t bandwidth = {
|
||||
.ghz_2g = CONFIG_WIFI_2G_BANDWIDTHS,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_wifi_set_bandwidths(ESP_IF_WIFI_STA, &bandwidth));
|
||||
#else
|
||||
ESP_ERROR_CHECK(esp_wifi_set_bandwidth(ESP_IF_WIFI_STA, CONFIG_WIFI_BANDWIDTH));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
|
||||
#if CONFIG_IDF_TARGET_ESP32C5
|
||||
if ((CONFIG_WIFI_BAND_MODE == WIFI_BAND_MODE_2G_ONLY && CONFIG_WIFI_2G_BANDWIDTHS == WIFI_BW_HT20)
|
||||
|| (CONFIG_WIFI_BAND_MODE == WIFI_BAND_MODE_5G_ONLY && CONFIG_WIFI_5G_BANDWIDTHS == WIFI_BW_HT20)) {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_NONE));
|
||||
} else {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_BELOW));
|
||||
}
|
||||
#elif (CONFIG_IDF_TARGET_ESP32C6 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)) || CONFIG_IDF_TARGET_ESP32C61
|
||||
if (CONFIG_WIFI_BAND_MODE == WIFI_BAND_MODE_2G_ONLY && CONFIG_WIFI_2G_BANDWIDTHS == WIFI_BW_HT20) {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_NONE));
|
||||
} else {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_BELOW));
|
||||
}
|
||||
#else
|
||||
if (CONFIG_WIFI_BANDWIDTH == WIFI_BW_HT20) {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_NONE));
|
||||
} else {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_BELOW));
|
||||
}
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mac(WIFI_IF_STA, CONFIG_CSI_SEND_MAC));
|
||||
}
|
||||
|
||||
static void wifi_esp_now_init(esp_now_peer_info_t peer)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_now_init());
|
||||
ESP_ERROR_CHECK(esp_now_set_pmk((uint8_t *)"pmk1234567890123"));
|
||||
esp_now_rate_config_t rate_config = {
|
||||
.phymode = CONFIG_ESP_NOW_PHYMODE,
|
||||
.rate = CONFIG_ESP_NOW_RATE,// WIFI_PHY_RATE_MCS0_LGI,
|
||||
.ersu = false,
|
||||
.dcm = false
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_now_add_peer(&peer));
|
||||
ESP_ERROR_CHECK(esp_now_set_peer_rate_config(peer.peer_addr, &rate_config));
|
||||
|
||||
}
|
||||
|
||||
static void wifi_csi_rx_cb(void *ctx, wifi_csi_info_t *info)
|
||||
{
|
||||
if (!info || !info->buf) {
|
||||
ESP_LOGW(TAG, "<%s> wifi_csi_cb", esp_err_to_name(ESP_ERR_INVALID_ARG));
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp(info->mac, CONFIG_CSI_SEND_MAC, 6)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wifi_pkt_rx_ctrl_t *rx_ctrl = &info->rx_ctrl;
|
||||
static int s_count = 0;
|
||||
float compensate_gain = 1.0f;
|
||||
static uint8_t agc_gain = 0;
|
||||
static int8_t fft_gain = 0;
|
||||
#if CONFIG_GAIN_CONTROL
|
||||
static uint8_t agc_gain_baseline = 0;
|
||||
static int8_t fft_gain_baseline = 0;
|
||||
esp_csi_gain_ctrl_get_rx_gain(rx_ctrl, &agc_gain, &fft_gain);
|
||||
if (s_count < 100) {
|
||||
esp_csi_gain_ctrl_record_rx_gain(agc_gain, fft_gain);
|
||||
} else if (s_count == 100) {
|
||||
esp_csi_gain_ctrl_get_rx_gain_baseline(&agc_gain_baseline, &fft_gain_baseline);
|
||||
#if CONFIG_FORCE_GAIN
|
||||
esp_csi_gain_ctrl_set_rx_force_gain(agc_gain_baseline, fft_gain_baseline);
|
||||
ESP_LOGD(TAG, "fft_force %d, agc_force %d", fft_gain_baseline, agc_gain_baseline);
|
||||
#endif
|
||||
}
|
||||
esp_csi_gain_ctrl_get_gain_compensation(&compensate_gain, agc_gain, fft_gain);
|
||||
ESP_LOGI(TAG, "compensate_gain %f, agc_gain %d, fft_gain %d", compensate_gain, agc_gain, fft_gain);
|
||||
#endif
|
||||
|
||||
uint32_t rx_id = *(uint32_t *)(info->payload + 15);
|
||||
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61
|
||||
if (!s_count) {
|
||||
ESP_LOGI(TAG, "================ CSI RECV ================");
|
||||
ets_printf("type,seq,mac,rssi,rate,noise_floor,fft_gain,agc_gain,channel,local_timestamp,sig_len,rx_state,len,first_word,data\n");
|
||||
}
|
||||
|
||||
ets_printf("CSI_DATA,%d," MACSTR ",%d,%d,%d,%d,%d,%d,%d,%d,%d",
|
||||
rx_id, MAC2STR(info->mac), rx_ctrl->rssi, rx_ctrl->rate,
|
||||
rx_ctrl->noise_floor, fft_gain, agc_gain, rx_ctrl->channel,
|
||||
rx_ctrl->timestamp, rx_ctrl->sig_len, rx_ctrl->rx_state);
|
||||
#else
|
||||
if (!s_count) {
|
||||
ESP_LOGI(TAG, "================ CSI RECV ================");
|
||||
ets_printf("type,id,mac,rssi,rate,sig_mode,mcs,bandwidth,smoothing,not_sounding,aggregation,stbc,fec_coding,sgi,noise_floor,ampdu_cnt,channel,secondary_channel,local_timestamp,ant,sig_len,rx_state,len,first_word,data\n");
|
||||
}
|
||||
|
||||
ets_printf("CSI_DATA,%d," MACSTR ",%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
|
||||
rx_id, MAC2STR(info->mac), rx_ctrl->rssi, rx_ctrl->rate, rx_ctrl->sig_mode,
|
||||
rx_ctrl->mcs, rx_ctrl->cwb, rx_ctrl->smoothing, rx_ctrl->not_sounding,
|
||||
rx_ctrl->aggregation, rx_ctrl->stbc, rx_ctrl->fec_coding, rx_ctrl->sgi,
|
||||
rx_ctrl->noise_floor, rx_ctrl->ampdu_cnt, rx_ctrl->channel, rx_ctrl->secondary_channel,
|
||||
rx_ctrl->timestamp, rx_ctrl->ant, rx_ctrl->sig_len, rx_ctrl->rx_state);
|
||||
|
||||
#endif
|
||||
#if (CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61) && CSI_FORCE_LLTF
|
||||
int16_t csi = ((int16_t)(((((uint16_t)info->buf[1]) << 8) | info->buf[0]) << 4) >> 4);
|
||||
ets_printf(",%d,%d,\"[%d", (info->len - 2) / 2, info->first_word_invalid, (int16_t)(compensate_gain * csi));
|
||||
for (int i = 2; i < (info->len - 2); i += 2) {
|
||||
csi = ((int16_t)(((((uint16_t)info->buf[i + 1]) << 8) | info->buf[i]) << 4) >> 4);
|
||||
ets_printf(",%d", (int16_t)(compensate_gain * csi));
|
||||
}
|
||||
#else
|
||||
ets_printf(",%d,%d,\"[%d", info->len, info->first_word_invalid, (int16_t)(compensate_gain * info->buf[0]));
|
||||
for (int i = 1; i < info->len; i++) {
|
||||
ets_printf(",%d", (int16_t)(compensate_gain * info->buf[i]));
|
||||
}
|
||||
#endif
|
||||
ets_printf("]\"\n");
|
||||
s_count++;
|
||||
}
|
||||
|
||||
static void wifi_csi_init()
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_wifi_set_promiscuous(true));
|
||||
|
||||
/**< default config */
|
||||
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
|
||||
wifi_csi_config_t csi_config = {
|
||||
.enable = true,
|
||||
.acquire_csi_legacy = false,
|
||||
.acquire_csi_force_lltf = CSI_FORCE_LLTF,
|
||||
.acquire_csi_ht20 = true,
|
||||
.acquire_csi_ht40 = true,
|
||||
.acquire_csi_vht = false,
|
||||
.acquire_csi_su = false,
|
||||
.acquire_csi_mu = false,
|
||||
.acquire_csi_dcm = false,
|
||||
.acquire_csi_beamformed = false,
|
||||
.acquire_csi_he_stbc_mode = 2,
|
||||
.val_scale_cfg = 0,
|
||||
.dump_ack_en = false,
|
||||
.reserved = false
|
||||
};
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
wifi_csi_config_t csi_config = {
|
||||
.enable = true,
|
||||
.acquire_csi_legacy = false,
|
||||
.acquire_csi_ht20 = true,
|
||||
.acquire_csi_ht40 = true,
|
||||
.acquire_csi_su = true,
|
||||
.acquire_csi_mu = true,
|
||||
.acquire_csi_dcm = true,
|
||||
.acquire_csi_beamformed = true,
|
||||
.acquire_csi_he_stbc = 2,
|
||||
.val_scale_cfg = false,
|
||||
.dump_ack_en = false,
|
||||
.reserved = false
|
||||
};
|
||||
#else
|
||||
wifi_csi_config_t csi_config = {
|
||||
.lltf_en = true,
|
||||
.htltf_en = true,
|
||||
.stbc_htltf2_en = true,
|
||||
.ltf_merge_en = true,
|
||||
.channel_filter_en = true,
|
||||
.manu_scale = false,
|
||||
.shift = false,
|
||||
};
|
||||
#endif
|
||||
ESP_ERROR_CHECK(esp_wifi_set_csi_config(&csi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_csi_rx_cb(wifi_csi_rx_cb, NULL));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_csi(true));
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
/**
|
||||
* @brief 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);
|
||||
|
||||
/**
|
||||
* @brief Initialize Wi-Fi
|
||||
*/
|
||||
wifi_init();
|
||||
|
||||
/**
|
||||
* @brief Initialize ESP-NOW
|
||||
* ESP-NOW protocol see: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html
|
||||
*/
|
||||
|
||||
esp_now_peer_info_t peer = {
|
||||
.channel = CONFIG_LESS_INTERFERENCE_CHANNEL,
|
||||
.ifidx = WIFI_IF_STA,
|
||||
.encrypt = false,
|
||||
.peer_addr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
};
|
||||
|
||||
wifi_esp_now_init(peer);
|
||||
|
||||
wifi_csi_init();
|
||||
}
|
||||
4
get-started/csi_recv/main/idf_component.yml
Normal file
4
get-started/csi_recv/main/idf_component.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
idf: ">=4.4.1"
|
||||
esp_csi_gain_ctrl: ">=0.1.4"
|
||||
10
get-started/csi_recv/sdkconfig.defaults
Normal file
10
get-started/csi_recv/sdkconfig.defaults
Normal file
@@ -0,0 +1,10 @@
|
||||
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||
# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration
|
||||
#
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0xa000
|
||||
CONFIG_ESP_CONSOLE_UART_CUSTOM=y
|
||||
CONFIG_ESP_CONSOLE_UART_BAUDRATE=921600
|
||||
CONFIG_ESP_TASK_WDT_TIMEOUT_S=30
|
||||
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=128
|
||||
CONFIG_ESP_WIFI_CSI_ENABLED=y
|
||||
CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=n
|
||||
19
get-started/csi_recv_router/CMakeLists.txt
Normal file
19
get-started/csi_recv_router/CMakeLists.txt
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctlycmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
add_compile_options(-fdiagnostics-color=always)
|
||||
|
||||
# (Not part of the boilerplate)
|
||||
# This example uses an extra component for common functions such as Wi-Fi and Ethernet connection.
|
||||
set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
message("EXTRA_COMPONENT_DIRS: " ${EXTRA_COMPONENT_DIRS})
|
||||
|
||||
string(REGEX REPLACE ".*/\(.*\)" "\\1" CURDIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
project(${CURDIR})
|
||||
|
||||
git_describe(PROJECT_VERSION ${COMPONENT_DIR})
|
||||
message("Project commit: " ${PROJECT_VERSION})
|
||||
119
get-started/csi_recv_router/README.md
Normal file
119
get-started/csi_recv_router/README.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Recv router CSI example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
The device triggers the router to send packets through the Ping command to obtain the CSI data between the device and the router
|
||||
|
||||
## How to use example
|
||||
Before project configuration and build, be sure to set the correct chip target using `idf.py set-target <chip_name>`.
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* A development board with ESP32/ESP32-S2/ESP32-C3/ESP32-S3/ESP32-C5/ESP32-C6/ESP32-C61 SoC (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.)
|
||||
* A USB cable for power supply and programming
|
||||
|
||||
### Configure the project
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
Open the project configuration menu (`idf.py menuconfig`) to configure Wi-Fi or Ethernet. See "Establishing Wi-Fi or Ethernet Connection" section in [examples/protocols/README.md](https://github.com/espressif/esp-idf/tree/master/examples/protocols#establishing-wi-fi-or-ethernet-connection) for more details.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run monitor tool to view serial output:
|
||||
|
||||
```
|
||||
idf.py -p PORT flash monitor
|
||||
```
|
||||
|
||||
(Replace PORT with the name of the serial port to use.)
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```shell
|
||||
I (0) cpu_start: App cpu up.
|
||||
I (288) cpu_start: Pro cpu start user code
|
||||
I (288) cpu_start: cpu freq: 240000000
|
||||
I (288) cpu_start: Application information:
|
||||
I (289) cpu_start: Project name: csi_recv_router
|
||||
I (289) cpu_start: App version: e1dd673-dirty
|
||||
I (290) cpu_start: Compile time: Jul 14 2022 18:55:08
|
||||
I (291) cpu_start: ELF file SHA256: 81680e58e5d0e53c...
|
||||
I (292) cpu_start: ESP-IDF: v4.4.1-dirty
|
||||
I (293) heap_init: Initializing. RAM available for dynamic allocation:
|
||||
I (293) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
|
||||
I (294) heap_init: At 3FFB7310 len 00028CF0 (163 KiB): DRAM
|
||||
I (295) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
|
||||
I (296) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
|
||||
I (296) heap_init: At 40093AA4 len 0000C55C (49 KiB): IRAM
|
||||
I (298) spi_flash: detected chip: generic
|
||||
I (299) spi_flash: flash io: dio
|
||||
W (299) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
|
||||
I (301) cpu_start: Starting scheduler on PRO CPU.
|
||||
I (0) cpu_start: Starting scheduler on APP CPU.
|
||||
I (331) wifi:wifi driver task: 3ffbfa74, prio:23, stack:6656, core=0
|
||||
I (332) system_api: Base MAC address is not set
|
||||
I (332) system_api: read default base MAC address from EFUSE
|
||||
I (335) wifi:wifi firmware version: 63017e0
|
||||
I (335) wifi:wifi certification version: v7.0
|
||||
I (335) wifi:config NVS flash: enabled
|
||||
I (335) wifi:config nano formatting: disabled
|
||||
I (336) wifi:Init data frame dynamic rx buffer num: 128
|
||||
I (336) wifi:Init management frame dynamic rx buffer num: 128
|
||||
I (337) wifi:Init management short buffer num: 32
|
||||
I (337) wifi:Init dynamic tx buffer num: 32
|
||||
I (338) wifi:Init static rx buffer size: 2212
|
||||
I (338) wifi:Init static rx buffer num: 10
|
||||
I (339) wifi:Init dynamic rx buffer num: 128
|
||||
I (340) wifi_init: tcpip mbox: 32
|
||||
I (340) wifi_init: udp mbox: 6
|
||||
I (340) wifi_init: tcp mbox: 6
|
||||
I (341) wifi_init: tcp tx win: 5744
|
||||
I (341) wifi_init: tcp rx win: 5744
|
||||
I (342) wifi_init: tcp mss: 1440
|
||||
I (342) wifi_init: WiFi IRAM OP enabled
|
||||
I (343) wifi_init: WiFi RX IRAM OP enabled
|
||||
I (344) example_connect: Connecting to TP-LINK_8C81...
|
||||
I (345) phy_init: phy_version 4670,719f9f6,Feb 18 2021,17:07:07
|
||||
I (433) wifi:mode : sta (ac:67:b2:53:78:d0)
|
||||
I (434) wifi:enable tsf
|
||||
I (437) example_connect: Waiting for IP(s)
|
||||
I (2483) wifi:new:<13,2>, old:<1,0>, ap:<255,255>, sta:<13,2>, prof:1
|
||||
I (2485) wifi:state: init -> auth (b0)
|
||||
I (2503) wifi:state: auth -> assoc (0)
|
||||
I (2509) wifi:state: assoc -> run (10)
|
||||
I (2510) wifi:connected with TP-LINK_8C81, aid = 1, channel 13, 40D, bssid = 94:d9:b3:80:8c:81
|
||||
I (2511) wifi:security: Open Auth, phy: bgn, rssi: -28
|
||||
I (2511) wifi:pm start, type: 1
|
||||
|
||||
I (2535) wifi:AP's beacon interval = 102400 us, DTIM period = 1
|
||||
I (3331) esp_netif_handlers: example_connect: sta ip: 192.168.0.107, mask: 255.255.255.0, gw: 192.168.0.1
|
||||
I (3332) example_connect: Got IPv4 event: Interface "example_connect: sta" address: 192.168.0.107
|
||||
I (4330) example_connect: Got IPv6 event: Interface "example_connect: sta" address: fe80:0000:0000:0000:ae67:b2ff:fe53:78d0, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (4332) example_connect: Connected to example_connect: sta
|
||||
I (4332) example_connect: - IPv4 address: 192.168.0.107
|
||||
I (4333) example_connect: - IPv6 address: fe80:0000:0000:0000:ae67:b2ff:fe53:78d0, type: ESP_IP6_ADDR_IS_LINK_LOCAL
|
||||
I (4335) csi_recv_router: got ip:192.168.0.107, gw: 192.168.0.1
|
||||
I (4346) csi_recv_router: ================ CSI RECV ================
|
||||
type,seq,mac,rssi,rate,sig_mode,mcs,bandwidth,smoothing,not_sounding,aggregation,stbc,fec_coding,sgi,noise_floor,ampdu_cnt,channel,secondary_channel,local_timestamp,ant,sig_len,rx_state,len,first_word,data
|
||||
CSI_DATA,0,94:d9:b3:80:8c:81,-30,11,1,6,1,0,1,0,1,0,0,-93,0,13,2,2751923,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,5,0,20,1,20,1,19,0,17,1,16,2,15,2,14,1,12,0,12,-1,12,-3,12,-4,13,-6,15,-7,16,-8,16,-8,16,-8,16,-6,15,-5,15,-4,14,-4,13,-4,12,-4,11,-4,10,-4,9,-5,8,-6,4,-4,8,-9,9,-10,9,-10,10,-11,11,-10,11,-10,12,-9,11,-8,11,-7,10,-6,9,-6,7,-6,6,-7,5,-7,5,-8,5,-9,5,-10,5,-11,5,-11,6,-11,7,-11,8,-11,9,-10,9,-9,8,-8,8,-7,1,-2,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,1,94:d9:b3:80:8c:81,-31,11,1,6,1,0,1,0,1,0,0,-93,0,13,2,2794394,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,5,-2,21,-7,20,-7,20,-6,19,-6,17,-5,15,-5,13,-5,11,-6,11,-7,10,-9,9,-10,10,-11,11,-13,12,-14,13,-14,13,-13,13,-12,13,-12,13,-11,13,-9,12,-8,10,-8,9,-8,8,-8,7,-9,6,-10,2,-6,5,-13,5,-13,5,-14,6,-14,7,-14,8,-14,8,-13,8,-12,7,-11,7,-11,6,-10,5,-10,4,-9,3,-10,2,-10,1,-11,1,-11,1,-12,1,-13,2,-14,3,-14,4,-14,5,-13,5,-12,5,-11,4,-10,1,-3,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,2,94:d9:b3:80:8c:81,-30,11,1,7,1,0,1,0,1,0,0,-93,0,13,2,2801990,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,-4,-7,-16,-27,-15,-27,-13,-27,-12,-25,-10,-24,-10,-21,-10,-19,-11,-16,-12,-14,-14,-13,-16,-12,-18,-12,-20,-12,-21,-13,-22,-14,-22,-15,-21,-16,-20,-16,-19,-16,-17,-16,-16,-14,-15,-13,-14,-11,-14,-9,-15,-7,-15,-6,-9,-2,-19,-3,-19,-3,-21,-3,-22,-3,-22,-4,-22,-5,-21,-6,-20,-6,-19,-7,-17,-6,-16,-5,-15,-4,-14,-2,-14,-1,-14,1,-15,2,-16,3,-18,4,-19,4,-20,3,-21,2,-21,1,-20,0,-19,-1,-18,-3,-16,-3,-4,-1,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,3,94:d9:b3:80:8c:81,-31,11,1,7,1,0,1,0,1,0,0,-93,0,13,2,2810526,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,-6,-1,-22,-3,-22,-4,-21,-5,-20,-5,-18,-5,-17,-4,-15,-3,-14,-2,-14,0,-14,2,-14,3,-15,4,-16,5,-17,5,-18,5,-18,4,-18,3,-18,3,-18,2,-17,2,-15,2,-14,2,-13,2,-12,3,-11,4,-10,5,-5,3,-10,8,-11,8,-11,9,-12,9,-13,9,-13,9,-13,8,-13,7,-12,6,-11,5,-10,5,-9,5,-8,6,-7,7,-7,7,-6,8,-7,9,-7,10,-7,10,-8,10,-9,10,-10,10,-10,9,-10,8,-9,7,-9,6,-2,1,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,4,94:d9:b3:80:8c:81,-31,11,1,7,1,0,1,0,1,0,0,-93,0,13,2,2835649,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,-5,3,-19,15,-19,14,-19,13,-19,11,-17,10,-16,10,-14,9,-12,9,-10,10,-8,12,-7,13,-7,15,-7,16,-8,17,-9,18,-10,18,-10,17,-10,17,-11,15,-11,14,-10,13,-9,12,-7,11,-6,11,-4,11,-3,11,-1,6,-1,14,-1,14,-1,15,-1,16,-2,17,-3,17,-3,16,-4,15,-4,14,-3,13,-3,12,-2,11,-1,10,0,10,2,10,3,10,4,11,4,12,4,13,3,14,2,14,1,15,0,14,0,13,-1,12,-1,11,-1,2,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,5,94:d9:b3:80:8c:81,-31,11,1,7,1,0,1,0,1,0,0,-93,0,13,2,2848462,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,-1,5,-2,21,-3,21,-4,20,-4,18,-4,17,-3,16,-2,14,-1,13,1,12,2,12,4,13,5,13,6,14,6,15,6,16,6,17,5,17,4,17,3,16,3,15,2,14,3,12,3,11,4,10,5,9,6,8,3,4,9,9,9,9,10,9,10,10,10,11,9,11,9,11,8,11,7,11,7,10,6,9,6,7,6,6,7,6,8,5,9,5,9,4,10,4,11,5,11,6,11,7,11,7,10,8,9,8,8,8,7,7,1,1,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,6,94:d9:b3:80:8c:81,-31,11,1,7,1,0,1,0,1,0,0,-93,0,13,2,2860318,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,-2,-6,-7,-22,-6,-21,-5,-21,-4,-20,-3,-19,-3,-17,-4,-15,-5,-13,-6,-12,-8,-12,-9,-12,-10,-12,-12,-13,-12,-13,-12,-14,-12,-15,-11,-15,-11,-15,-10,-15,-9,-14,-8,-13,-8,-12,-8,-11,-8,-9,-9,-8,-10,-7,-6,-4,-12,-6,-13,-6,-13,-7,-14,-8,-14,-8,-13,-9,-13,-9,-12,-9,-11,-9,-10,-8,-10,-7,-9,-6,-9,-5,-9,-4,-10,-3,-11,-3,-11,-2,-12,-2,-13,-3,-14,-3,-14,-4,-13,-5,-13,-5,-12,-6,-11,-6,-10,-6,-3,-2,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,7,94:d9:b3:80:8c:81,-30,11,1,7,1,0,1,0,1,0,0,-93,0,13,2,2867650,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,6,3,25,15,25,15,24,16,22,15,20,14,18,13,16,11,16,8,16,6,17,4,19,2,20,1,22,1,24,1,25,1,25,2,25,3,24,4,23,4,21,5,19,4,18,4,17,2,16,1,15,-1,15,-3,7,-3,17,-6,17,-7,18,-8,19,-7,20,-7,20,-6,20,-5,20,-4,19,-3,17,-3,16,-3,14,-4,13,-5,12,-6,12,-8,12,-9,12,-11,13,-11,14,-12,15,-11,17,-11,17,-10,18,-9,17,-7,16,-6,14,-5,3,-2,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,8,94:d9:b3:80:8c:81,-31,11,1,7,1,0,1,0,1,0,0,-93,0,13,2,2874562,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,-6,0,-22,1,-22,0,-21,-1,-20,-2,-18,-2,-17,-1,-15,-1,-14,1,-14,2,-13,4,-13,5,-14,6,-15,7,-16,8,-17,8,-17,7,-17,6,-17,5,-17,5,-16,4,-15,4,-13,4,-12,4,-11,4,-10,5,-9,6,-5,4,-9,9,-10,10,-10,10,-11,10,-12,10,-12,10,-12,9,-12,8,-11,7,-11,6,-10,6,-9,6,-8,7,-7,7,-6,8,-6,8,-6,9,-6,10,-6,11,-7,11,-8,11,-9,10,-10,9,-10,8,-9,7,-8,7,-2,1,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,9,94:d9:b3:80:8c:81,-31,11,1,7,1,0,1,0,1,0,0,-93,0,13,2,2881588,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,3,-7,13,-26,13,-25,14,-23,14,-22,13,-20,11,-18,9,-17,7,-16,4,-16,2,-17,0,-18,-1,-20,-1,-22,-1,-23,-1,-24,0,-25,1,-25,1,-24,2,-23,3,-21,3,-19,2,-18,0,-16,-1,-15,-3,-14,-4,-14,-4,-8,-8,-16,-9,-16,-9,-17,-10,-18,-9,-19,-8,-19,-7,-19,-6,-19,-6,-18,-5,-17,-5,-15,-5,-13,-6,-12,-8,-11,-9,-10,-11,-10,-12,-10,-13,-11,-14,-12,-14,-13,-13,-14,-12,-15,-11,-15,-10,-15,-9,-14,-8,-13,-2,-4,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,10,94:d9:b3:80:8c:81,-32,11,1,7,1,0,1,0,1,0,0,-93,0,13,2,2893341,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,-5,-3,-20,-13,-20,-13,-18,-14,-17,-13,-15,-13,-14,-11,-13,-9,-13,-7,-13,-5,-14,-4,-15,-2,-16,-2,-18,-1,-19,-1,-20,-2,-20,-3,-20,-4,-19,-4,-18,-4,-17,-4,-15,-4,-14,-4,-13,-3,-12,-1,-12,0,-12,2,-7,1,-13,4,-14,4,-15,5,-15,5,-16,5,-16,4,-16,3,-15,2,-15,2,-13,1,-12,2,-11,2,-10,3,-9,4,-9,5,-9,6,-9,7,-10,8,-11,8,-12,8,-13,8,-13,7,-13,6,-13,5,-12,4,-11,4,-3,1,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,11,94:d9:b3:80:8c:81,-32,11,1,7,1,0,1,0,1,0,0,-93,0,13,2,2912696,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,5,1,22,4,22,5,21,5,19,5,17,5,16,5,14,3,13,1,13,0,13,-2,14,-4,15,-5,16,-6,17,-6,18,-6,18,-5,18,-4,18,-4,17,-3,16,-2,15,-2,13,-2,12,-3,11,-4,10,-5,9,-6,4,-4,10,-10,10,-10,11,-11,11,-10,12,-10,13,-10,13,-9,13,-8,12,-7,11,-7,10,-6,8,-7,7,-7,6,-8,6,-9,5,-10,5,-11,5,-12,6,-12,7,-12,8,-12,9,-12,9,-11,9,-10,9,-9,8,-7,2,-2,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,12,94:d9:b3:80:8c:81,-32,11,1,7,1,0,1,0,1,0,0,-93,0,13,2,2921403,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,-6,0,-24,2,-23,1,-22,0,-21,-1,-20,-1,-18,-1,-16,0,-15,1,-14,3,-13,5,-13,7,-14,8,-15,9,-16,10,-17,10,-17,9,-18,9,-17,8,-17,7,-16,6,-15,5,-13,5,-12,6,-10,6,-9,7,-8,8,-4,5,-8,11,-8,12,-8,12,-9,13,-10,13,-11,12,-11,12,-11,11,-10,10,-9,9,-8,8,-7,8,-6,8,-4,9,-3,9,-3,10,-3,11,-3,12,-3,12,-4,13,-5,13,-6,13,-6,12,-7,11,-6,10,-6,9,-2,2,0,0,0,0,0,0,0,0]"
|
||||
CSI_DATA,13,94:d9:b3:80:8c:81,-32,11,1,7,1,0,1,0,1,0,0,-93,0,13,2,2936234,0,67,0,128,1,"[67,48,4,0,0,0,0,0,0,0,-6,-1,-23,-1,-23,-1,-22,-2,-21,-3,-19,-3,-18,-2,-16,-1,-14,0,-14,2,-13,4,-14,5,-14,7,-16,8,-17,8,-18,8,-18,7,-18,6,-18,6,-18,5,-17,5,-15,4,-14,4,-12,4,-11,5,-9,6,-9,7,-5,4,-9,10,-9,11,-10,12,-10,12,-11,12,-12,11,-12,10,-11,9,-11,9,-10,8,-9,7,-8,7,-6,8,-5,8,-5,9,-4,10,-4,11,-4,11,-5,12,-5,12,-6,13,-7,12,-8,12,-8,10,-8,9,-7,8,-2,2,0,0,0,0,0,0,0,0]"
|
||||
C
|
||||
```
|
||||
156
get-started/csi_recv_router/dependencies.lock
Normal file
156
get-started/csi_recv_router/dependencies.lock
Normal file
@@ -0,0 +1,156 @@
|
||||
dependencies:
|
||||
espressif/cmake_utilities:
|
||||
component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f
|
||||
dependencies:
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=4.1'
|
||||
source:
|
||||
registry_url: https://components.espressif.com
|
||||
type: service
|
||||
version: 0.5.3
|
||||
espressif/esp_csi_gain_ctrl:
|
||||
component_hash: bb7b9e28f47f4de968d4aaa2a083ea5764e5994dd0c5146146958060e6b66247
|
||||
dependencies:
|
||||
- name: espressif/cmake_utilities
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: 0.*
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=4.4.1'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
targets:
|
||||
- esp32
|
||||
- esp32s3
|
||||
- esp32s2
|
||||
- esp32c3
|
||||
- esp32c5
|
||||
- esp32c6
|
||||
- esp32c61
|
||||
version: 0.1.4~1
|
||||
espressif/ethernet_init:
|
||||
component_hash: b7303f1482091766b9b653ec8332775a9485f4df3c69141577de82847c4d2adf
|
||||
dependencies:
|
||||
- matches:
|
||||
- if: $CONFIG{ETHERNET_SPI_USE_CH390} == 1
|
||||
name: espressif/ch390
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: '*'
|
||||
- matches:
|
||||
- if: $CONFIG{ETHERNET_SPI_USE_DM9051} == 1
|
||||
name: espressif/dm9051
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
version: '*'
|
||||
- matches:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
name: espressif/dp83848
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
rules:
|
||||
- if: target in [esp32, esp32p4]
|
||||
- if: $CONFIG{ETHERNET_PHY_USE_DP83848} == 1
|
||||
version: '*'
|
||||
- matches:
|
||||
- if: $CONFIG{ETHERNET_SPI_USE_ENC28J60} == 1
|
||||
name: espressif/enc28j60
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: '*'
|
||||
- matches:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
name: espressif/ip101
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
rules:
|
||||
- if: target in [esp32, esp32p4]
|
||||
- if: $CONFIG{ETHERNET_PHY_USE_IP101} == 1
|
||||
version: '*'
|
||||
- matches:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
name: espressif/ksz80xx
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
rules:
|
||||
- if: target in [esp32, esp32p4]
|
||||
- if: $CONFIG{ETHERNET_PHY_USE_KSZ80XX} == 1
|
||||
version: '*'
|
||||
- matches:
|
||||
- if: $CONFIG{ETHERNET_SPI_USE_KSZ8851SNL} == 1
|
||||
name: espressif/ksz8851snl
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
version: '*'
|
||||
- matches:
|
||||
- if: $CONFIG{ETHERNET_SPI_USE_LAN865X} == 1
|
||||
name: espressif/lan865x
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
version: ^0.1.1
|
||||
- name: espressif/lan867x
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
rules:
|
||||
- if: target in [esp32, esp32p4]
|
||||
- if: $CONFIG{ETHERNET_PHY_USE_LAN867X} == 1
|
||||
version: '>=2.0.0'
|
||||
- matches:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
name: espressif/lan87xx
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
rules:
|
||||
- if: target in [esp32, esp32p4]
|
||||
- if: $CONFIG{ETHERNET_PHY_USE_LAN87XX} == 1
|
||||
version: '*'
|
||||
- matches:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
name: espressif/rtl8201
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
rules:
|
||||
- if: target in [esp32, esp32p4]
|
||||
- if: $CONFIG{ETHERNET_PHY_USE_RTL8201} == 1
|
||||
version: '*'
|
||||
- matches:
|
||||
- if: $CONFIG{ETHERNET_SPI_USE_W5500} == 1
|
||||
name: espressif/w5500
|
||||
registry_url: https://components.espressif.com
|
||||
require: private
|
||||
rules:
|
||||
- if: idf_version >=6.0
|
||||
version: ^1.0.0
|
||||
version: '*'
|
||||
- name: idf
|
||||
require: private
|
||||
version: '>=5.4.3,!=5.5.0,!=5.5.1'
|
||||
source:
|
||||
registry_url: https://components.espressif.com/
|
||||
type: service
|
||||
version: 1.2.0
|
||||
idf:
|
||||
source:
|
||||
type: idf
|
||||
version: 6.1.0
|
||||
direct_dependencies:
|
||||
- espressif/esp_csi_gain_ctrl
|
||||
- espressif/ethernet_init
|
||||
- idf
|
||||
manifest_hash: 612e7d3604727e429e9b873bb7becd2967d5d7c5dc868ee1ff7595b6ee816ffd
|
||||
target: esp32
|
||||
version: 2.0.0
|
||||
2
get-started/csi_recv_router/main/CMakeLists.txt
Normal file
2
get-started/csi_recv_router/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
INCLUDE_DIRS ".")
|
||||
16
get-started/csi_recv_router/main/Kconfig.projbuild
Normal file
16
get-started/csi_recv_router/main/Kconfig.projbuild
Normal file
@@ -0,0 +1,16 @@
|
||||
menu "CSI UDP Configuration"
|
||||
|
||||
config CSI_UDP_TARGET_IP
|
||||
string "UDP target IP address"
|
||||
default "192.168.129.11"
|
||||
help
|
||||
IP address of the host receiving CSI data (e.g., Raspberry Pi).
|
||||
|
||||
config CSI_UDP_TARGET_PORT
|
||||
int "UDP target port"
|
||||
default 5500
|
||||
range 1024 65535
|
||||
help
|
||||
UDP port on the target host for receiving CSI data.
|
||||
|
||||
endmenu
|
||||
254
get-started/csi_recv_router/main/app_main.c
Normal file
254
get-started/csi_recv_router/main/app_main.c
Normal file
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
/* Get recv router csi
|
||||
|
||||
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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/event_groups.h"
|
||||
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#include "esp_mac.h"
|
||||
#include "rom/ets_sys.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_now.h"
|
||||
|
||||
#include "lwip/inet.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "ping/ping_sock.h"
|
||||
|
||||
#include "protocol_examples_common.h"
|
||||
#include "esp_csi_gain_ctrl.h"
|
||||
|
||||
#define CONFIG_SEND_FREQUENCY 100
|
||||
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
|
||||
#define CSI_FORCE_LLTF 0
|
||||
#endif
|
||||
#define CONFIG_FORCE_GAIN 0
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61
|
||||
#define CONFIG_GAIN_CONTROL 1
|
||||
#endif
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
|
||||
#define ESP_IF_WIFI_STA ESP_MAC_WIFI_STA
|
||||
#endif
|
||||
|
||||
static const char *TAG = "csi_recv_router";
|
||||
|
||||
/* UDP socket for CSI data transmission */
|
||||
static int s_udp_socket = -1;
|
||||
static struct sockaddr_in s_dest_addr;
|
||||
static char s_udp_buffer[2048];
|
||||
|
||||
static void wifi_csi_rx_cb(void *ctx, wifi_csi_info_t *info)
|
||||
{
|
||||
if (!info || !info->buf) {
|
||||
ESP_LOGW(TAG, "<%s> wifi_csi_cb", esp_err_to_name(ESP_ERR_INVALID_ARG));
|
||||
return;
|
||||
}
|
||||
|
||||
if (memcmp(info->mac, ctx, 6)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wifi_pkt_rx_ctrl_t *rx_ctrl = &info->rx_ctrl;
|
||||
static int s_count = 0;
|
||||
float compensate_gain = 1.0f;
|
||||
static uint8_t agc_gain = 0;
|
||||
static int8_t fft_gain = 0;
|
||||
#if CONFIG_GAIN_CONTROL
|
||||
static uint8_t agc_gain_baseline = 0;
|
||||
static int8_t fft_gain_baseline = 0;
|
||||
esp_csi_gain_ctrl_get_rx_gain(rx_ctrl, &agc_gain, &fft_gain);
|
||||
if (s_count < 100) {
|
||||
esp_csi_gain_ctrl_record_rx_gain(agc_gain, fft_gain);
|
||||
} else if (s_count == 100) {
|
||||
esp_csi_gain_ctrl_get_rx_gain_baseline(&agc_gain_baseline, &fft_gain_baseline);
|
||||
#if CONFIG_FORCE_GAIN
|
||||
esp_csi_gain_ctrl_set_rx_force_gain(agc_gain_baseline, fft_gain_baseline);
|
||||
ESP_LOGI(TAG, "fft_force %d, agc_force %d", fft_gain_baseline, agc_gain_baseline);
|
||||
#endif
|
||||
}
|
||||
esp_csi_gain_ctrl_get_gain_compensation(&compensate_gain, agc_gain, fft_gain);
|
||||
ESP_LOGD(TAG, "compensate_gain %f, agc_gain %d, fft_gain %d", compensate_gain, agc_gain, fft_gain);
|
||||
#endif
|
||||
|
||||
/* Build CSI data into buffer for UDP transmission */
|
||||
int pos = 0;
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C6 || CONFIG_IDF_TARGET_ESP32C61
|
||||
if (!s_count) {
|
||||
ESP_LOGI(TAG, "================ CSI RECV (UDP) ================");
|
||||
}
|
||||
pos = snprintf(s_udp_buffer, sizeof(s_udp_buffer),
|
||||
"CSI_DATA,%d," MACSTR ",%d,%d,%d,%d,%d,%d,%d,%d,%d",
|
||||
s_count, MAC2STR(info->mac), rx_ctrl->rssi, rx_ctrl->rate,
|
||||
rx_ctrl->noise_floor, fft_gain, agc_gain, rx_ctrl->channel,
|
||||
rx_ctrl->timestamp, rx_ctrl->sig_len, rx_ctrl->rx_state);
|
||||
#else
|
||||
if (!s_count) {
|
||||
ESP_LOGI(TAG, "================ CSI RECV (UDP) ================");
|
||||
}
|
||||
pos = snprintf(s_udp_buffer, sizeof(s_udp_buffer),
|
||||
"CSI_DATA,%d," MACSTR ",%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
|
||||
s_count, MAC2STR(info->mac), rx_ctrl->rssi, rx_ctrl->rate, rx_ctrl->sig_mode,
|
||||
rx_ctrl->mcs, rx_ctrl->cwb, rx_ctrl->smoothing, rx_ctrl->not_sounding,
|
||||
rx_ctrl->aggregation, rx_ctrl->stbc, rx_ctrl->fec_coding, rx_ctrl->sgi,
|
||||
rx_ctrl->noise_floor, rx_ctrl->ampdu_cnt, rx_ctrl->channel, rx_ctrl->secondary_channel,
|
||||
rx_ctrl->timestamp, rx_ctrl->ant, rx_ctrl->sig_len, rx_ctrl->rx_state);
|
||||
#endif
|
||||
|
||||
#if (CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61) && CSI_FORCE_LLTF
|
||||
int16_t csi = ((int16_t)(((((uint16_t)info->buf[1]) << 8) | info->buf[0]) << 4) >> 4);
|
||||
pos += snprintf(s_udp_buffer + pos, sizeof(s_udp_buffer) - pos,
|
||||
",%d,%d,\"[%d", (info->len - 2) / 2, info->first_word_invalid, (int16_t)(compensate_gain * csi));
|
||||
for (int i = 2; i < (info->len - 2); i += 2) {
|
||||
csi = ((int16_t)(((((uint16_t)info->buf[i + 1]) << 8) | info->buf[i]) << 4) >> 4);
|
||||
pos += snprintf(s_udp_buffer + pos, sizeof(s_udp_buffer) - pos, ",%d", (int16_t)(compensate_gain * csi));
|
||||
}
|
||||
#else
|
||||
pos += snprintf(s_udp_buffer + pos, sizeof(s_udp_buffer) - pos,
|
||||
",%d,%d,\"[%d", info->len, info->first_word_invalid, (int16_t)(compensate_gain * info->buf[0]));
|
||||
for (int i = 1; i < info->len; i++) {
|
||||
pos += snprintf(s_udp_buffer + pos, sizeof(s_udp_buffer) - pos, ",%d", (int16_t)(compensate_gain * info->buf[i]));
|
||||
}
|
||||
#endif
|
||||
pos += snprintf(s_udp_buffer + pos, sizeof(s_udp_buffer) - pos, "]\"\n");
|
||||
|
||||
/* Send via UDP */
|
||||
if (s_udp_socket >= 0) {
|
||||
sendto(s_udp_socket, s_udp_buffer, pos, 0, (struct sockaddr *)&s_dest_addr, sizeof(s_dest_addr));
|
||||
}
|
||||
|
||||
s_count++;
|
||||
}
|
||||
|
||||
static void wifi_csi_init()
|
||||
{
|
||||
/**
|
||||
* @brief In order to ensure the compatibility of routers, only LLTF sub-carriers are selected.
|
||||
*/
|
||||
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61
|
||||
wifi_csi_config_t csi_config = {
|
||||
.enable = true,
|
||||
.acquire_csi_legacy = true,
|
||||
.acquire_csi_force_lltf = CSI_FORCE_LLTF,
|
||||
.acquire_csi_ht20 = true,
|
||||
.acquire_csi_ht40 = true,
|
||||
.acquire_csi_vht = false,
|
||||
.acquire_csi_su = false,
|
||||
.acquire_csi_mu = false,
|
||||
.acquire_csi_dcm = false,
|
||||
.acquire_csi_beamformed = false,
|
||||
.acquire_csi_he_stbc_mode = 2,
|
||||
.val_scale_cfg = 0,
|
||||
.dump_ack_en = false,
|
||||
.reserved = false
|
||||
};
|
||||
#elif CONFIG_IDF_TARGET_ESP32C6
|
||||
wifi_csi_config_t csi_config = {
|
||||
.enable = true,
|
||||
.acquire_csi_legacy = true,
|
||||
.acquire_csi_ht20 = true,
|
||||
.acquire_csi_ht40 = true,
|
||||
.acquire_csi_su = false,
|
||||
.acquire_csi_mu = false,
|
||||
.acquire_csi_dcm = false,
|
||||
.acquire_csi_beamformed = false,
|
||||
.acquire_csi_he_stbc = 2,
|
||||
.val_scale_cfg = false,
|
||||
.dump_ack_en = false,
|
||||
.reserved = false
|
||||
};
|
||||
#else
|
||||
wifi_csi_config_t csi_config = {
|
||||
.lltf_en = true,
|
||||
.htltf_en = false,
|
||||
.stbc_htltf2_en = false,
|
||||
.ltf_merge_en = true,
|
||||
.channel_filter_en = true,
|
||||
.manu_scale = true,
|
||||
.shift = true,
|
||||
};
|
||||
#endif
|
||||
static wifi_ap_record_t s_ap_info = {0};
|
||||
ESP_ERROR_CHECK(esp_wifi_sta_get_ap_info(&s_ap_info));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_csi_config(&csi_config));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_csi_rx_cb(wifi_csi_rx_cb, s_ap_info.bssid));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_csi(true));
|
||||
}
|
||||
|
||||
static void udp_socket_init(void)
|
||||
{
|
||||
s_udp_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (s_udp_socket < 0) {
|
||||
ESP_LOGE(TAG, "Failed to create UDP socket: errno %d", errno);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&s_dest_addr, 0, sizeof(s_dest_addr));
|
||||
s_dest_addr.sin_family = AF_INET;
|
||||
s_dest_addr.sin_port = htons(CONFIG_CSI_UDP_TARGET_PORT);
|
||||
inet_pton(AF_INET, CONFIG_CSI_UDP_TARGET_IP, &s_dest_addr.sin_addr);
|
||||
|
||||
ESP_LOGI(TAG, "UDP socket initialized, sending to %s:%d",
|
||||
CONFIG_CSI_UDP_TARGET_IP, CONFIG_CSI_UDP_TARGET_PORT);
|
||||
}
|
||||
|
||||
static esp_err_t wifi_ping_router_start()
|
||||
{
|
||||
static esp_ping_handle_t ping_handle = NULL;
|
||||
|
||||
esp_ping_config_t ping_config = ESP_PING_DEFAULT_CONFIG();
|
||||
ping_config.count = 0;
|
||||
ping_config.interval_ms = 1000 / CONFIG_SEND_FREQUENCY;
|
||||
ping_config.task_stack_size = 3072;
|
||||
ping_config.data_size = 1;
|
||||
|
||||
esp_netif_ip_info_t local_ip;
|
||||
esp_netif_get_ip_info(esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"), &local_ip);
|
||||
ESP_LOGI(TAG, "got ip:" IPSTR ", gw: " IPSTR, IP2STR(&local_ip.ip), IP2STR(&local_ip.gw));
|
||||
ping_config.target_addr.u_addr.ip4.addr = ip4_addr_get_u32(&local_ip.gw);
|
||||
ping_config.target_addr.type = ESP_IPADDR_TYPE_V4;
|
||||
|
||||
esp_ping_callbacks_t cbs = { 0 };
|
||||
esp_ping_new_session(&ping_config, &cbs, &ping_handle);
|
||||
esp_ping_start(ping_handle);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_flash_init());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
/**
|
||||
* @brief This helper function configures Wi-Fi, as selected in menuconfig.
|
||||
* Read "Establishing Wi-Fi Connection" section in esp-idf/examples/protocols/README.md
|
||||
* for more information about this function.
|
||||
*/
|
||||
ESP_ERROR_CHECK(example_connect());
|
||||
|
||||
udp_socket_init();
|
||||
wifi_csi_init();
|
||||
wifi_ping_router_start();
|
||||
}
|
||||
4
get-started/csi_recv_router/main/idf_component.yml
Normal file
4
get-started/csi_recv_router/main/idf_component.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
idf: ">=4.4.1"
|
||||
esp_csi_gain_ctrl: ">=0.1.4"
|
||||
3575
get-started/csi_recv_router/sdkconfig
Normal file
3575
get-started/csi_recv_router/sdkconfig
Normal file
File diff suppressed because it is too large
Load Diff
51
get-started/csi_recv_router/sdkconfig.defaults
Normal file
51
get-started/csi_recv_router/sdkconfig.defaults
Normal file
@@ -0,0 +1,51 @@
|
||||
#
|
||||
# Wi-Fi
|
||||
#
|
||||
CONFIG_ESP32_WIFI_CSI_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=
|
||||
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=128
|
||||
|
||||
#
|
||||
# UART
|
||||
#
|
||||
CONFIG_ESP_CONSOLE_UART_CUSTOM=y
|
||||
CONFIG_ESP_CONSOLE_UART=y
|
||||
CONFIG_ESP_CONSOLE_UART_NUM=0
|
||||
CONFIG_CONSOLE_UART_BAUDRATE=921600
|
||||
|
||||
CONFIG_ESP_TASK_WDT_TIMEOUT_S=30
|
||||
|
||||
CONFIG_ESPTOOLPY_MONITOR_BAUD_921600B=y
|
||||
CONFIG_ESPTOOLPY_MONITOR_BAUD=921600
|
||||
|
||||
|
||||
#
|
||||
# Wi-Fi
|
||||
#
|
||||
CONFIG_ESP32_WIFI_CSI_ENABLED=y
|
||||
CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=
|
||||
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=
|
||||
|
||||
#
|
||||
# Compiler options
|
||||
#
|
||||
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||
|
||||
#
|
||||
# FreeRTOS
|
||||
#
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
#
|
||||
# ESP32-specific
|
||||
#
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
|
||||
|
||||
CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM=32
|
||||
|
||||
#
|
||||
# ESP32S3-specific
|
||||
#
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240
|
||||
3566
get-started/csi_recv_router/sdkconfig.old
Normal file
3566
get-started/csi_recv_router/sdkconfig.old
Normal file
File diff suppressed because it is too large
Load Diff
13
get-started/csi_send/CMakeLists.txt
Normal file
13
get-started/csi_send/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctlycmake_minimum_required(VERSION 3.5)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
add_compile_options(-fdiagnostics-color=always)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
string(REGEX REPLACE ".*/\(.*\)" "\\1" CURDIR ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
project(${CURDIR})
|
||||
|
||||
git_describe(PROJECT_VERSION ${COMPONENT_DIR})
|
||||
message("Project commit: " ${PROJECT_VERSION})
|
||||
1
get-started/csi_send/README.md
Normal file
1
get-started/csi_send/README.md
Normal file
@@ -0,0 +1 @@
|
||||
# CSI_SEND
|
||||
2
get-started/csi_send/main/CMakeLists.txt
Normal file
2
get-started/csi_send/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRC_DIRS "."
|
||||
INCLUDE_DIRS ".")
|
||||
170
get-started/csi_send/main/app_main.c
Normal file
170
get-started/csi_send/main/app_main.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
/* Get Start 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "nvs_flash.h"
|
||||
|
||||
#include "esp_mac.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "esp_netif.h"
|
||||
#include "esp_now.h"
|
||||
|
||||
#define CONFIG_LESS_INTERFERENCE_CHANNEL 11
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C5 || CONFIG_IDF_TARGET_ESP32C61 || (CONFIG_IDF_TARGET_ESP32C6 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0))
|
||||
#define CONFIG_WIFI_BAND_MODE WIFI_BAND_MODE_2G_ONLY
|
||||
#define CONFIG_WIFI_2G_BANDWIDTHS WIFI_BW_HT40
|
||||
#define CONFIG_WIFI_5G_BANDWIDTHS WIFI_BW_HT40
|
||||
#define CONFIG_WIFI_2G_PROTOCOL WIFI_PROTOCOL_11N
|
||||
#define CONFIG_WIFI_5G_PROTOCOL WIFI_PROTOCOL_11N
|
||||
#else
|
||||
#define CONFIG_WIFI_BANDWIDTH WIFI_BW_HT40
|
||||
#endif
|
||||
|
||||
#define CONFIG_ESP_NOW_PHYMODE WIFI_PHY_MODE_HT40
|
||||
#define CONFIG_ESP_NOW_RATE WIFI_PHY_RATE_MCS0_LGI
|
||||
#define CONFIG_SEND_FREQUENCY 100
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
|
||||
#define ESP_IF_WIFI_STA ESP_MAC_WIFI_STA
|
||||
#endif
|
||||
|
||||
static const uint8_t CONFIG_CSI_SEND_MAC[] = {0x1a, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
static const char *TAG = "csi_send";
|
||||
|
||||
static void wifi_init()
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
|
||||
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32C5
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
esp_wifi_set_band_mode(CONFIG_WIFI_BAND_MODE);
|
||||
wifi_protocols_t protocols = {
|
||||
.ghz_2g = CONFIG_WIFI_2G_PROTOCOL,
|
||||
.ghz_5g = CONFIG_WIFI_5G_PROTOCOL
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_wifi_set_protocols(ESP_IF_WIFI_STA, &protocols));
|
||||
wifi_bandwidths_t bandwidth = {
|
||||
.ghz_2g = CONFIG_WIFI_2G_BANDWIDTHS,
|
||||
.ghz_5g = CONFIG_WIFI_5G_BANDWIDTHS
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_wifi_set_bandwidths(ESP_IF_WIFI_STA, &bandwidth));
|
||||
#elif (CONFIG_IDF_TARGET_ESP32C6 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)) || CONFIG_IDF_TARGET_ESP32C61
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
esp_wifi_set_band_mode(CONFIG_WIFI_BAND_MODE);
|
||||
wifi_protocols_t protocols = {
|
||||
.ghz_2g = CONFIG_WIFI_2G_PROTOCOL,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_wifi_set_protocols(ESP_IF_WIFI_STA, &protocols));
|
||||
wifi_bandwidths_t bandwidth = {
|
||||
.ghz_2g = CONFIG_WIFI_2G_BANDWIDTHS,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_wifi_set_bandwidths(ESP_IF_WIFI_STA, &bandwidth));
|
||||
#else
|
||||
ESP_ERROR_CHECK(esp_wifi_set_bandwidth(ESP_IF_WIFI_STA, CONFIG_WIFI_BANDWIDTH));
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
|
||||
#if CONFIG_IDF_TARGET_ESP32C5
|
||||
if ((CONFIG_WIFI_BAND_MODE == WIFI_BAND_MODE_2G_ONLY && CONFIG_WIFI_2G_BANDWIDTHS == WIFI_BW_HT20)
|
||||
|| (CONFIG_WIFI_BAND_MODE == WIFI_BAND_MODE_5G_ONLY && CONFIG_WIFI_5G_BANDWIDTHS == WIFI_BW_HT20)) {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_NONE));
|
||||
} else {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_BELOW));
|
||||
}
|
||||
#elif (CONFIG_IDF_TARGET_ESP32C6 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)) || CONFIG_IDF_TARGET_ESP32C61
|
||||
if (CONFIG_WIFI_BAND_MODE == WIFI_BAND_MODE_2G_ONLY && CONFIG_WIFI_2G_BANDWIDTHS == WIFI_BW_HT20) {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_NONE));
|
||||
} else {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_BELOW));
|
||||
}
|
||||
#else
|
||||
if (CONFIG_WIFI_BANDWIDTH == WIFI_BW_HT20) {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_NONE));
|
||||
} else {
|
||||
ESP_ERROR_CHECK(esp_wifi_set_channel(CONFIG_LESS_INTERFERENCE_CHANNEL, WIFI_SECOND_CHAN_BELOW));
|
||||
}
|
||||
#endif
|
||||
ESP_ERROR_CHECK(esp_wifi_set_mac(WIFI_IF_STA, CONFIG_CSI_SEND_MAC));
|
||||
}
|
||||
|
||||
static void wifi_esp_now_init(esp_now_peer_info_t peer)
|
||||
{
|
||||
ESP_ERROR_CHECK(esp_now_init());
|
||||
ESP_ERROR_CHECK(esp_now_set_pmk((uint8_t *)"pmk1234567890123"));
|
||||
ESP_ERROR_CHECK(esp_now_add_peer(&peer));
|
||||
esp_now_rate_config_t rate_config = {
|
||||
.phymode = CONFIG_ESP_NOW_PHYMODE,
|
||||
.rate = CONFIG_ESP_NOW_RATE,
|
||||
.ersu = false,
|
||||
.dcm = false
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_now_set_peer_rate_config(peer.peer_addr, &rate_config));
|
||||
}
|
||||
|
||||
void app_main()
|
||||
{
|
||||
/**
|
||||
* @brief 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);
|
||||
|
||||
/**
|
||||
* @brief Initialize Wi-Fi
|
||||
*/
|
||||
wifi_init();
|
||||
|
||||
/**
|
||||
* @brief Initialize ESP-NOW
|
||||
* ESP-NOW protocol see: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html
|
||||
*/
|
||||
esp_now_peer_info_t peer = {
|
||||
.channel = CONFIG_LESS_INTERFERENCE_CHANNEL,
|
||||
.ifidx = WIFI_IF_STA,
|
||||
.encrypt = false,
|
||||
.peer_addr = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
};
|
||||
wifi_esp_now_init(peer);
|
||||
|
||||
ESP_LOGI(TAG, "================ CSI SEND ================");
|
||||
ESP_LOGI(TAG, "wifi_channel: %d, send_frequency: %d, mac: " MACSTR,
|
||||
CONFIG_LESS_INTERFERENCE_CHANNEL, CONFIG_SEND_FREQUENCY, MAC2STR(CONFIG_CSI_SEND_MAC));
|
||||
|
||||
for (uint32_t count = 0; ; ++count) {
|
||||
esp_err_t ret = esp_now_send(peer.peer_addr, (const uint8_t *)&count, sizeof(count));
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGW(TAG, "free_heap: %ld <%s> ESP-NOW send error", esp_get_free_heap_size(), esp_err_to_name(ret));
|
||||
}
|
||||
|
||||
usleep(1000 * 1000 / CONFIG_SEND_FREQUENCY);
|
||||
}
|
||||
}
|
||||
3
get-started/csi_send/main/idf_component.yml
Normal file
3
get-started/csi_send/main/idf_component.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
idf: ">=4.4.1"
|
||||
8
get-started/csi_send/sdkconfig.defaults
Normal file
8
get-started/csi_send/sdkconfig.defaults
Normal file
@@ -0,0 +1,8 @@
|
||||
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||
# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration
|
||||
#
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0xa000
|
||||
CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER_NUM=128
|
||||
CONFIG_ESP_WIFI_CSI_ENABLED=y
|
||||
CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=n
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
2
get-started/tools/.gitignore
vendored
Normal file
2
get-started/tools/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.csv
|
||||
*.txt
|
||||
336
get-started/tools/csi_data_read_parse.py
Executable file
336
get-started/tools/csi_data_read_parse.py
Executable file
@@ -0,0 +1,336 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*-coding:utf-8-*-
|
||||
|
||||
# SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# WARNING: we don't check for Python build-time dependencies until
|
||||
# check_environment() function below. If possible, avoid importing
|
||||
# any external libraries here - put in external script, or import in
|
||||
# their specific function instead.
|
||||
|
||||
import sys
|
||||
import csv
|
||||
import json
|
||||
import argparse
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
import serial
|
||||
from os import path
|
||||
from io import StringIO
|
||||
|
||||
from PyQt5.Qt import *
|
||||
from pyqtgraph import PlotWidget
|
||||
from PyQt5 import QtCore
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph import ScatterPlotItem
|
||||
from PyQt5.QtCore import pyqtSignal, QThread
|
||||
import threading
|
||||
import time
|
||||
from scipy.optimize import minimize
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.stats import linregress
|
||||
import statsmodels.api as sm
|
||||
|
||||
# Reduce displayed waveforms to avoid display freezes
|
||||
CSI_VAID_SUBCARRIER_INTERVAL = 1
|
||||
csi_vaid_subcarrier_len =0
|
||||
|
||||
CSI_DATA_INDEX = 200 # buffer size
|
||||
CSI_DATA_COLUMNS = 490
|
||||
DATA_COLUMNS_NAMES_C5C6 = ['type', 'id', 'mac', 'rssi', 'rate','noise_floor','fft_gain','agc_gain', 'channel', 'local_timestamp', 'sig_len', 'rx_state', 'len', 'first_word', 'data']
|
||||
DATA_COLUMNS_NAMES = ['type', 'id', 'mac', 'rssi', 'rate', 'sig_mode', 'mcs', 'bandwidth', 'smoothing', 'not_sounding', 'aggregation', 'stbc', 'fec_coding',
|
||||
'sgi', 'noise_floor', 'ampdu_cnt', 'channel', 'secondary_channel', 'local_timestamp', 'ant', 'sig_len', 'rx_state', 'len', 'first_word', 'data']
|
||||
|
||||
csi_data_array = np.zeros(
|
||||
[CSI_DATA_INDEX, CSI_DATA_COLUMNS], dtype=np.float64)
|
||||
csi_data_phase = np.zeros([CSI_DATA_INDEX, CSI_DATA_COLUMNS], dtype=np.float64)
|
||||
csi_data_complex = np.zeros([CSI_DATA_INDEX, CSI_DATA_COLUMNS], dtype=np.complex64)
|
||||
agc_gain_data = np.zeros([CSI_DATA_INDEX], dtype=np.float64)
|
||||
fft_gain_data = np.zeros([CSI_DATA_INDEX], dtype=np.float64)
|
||||
fft_gains = []
|
||||
agc_gains = []
|
||||
|
||||
class csi_data_graphical_window(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.resize(1280, 900)
|
||||
|
||||
self.plotWidget_ted = PlotWidget(self)
|
||||
self.plotWidget_ted.setGeometry(QtCore.QRect(0, 0, 640, 300))
|
||||
self.plotWidget_ted.setYRange(-2*np.pi, 2*np.pi)
|
||||
self.plotWidget_ted.addLegend()
|
||||
self.plotWidget_ted.setTitle('Phase Data - Last Frame') # 添加标题
|
||||
self.plotWidget_ted.setLabel('left', 'Phase (rad)') # Y轴标签
|
||||
self.plotWidget_ted.setLabel('bottom', 'Subcarrier Index') # X轴标签
|
||||
|
||||
self.csi_amplitude_array = np.abs(csi_data_complex)
|
||||
self.csi_phase_array = np.angle(csi_data_complex)
|
||||
self.curve = self.plotWidget_ted.plot([], name='CSI Row Data', pen='r')
|
||||
|
||||
self.plotWidget_multi_data = PlotWidget(self)
|
||||
self.plotWidget_multi_data.setGeometry(QtCore.QRect(0, 300, 1280, 300))
|
||||
self.plotWidget_multi_data.getViewBox().enableAutoRange(axis=pg.ViewBox.YAxis)
|
||||
self.plotWidget_multi_data.addLegend()
|
||||
self.plotWidget_multi_data.setTitle('Subcarrier Amplitude Data') # 添加标题
|
||||
self.plotWidget_multi_data.setLabel('left', 'Amplitude') # Y轴标签
|
||||
self.plotWidget_multi_data.setLabel('bottom', 'Time (Cumulative Packet Count)') # X轴标签
|
||||
|
||||
self.curve_list = []
|
||||
agc_curve = self.plotWidget_multi_data.plot(
|
||||
agc_gain_data, name='AGC Gain', pen=[255,255,0])
|
||||
fft_curve = self.plotWidget_multi_data.plot(
|
||||
fft_gain_data, name='FFT Gain', pen=[255,255,0])
|
||||
self.curve_list.append(agc_curve)
|
||||
self.curve_list.append(fft_curve)
|
||||
|
||||
for i in range(CSI_DATA_COLUMNS):
|
||||
curve = self.plotWidget_multi_data.plot(
|
||||
self.csi_amplitude_array[:, i], name=str(i), pen=(255, 255, 255))
|
||||
self.curve_list.append(curve)
|
||||
|
||||
|
||||
|
||||
self.plotWidget_phase_data = PlotWidget(self)
|
||||
self.plotWidget_phase_data.setGeometry(QtCore.QRect(0, 600, 1280, 300))
|
||||
self.plotWidget_phase_data.getViewBox().enableAutoRange(axis=pg.ViewBox.YAxis)
|
||||
self.plotWidget_phase_data.addLegend()
|
||||
self.plotWidget_multi_data.setTitle('Subcarrier Phase Data') # 添加标题
|
||||
self.plotWidget_multi_data.setLabel('left', 'Phase (rad)') # Y轴标签
|
||||
self.plotWidget_multi_data.setLabel('bottom', 'Time (Cumulative Packet Count)') # X轴标签
|
||||
|
||||
|
||||
self.curve_phase_list = []
|
||||
for i in range(CSI_DATA_COLUMNS):
|
||||
phase_curve = self.plotWidget_phase_data.plot(
|
||||
np.angle(self.csi_amplitude_array[:, i]), name=str(i), pen=(255, 255, 255))
|
||||
self.curve_phase_list.append(phase_curve)
|
||||
|
||||
|
||||
# IQ 图窗口
|
||||
self.plotWidget_iq = PlotWidget(self)
|
||||
self.plotWidget_iq.setGeometry(QtCore.QRect(640, 0, 640, 300))
|
||||
self.plotWidget_iq.setLabel('left', 'Q (Imag)')
|
||||
self.plotWidget_iq.setLabel('bottom', 'I (Real)')
|
||||
self.plotWidget_iq.setTitle('IQ Plot - Last Frame')
|
||||
view_box = self.plotWidget_iq.getViewBox()
|
||||
view_box.setRange(QtCore.QRectF(-30, -30, 60, 60)) # 可以调整范围的大小,保证原点在中间
|
||||
|
||||
self.plotWidget_iq.getViewBox().setAspectLocked(True)
|
||||
self.iq_scatter = ScatterPlotItem(size=6)
|
||||
self.plotWidget_iq.addItem(self.iq_scatter)
|
||||
|
||||
self.iq_colors = []
|
||||
|
||||
|
||||
|
||||
self.timer = pg.QtCore.QTimer()
|
||||
self.timer.timeout.connect(self.update_data)
|
||||
self.timer.start(100)
|
||||
self.deta_len = 0
|
||||
|
||||
def update_curve_colors(self, color_list):
|
||||
self.deta_len = len(color_list)
|
||||
self.iq_colors = color_list
|
||||
self.plotWidget_ted.setXRange(0, self.deta_len//2)
|
||||
for i in range(self.deta_len):
|
||||
self.curve_list[i].setPen(color_list[i])
|
||||
self.curve_phase_list[i].setPen(color_list[i])
|
||||
|
||||
def update_data(self):
|
||||
|
||||
i = np.real(csi_data_complex[-1, :])
|
||||
q = np.imag(csi_data_complex[-1, :])
|
||||
|
||||
points = []
|
||||
for idx in range(self.deta_len):
|
||||
if idx < len(self.iq_colors):
|
||||
color = self.iq_colors[idx]
|
||||
else:
|
||||
color = (200, 200, 200)
|
||||
points.append({'pos': (i[idx], q[idx]), 'brush': pg.mkBrush(color)})
|
||||
|
||||
self.iq_scatter.setData(points)
|
||||
|
||||
|
||||
self.csi_amplitude_array = np.abs(csi_data_complex)
|
||||
self.csi_phase_array = np.angle(csi_data_complex)
|
||||
self.csi_row_data = self.csi_phase_array[-1, :]
|
||||
|
||||
self.curve.setData(self.csi_row_data)
|
||||
|
||||
self.curve_list[CSI_DATA_COLUMNS].setData(agc_gain_data)
|
||||
self.curve_list[CSI_DATA_COLUMNS+1].setData(fft_gain_data)
|
||||
|
||||
for i in range(CSI_DATA_COLUMNS):
|
||||
self.curve_list[i].setData(self.csi_amplitude_array[:, i])
|
||||
self.curve_phase_list[i].setData(self.csi_phase_array[:, i])
|
||||
|
||||
def generate_subcarrier_colors(red_range, green_range, yellow_range, total_num,interval=1):
|
||||
colors = []
|
||||
for i in range(total_num):
|
||||
if red_range and red_range[0] <= i <= red_range[1]:
|
||||
intensity = int(255 * (i - red_range[0]) / (red_range[1] - red_range[0]))
|
||||
colors.append((intensity, 0, 0))
|
||||
elif green_range and green_range[0] <= i <= green_range[1]:
|
||||
intensity = int(255 * (i - green_range[0]) / (green_range[1] - green_range[0]))
|
||||
colors.append((0, intensity, 0))
|
||||
elif yellow_range and yellow_range[0] <= i <= yellow_range[1]:
|
||||
intensity = int(255 * (i - yellow_range[0]) / (yellow_range[1] - yellow_range[0]))
|
||||
colors.append((0, intensity, intensity))
|
||||
else:
|
||||
colors.append((200, 200, 200))
|
||||
|
||||
return colors
|
||||
|
||||
|
||||
def csi_data_read_parse(port: str, csv_writer, log_file_fd,callback=None):
|
||||
global fft_gains, agc_gains
|
||||
set = serial.Serial(port=port, baudrate=921600,bytesize=8, parity='N', stopbits=1)
|
||||
count =0
|
||||
if set.isOpen():
|
||||
print('open success')
|
||||
else:
|
||||
print('open failed')
|
||||
return
|
||||
while True:
|
||||
strings = str(set.readline())
|
||||
if not strings:
|
||||
break
|
||||
strings = strings.lstrip('b\'').rstrip('\\r\\n\'')
|
||||
index = strings.find('CSI_DATA')
|
||||
|
||||
if index == -1:
|
||||
log_file_fd.write(strings + '\n')
|
||||
log_file_fd.flush()
|
||||
continue
|
||||
|
||||
csv_reader = csv.reader(StringIO(strings))
|
||||
csi_data = next(csv_reader)
|
||||
csi_data_len = int (csi_data[-3])
|
||||
if len(csi_data) != len(DATA_COLUMNS_NAMES) and len(csi_data) != len(DATA_COLUMNS_NAMES_C5C6):
|
||||
print('element number is not equal',len(csi_data),len(DATA_COLUMNS_NAMES) )
|
||||
# print(csi_data)
|
||||
log_file_fd.write('element number is not equal\n')
|
||||
log_file_fd.write(strings + '\n')
|
||||
log_file_fd.flush()
|
||||
continue
|
||||
|
||||
try:
|
||||
csi_raw_data = json.loads(csi_data[-1])
|
||||
except json.JSONDecodeError:
|
||||
print('data is incomplete')
|
||||
log_file_fd.write('data is incomplete\n')
|
||||
log_file_fd.write(strings + '\n')
|
||||
log_file_fd.flush()
|
||||
continue
|
||||
if csi_data_len != len(csi_raw_data):
|
||||
print('csi_data_len is not equal',csi_data_len,len(csi_raw_data))
|
||||
log_file_fd.write('csi_data_len is not equal\n')
|
||||
log_file_fd.write(strings + '\n')
|
||||
log_file_fd.flush()
|
||||
continue
|
||||
|
||||
fft_gain = int(csi_data[6])
|
||||
agc_gain = int(csi_data[7])
|
||||
|
||||
fft_gains.append(fft_gain)
|
||||
agc_gains.append(agc_gain)
|
||||
|
||||
csv_writer.writerow(csi_data)
|
||||
|
||||
# Rotate data to the left
|
||||
# csi_data_array[:-1] = csi_data_array[1:]
|
||||
# csi_data_phase[:-1] = csi_data_phase[1:]
|
||||
csi_data_complex[:-1] = csi_data_complex[1:]
|
||||
agc_gain_data[:-1] = agc_gain_data[1:]
|
||||
fft_gain_data[:-1] = fft_gain_data[1:]
|
||||
agc_gain_data[-1] = agc_gain
|
||||
fft_gain_data[-1] = fft_gain
|
||||
|
||||
if count ==0:
|
||||
count = 1
|
||||
print('none',csi_data_len)
|
||||
if csi_data_len == 106:
|
||||
colors = generate_subcarrier_colors((0,25), (27,53), None, len(csi_raw_data))
|
||||
elif csi_data_len == 114:
|
||||
colors = generate_subcarrier_colors((0,27), (29,56), None, len(csi_raw_data))
|
||||
elif csi_data_len == 52:
|
||||
colors = generate_subcarrier_colors((0,12), (13,26), None, len(csi_raw_data))
|
||||
elif csi_data_len == 234 :
|
||||
colors = generate_subcarrier_colors((0,28), (29,56), (60,116), len(csi_raw_data))
|
||||
elif csi_data_len == 228 :
|
||||
colors = generate_subcarrier_colors((0,28), (29,57), (57,113), len(csi_raw_data))
|
||||
elif csi_data_len == 490 :
|
||||
colors = generate_subcarrier_colors((0,61), (62,122), (123,245), len(csi_raw_data))
|
||||
elif csi_data_len == 128 :
|
||||
colors = generate_subcarrier_colors((0,31), (32,63), None, len(csi_raw_data))
|
||||
elif csi_data_len == 256 :
|
||||
colors = generate_subcarrier_colors((0,32), (32,63), (64,128), len(csi_raw_data))
|
||||
elif csi_data_len == 512 :
|
||||
colors = generate_subcarrier_colors((0,63), (64,127), (128,256), len(csi_raw_data))
|
||||
elif csi_data_len == 384 :
|
||||
colors = generate_subcarrier_colors((0,63), (64,127), (128,192), len(csi_raw_data))
|
||||
elif csi_data_len > 0 and csi_data_len <= 612:
|
||||
raw_len = len(csi_raw_data)
|
||||
colors = generate_subcarrier_colors((0,raw_len//2), (raw_len//2+1,raw_len-1), None, raw_len)
|
||||
callback(colors)
|
||||
|
||||
for i in range(csi_data_len // 2):
|
||||
csi_data_complex[-1][i] = complex(csi_raw_data[i * 2 + 1],
|
||||
csi_raw_data[i * 2])
|
||||
set.close()
|
||||
return
|
||||
|
||||
|
||||
class SubThread (QThread):
|
||||
data_ready = pyqtSignal(object)
|
||||
def __init__(self, serial_port, save_file_name, log_file_name):
|
||||
super().__init__()
|
||||
self.serial_port = serial_port
|
||||
|
||||
save_file_fd = open(save_file_name, 'w')
|
||||
self.log_file_fd = open(log_file_name, 'w')
|
||||
self.csv_writer = csv.writer(save_file_fd)
|
||||
self.csv_writer.writerow(DATA_COLUMNS_NAMES)
|
||||
|
||||
def run(self):
|
||||
csi_data_read_parse(self.serial_port, self.csv_writer, self.log_file_fd,callback=self.data_ready.emit)
|
||||
|
||||
def __del__(self):
|
||||
self.wait()
|
||||
self.log_file_fd.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.version_info < (3, 6):
|
||||
print(' Python version should >= 3.6')
|
||||
exit()
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Read CSI data from serial port and display it graphically')
|
||||
parser.add_argument('-p', '--port', dest='port', action='store', required=True,
|
||||
help='Serial port number of csv_recv device')
|
||||
parser.add_argument('-s', '--store', dest='store_file', action='store', default='./csi_data.csv',
|
||||
help='Save the data printed by the serial port to a file')
|
||||
parser.add_argument('-l', '--log', dest='log_file', action='store', default='./csi_data_log.txt',
|
||||
help='Save other serial data the bad CSI data to a log file')
|
||||
|
||||
args = parser.parse_args()
|
||||
serial_port = args.port
|
||||
file_name = args.store_file
|
||||
log_file_name = args.log_file
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
subthread = SubThread(serial_port, file_name, log_file_name)
|
||||
|
||||
window = csi_data_graphical_window()
|
||||
subthread.data_ready.connect(window.update_curve_colors)
|
||||
subthread.start()
|
||||
window.show()
|
||||
|
||||
sys.exit(app.exec())
|
||||
399
get-started/tools/csi_data_read_parse_1.py
Executable file
399
get-started/tools/csi_data_read_parse_1.py
Executable file
@@ -0,0 +1,399 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*-coding:utf-8-*-
|
||||
|
||||
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
# WARNING: we don't check for Python build-time dependencies until
|
||||
# check_environment() function below. If possible, avoid importing
|
||||
# any external libraries here - put in external script, or import in
|
||||
# their specific function instead.
|
||||
|
||||
import sys
|
||||
import csv
|
||||
import json
|
||||
import argparse
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
import serial
|
||||
from os import path
|
||||
from io import StringIO
|
||||
|
||||
from PyQt5.Qt import *
|
||||
from pyqtgraph import PlotWidget
|
||||
from PyQt5 import QtCore
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph import ScatterPlotItem
|
||||
from PyQt5.QtCore import pyqtSignal, QThread
|
||||
import threading
|
||||
import time
|
||||
from scipy.optimize import minimize
|
||||
import matplotlib.pyplot as plt
|
||||
from scipy.stats import linregress
|
||||
import statsmodels.api as sm
|
||||
|
||||
# Reduce displayed waveforms to avoid display freezes
|
||||
CSI_VAID_SUBCARRIER_INTERVAL = 1
|
||||
csi_vaid_subcarrier_len =0
|
||||
|
||||
CSI_DATA_INDEX = 200 # buffer size
|
||||
CSI_DATA_COLUMNS = 490
|
||||
DATA_COLUMNS_NAMES_C5C6 = ['type', 'id', 'mac', 'rssi', 'rate','noise_floor','fft_gain','agc_gain', 'channel', 'local_timestamp', 'sig_len', 'rx_state', 'len', 'first_word', 'data']
|
||||
DATA_COLUMNS_NAMES_NEW = ['type', 'mac','len', 'first_word', 'data']
|
||||
DATA_COLUMNS_NAMES = ['type', 'id', 'mac', 'rssi', 'rate', 'sig_mode', 'mcs', 'bandwidth', 'smoothing', 'not_sounding', 'aggregation', 'stbc', 'fec_coding',
|
||||
'sgi', 'noise_floor', 'ampdu_cnt', 'channel', 'secondary_channel', 'local_timestamp', 'ant', 'sig_len', 'rx_state', 'len', 'first_word', 'data']
|
||||
|
||||
csi_data_array = np.zeros(
|
||||
[CSI_DATA_INDEX, CSI_DATA_COLUMNS], dtype=np.float64)
|
||||
csi_data_phase = np.zeros([CSI_DATA_INDEX, CSI_DATA_COLUMNS], dtype=np.float64)
|
||||
csi_data_complex = np.zeros([CSI_DATA_INDEX, CSI_DATA_COLUMNS], dtype=np.complex64)
|
||||
agc_gain_data = np.zeros([CSI_DATA_INDEX], dtype=np.float64)
|
||||
fft_gain_data = np.zeros([CSI_DATA_INDEX], dtype=np.float64)
|
||||
fft_gains = []
|
||||
agc_gains = []
|
||||
|
||||
# RAW_DATA 独立数据数组
|
||||
RAW_DATA_COLUMNS = 612 # RAW_DATA 最大长度
|
||||
raw_data_complex = np.zeros([CSI_DATA_INDEX, RAW_DATA_COLUMNS], dtype=np.complex64)
|
||||
|
||||
class csi_data_graphical_window(QWidget):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
self.resize(1280, 900)
|
||||
|
||||
self.plotWidget_ted = PlotWidget(self)
|
||||
self.plotWidget_ted.setGeometry(QtCore.QRect(0, 0, 640, 300))
|
||||
self.plotWidget_ted.setYRange(-2*np.pi, 2*np.pi)
|
||||
self.plotWidget_ted.addLegend()
|
||||
self.plotWidget_ted.setTitle('Phase Data - Last Frame') # 添加标题
|
||||
self.plotWidget_ted.setLabel('left', 'Phase (rad)') # Y轴标签
|
||||
self.plotWidget_ted.setLabel('bottom', 'Subcarrier Index') # X轴标签
|
||||
|
||||
self.csi_amplitude_array = np.abs(csi_data_complex)
|
||||
self.csi_phase_array = np.angle(csi_data_complex)
|
||||
self.curve = self.plotWidget_ted.plot([], name='CSI Phase', pen='r')
|
||||
|
||||
# RAW_DATA 相位曲线(蓝色)
|
||||
self.raw_amplitude_array = np.abs(raw_data_complex)
|
||||
self.raw_phase_array = np.angle(raw_data_complex)
|
||||
self.curve_raw = self.plotWidget_ted.plot([], name='RAW Phase', pen='b')
|
||||
|
||||
self.plotWidget_multi_data = PlotWidget(self)
|
||||
self.plotWidget_multi_data.setGeometry(QtCore.QRect(0, 300, 1280, 300))
|
||||
self.plotWidget_multi_data.getViewBox().enableAutoRange(axis=pg.ViewBox.YAxis)
|
||||
self.plotWidget_multi_data.addLegend()
|
||||
self.plotWidget_multi_data.setTitle('Subcarrier Amplitude Data') # 添加标题
|
||||
self.plotWidget_multi_data.setLabel('left', 'Amplitude') # Y轴标签
|
||||
self.plotWidget_multi_data.setLabel('bottom', 'Time (Cumulative Packet Count)') # X轴标签
|
||||
|
||||
self.curve_list = []
|
||||
agc_curve = self.plotWidget_multi_data.plot(
|
||||
agc_gain_data, name='AGC Gain', pen=[255,255,0])
|
||||
fft_curve = self.plotWidget_multi_data.plot(
|
||||
fft_gain_data, name='FFT Gain', pen=[255,255,0])
|
||||
self.curve_list.append(agc_curve)
|
||||
self.curve_list.append(fft_curve)
|
||||
|
||||
for i in range(CSI_DATA_COLUMNS):
|
||||
curve = self.plotWidget_multi_data.plot(
|
||||
self.csi_amplitude_array[:, i], name=str(i), pen=(255, 255, 255))
|
||||
self.curve_list.append(curve)
|
||||
|
||||
|
||||
|
||||
self.plotWidget_phase_data = PlotWidget(self)
|
||||
self.plotWidget_phase_data.setGeometry(QtCore.QRect(0, 600, 1280, 300))
|
||||
self.plotWidget_phase_data.getViewBox().enableAutoRange(axis=pg.ViewBox.YAxis)
|
||||
self.plotWidget_phase_data.addLegend()
|
||||
self.plotWidget_multi_data.setTitle('Subcarrier Phase Data') # 添加标题
|
||||
self.plotWidget_multi_data.setLabel('left', 'Phase (rad)') # Y轴标签
|
||||
self.plotWidget_multi_data.setLabel('bottom', 'Time (Cumulative Packet Count)') # X轴标签
|
||||
|
||||
|
||||
self.curve_phase_list = []
|
||||
for i in range(CSI_DATA_COLUMNS):
|
||||
phase_curve = self.plotWidget_phase_data.plot(
|
||||
np.angle(self.csi_amplitude_array[:, i]), name=str(i), pen=(255, 255, 255))
|
||||
self.curve_phase_list.append(phase_curve)
|
||||
|
||||
|
||||
# IQ 图窗口
|
||||
self.plotWidget_iq = PlotWidget(self)
|
||||
self.plotWidget_iq.setGeometry(QtCore.QRect(640, 0, 640, 300))
|
||||
self.plotWidget_iq.setLabel('left', 'Q (Imag)')
|
||||
self.plotWidget_iq.setLabel('bottom', 'I (Real)')
|
||||
self.plotWidget_iq.setTitle('IQ Plot - Last Frame')
|
||||
view_box = self.plotWidget_iq.getViewBox()
|
||||
view_box.setRange(QtCore.QRectF(-30, -30, 60, 60)) # 可以调整范围的大小,保证原点在中间
|
||||
|
||||
self.plotWidget_iq.getViewBox().setAspectLocked(True)
|
||||
self.iq_scatter = ScatterPlotItem(size=6)
|
||||
self.plotWidget_iq.addItem(self.iq_scatter)
|
||||
|
||||
self.iq_colors = []
|
||||
|
||||
|
||||
|
||||
self.timer = pg.QtCore.QTimer()
|
||||
self.timer.timeout.connect(self.update_data)
|
||||
self.timer.start(100)
|
||||
self.deta_len = 0
|
||||
|
||||
def update_curve_colors(self, color_list):
|
||||
self.deta_len = len(color_list)
|
||||
self.iq_colors = color_list
|
||||
self.plotWidget_ted.setXRange(0, self.deta_len//2)
|
||||
for i in range(self.deta_len):
|
||||
self.curve_list[i].setPen(color_list[i])
|
||||
self.curve_phase_list[i].setPen(color_list[i])
|
||||
|
||||
def update_data(self):
|
||||
|
||||
i = np.real(csi_data_complex[-1, :])
|
||||
q = np.imag(csi_data_complex[-1, :])
|
||||
|
||||
# points = []
|
||||
# for idx in range(self.deta_len):
|
||||
# if idx < len(self.iq_colors):
|
||||
# color = self.iq_colors[idx]
|
||||
# else:
|
||||
# color = (200, 200, 200)
|
||||
# points.append({'pos': (i[idx], q[idx]), 'brush': pg.mkBrush(color)})
|
||||
|
||||
# self.iq_scatter.setData(points)
|
||||
|
||||
# CSI_DATA 相位更新
|
||||
self.csi_amplitude_array = np.abs(csi_data_complex)
|
||||
self.csi_phase_array = np.angle(csi_data_complex)
|
||||
self.csi_row_data = self.csi_phase_array[-1, :]
|
||||
|
||||
self.csi_row_data = np.unwrap(self.csi_row_data)
|
||||
self.curve.setData(self.csi_row_data)
|
||||
|
||||
# RAW_DATA 相位更新
|
||||
self.raw_amplitude_array = np.abs(raw_data_complex)
|
||||
self.raw_phase_array = np.angle(raw_data_complex)
|
||||
self.raw_row_data = self.raw_phase_array[-1, :]
|
||||
|
||||
self.raw_row_data = np.unwrap(self.raw_row_data)
|
||||
self.curve_raw.setData(self.raw_row_data)
|
||||
|
||||
# self.curve_list[CSI_DATA_COLUMNS].setData(agc_gain_data)
|
||||
# self.curve_list[CSI_DATA_COLUMNS+1].setData(fft_gain_data)
|
||||
|
||||
for i in range(CSI_DATA_COLUMNS):
|
||||
self.curve_list[i].setData(self.csi_amplitude_array[:, i])
|
||||
# self.curve_phase_list[i].setData(self.csi_phase_array[:, i])
|
||||
|
||||
def generate_subcarrier_colors(red_range, green_range, yellow_range, total_num,interval=1):
|
||||
colors = []
|
||||
for i in range(total_num):
|
||||
if red_range and red_range[0] <= i <= red_range[1]:
|
||||
intensity = int(255 * (i - red_range[0]) / (red_range[1] - red_range[0]))
|
||||
colors.append((intensity, 0, 0))
|
||||
elif green_range and green_range[0] <= i <= green_range[1]:
|
||||
intensity = int(255 * (i - green_range[0]) / (green_range[1] - green_range[0]))
|
||||
colors.append((0, intensity, 0))
|
||||
elif yellow_range and yellow_range[0] <= i <= yellow_range[1]:
|
||||
intensity = int(255 * (i - yellow_range[0]) / (yellow_range[1] - yellow_range[0]))
|
||||
colors.append((0, intensity, intensity))
|
||||
else:
|
||||
colors.append((200, 200, 200))
|
||||
|
||||
return colors
|
||||
|
||||
|
||||
def csi_data_read_parse(port: str, csv_writer, log_file_fd,callback=None):
|
||||
global fft_gains, agc_gains
|
||||
set = serial.Serial(port=port, baudrate=921600,bytesize=8, parity='N', stopbits=1)
|
||||
csi_count = 0 # CSI_DATA 计数器
|
||||
raw_count = 0 # RAW_DATA 计数器
|
||||
if set.isOpen():
|
||||
print('open success')
|
||||
else:
|
||||
print('open failed')
|
||||
return
|
||||
while True:
|
||||
strings = str(set.readline())
|
||||
if not strings:
|
||||
break
|
||||
strings = strings.lstrip('b\'').rstrip('\\r\\n\'')
|
||||
index = strings.find('CSI_DATA')
|
||||
raw_index = strings.find('RAW_DATA')
|
||||
|
||||
if index == -1 and raw_index == -1:
|
||||
log_file_fd.write(strings + '\n')
|
||||
log_file_fd.flush()
|
||||
continue
|
||||
|
||||
# RAW_DATA 单独处理
|
||||
if raw_index != -1:
|
||||
csv_reader = csv.reader(StringIO(strings))
|
||||
csi_data = next(csv_reader)
|
||||
csi_data_len = int(csi_data[-3])
|
||||
|
||||
try:
|
||||
csi_raw_data = json.loads(csi_data[-1])
|
||||
except json.JSONDecodeError:
|
||||
print('RAW_DATA is incomplete')
|
||||
log_file_fd.write('RAW_DATA is incomplete\n')
|
||||
log_file_fd.write(strings + '\n')
|
||||
log_file_fd.flush()
|
||||
continue
|
||||
|
||||
if csi_data_len != len(csi_raw_data):
|
||||
print('RAW_DATA csi_data_len is not equal',csi_data_len,len(csi_raw_data))
|
||||
log_file_fd.write('RAW_DATA csi_data_len is not equal\n')
|
||||
log_file_fd.write(strings + '\n')
|
||||
log_file_fd.flush()
|
||||
continue
|
||||
|
||||
# RAW_DATA 使用独立的数据数组
|
||||
raw_data_complex[:-1] = raw_data_complex[1:]
|
||||
# 清空当前行
|
||||
raw_data_complex[-1, :] = 0
|
||||
|
||||
if raw_count == 0:
|
||||
raw_count = 1
|
||||
print('RAW_DATA detected, length:', csi_data_len)
|
||||
# RAW_DATA 不需要初始化颜色,只是接收数据显示相位
|
||||
|
||||
# RAW_DATA 转换为复数存入独立数组
|
||||
for i in range(csi_data_len // 2):
|
||||
raw_data_complex[-1][i] = complex(csi_raw_data[i * 2 + 1],
|
||||
csi_raw_data[i * 2])
|
||||
continue
|
||||
|
||||
# CSI_DATA 处理
|
||||
csv_reader = csv.reader(StringIO(strings))
|
||||
csi_data = next(csv_reader)
|
||||
csi_data_len = int (csi_data[-3])
|
||||
if len(csi_data) != len(DATA_COLUMNS_NAMES) and len(csi_data) != len(DATA_COLUMNS_NAMES_C5C6 ) and len(csi_data) != len(DATA_COLUMNS_NAMES_NEW):
|
||||
print('element number is not equal',len(csi_data),len(DATA_COLUMNS_NAMES) )
|
||||
print(strings)
|
||||
log_file_fd.write('element number is not equal\n')
|
||||
log_file_fd.write(strings + '\n')
|
||||
log_file_fd.flush()
|
||||
continue
|
||||
|
||||
try:
|
||||
csi_raw_data = json.loads(csi_data[-1])
|
||||
except json.JSONDecodeError:
|
||||
print('data is incomplete')
|
||||
log_file_fd.write('data is incomplete\n')
|
||||
log_file_fd.write(strings + '\n')
|
||||
log_file_fd.flush()
|
||||
continue
|
||||
if csi_data_len != len(csi_raw_data):
|
||||
print('csi_data_len is not equal',csi_data_len,len(csi_raw_data))
|
||||
log_file_fd.write('csi_data_len is not equal\n')
|
||||
log_file_fd.write(strings + '\n')
|
||||
log_file_fd.flush()
|
||||
continue
|
||||
|
||||
fft_gain = 0 # int(csi_data[6])
|
||||
agc_gain = 0 # int(csi_data[7])
|
||||
|
||||
fft_gains.append(fft_gain)
|
||||
agc_gains.append(agc_gain)
|
||||
|
||||
csv_writer.writerow(csi_data)
|
||||
|
||||
# Rotate data to the left
|
||||
# csi_data_array[:-1] = csi_data_array[1:]
|
||||
# csi_data_phase[:-1] = csi_data_phase[1:]
|
||||
csi_data_complex[:-1] = csi_data_complex[1:]
|
||||
agc_gain_data[:-1] = agc_gain_data[1:]
|
||||
fft_gain_data[:-1] = fft_gain_data[1:]
|
||||
agc_gain_data[-1] = agc_gain
|
||||
fft_gain_data[-1] = fft_gain
|
||||
|
||||
if csi_count == 0:
|
||||
csi_count = 1
|
||||
print('CSI_DATA detected, length:',csi_data_len)
|
||||
if csi_data_len == 106:
|
||||
colors = generate_subcarrier_colors((0,25), (27,53), None, len(csi_raw_data))
|
||||
elif csi_data_len == 114:
|
||||
colors = generate_subcarrier_colors((0,27), (29,56), None, len(csi_raw_data))
|
||||
elif csi_data_len == 52:
|
||||
colors = generate_subcarrier_colors((0,12), (13,26), None, len(csi_raw_data))
|
||||
elif csi_data_len == 234 :
|
||||
colors = generate_subcarrier_colors((0,28), (29,56), (60,116), len(csi_raw_data))
|
||||
elif csi_data_len == 228 :
|
||||
colors = generate_subcarrier_colors((0,28), (29,56), (57,114), len(csi_raw_data))
|
||||
elif csi_data_len == 328 :
|
||||
colors = generate_subcarrier_colors((0,164), None, None, len(csi_raw_data))
|
||||
elif csi_data_len == 490 :
|
||||
colors = generate_subcarrier_colors((0,61), (62,122), (123,245), len(csi_raw_data))
|
||||
elif csi_data_len == 128 :
|
||||
colors = generate_subcarrier_colors((0,31), (32,63), None, len(csi_raw_data))
|
||||
elif csi_data_len == 256 :
|
||||
colors = generate_subcarrier_colors((0,32), (32,63), (64,128), len(csi_raw_data))
|
||||
elif csi_data_len == 512 :
|
||||
colors = generate_subcarrier_colors((0,63), (64,127), (128,256), len(csi_raw_data))
|
||||
elif csi_data_len == 384 :
|
||||
colors = generate_subcarrier_colors((0,63), (64,127), (128,192), len(csi_raw_data))
|
||||
else:
|
||||
print('Please add more color schemes.')
|
||||
csi_count = 0
|
||||
continue
|
||||
callback(colors)
|
||||
|
||||
for i in range(csi_data_len // 2):
|
||||
csi_data_complex[-1][i] = complex(csi_raw_data[i * 2 + 1],
|
||||
csi_raw_data[i * 2])
|
||||
set.close()
|
||||
return
|
||||
|
||||
|
||||
class SubThread (QThread):
|
||||
data_ready = pyqtSignal(object)
|
||||
def __init__(self, serial_port, save_file_name, log_file_name):
|
||||
super().__init__()
|
||||
self.serial_port = serial_port
|
||||
|
||||
save_file_fd = open(save_file_name, 'w')
|
||||
self.log_file_fd = open(log_file_name, 'w')
|
||||
self.csv_writer = csv.writer(save_file_fd)
|
||||
self.csv_writer.writerow(DATA_COLUMNS_NAMES)
|
||||
|
||||
def run(self):
|
||||
csi_data_read_parse(self.serial_port, self.csv_writer, self.log_file_fd,callback=self.data_ready.emit)
|
||||
|
||||
def __del__(self):
|
||||
self.wait()
|
||||
self.log_file_fd.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if sys.version_info < (3, 6):
|
||||
print(' Python version should >= 3.6')
|
||||
exit()
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Read CSI data from serial port and display it graphically')
|
||||
parser.add_argument('-p', '--port', dest='port', action='store', required=True,
|
||||
help='Serial port number of csv_recv device')
|
||||
parser.add_argument('-s', '--store', dest='store_file', action='store', default='./csi_data.csv',
|
||||
help='Save the data printed by the serial port to a file')
|
||||
parser.add_argument('-l', '--log', dest='log_file', action='store', default='./csi_data_log.txt',
|
||||
help='Save other serial data the bad CSI data to a log file')
|
||||
|
||||
args = parser.parse_args()
|
||||
serial_port = args.port
|
||||
file_name = args.store_file
|
||||
log_file_name = args.log_file
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
subthread = SubThread(serial_port, file_name, log_file_name)
|
||||
|
||||
window = csi_data_graphical_window()
|
||||
subthread.data_ready.connect(window.update_curve_colors)
|
||||
subthread.start()
|
||||
window.show()
|
||||
|
||||
sys.exit(app.exec())
|
||||
Reference in New Issue
Block a user