Skip to content

Getting Started

Ryan Kuederle edited this page Nov 5, 2025 · 2 revisions

This page will help you get started with collecting WiFi CSI data with python. Before following this guide, make sure you have configured a CSI transmitter and receiver using one of the following guides:

Setting Up the Python Environment

When using Python, it is often best to setup a virtual environment to isolate your dependencies. To do this in a new project directory, run the following command:

python3 -m venv .venv

This will create a python virtual environment in the .venv directory. To use this environment source the activation script. You should do this every time you use the python environment in the terminal, or your IDE may activate this automatically (VS Code with the python extension does):

. ./.venv/bin/activate

CSI-Py Installation

Install the csi-py python package to make collecting csi data easier.

pip install csi-py

Using csi-py to collect CSI data

First, create a python file, which can be called anything, but lets use csi_collection.py. In this python file, add the following code.

import sys
from csi_py.csireader import CSIReader

def print_rssi(data):
    print(f"RSSI: {data.meta['rssi']}")

def main():
    if len(sys.argv) < 2:
        print("Usage: python main.py <serial_port>")
        sys.exit(1)
    port = sys.argv[1]
    reader = CSIReader(port, data_callback=print_rssi, rate=20)
    try:
        reader.run()
    except KeyboardInterrupt:
        reader.stop()
        print("Stopped.")

if __name__ == "__main__":
    main()

Let's first look at the first couple of lines:

import sys
from csi_py.csireader import CSIReader

First, we import python's built-in sys package which will allow us to take in command line arguments. In addition, we will import the CSIReader class from the csi-py package's csireader file.

Then, we define a callback function, called print_rssi, to handle CSI data. In the main function, we process the first command line argument with the following snippet:

    if len(sys.argv) < 2:
        print("Usage: python main.py <serial_port>")
        sys.exit(1)
    port = sys.argv[1]

To collect the CSI data, we will create an object of the CSIReader class. To do so, we'll define a port, a callback function, and a refresh rate. By calling the run function of the CSIReader class, the callback function will be called passing one object that represents the CSI data.

The data object has the following format:

class CSIData:
    def __init__(self, amplitude, phase, raw, meta):
        self.amplitude = amplitude # CSI data amplitude array
        self.phase = phase # CSI data phase array
        self.raw = raw # CSI data raw array
        self.meta = meta # CSI data metadata

The meta object is a python dictionary with the following fields:

channel # the WiFi signal channel
bandwidth # WiFi signal bandwidth
noise_floor # WiFi noise floor
rate_index # WiFi physical layer data rate
rssi # Received signal strength indicator
mac # Sending device MAC address.

To run this script, after sourcing your virtual environment, run the command:

python3 csi_collection.py <serial_port>

Be sure to replace <serial_port> with the serial port of your ESP32 device. To find this on Mac, for example, run the command in terminal: ls /dev/tty.usb*. The port should be listed in the terminal.

Making Your Own Script

Say we want to make a script that adds the values in the CSI raw data array. This would look like the following:

import sys
from csi_py.csireader import CSIReader

def sum_raw(data): # The callback function has been renamed.
    raw = data.raw
    output = sum(raw) # Not doing anything with this now, but it can be used.

def main():
    if len(sys.argv) < 2:
        print("Usage: python main.py <serial_port>")
        sys.exit(1)
    port = sys.argv[1]
    reader = CSIReader(port, data_callback=sum_raw, rate=20)
    try:
        reader.run()
    except KeyboardInterrupt:
        reader.stop()
        print("Stopped.")

if __name__ == "__main__":
    main()

Clone this wiki locally