diff --git a/pwnagotchi/defaults.yml b/pwnagotchi/defaults.yml
index cceac71..813b97f 100644
--- a/pwnagotchi/defaults.yml
+++ b/pwnagotchi/defaults.yml
@@ -39,6 +39,9 @@ main:
       wpa-sec:
         enabled: false
         api_key: ~
+      wigle:
+        enabled: false
+        api_key: ~
 
     # monitor interface to use
     iface: mon0
diff --git a/pwnagotchi/plugins/default/wigle.py b/pwnagotchi/plugins/default/wigle.py
new file mode 100644
index 0000000..b87e71b
--- /dev/null
+++ b/pwnagotchi/plugins/default/wigle.py
@@ -0,0 +1,261 @@
+__author__ = '33197631+dadav@users.noreply.github.com'
+__version__ = '1.0.0'
+__name__ = 'wigle'
+__license__ = 'GPL3'
+__description__ = 'This plugin automatically uploades collected wifis to wigle.net'
+
+import os
+import logging
+import json
+from io import StringIO
+import csv
+from datetime import datetime
+import requests
+from pwnagotchi.mesh.wifi import freq_to_channel
+from scapy.all import RadioTap, Dot11Elt, Dot11Beacon, rdpcap, Scapy_Exception, Dot11, Dot11ProbeResp, Dot11AssoReq, Dot11ReassoReq, Dot11EltRSN, Dot11EltVendorSpecific, Dot11EltMicrosoftWPA
+
+READY = False
+ALREADY_UPLOADED = None
+SKIP = None
+OPTIONS = dict()
+
+AKMSUITE_TYPES = {
+    0x00: "Reserved",
+    0x01: "802.1X",
+    0x02: "PSK",
+}
+
+def _handle_packet(packet, result):
+    """
+    Analyze each packet and extract the data from Dot11 layers
+    """
+
+    if hasattr(packet, 'cap') and 'privacy' in packet.cap:
+        # packet is encrypted
+        if 'encryption' not in result:
+            result['encryption'] = set()
+
+    if packet.haslayer(Dot11Beacon):
+        if packet.haslayer(Dot11Beacon)\
+                or packet.haslayer(Dot11ProbeResp)\
+                or packet.haslayer(Dot11AssoReq)\
+                or packet.haslayer(Dot11ReassoReq):
+            if 'bssid' not in result and hasattr(packet[Dot11], 'addr3'):
+                result['bssid'] = packet[Dot11].addr3
+            if 'essid' not in result and hasattr(packet[Dot11Elt], 'info'):
+                result['essid'] = packet[Dot11Elt].info
+            if 'channel' not in result and hasattr(packet[Dot11Elt:3], 'info'):
+                result['channel'] = int(ord(packet[Dot11Elt:3].info))
+
+    if packet.haslayer(RadioTap):
+        if 'rssi' not in result and hasattr(packet[RadioTap], 'dBm_AntSignal'):
+            result['rssi'] = packet[RadioTap].dBm_AntSignal
+        if 'channel' not in result and hasattr(packet[RadioTap], 'ChannelFrequency'):
+            result['channel'] = freq_to_channel(packet[RadioTap].ChannelFrequency)
+
+    # see: https://fossies.org/linux/scapy/scapy/layers/dot11.py
+    if packet.haslayer(Dot11EltRSN):
+        if hasattr(packet[Dot11EltRSN], 'akm_suites'):
+            auth = AKMSUITE_TYPES.get(packet[Dot11EltRSN].akm_suites[0].suite)
+            result['encryption'].add(f"WPA2/{auth}")
+        else:
+            result['encryption'].add("WPA2")
+
+    if packet.haslayer(Dot11EltVendorSpecific)\
+    and (packet.haslayer(Dot11EltMicrosoftWPA)
+         or packet.info.startswith(b'\x00P\xf2\x01\x01\x00')):
+
+        if hasattr(packet, 'akm_suites'):
+            auth = AKMSUITE_TYPES.get(packet.akm_suites[0].suite)
+            result['encryption'].add(f"WPA2/{auth}")
+        else:
+            result['encryption'].add("WPA2")
+    # end see
+
+    return result
+
+
+def _analyze_pcap(pcap):
+    """
+    Iterate over the packets and extract data
+    """
+    result = dict()
+
+    try:
+        packets = rdpcap(pcap)
+        for packet in packets:
+            result = _handle_packet(packet, result)
+    except Scapy_Exception as sc_e:
+        raise sc_e
+
+    return result
+
+
+def on_loaded():
+    """
+    Gets called when the plugin gets loaded
+    """
+    global READY
+    global ALREADY_UPLOADED
+    global SKIP
+
+    SKIP = list()
+
+    if 'api_key' not in OPTIONS or ('api_key' in OPTIONS and OPTIONS['api_key'] is None):
+        logging.error("WIGLE: api_key isn't set. Can't upload to wigle.net")
+        return
+
+    try:
+        with open('/root/.wigle_uploads', 'r') as f:
+            ALREADY_UPLOADED = f.read().splitlines()
+    except OSError:
+        logging.warning('WIGLE: No upload-file found.')
+        ALREADY_UPLOADED = []
+
+    READY = True
+
+
+def _extract_gps_data(path):
+    """
+    Extract data from gps-file
+
+    return json-obj
+    """
+
+    try:
+        with open(path, 'r') as json_file:
+            return json.load(json_file)
+    except OSError as os_err:
+        logging.error("WIGLE: %s", os_err)
+    except json.JSONDecodeError as json_err:
+        logging.error("WIGLE: %s", json_err)
+
+    return None
+
+def _format_auth(data):
+    out = ""
+    for auth in data:
+        out = f"{out}[{auth}]"
+    return out
+
+def _transform_wigle_entry(gps_data, pcap_data):
+    """
+    Transform to wigle entry in file
+    """
+    dummy = StringIO()
+    # write kismet header
+    dummy.write("WigleWifi-1.4,appRelease=20190201,model=Kismet,release=2019.02.01.{},device=kismet,display=kismet,board=kismet,brand=kismet\n")
+    dummy.write("MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type")
+
+    writer = csv.writer(dummy, delimiter=",", quoting=csv.QUOTE_NONE)
+    writer.writerow([
+        pcap_data['bssid'],
+        pcap_data['essid'].decode('utf-8'),
+        _format_auth(pcap_data['encryption']),
+        datetime.strptime(gps_data['Updated'].rsplit('.')[0],
+                          "%Y-%m-%dT%H:%M:%S").strftime('%Y-%m-%d %H:%M:%S'),
+        pcap_data['channel'],
+        pcap_data['rssi'],
+        gps_data['Latitude'],
+        gps_data['Longitude'],
+        gps_data['Altitude'],
+        0, # accuracy?
+        'WIFI'])
+    return dummy.getvalue()
+
+def _send_to_wigle(lines, api_key, timeout=30):
+    """
+    Uploads the file to wigle-net
+    """
+
+    dummy = StringIO()
+
+    for line in lines:
+        dummy.write(f"{line}")
+
+    dummy.seek(0)
+
+    headers = {'Authorization': f"Basic {api_key}",
+               'Accept': 'application/json'}
+    data = {'donate': 'false'}
+    payload = {'file': dummy, 'type': 'text/csv'}
+
+    try:
+        res = requests.post('https://api.wigle.net/api/v2/file/upload',
+                            data=data,
+                            headers=headers,
+                            files=payload,
+                            timeout=timeout)
+        json_res = res.json()
+        if not json_res['success']:
+            raise requests.exceptions.RequestException(json_res['message'])
+    except requests.exceptions.RequestException as re_e:
+        raise re_e
+
+
+def on_internet_available(display, config, log):
+    """
+    Called in manual mode when there's internet connectivity
+    """
+    global ALREADY_UPLOADED
+    global SKIP
+
+    if READY:
+        handshake_dir = config['bettercap']['handshakes']
+        all_files = os.listdir(handshake_dir)
+        gps_files = [os.path.join(handshake_dir, filename)
+                     for filename in all_files
+                     if filename.endswith('.gps.json')]
+        gps_new = set(gps_files) - set(ALREADY_UPLOADED) - set(SKIP)
+
+        if gps_new:
+            logging.info("WIGLE: Internet connectivity detected. Uploading new handshakes to wigle.net")
+
+            lines = list()
+            for gps_file in gps_new:
+                pcap_filename = gps_file.replace('.gps.json', '.pcap')
+
+                if not os.path.exists(pcap_filename):
+                    logging.error("WIGLE: Can't find pcap for %s", gps_file)
+                    SKIP.append(gps_file)
+                    continue
+
+                gps_data = _extract_gps_data(gps_file)
+                try:
+                    pcap_data = _analyze_pcap(pcap_filename)
+                except Scapy_Exception as sc_e:
+                    logging.error("WIGLE: %s", sc_e)
+                    SKIP.append(gps_file)
+                    continue
+
+                if 'encryption' in pcap_data:
+                    if not pcap_data['encryption']:
+                        pcap_data['encryption'].add('WEP')
+                else:
+                    pcap_data['encryption'] = set()
+                    pcap_data['encryption'].add('OPN')
+
+                if len(pcap_data) < 5:
+                    # not enough data
+                    SKIP.append(gps_file)
+                    continue
+
+                new_entry = _transform_wigle_entry(gps_data, pcap_data)
+                lines.append(new_entry)
+
+            if lines:
+                display.set('status', "Uploading gps-data to wigle.net ...")
+                display.update(force=True)
+                try:
+                    _send_to_wigle(lines, OPTIONS['api_key'])
+                    ALREADY_UPLOADED += gps_new
+                    with open('/root/.wigle_uploads', 'a') as up_file:
+                        for gps in gps_new:
+                            up_file.write(gps + "\n")
+                    logging.info("WIGLE: Successfuly uploaded %d files", len(gps_new))
+                except requests.exceptions.RequestException as re_e:
+                    SKIP += lines
+                    logging.error("WIGLE: Got an exception while uploading %s", re_e)
+                except OSError as os_e:
+                    SKIP += lines
+                    logging.error("WIGLE: Got the following error: %s", os_e)