ESP8266 Sniffing and sending Mac address
Asked Answered
J

1

7

I'm trying to make my ESP8266 sniffing nearby devices, then posting them by with a HTTP request. With purpose is to record when my roommate and I are at home. Then in the future, trigger certain tasks like turning on/off the lights if we're home or not. I don't care at all about the packets content just the mac addresses.

So fare I've found this, script that prints out the mac adresses for nearby devices, created by kalanda: esp8266-sniffer. Aswell as this HTTP posting script ESP8266 http get requests.

I've tried to combine those two and in the callback function make the ESP send the found data, but doesn't look like the ESP establish the wifi connection.

I tried using different WIFI modes: STATION_MODE, SOFTAP_MODE, STATIONAP_MODE. None of them worked for both sniffing and http request at the same time. I know that the STATIONAP_MODE do have some flaws. What I've found is that it has to switch both somehow, but unfortunately I'm not a ESP expert and don't know how this can be done.

Here is my code(srry for any rubbish coding on my side):

#include <ESP8266WiFi.h>       // added this
#include <ESP8266HTTPClient.h> // added this

const char* ssid     = "**********";  // Wifi SSID
const char* password = "**********";  // Wifi Password
String main_url      = "http://*********.php?"; // Website url to post the information
String temp_url      = "";                      // Url with information

extern "C" {
  #include <user_interface.h>
}

#define DATA_LENGTH           112

#define TYPE_MANAGEMENT       0x00
#define TYPE_CONTROL          0x01
#define TYPE_DATA             0x02
#define SUBTYPE_PROBE_REQUEST 0x04


struct RxControl {
 signed rssi:8; // signal intensity of packet
 unsigned rate:4;
 unsigned is_group:1;
 unsigned:1;
 unsigned sig_mode:2; // 0:is 11n packet; 1:is not 11n packet;
 unsigned legacy_length:12; // if not 11n packet, shows length of packet.
 unsigned damatch0:1;
 unsigned damatch1:1;
 unsigned bssidmatch0:1;
 unsigned bssidmatch1:1;
 unsigned MCS:7; // if is 11n packet, shows the modulation and code used (range from 0 to 76)
 unsigned CWB:1; // if is 11n packet, shows if is HT40 packet or not
 unsigned HT_length:16;// if is 11n packet, shows length of packet.
 unsigned Smoothing:1;
 unsigned Not_Sounding:1;
 unsigned:1;
 unsigned Aggregation:1;
 unsigned STBC:2;
 unsigned FEC_CODING:1; // if is 11n packet, shows if is LDPC packet or not.
 unsigned SGI:1;
 unsigned rxend_state:8;
 unsigned ampdu_cnt:8;
 unsigned channel:4; //which channel this packet in.
 unsigned:12;
};

struct SnifferPacket{
    struct RxControl rx_ctrl;
    uint8_t data[DATA_LENGTH];
    uint16_t cnt;
    uint16_t len;
};

static void showMetadata(SnifferPacket *snifferPacket) {

  unsigned int frameControl = ((unsigned int)snifferPacket->data[1] << 8) + snifferPacket->data[0];

  uint8_t version      = (frameControl & 0b0000000000000011) >> 0;
  uint8_t frameType    = (frameControl & 0b0000000000001100) >> 2;
  uint8_t frameSubType = (frameControl & 0b0000000011110000) >> 4;
  uint8_t toDS         = (frameControl & 0b0000000100000000) >> 8;
  uint8_t fromDS       = (frameControl & 0b0000001000000000) >> 9;

  // Only look for probe request packets
  if (frameType != TYPE_MANAGEMENT ||
      frameSubType != SUBTYPE_PROBE_REQUEST)
        return;

  Serial.print("RSSI: ");
  Serial.print(snifferPacket->rx_ctrl.rssi, DEC);

  Serial.print(" Ch: ");
  Serial.print(wifi_get_channel());

  char addr[] = "00:00:00:00:00:00";
  getMAC(addr, snifferPacket->data, 10);
  Serial.print(" Peer MAC: ");
  Serial.print(addr);

  uint8_t SSID_length = snifferPacket->data[25];
  Serial.print(" SSID: ");
  printDataSpan(26, SSID_length, snifferPacket->data);



  Serial.println();
  if (WiFi.status() == WL_CONNECTED) //Check WiFi connection status 
  { 

    HTTPClient http;  //Declare an object of class HTTPClient
    temp_url = main_url;
    temp_url = temp_url + "mac=30:a8:db:96:a4:75";
    temp_url = temp_url + "&rssi=-90";
    temp_url = temp_url + "&ssid=none";
    http.begin(temp_url);  //Specify request destination
    int httpCode = http.GET();                                                                  //Send the request
    temp_url = "";

    if (httpCode > 0) 
    { //Check the returning code

      String payload = http.getString();   //Get the request response payload
      Serial.println(payload);                     //Print the response payload

    }

    http.end();   //Close connection

  }
  else
  {
    Serial.println("Wifi connection failed"); //Prints out this
  }

}

/**
 * Callback for promiscuous mode
 */
static void ICACHE_FLASH_ATTR sniffer_callback(uint8_t *buffer, uint16_t length) {
  struct SnifferPacket *snifferPacket = (struct SnifferPacket*) buffer;
  showMetadata(snifferPacket);
}

static void printDataSpan(uint16_t start, uint16_t size, uint8_t* data) {
  for(uint16_t i = start; i < DATA_LENGTH && i < start+size; i++) {
    Serial.write(data[i]);    
  }
}

static void getMAC(char *addr, uint8_t* data, uint16_t offset) {
  sprintf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", data[offset+0], data[offset+1], data[offset+2], data[offset+3], data[offset+4], data[offset+5]);
}

#define CHANNEL_HOP_INTERVAL_MS   1000
static os_timer_t channelHop_timer;

/**
 * Callback for channel hoping
 */
void channelHop()
{
  // hoping channels 1-14
  uint8 new_channel = wifi_get_channel() + 1;
  if (new_channel > 14)
    new_channel = 1;
  wifi_set_channel(new_channel);
}

#define DISABLE 0
#define ENABLE  1

void setup() {
  // set the WiFi chip to "promiscuous" mode aka monitor mode
  Serial.begin(115200);



  delay(10);
  wifi_set_opmode(STATION_MODE);
  wifi_set_channel(1);
  wifi_promiscuous_enable(DISABLE);
  delay(10);
  wifi_set_promiscuous_rx_cb(sniffer_callback);
  delay(10);
  wifi_promiscuous_enable(ENABLE);

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {

    delay(1000);
    Serial.print("Connecting..");

  }
  Serial.println("");
  Serial.println("Connected..");

  // setup the channel hoping callback timer
  os_timer_disarm(&channelHop_timer);
  os_timer_setfn(&channelHop_timer, (os_timer_func_t *) channelHop, NULL);
  os_timer_arm(&channelHop_timer, CHANNEL_HOP_INTERVAL_MS, 1);
}

void loop() {
  delay(10);  
}
Jevons answered 13/7, 2017 at 20:3 Comment(0)
D
9

Here's the code which aggregates probe requests (MAC addresses and RSSIs) for 3 seconds and then sends them to specified server's endpoint using json (WIFI_AP_STA mode):

#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266HTTPClient.h>
#include <vector>

const char* apSsid     = "ap-ssid";
const char* apPassword = "ap-password";
const char* clientSsid     = "client-ssid";
const char* clientPassword = "client-password";

HTTPClient http;

WiFiEventHandler probeRequestPrintHandler;

String macToString(const unsigned char* mac) {
  char buf[20];
  snprintf(buf, sizeof(buf), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
  return String(buf);
}

std::vector<WiFiEventSoftAPModeProbeRequestReceived> myList;

void onProbeRequestPrint(const WiFiEventSoftAPModeProbeRequestReceived& evt) {
  myList.push_back(evt);
}

void setup() {
  Serial.begin(115200);
  Serial.println("Hello!");

  // Don't save WiFi configuration in flash - optional
  WiFi.persistent(false);

  WiFi.mode(WIFI_AP_STA);
  WiFi.softAP(apSsid, apPassword);
  WiFi.begin(clientSsid, clientPassword);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(100);
  }
  Serial.println("");
  probeRequestPrintHandler = WiFi.onSoftAPModeProbeRequestReceived(&onProbeRequestPrint);
}

void loop() {
  delay(3000);
  String json = "";
  DynamicJsonBuffer jsonBuffer;
  JsonObject& root = jsonBuffer.createObject();
  JsonArray& probes = root.createNestedArray("probes");
  for(WiFiEventSoftAPModeProbeRequestReceived w : myList){
    JsonObject& probe = probes.createNestedObject();
    probe["address"] = macToString(w.mac);
    probe["rssi"] = w.rssi;
  }
  myList.clear();
  root.printTo(json);
  Serial.println("json:" + json);

  http.begin("http://example.com/api/v1/probe");
  http.addHeader("Content-Type", "application/json");
  http.POST(json);
  http.end();
}

Remember to use the latest version (actually a pre-release - 2.4.0-rc1 or newer) of arduino-esp8266 library since those WiFiEvents were just recently added.

You can download ArduinoJson library using Library Manager (Sketch -> Include Library -> Manage Libraries...) if you don't have that already.

Dronski answered 14/7, 2017 at 2:9 Comment(8)
It works awesome, thanks a lot @Defozo! I do have some questions though. First, what's the variables clientSsid and clientPassword for, in your example I set them equal to ssid and password for our wifi network, not sure if that's supposed to be like that? Also does this check one specific channel or does it check all possible channels when in station mode? and last is it possible to include the SSID as well? If anyone would be interested in the webserver code as well as the database table, please give me a call.Jevons
In order to receive probe requests ESP8266 should be in AP mode (arduino-esp8266 library requirement) - so that's what apSsid and apPassword are for - they're for the access point that esp8266 is creating. So actually, I think they could be random - probably not the best idea to set them the same as your network ssid/pass since some devices may connect to your esp8266 instead of your network.Dronski
As far as I know, the arduino-esp8266 library API has only MAC and RSSI variables available, so in order to receive the SSIDs of the networks that the devices are looking for you would have to modify the code using SDKs calls.Dronski
Oh well, then it makes sense why we were able to connect to our "wifi" network, but with no internet connection...Jevons
Okay, I don't think I'm smart enough to be able to do that just yet. Hopefully they will add that later on.Jevons
Cool answer in hereResiduum
Thank you so much on this! I was not aware that ESP can do that simultaniously!Rogan
You saved me digging through a bunch of libraries trying to find the functions and definitions I needed... man this is awesome! Thanks so much!Lewan

© 2022 - 2024 — McMap. All rights reserved.