Counting people by using wifi packages from their mobile Phones

Hi everyone
i found a source code that count people using wifi packets. i upload this code on my ESP32. it starts counting wifis and mac addresses but i cant find my cell phone mac address. is this code counting cell phones Wifi mac address when if only wifi is tuned on ? or i should turn my hotspot?
here is the link of source code:

here is my code :

/*
Change from https://github.com/ESP-EOS/ESP32-WiFi-Sniffer

*/

#include "freertos/FreeRTOS.h"
#include "esp_wifi.h"
#include "esp_wifi_types.h"
#include "esp_system.h"
#include "esp_event.h"
#include "esp_event_loop.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#include <Arduino.h>
#include <Wire.h>
#include <WirePacker.h>

#define SDA_PIN 21
#define SCL_PIN 22
#define I2C_SLAVE_ADDR 0x04

//Makerfabs init
// #include "makerfabs_pin.h"
// #include "SPI.h"
// #include "SD.h"
// #include "FS.h"
// #include <Adafruit_GFX.h>
// #include <Adafruit_SSD1306.h>

//#define THINGSPEAK

#ifdef THINGSPEAK

#define DEBUG true //true: debug on; false:debug off
bool SIM800C_ON = false;
int my_index = 0;
#define MP_RX1 21
#define MP_TX1 22

#define A9G_POWER 27
#define A9G_RST 33

#endif
int rssi_value = 0;
String filename = "";
// Adafruit_SSD1306 display(MP_ESP32_SSD1306_WIDTH, MP_ESP32_SSD1306_HEIGHT, &Wire, MP_ESP32_SSD1306_RST);

#define WIFI_CHANNEL_SWITCH_INTERVAL (500)
#define WIFI_CHANNEL_MAX (13)

uint8_t mac_lib[100][6];
int mac_count = 0;
String CompareStr;
String _str = "";

uint8_t level = 0, channel = 1;

static wifi_country_t wifi_country = {.cc = "IR", .schan = 1, .nchan = 13}; //Most recent esp32 library struct

typedef struct
{
  unsigned frame_ctrl : 16;
  unsigned duration_id : 16;
  uint8_t addr1[6]; /* receiver address */
  uint8_t addr2[6]; /* sender address */
  uint8_t addr3[6]; /* filtering address */
  unsigned sequence_ctrl : 16;
  uint8_t addr4[6]; /* optional */
} wifi_ieee80211_mac_hdr_t;

typedef struct
{
  wifi_ieee80211_mac_hdr_t hdr;
  uint8_t payload[0]; /* network data ended with 4 bytes csum (CRC32) */
} wifi_ieee80211_packet_t;

static esp_err_t event_handler(void *ctx, system_event_t *event);
static void wifi_sniffer_init(void);
static void wifi_sniffer_set_channel(uint8_t channel);
static const char *wifi_sniffer_packet_type2str(wifi_promiscuous_pkt_type_t type);
static void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type);

// the setup function runs once when you press reset or power the board
void setup()
{
  // initialize digital pin 5 as an output.
  Serial.begin(115200);
  delay(10);
    Wire.begin(SDA_PIN, SCL_PIN);   // join i2c bus
  //LCD init
  // Wire.begin(MP_ESP32_I2C_SDA, MP_ESP32_I2C_SCL);
  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  // if (!display.begin(SSD1306_SWITCHCAPVCC, MP_ESP32_SSD1306_I2C_ADDR))
  // { // Address 0x3C for 128x32
  //   Serial.println(F("SSD1306 allocation failed"));
  //   for (;;)
  //     ; // Don't proceed, loop forever
  // }
  // display.clearDisplay();
  // lcd_text("WIFI SNIFFER");

#ifdef THINGSPEAK
  thingspeak_init();
#endif

  //SD(SPI)
  // pinMode(MP_A9G_SD_CS, OUTPUT);
  // digitalWrite(MP_A9G_SD_CS, HIGH);
  // SPI.begin(MP_A9G_SPI_SCK, MP_A9G_SPI_MISO, MP_A9G_SPI_MOSI);
  // SPI.setFrequency(1000000);
  // if (!SD.begin(MP_A9G_SD_CS, SPI))
  // {
  //   Serial.println("Card Mount Failed");
  //   lcd_text("Card Mount Failed");
  //   while (1)
  //     ;
  // }
  // else
  // {
  //   Serial.println("SD OK");
  // }

  int log_index = 0;
  // while (1)
  // {
  //   filename = "/log" + String(log_index) + ".txt";
  //   Serial.println("Check if the " + filename + " exists ");
  //   // if (!SD.exists(filename))
  //   // {
  //   //   //Open log
  //   //   writeFile(SD, filename, "WIFI COUNT:");
  //   //   break;
  //   // }
  //   log_index++;
  // }

  // lcd_text(filename);

  //Init sniffer
  wifi_sniffer_init();
}

// the loop function runs over and over again forever
void loop()
{

  Serial.printf("______________CHANNEL:%02d____________\n", channel);
  delay(1000); // wait for a second

  vTaskDelay(WIFI_CHANNEL_SWITCH_INTERVAL / portTICK_PERIOD_MS);
  wifi_sniffer_set_channel(channel);
  channel = (channel % WIFI_CHANNEL_MAX) + 1;
  if (channel == 1)
  {
    String data = create_data_str(mac_count, get_run_time());
    Serial.println(data);
    // appendFile(SD, filename, data);

    //lcd_text(String(mac_count) + "/" + String(millis() / 1000));
    // lcd_data_show(mac_count);
    String mac_count_recognizer = "111";

            char recognizer[mac_count_recognizer.length() + 1]; // +1 for null terminator
        mac_count_recognizer.toCharArray(recognizer, mac_count_recognizer.length() + 1);

        WirePacker packer;


                  String count_str = String(mac_count);
     
        char McCount[count_str.length() + 1]; // +1 for null terminator
        count_str.toCharArray(McCount, count_str.length() + 1);
        packer.write((const uint8_t*)recognizer, mac_count_recognizer.length() + 1);
        packer.write((const uint8_t*)McCount, count_str.length() + 1);
        // then add data the same way as you would with Wire
        // packer.write("Count is ");
        // packer.write(mac_count_recognizer);
        // packer.write(mac_count);

        // after adding all data you want to send, close the packet
        packer.end();

        // now transmit the packed data
        Wire.beginTransmission(I2C_SLAVE_ADDR);
        while (packer.available()) {    // write every packet byte
            Wire.write(packer.read());
        }
        Wire.endTransmission();         // stop transmitting


#ifdef THINGSPEAK
    thingspeak_upload(mac_count);
#endif

    mac_count = 0;
  }
}

//Check and Save Only MAC
int check_mac_only(const uint8_t addr3[6])
{
  for (char i = 0; i < mac_count; i++)
  {
    bool flag = true;
    for (char j = 0; j < 6; j++)
    {
      if (mac_lib[i][j] != addr3[j])
      {
        flag = false;
        break;
      }
    }
    if (flag == true)
      return 0;
  }
  for (char j = 0; j < 6; j++)
  {
    mac_lib[mac_count][j] = addr3[j];
  }
  mac_count++;
  return 1;
}

// void writeFile(fs::FS &fs, String path, String message)
// {
//   Serial.println("Writing file: " + path);

//   File file = fs.open(path, FILE_WRITE);
//   if (!file)
//   {
//     Serial.println("Failed to open file for writing");
//     return;
//   }
//   if (file.println(message))
//   {
//     Serial.println("File written");
//   }
//   else
//   {
//     Serial.println("Write failed");
//   }
//   file.close();
// }

// void appendFile(fs::FS &fs, String path, String message)
// {
//   Serial.println("Appending to file: " + path);

//   File file = fs.open(path, FILE_APPEND);

//   if (bool(file))
//   {
//     if (file.println(message))
//     {
//       Serial.println("Message appended");
//     }
//     else
//     {
//       Serial.println("Append failed");
//       lcd_text("SD WRITE ERROR");
//       while (1)
//         ;
//     }
//     file.close();
//   }
//   else
//   {
//     Serial.println("Failed to open file for appending");
//     file.close();
//     return;
//   }
// }

int get_run_time()
{
  return millis() / 1000;
}

String create_data_str(int count, int run_time)
{
  String data = "";
  data = "{'COUNT':'" + String(mac_count) + "','TIME':'" + String(get_run_time()) + "'}";
  return data;
}

// void lcd_text(String text)
// {
//   display.clearDisplay();

//   display.setTextSize(2);              // Normal 1:1 pixel scale
//   display.setTextColor(SSD1306_WHITE); // Draw white text
//   display.setCursor(0, 0);             // Start at top-left corner
//   display.println(text);
//   display.display();
//   delay(500);
// }

// void lcd_data_show(int count)
// {
//   display.clearDisplay();

//   display.setTextSize(2);              // Normal 1:1 pixel scale
//   display.setTextColor(SSD1306_WHITE); // Draw white text
//   display.setCursor(0, 0);             // Start at top-left corner
//   display.print("Count:");
//   display.println(count);
//   display.setTextSize(1);
//   display.setCursor(0, 20);
//   display.print("Time(s):");
//   display.println(millis() / 1000);
//   display.setCursor(0, 40);
//   display.println("Filename:");
//   display.println(filename);
//   display.display();
//   delay(500);
// }

esp_err_t event_handler(void *ctx, system_event_t *event)
{
  return ESP_OK;
}

void wifi_sniffer_init(void)
{
  nvs_flash_init();
  tcpip_adapter_init();
  ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
  wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  ESP_ERROR_CHECK(esp_wifi_init(&cfg));
  ESP_ERROR_CHECK(esp_wifi_set_country(&wifi_country)); /* set country for channel range [1, 13] */
  ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
  ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
  ESP_ERROR_CHECK(esp_wifi_start());
  esp_wifi_set_promiscuous(true);
  esp_wifi_set_promiscuous_rx_cb(&wifi_sniffer_packet_handler);
}

void wifi_sniffer_set_channel(uint8_t channel)
{
  esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE);
}

const char *wifi_sniffer_packet_type2str(wifi_promiscuous_pkt_type_t type)
{
  switch (type)
  {
  case WIFI_PKT_MGMT:
    return "MGMT";
  case WIFI_PKT_DATA:
    return "DATA";
  default:
  case WIFI_PKT_MISC:
    return "MISC";
  }
}

void wifi_sniffer_packet_handler(void *buff, wifi_promiscuous_pkt_type_t type)
{
  if (type != WIFI_PKT_MGMT)
    return;

  const wifi_promiscuous_pkt_t *ppkt = (wifi_promiscuous_pkt_t *)buff;
  const wifi_ieee80211_packet_t *ipkt = (wifi_ieee80211_packet_t *)ppkt->payload;
  const wifi_ieee80211_mac_hdr_t *hdr = &ipkt->hdr;

char addr3_str[18];
  if (check_mac_only(hdr->addr3)){


    Serial.printf("PACKET TYPE=%s, CHAN=%02d, RSSI=%02d,"
                  " ADDR1=%02x:%02x:%02x:%02x:%02x:%02x,"
                  " ADDR2=%02x:%02x:%02x:%02x:%02x:%02x,"
                  " ADDR3=%02x:%02x:%02x:%02x:%02x:%02x\n",
                  wifi_sniffer_packet_type2str(type),
                  ppkt->rx_ctrl.channel,
                  ppkt->rx_ctrl.rssi,


                  // addr3_str,
                  // Serial.println(addr3_str),
                  /* ADDR1 */
                  hdr->addr1[0], hdr->addr1[1], hdr->addr1[2],
                  hdr->addr1[3], hdr->addr1[4], hdr->addr1[5],
                  /* ADDR2 */
                  hdr->addr2[0], hdr->addr2[1], hdr->addr2[2],
                  hdr->addr2[3], hdr->addr2[4], hdr->addr2[5],
                  /* ADDR3 */
                  hdr->addr3[0], hdr->addr3[1], hdr->addr3[2],
                  hdr->addr3[3], hdr->addr3[4], hdr->addr3[5]);

                  // Serial.println(hdr->addr3[0], HEX);
                  // Serial.println(hdr->addr3[1], HEX);
                  // Serial.println(hdr->addr3[2], HEX);
                  // Serial.println(hdr->addr3[3], HEX);
                  // Serial.println(hdr->addr3[4], HEX);
                  // Serial.println(hdr->addr3[5], HEX);

                 rssi_value = ppkt->rx_ctrl.rssi;
                  Serial.println(rssi_value);

                  String num_str = String(rssi_value);

WirePacker packer;
     
        char rssi[num_str.length() + 1]; // +1 for null terminator
        num_str.toCharArray(rssi, num_str.length() + 1);
        packer.write((const uint8_t*)rssi, num_str.length() + 1);

        // after adding all data you want to send, close the packet
        // packer.end();
        // Wire.beginTransmission(I2C_SLAVE_ADDR);
        // while (packer.available()) {    // write every packet byte
        //     Wire.write(packer.read());
        // }
        // Wire.endTransmission();         // stop transmitting


                   _str = "";
                  _str += String(hdr->addr3[0], HEX);
                  for (int i = 1; i < 6; i++) {
                  _str += ":" + String(hdr->addr3[i], HEX);
                  }
                  _str += "\n";
                  Serial.println(_str);
// WirePacker packer;
// Convert the string to a byte array
char addr3_chars[_str.length() + 1]; // +1 for null terminator
_str.toCharArray(addr3_chars, _str.length() + 1);

// Transmit the character array via I2C
// Wire.beginTransmission(I2C_SLAVE_ADDR);
// Wire.write((const uint8_t*)addr3_chars, _str.length() + 1); // Include null terminator
// Wire.endTransmission();


        

        // then add data the same way as you would with Wire
        // packer.write("Count is ");
        packer.write((const uint8_t*)addr3_chars, _str.length());

        // after adding all data you want to send, close the packet
        packer.end();
        Wire.beginTransmission(I2C_SLAVE_ADDR);
        while (packer.available()) {    // write every packet byte
            Wire.write(packer.read());
        }
        Wire.endTransmission();         // stop transmitting

                  }





                  
}

#ifdef THINGSPEAK

void thingspeak_init()
{
  Serial1.begin(115200, SERIAL_8N1, MP_RX1, MP_TX1);
  lcd_text("INIT A9G");

  //init

  pinMode(A9G_RST, OUTPUT);
  digitalWrite(A9G_RST, HIGH);
  delay(1000);
  digitalWrite(A9G_RST, LOW);
  delay(1000);
  pinMode(A9G_POWER, OUTPUT);
  digitalWrite(A9G_POWER, LOW);
  delay(1000);
  digitalWrite(A9G_POWER, HIGH);
  while (!SIM800C_ON)
  {
    sendData("AT", 2000, DEBUG);
  }

  sendData("AT+CCID", 2000, DEBUG);
  sendData("AT+CREG?", 2000, DEBUG);
  lcd_text("A9G Open");
}

void thingspeak_upload(int count)
{
  sendData("AT+CGATT=1", 2000, DEBUG);
  sendData("AT+CGDCONT=1,\"IP\",\"CMNET\"", 2000, DEBUG);
  sendData("AT+CGACT=1,1", 2000, DEBUG);
  //String command = "AT+HTTPGET=\"http://api.thingspeak.com/update?api_key=2ZOQP7ZGJ9OVGU6X&second=1&count=2&index=3&uuid=4&name=5&field1=6&field2=7\"";
  String command = "AT+HTTPGET=\"http://api.thingspeak.com/update?api_key=2ZOQP7ZGJ9OVGU6X&field1=";
  command += String(millis() / 1000);
  command += "&field2=";
  //command += String(random(50));count
  command += String(count);
  command += "&field3=";
  command += String(my_index++);
  command += "&field4=4&field5=5&field6=6&field7=7&field8=8\"";
  sendData(command, 5000, DEBUG);
}

String sendData(String command, const int timeout, boolean debug)
{
  String response = "";
  Serial1.println(command);

  String commandpre = getcommand_pref(command);
  //SerialUSB.println(commandpre);

  long int time = millis();
  while ((time + timeout) > millis())
  {
    if (commandpre == "AT")
    {
      if (Serial1.find("OK"))
      {
        SIM800C_ON = true;
        Serial.println("A9G is ON");
      }
    }

    while (Serial1.available())
    {
      String c = Serial1.readString();
      response += c;
    }
  }

  if (debug)
  {
    Serial.println(response);
  }
  return response;
}

String getcommand_pref(String command)
{

  String command_pref = "";
  char *cstr = new char[command.length() + 1];
  strcpy(cstr, command.c_str());
  char *token = strtok(cstr, "=");
  int i = 0;
  while (token != NULL)
  {
    //SerialUSB.print(token);
    //SerialUSB.println("  line" + (String)i);

    switch (i)
    {
    case 0:
      command_pref = token;
      break;

    default:
      break;
    }

    token = strtok(NULL, ",");
    i = i + 1;
  }

  if (command_pref == "")
    command_pref = command;

  return command_pref;
}

#endif

It (should) count wifi assiociation attempts. If it does I can't say. It definitly does not count people.

Of course it can't count people, but my question is, can cell phones with wifi on be counted, or do people have to turn on their hotspot to connect to another access point in order to be counted?

Then please change the subject line.

Anywa, you basicly count the mac adresses of wifi cards as they try to accociate with hotspots. as these spy devices do this permanently you can identify which device it is and according to the hotspots it tries to connect you can pinpoint the home location of the user. there's a talc on CCC on this topic, just look for it.

simply having access to WiFi isn't enough. how would some device be able to recognize some other device if that device doesn't transmit any packets?

Modern cell phones use MAC address randomization techniques to prevent user tracking

Which does not work as advertised at all :slight_smile:

I did it for him.

what do you mean?

MAC randomization. It is usually not done every couple of minutes --> MAC tracking is good enough to track people in shopping malls. IITC MAC randomization has to be activated by the user, otherwise noobs could not get past simple MAC filters on APs. And then there's the problem that mobile devices including laptops use active probes for known APs. This is used (including a publicly accessible known-BSSID-database) to identify users even when their MAC changes. There are products on the market that do exacly this, just google.

I have the WiFi turned off on my phone, so do several of my friends.

It’s on by default on Apple products

Well, not exactly: "Note that Wi-Fi scans that happen while trying to connect to a preferred Wi-Fi network aren’t randomized." ... and that's what makes you identifyable.

Well, if it’s a preferred network then you want the service so handing over a specific MAC isn’t a major issue

Being tracked by just walking around untrusted networks is

Definitly. But you know, your car also sends it's id wherever if goes. And your tyres, too, and these are 4 pieces to identify where you go and how much you have loaded - equals if you drive alone or in company of how many people.

How your tires know your load?

It’s a mix of factual and speculative elements .

Willingly sending information to a specific service provider is a choice you make, based on trust and/or value for the service.

Same happens when you use Apple Maps or Google Maps or Waze etc to go to a specific destination or when your smartphone checks in for towers in the vicinity or when you have willingly joined the shopping mall’s WiFi network.

You can’t put that on the same level as passive WiFi tracking inside a shopping mall for example.

Regarding your car example - Telematics systems in many modern cars transmit indeed location, speed, and diagnostic data to manufacturers or service providers for navigation, maintenance, and emergency services using 4G LTE and 5G, which could potentially be intercepted by spoofers but it requires sophisticated equipment and expertise (➜ involves creating a fake cell tower to intercept or manipulate communications).

Such activities are illegal (and under surveillance). Governments agencies may get access but it’s heavily regulated in democratic countries.

Tires with TPMS or RFID chips typically send very limited data to the vehicle itself for pressure monitoring or is used in inventory tracking. Direct TPMS typically uses proprietary protocols over radio frequencies of 315 MHz or 433 MHz with a range of approximately 1 to 6 meters to transmit data to the vehicle's onboard receiver. It’s not easily spoofed for real-time tracking.

Note as well that passive WiFi tracking based on MAC addresses is generally restricted under European privacy laws, including the General Data Protection Regulation (GDPR), as MAC addresses are considered personal data. Collecting and processing this information typically requires clear consent from individuals, as well as compliance with strict data protection principles and transparency requirements.

For cars: It's not illegal. EU policy forces your car vendor to actively monitor your habits and send it to the commissions database (over the vendors servers ...) which is basicly located at "our friends". If you want to identify cars you just need SDR and suitable software.

For shopping malls: again, EU "anti-terrorist" laws force you to do so as soon as you have a "free" WiFi. Usually the WiFi in shopping malls are a third party service where different laws apply.

GDRP is for you not spying on "them", not the other way round. EU has different laws for "you" and "the others". Hard to believe, but ask a lawyer.

Not really..
More load is only increasing tires surface contact area to the street.
To increase pressure you have to add more air or raise air temperature or shrink volume of tire.

1 Like

What’s this conspiracy theory ???

No, it is not accurate to say that EU policy forces car vendors to actively monitor and send your driving habits to the European Commission.

You can elect to share your data with your car manufacturer or not.

The collection and processing of this data are subject to strict data protection regulations under the General Data Protection Regulation (GDPR)

Not sure what you are saying there and who is « them »….

1 Like