### Initial Setup and Rendering Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Calls the necessary functions to bind events, apply language settings, clear the configuration form, start the rendering loop, perform an initial render, and log a ready message. ```javascript bindEvents(); applyLanguage(); clearConfigForm(); startRenderLoop(); renderAll(); logEvent(t("logReady")); ``` -------------------------------- ### Install Python Dependencies and Run Graphical Display Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/README.md Install the necessary Python dependencies for data analysis and then run the graphical display script. Ensure idf.py monitor is closed before running. ```shell cd esp-csi-gitlab/examples/get-started/tools pip install -r requirements.txt python csi_data_read_parse.py -p /dev/ttyUSB1 ``` -------------------------------- ### Build and Flash Project Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/csi_recv_router/README.md Build the project, flash it to the development board, and start the serial monitor to view output. Replace PORT with your device's serial port. ```bash idf.py -p PORT flash monitor ``` -------------------------------- ### Start Data Streaming Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Begins streaming data from the connected device. This is necessary to view real-time charts and logs. ```javascript startStreamBtn: "开始流" ``` -------------------------------- ### Motion Detection Chart Configuration Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Guides the user on interpreting the motion detection chart and adjusting sensitivity. ```javascript currentStateTitle: "运动检测" ``` ```javascript processingChartTitle: "运动检测" ``` ```javascript processingChartDesc: "显示 exttt{smooth\_scaled}(绿)、 exttt{enter\_level\_scaled}(橙)、 exttt{exit\_level\_scaled}(红)。" ``` ```javascript motionThresholdGuide: "曲线与运动状态 exttt{smooth\_scaled} 持续高于 橙色 exttt{enter\_level\_scaled} → 趋向 运动 ACTIVE持续低于 红色 exttt{exit\_level\_scaled} → 趋向 运动 INACTIVE。在配置区调节 运动检测灵敏度。" ``` -------------------------------- ### Presence Detection and Training Guide Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Explains how to interpret the presence detection chart and the role of training and sensitivity settings. ```javascript presenceSectionTitle: "存在检测 / 训练" ``` ```javascript presenceThresholdGuide: "曲线与存在状态 训练就绪后,绿线(平滑后的 wander)高于 橙色 exttt{presence\_someone\_threshold} → 有人;不高于 → 无人。在配置区调节 存在检测灵敏度(与 运动检测灵敏度 分开)。" ``` -------------------------------- ### CSI Receive Router Example Output Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/csi_recv_router/README.md This is the typical output log from the CSI Receive Router example, showing Wi-Fi connection details and parsed CSI data packets. It illustrates the format and content of captured CSI information. ```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: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]" ``` -------------------------------- ### Set up ESP-IDF Development Environment Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/console_test/README.md Installs and configures the necessary ESP-IDF version for the project. Ensure you are using a version of ESP-IDF that supports destination address filtering for better results. ```bash cd esp-idf git checkout v5.0.2 git submodule update --init --recursive ./install.sh . ./export.sh ``` -------------------------------- ### Build and Flash ESP-IDF Project Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/README.md Standard commands to set the target chip and then build and flash the ESP-IDF project. Ensure Wi-Fi credentials are configured if required by your local setup. ```bash idf.py set-target idf.py flash monitor ``` -------------------------------- ### Start Train Automation Ticker Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Starts an interval timer that calls 'renderTrainAutomationStatus' every 250ms to provide real-time updates. Clears any existing ticker first. ```javascript function startTrainAutomationTicker() { clearTrainAutomationTicker(); trainAutomation.tickerTimer = window.setInterval(renderTrainAutomationStatus, 250); renderTrainAutomationStatus(); } ``` -------------------------------- ### Set Component Compile Options Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/connect_rainmaker/main/CMakeLists.txt Applies a specific compile option to the component library. This example disables format string warnings. ```cmake target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") ``` -------------------------------- ### ESP-CRAB CSI Data Format Example Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-crab/README.md This is an example of the CSI data printed to the serial port by the ESP-CRAB device. It includes various parameters related to the Wi-Fi packet reception. ```text CSI_DATA,3537,1a:00:00:00:00:00,-17,11,159,22,5,8,859517,47,0,234,0,"[14,9,13,...,-11]" ``` -------------------------------- ### Run CSI Visualization Tool Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/console_test/README.md Installs Python dependencies and launches the `esp_csi_tool.py` script for visualizing CSI data. Ensure `idf.py monitor` is closed before running this command. Use the UART port for communication. ```bash cd esp-csi/examples/console_test/tools # Install python related dependencies pip install -r requirements.txt # Graphical display python esp_csi_tool.py -p /dev/ttyUSB1 ``` -------------------------------- ### Start Render Loop Function Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Initializes a recurring interval timer to call the renderAll function, ensuring the UI is continuously updated. Prevents multiple timers from being created if already running. ```javascript function startRenderLoop() { if (redrawTimer) { return; } redrawTimer = window.setInterval(renderAll, 120); } ``` -------------------------------- ### Get Display Source Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Returns the current state or a paused snapshot of the state, depending on whether the UI is paused. ```javascript function getDisplaySource() { return state.uiPaused && state.pausedSnapshot ? state.pausedSnapshot : state; } ``` -------------------------------- ### Start Delayed Training Function Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Initiates training on a selected peer with optional delay and duration. Handles stopping any ongoing training before scheduling the new one. Logs the scheduled training event. ```javascript async function startDelayedTraining() { if (!selectedPeer) { logEvent(t("logTrainNoPeer")); return; } const peerName = selectedPeer; const delayValue = Number(trainDelayInput.value || 0); const durationValue = Number(trainDurationInput.value || 0); const delaySec = Number.isFinite(delayValue) ? Math.max(0, delayValue) : 0; const durationSec = Number.isFinite(durationValue) ? Math.max(1, durationValue) : 1; if (trainAutomation.phase === "running" && trainAutomation.peer) { await sendTrainCommand("TRAIN_STOP", trainAutomation.peer); } cancelTrainAutomation(null); if (delaySec <= 0) { await beginAutomatedTraining(peerName, durationSec); return; } trainAutomation.phase = "waiting"; trainAutomation.peer = peerName; trainAutomation.startAtMs = Date.now() + delaySec * 1000; trainAutomation.startTimer = window.setTimeout(async () => { await beginAutomatedTraining(peerName, durationSec); }, delaySec * 1000); startTrainAutomationTicker(); logEvent(`${t("logTrainScheduled")}: ${peerName}, delay=${delaySec}s, duration=${durationSec}s`); } ``` -------------------------------- ### Example CSI Data Output Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/csi_recv_router/README.md This snippet shows the typical output format for CSI data captured by the ESP32. It includes MAC addresses, signal strength, and a detailed array of CSI values. ```text 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 ``` -------------------------------- ### Chart Enablement and Interaction Setup Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Configures event listeners for chart toggles to manage which charts are displayed and binds chart interactions for zooming and panning. ```javascript chartToggleConfigs.forEach((config) => { const input = document.getElementById(config.inputId); if (!input) { return; } input.checked = state.chartEnabled[config.key] === true; input.addEventListener("change", () => { state.chartEnabled[config.key] = input.checked; applyChartEnabled(config.key); if (state.uiPaused) { renderAll(true); } else { scheduleRender(); } }); }); applyAllChartEnabled(); bindChartInteractions(phaseCanvas, state.chartViews.phase); bindChartInteractions(iqCanvas, state.chartViews.iq); bindChartInteractions(amplitudeCanvas, state.chartViews.amplitude); bindChartInteractions(phaseHistoryCanvas, state.chartViews.phaseHistory); ``` -------------------------------- ### Get Selected Peer Samples Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Retrieves the samples associated with the currently selected peer. Returns an empty array if no peer is selected or if the peer has no samples. ```javascript function getSelectedSamples() { const peer = selectedPeer ? peers.get(selectedPeer) : null; return peer?.samples || []; } ``` -------------------------------- ### Get Sample Ratio Text Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Returns the applied sample stride as a formatted string (e.g., '3:1'). ```javascript function getSampleRatioText() { return String(state.appliedSampleStride) + ":1"; } ``` -------------------------------- ### CMakeLists.txt Boilerplate Configuration Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-crab/slave_recv/CMakeLists.txt Essential CMake commands that must be included in your project's CMakeLists.txt file in the specified order for the build system to operate correctly. This setup is standard for ESP-IDF projects. ```cmake 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}) add_definitions(-D CHIP_ID=0) string(REGEX REPLACE ".*/\(.*".*:" \1 CURDIR ${CMAKE_CURRENT_SOURCE_DIR}) project(${CURDIR}) git_describe(PROJECT_VERSION ${COMPONENT_DIR}) message("Project commit: " ${PROJECT_VERSION}) ``` -------------------------------- ### Start Serial Data Reading Loop Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Continuously reads data from the serial port, decodes it, and processes each line. Handles read errors and connection termination. ```javascript async function startReadLoop() { if (!state.port?.readable) { return; } const decoder = new TextDecoder(); let buffer = ""; try { state.reader = state.port.readable.getReader(); while (true) { const { value, done } = await state.reader.read(); if (done) { break; } buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop() || ""; for (const rawLine of lines) { handleSerialLine(rawLine.replace(/\r$/, "")); } } } catch (error) { if (!state.disconnecting) { addEvent("eventReadError", { message: error.message }); } } finally { if (state.reader) { state.reader.releaseLock(); state.reader = null; } if (state.connected && !state.disconnecting) { await disconnectSerial(false); addEvent("eventReadEnded"); } } } ``` -------------------------------- ### Get History Live Window X Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Calculates the visible X-axis range for history charts based on packet IDs and a default window size. Returns null if no valid packet IDs are found. ```javascript function getHistoryLiveWindowX(packetIds) { if (packetIds.length === 0) { return null; } let firstId = Number.POSITIVE_INFINITY; let lastId = Number.NEGATIVE_INFINITY; for (let i = 0; i < packetIds.length; i += 1) { const v = packetIds[i]; if (!Number.isFinite(v)) continue; if (v < firstId) firstId = v; if (v > lastId) lastId = v; } if (!Number.isFinite(firstId) || !Number.isFinite(lastId)) { return null; } const liveMin = Math.max(firstId, lastId - (DEFAULT_HISTORY_WINDOW_SIZE - 1)); return { min: liveMin, max: lastId }; } ``` -------------------------------- ### Get Input Sample Stride Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Retrieves the sample stride value from the input element, ensuring it's a finite number greater than or equal to 1. Defaults to 3 if invalid. ```javascript function getInputSampleStride() { const value = Number.parseInt(sampleStrideInput.value, 10); return Number.isFinite(value) && value >= 1 ? value : 3; } ``` -------------------------------- ### Start Serial Read Loop Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Continuously reads data from the serial port, decodes it, and processes incoming lines. It buffers data, splits it into lines, appends raw lines to the log, and attempts to parse JSON messages. ```javascript async function startReadLoop() { if (!port?.readable) { return; } reader = port.readable.getReader(); let buffer = ""; try { while (true) { const { value, done } = await reader.read(); if (done) { break; } buffer += new TextDecoder().decode(value, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop(); for (const rawLine of lines) { const line = rawLine.replace(/\r$/, ""); appendRawLine(line); const trimmedLine = line.trim(); if (!trimmedLine) { continue; } if (!trimmedLine.startsWith(HMS_PREFIX)) { continue; } try { handleMessage(JSON.parse(trimmedLine.slice(HMS_PREFIX.length))); } catch (error) { logEvent(`${t("logJsonParseFailed")}: ${error.message}`); } } } } catch (error) { logEvent(`${t("logReadEnded")}: ${error.message}`); } finally { if (reader) { reader.releaseLock(); reader = null; } } } ``` -------------------------------- ### Configure Project Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/csi_recv_router/README.md Open the project configuration menu to set up Wi-Fi or Ethernet connections. Refer to the ESP-IDF protocols README for detailed connection instructions. ```bash idf.py menuconfig ``` -------------------------------- ### Serve Demo Directory Locally Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/README.md Command to serve the 'tools' directory locally using Python's http.server. This is useful if the browser blocks file:// access for Web Serial. ```bash cd tools python3 -m http.server ``` -------------------------------- ### Initialize and Update Configuration Form Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Handles the initialization and updating of the configuration form based on device settings. It clears the form, sets placeholders, and populates input fields with current configuration values. ```javascript const placeholder = (!peer?.cfg) ? t("configWaitingPlaceholder") : ""; for (const input of Object.values(configInputs)) { input.placeholder = placeholder; } ``` ```javascript function clearConfigForm() { for (const input of Object.values(configInputs)) { input.value = ""; } setConfigFormEnabled(false); updateConfigFormPlaceholder(); } ``` ```javascript function hydrateConfigForm() { const peer = selectedPeer ? peers.get(selectedPeer) : null; if (!peer?.cfg) { clearConfigForm(); return; } for (const [key, input] of Object.entries(configInputs)) { const v = peer.cfg[key]; if (v !== undefined && v !== null && v !== "") { input.value = String(v); } else if (key === "presence_sensitivity") { input.value = "0.25"; } else { input.value = ""; } } setConfigFormEnabled(true); updateConfigFormPlaceholder(); } ``` -------------------------------- ### Interpolate RGB Color Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Interpolates an RGB color between a start and end color based on a value within a min/max range. ```javascript function interpolateColor(start, end, value, min, max) { if (max <= min) { return "rgb(" + end.join(",") + ")"; } const ratio = (value - min) / (max - min); const rgb = start.map((channel, index) => { return Math.round(channel + (end[index] - channel) * ratio); }); return "rgb(" + rgb.join(",") + ")"; } ``` -------------------------------- ### Clear Train Automation Timers Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Clears all active timers associated with train automation (start, stop, ticker) to prevent them from firing. ```javascript function clearTrainAutomationTimers() { if (trainAutomation.startTimer !== null) { window.clearTimeout(trainAutomation.startTimer); trainAutomation.startTimer = null; } if (trainAutomation.stopTimer !== null) { window.clearTimeout(trainAutomation.stopTimer); trainAutomation.stopTimer = null; } if (trainAutomation.tickerTimer !== null) { window.clearInterval(trainAutomation.tickerTimer); trainAutomation.tickerTimer = null; } } ``` -------------------------------- ### Registering Project Components and Sources Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-crab/master_recv/main/CMakeLists.txt This snippet shows how to define source directories and register them with the ESP-IDF build system. It uses `file(GLOB_RECURSE)` to find all `.c` files in specified directories and then registers these sources and include directories using `idf_component_register`. ```cmake set(LV_UI_DIR ui) file(GLOB_RECURSE LV_UI_SOURCES ${LV_UI_DIR}/*.c) set(APP_DIR app) file(GLOB_RECURSE APP_SOURCES ${APP_DIR}/*.c) idf_component_register(SRCS "app_main.c" ${LV_UI_SOURCES} ${APP_SOURCES} INCLUDE_DIRS "." ${LV_UI_DIR} ${APP_DIR}) ``` -------------------------------- ### Get Viewport Axis Range Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Calculates the appropriate axis range for a chart viewport, considering user-defined views, base ranges, and constraints. ```javascript function getViewportAxisRange(viewMin, viewMax, baseMin, baseMax, opts) { const baseSpan = Math.max(baseMax - baseMin, 1e-9); const minSpan = Math.max(baseSpan * 0.005, 1e-6); const maxSpan = baseSpan * 20; let min = Number.isFinite(viewMin) ? viewMin : baseMin; let max = Number.isFinite(viewMax) ? viewMax : baseMax; if (!(max > min)) { min = baseMin; max = baseMax; } if (opts && opts.freeRange && Number.isFinite(viewMin) && Number.isFinite(viewMax) && viewMax > viewMin) { return { min, max, baseSpan, minSpan }; } let span = max - min; if (span > maxSpan) { const center = (min + max) / 2; min = center - maxSpan / 2; max = center + maxSpan / 2; span = maxSpan; } if (span > baseSpan) { return { min, max, baseSpan, minSpan }; } if (span < minSpan) { const center = (min + max) / 2; min = center - minSpan / 2; max = center + minSpan / 2; span = max - min; } if (min < baseMin) { max += baseMin - min; min = baseMin; } if (max > baseMax) { min -= max - baseMax; max = baseMax; } if (min < baseMin) { min = baseMin; } if (max > baseMa } ``` -------------------------------- ### Get Canvas Local Point Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Converts client coordinates (relative to the viewport) to coordinates local to the canvas element. Useful for precise positioning within the canvas. ```javascript function getCanvasLocalPoint(canvas, clientX, clientY) { const rect = canvas.getBoundingClientRect(); return { x: clientX - rect.left, y: clientY - rect.top }; } ``` -------------------------------- ### Initialize Chart Viewports Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Creates and initializes viewport objects for different charts based on canvas elements. Sets up initial state for chart axes and domains. ```javascript const phaseCanvas = document.getElementById("phaseCanvas"); const iqCanvas = document.getElementById("iqCanvas"); const amplitudeCanvas = document.getElementById("amplitudeCanvas"); const phaseHistoryCanvas = document.getElementById("phaseHistoryCanvas"); function createChartViewport(canvas) { return { canvas, xActive: false, yActive: false, xMin: null, xMax: null, yMin: null, yMax: null, plotRect: null, baseDomain: null, currentDomain: null, freeX: false, xPartner: null, }; } state.chartViews = { phase: createChartViewport(phaseCanvas), iq: createChartViewport(iqCanvas), amplitude: createChartViewport(amplitudeCanvas), phaseHistory: createChartViewport(phaseHistoryCanvas), }; state.chartViews.amplitude.freeX = true; state.chartViews.phaseHistory.freeX = true; state.chartViews.amplitude.xPartner = state.chartViews.phaseHistory; state.chartViews.phaseHistory.xPartner = state.chartViews.amplitude; ``` -------------------------------- ### Render All UI Components Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Updates the entire user interface, including target information, amplitude logging status, peer list, statistics, and charts. ```javascript function renderAll() { targetText.textContent = appInfo.target || "-"; amplitudeLogText.textContent = appInfo.amplitude_log_enabled ? "ON" : "OFF"; renderPeerList(); renderStats(); renderCharts(); } ``` -------------------------------- ### Peer State and Initialization Labels Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Labels related to the state and initialization of peers or channels. ```javascript peerStateLabel: "State" ``` ```javascript peerInitLabel: "Init" ``` ```javascript peerJitterLabel: "Jitter" ``` ```javascript peerWanderLabel: "Wander" ``` ```javascript peerTrainThrLabel: "Train wander thr" ``` -------------------------------- ### Download Text File Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Initiates a file download for the given text content with the specified filename and MIME type. ```javascript function downloadText(filename, content, mimeType) { const blob = new Blob([content], { type: mimeType }); const url = URL.createObjectURL(blob); const anchor = document.createElement("a"); anchor.href = url; anchor.download = filename; anchor.style.display = "none"; document.body.appendChild(anchor); anchor.click(); setTimeout(() => { anchor.remove(); URL.revokeObjectURL(url); }, 0); } ``` -------------------------------- ### Training Status and Action Labels Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Defines the possible statuses and actions during the training process. ```javascript trainStatusNone: "none" ``` ```javascript trainStatusProgress: "in progress" ``` ```javascript trainStatusComplete: "complete" ``` ```javascript trainActionIdle: "idle" ``` ```javascript trainActionWaitBuffer: "waiting buffer" ``` ```javascript trainActionDiscardOutlier: "discard outlier" ``` ```javascript trainActionCollectSample: "collect sample" ``` ```javascript trainActionAccumulateBackground: "accumulate bg" ``` -------------------------------- ### Configuration Panel Labels Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Labels for various configuration options within the settings panel. ```javascript configPanelTitle: "配置面板" ``` ```javascript cfgMotionSensitivityLabel: "运动检测灵敏度" ``` ```javascript cfgPresenceSensitivityLabel: "存在检测灵敏度" ``` ```javascript applyConfigBtn: "应用当前配置" ``` ```javascript configWaitingPlaceholder: "等待设备同步..." ``` -------------------------------- ### Handle Manual Train Stop Function Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Stops an ongoing or scheduled training session. If the training is in a 'waiting' state, it cancels the scheduled start. Otherwise, it sends a TRAIN_STOP command to the peer. ```javascript async function handleManualTrainStop() { if (trainAutomation.phase === "waiting") { cancelTrainAutomation("logTrainDelayCancelled"); return; } const peerName = trainAutomation.peer || selectedPeer; if (!peerName) { logEvent(t("logTrainNoPeer")); return; } cancelTrainAutomation("logTrainTimerCleared"); await sendTrainCommand("TRAIN_STOP", peerName); } ``` -------------------------------- ### Get Chart Interaction Region Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Identifies which region of the chart the client coordinates correspond to (plot area, x-axis, y-axis, or outside). Essential for handling different types of user interactions. ```javascript function getChartInteractionRegion(view, clientX, clientY) { if (!view.plotRect) { return "outside"; } const point = getCanvasLocalPoint(view.canvas, clientX, clientY); const plotLeft = view.plotRect.left; const plotRight = view.plotRect.left + view.plotRect.width; const plotTop = view.plotRect.top; const plotBottom = view.plotRect.top + view.plotRect.height; if ( point.x >= plotLeft && point.x <= plotRight && point.y >= plotTop && point.y <= plotBottom ) { return "plot"; } if ( point.x >= plotLeft && point.x <= plotRight && point.y > plotBottom && point.y <= view.canvas.clientHeight ) { return "x-axis"; } if ( point.x >= 0 && point.x < plotLeft && point.y >= plotTop && point.y <= plotBottom ) { return "y-axis"; } return "outside"; } ``` -------------------------------- ### Flash csi_recv Firmware Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/README.md Flash the csi_recv firmware onto your development board. Ensure the target is set correctly and use the specified baud rate and serial port. ```shell cd esp-csi/examples/get-started/csi_recv idf.py set-target esp32c3 idf.py flash -b 921600 -p /dev/ttyUSB1 ``` -------------------------------- ### Apply Sample Stride Setting Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Applies the selected sample stride value to the application state and triggers a re-render. It also logs an event indicating the sample stride has been applied. ```javascript function applySampleStride() { const stride = getInputSampleStride(); sampleStrideInput.value = String(stride); state.appliedSampleStride = stride; scheduleRender(); addEvent("eventSampleStrideApplied", { stride }); } ``` -------------------------------- ### Flash csi_send Firmware Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/README.md Flash the csi_send firmware onto your development board. Ensure the target is set correctly and use the specified baud rate and serial port. ```shell cd esp-csi/examples/get-started/csi_send idf.py set-target esp32c3 idf.py flash -b 921600 -p /dev/ttyUSB0 monitor ``` -------------------------------- ### Window Resize and Beforeunload Event Handlers Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Handles window resize events to schedule UI rendering and manages the serial port disconnection before the page unloads. ```javascript window.addEventListener("resize", scheduleRender); window.addEventListener("beforeunload", () => { if (state.port) { disconnectSerial(false); } }); ``` -------------------------------- ### Get Displayed Carrier Indices Source: https://github.com/espressif/esp-csi/blob/master/examples/get-started/tools/csi_viewer.html Calculates the indices of carriers to be displayed based on the applied sample stride and the total count of carriers. Ensures at least one index is returned if count > 0. ```javascript function getDisplayedCarrierIndices(count) { const stride = state.appliedSampleStride; const indices = []; for (let i = 0; i < count; i += stride) { indices.push(i); } if (indices.length === 0 && count > 0) { indices.push(0); } return indices; } ``` -------------------------------- ### ESP Wi-Fi Sensing Web Serial Monitor JavaScript Configuration Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Defines constants and initial states for the ESP Wi-Fi Sensing Web Serial Monitor application. Includes settings for message prefixes, maximum sample/line counts, and timers. ```javascript const HMS_PREFIX = "HMS:"; const MAX_SAMPLES = 240; const MAX_RAW_LINES = 400; const MAX_RAW_CHARS = 120000; const RAW_LOG_FLUSH_MS = 80; const peers = new Map(); const peerCards = new Map(); const eventLog = []; const rawSerialLines = []; let port = null; let reader = null; let writer = null; let selectedPeer = null; let appInfo = { target: "-", stream_period_ms: 0, amplitude_log_enabled: false }; let redrawTimer = null; let rawLogCharCount = 0; let rawLogFlushTimer = null; let currentLanguage = "en"; ``` -------------------------------- ### CMakeLists.txt Configuration for ESP-CRAB Slave Receiver Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-crab/slave_recv/main/CMakeLists.txt This snippet configures the build process for the ESP-CRAB slave receiver. It sets the application directory, finds all C source files recursively, and registers the component with its source files and include directories. ```cmake set(APP_DIR app) file(GLOB_RECURSE APP_SOURCES ${APP_DIR}/*.c) idf_component_register(SRCS "app_main.c" ${APP_SOURCES} INCLUDE_DIRS "." ${APP_DIR}) ``` -------------------------------- ### Compile and Flash CSI Receiver Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/console_test/README.md Compiles and flashes the `console_test` project to a separate ESP32 DevKitC board. This board will receive and process the Wi-Fi CSI data. ```bash cd esp-csi/examples/console_test idf.py set-target esp32s3 idf.py flash -b 921600 -p /dev/ttyUSB1 ``` -------------------------------- ### Peer Management and Rendering Functions Source: https://github.com/espressif/esp-csi/blob/master/examples/esp-radar/wifi_sensing_demo/tools/web_serial_monitor.html Manages peer connections, updates peer information, and renders the peer list UI. Includes functions for state badge styling and selecting active peers. ```javascript function addPeer(name, mac = "") { if (!peers.has(name)) { peers.set(name, { name, mac, latest: null, cfg: null, samples: [], }); } const peer = peers.get(name); if (mac && !peer.mac) { peer.mac = mac; } if (!selectedPeer) { selectedPeer = name; } return peer; } function stateBadgeClass(name) { if (name === "ACTIVE") { return "active"; } if (name && name.startsWith("DEBOUNCE")) { return "debounce"; } return ""; } function selectPeer(name) { if (!name || selectedPeer === name) { return; } selectedPeer = name; hydrateConfigForm(); renderAll(); } function renderPeerList() { const list = Array.from(peers.values()); const visiblePeers = new Set(); for (const peer of list) { let card = peerCards.get(peer.name); if (!card) { card = document.createElement("div"); card.tabIndex = 0; card.addEventListener("pointerdown", (event) => { event.preventDefault(); selectPeer(peer.name); }); card.addEventListener("keydown", (event) => { if (event.key === "Enter" || event.key === " ") { event.preventDefault(); selectPeer(peer.name); } }); peerCards.set(peer.name, card); } visiblePeers.add(peer.name); card.className = "peer-card" + (peer.name === selectedPeer ? " active" : ""); const latest = peer.latest || {}; const badgeClass = stateBadgeClass(latest.state_name); card.innerHTML = `
${peer.name} ${latest.state_name || "-"}
MAC
${peer.mac || "-"}
${t("peerStateLabel")}
${latest.state_name || "-"}
${t("peerInitLabel")}
${latest.init_stage_name || "-"}
${t("peerJitterLabel")}
${Number(latest.jitter_value || 0).toFixed(3)}
${t("peerWanderLabel")}
${Number(latest.wander_value || 0).toFixed(4)}
${t("peerTrainThrLabel")}
${latest.train_thresholds_valid ? Number(latest.train_wander_threshold || 0).toFixed(4) : "-"}
`; peerList.appendChild(card); } for (const [name, card] of peerCards.entries()) { if (!visiblePeers.has(name)) { card.remove(); peerCards.delete(name); } } } ```