diff --git a/preload/apps/en_count/__init__.py b/preload/apps/en_count/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e73eb86d0327e42722e76218ca2a3f324240412
--- /dev/null
+++ b/preload/apps/en_count/__init__.py
@@ -0,0 +1,122 @@
+import interrupt
+import sys_ble
+import time
+import vibra
+import display
+import color
+
+DM_ADV_TYPE_FLAGS = 0x01
+DM_ADV_TYPE_16_UUID = 0x03
+DM_ADV_TYPE_SERVICE_DATA = 0x16
+UUID = b"\x6f\xfd"
+TIMEOUT = 100
+
+seen = {}
+
+
+def parse_advertisement_data(data):
+    ads = {}
+
+    l = len(data)
+    p = 0
+    while p < l:
+        ad_len = data[p]
+        p += 1
+        if ad_len > 0:
+            ad_type = data[p]
+            ad_data = b""
+            p += 1
+            if ad_len > 1:
+                ad_data = data[p : p + ad_len - 1]
+                p += ad_len - 1
+            ads[ad_type] = ad_data
+    return ads
+
+
+def bytes2hex(bin, sep=""):
+    return sep.join(["%02x" % x for x in bin])
+
+
+def process_covid_dada(mac, service_data, rssi, flags):
+    vibra.vibrate(10)
+    print(bytes2hex(mac, ":"), rssi, bytes2hex(service_data), flags)
+
+    # try to produce a small int
+    seen[mac] = [int(time.time() - t0), flags]
+
+
+def prune():
+    global seen
+    seen_pruned = {}
+    now = time.time() - t0
+
+    for mac in seen:
+        if seen[mac][0] + TIMEOUT > now:
+            seen_pruned[mac] = seen[mac]
+
+    seen = seen_pruned
+
+
+def process_scan_report(scan_report):
+    ads = parse_advertisement_data(scan_report[0])
+    mac = scan_report[4]
+    mac = bytes([mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]])
+    rssi = scan_report[1]
+
+    # print(bytes2hex(mac, ':'), rssi, bytes2hex(scan_report[0]), ads)
+    # According to spec there is no other service announced and the
+    # service is always listed in the complete list of 16 bit services
+    if DM_ADV_TYPE_16_UUID in ads:
+        if ads[DM_ADV_TYPE_16_UUID] == UUID:
+            if DM_ADV_TYPE_SERVICE_DATA in ads:
+                flags = None
+                if DM_ADV_TYPE_FLAGS in ads:
+                    flags = ads[DM_ADV_TYPE_FLAGS]
+                # service data contains another copy of the service UUID
+                process_covid_dada(mac, ads[DM_ADV_TYPE_SERVICE_DATA][2:], rssi, flags)
+
+
+def ble_callback(_):
+    event = sys_ble.get_event()
+    prune()
+    while True:
+        scan_report = sys_ble.get_scan_report()
+        if scan_report == None:
+            return
+        process_scan_report(scan_report)
+
+
+t0 = time.time()
+disp = display.open()
+
+interrupt.set_callback(interrupt.BLE, ble_callback)
+interrupt.enable_callback(interrupt.BLE)
+
+sys_ble.scan_start()
+
+while True:
+    seen_google = 0
+    seen_apple = 0
+    t = time.time() - t0
+
+    t_min = t
+    for mac in seen:
+        if seen[mac][1]:
+            seen_apple += 1
+        else:
+            seen_google += 1
+        if seen[mac][0] < t_min:
+            t_min = seen[mac][0]
+    seen_total = seen_google + seen_apple
+
+    window = t - t_min
+
+    disp.clear()
+    disp.print("Last %u s:" % window, posy=0, fg=color.WHITE)
+    disp.print("Google: %d" % seen_google, posy=20, fg=color.GREEN)
+    disp.print("Apple:  %d" % seen_apple, posy=40, fg=color.BLUE)
+    disp.print("Total:  %d" % seen_total, posy=60, fg=color.WHITE)
+    disp.update()
+    # print(seen)
+
+    time.sleep(1)
diff --git a/preload/apps/en_count/metadata.json b/preload/apps/en_count/metadata.json
new file mode 100644
index 0000000000000000000000000000000000000000..03afc14dae8342434c706d37e77da6c38c3ccf0d
--- /dev/null
+++ b/preload/apps/en_count/metadata.json
@@ -0,0 +1 @@
+{"author": "schneider", "name": "Exposure Notification Counter", "description": "Count exposure notifications received via BLE", "category": "Hardware", "revision": 1, "source":"preload"}