Initial Commit
This commit is contained in:
143
ESP32-ExposureAPI-Scanner.ino
Normal file
143
ESP32-ExposureAPI-Scanner.ino
Normal file
@@ -0,0 +1,143 @@
|
||||
#include <Arduino.h>
|
||||
#include <BLEDevice.h>
|
||||
#include <BLEScan.h>
|
||||
#include <BLEAdvertisedDevice.h>
|
||||
#include "Tone32.h"
|
||||
#include <Adafruit_GFX.h>
|
||||
#include <Wire.h>
|
||||
#include <SSD1306Wire.h>
|
||||
#include <SPI.h>
|
||||
|
||||
SSD1306Wire display(0x3c, 5, 4);
|
||||
#define SSD1306_128_64
|
||||
|
||||
// Pin where the + of an LED is connected.
|
||||
#define LED_PIN 0
|
||||
|
||||
// Pin where the + of a piezo buzzer is connected.
|
||||
#define BUZZER_PIN 2
|
||||
|
||||
// Tone to play.
|
||||
#define BEEP_NOTE NOTE_C6
|
||||
|
||||
// Tone duration in milliseconds.
|
||||
#define BEEP_DURATION_MS 100
|
||||
|
||||
// Scan update time, 5 seems to be a good value.
|
||||
#define SCAN_TIME_SECONDS 5
|
||||
|
||||
// When to forget old senders.
|
||||
#define FORGET_AFTER_MINUTES 20
|
||||
|
||||
BLEScan *scanner;
|
||||
std::map<std::string, unsigned long> seenNotifiers;
|
||||
|
||||
|
||||
/**
|
||||
* Called when a new exposure notifier is seen.
|
||||
*/
|
||||
void onNewNotifierFound() {
|
||||
Serial.println("BEEP");
|
||||
digitalWrite(LED_PIN, HIGH);
|
||||
tone(BUZZER_PIN, BEEP_NOTE, BEEP_DURATION_MS, 0);
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
}
|
||||
|
||||
|
||||
class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
|
||||
/**
|
||||
* Called when a BLE device is discovered.
|
||||
*/
|
||||
void onResult(BLEAdvertisedDevice advertisedDevice) override {
|
||||
if (!advertisedDevice.haveServiceUUID()
|
||||
|| !advertisedDevice.getServiceUUID().equals(BLEUUID((uint16_t) 0xfd6f))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!seenNotifiers.count(advertisedDevice.getAddress().toString())) {
|
||||
// New notifier found.
|
||||
Serial.printf("+ %s \r\n", advertisedDevice.getAddress().toString().c_str());
|
||||
onNewNotifierFound();
|
||||
}
|
||||
else {
|
||||
// We've already seen this one.
|
||||
Serial.printf("... %s \r\n", advertisedDevice.getAddress().toString().c_str());
|
||||
}
|
||||
|
||||
// Remember, with time.
|
||||
seenNotifiers[advertisedDevice.getAddress().toString()] = millis();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Remove notifiers last seen over FORGET_AFTER_MINUTES ago.
|
||||
*/
|
||||
void forgetOldNotifiers() {
|
||||
for (auto const ¬ifier : seenNotifiers) {
|
||||
if (notifier.second + (FORGET_AFTER_MINUTES * 60 * 1000) < millis()) {
|
||||
Serial.printf("- %s \r\n", notifier.first.c_str());
|
||||
seenNotifiers.erase(notifier.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("Hi.");
|
||||
|
||||
// Initialize pins.
|
||||
pinMode(LED_PIN, OUTPUT);
|
||||
pinMode(BUZZER_PIN, OUTPUT);
|
||||
|
||||
// Initialize display
|
||||
Serial.println("Init Display");
|
||||
display.init();
|
||||
// display.clear();
|
||||
// display.display();
|
||||
|
||||
// Initialize scanner.
|
||||
BLEDevice::init("ESP");
|
||||
scanner = BLEDevice::getScan();
|
||||
scanner->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
|
||||
scanner->setActiveScan(true); // Active scan uses more power, but gets results faster.
|
||||
scanner->setInterval(100);
|
||||
scanner->setWindow(99);
|
||||
|
||||
Serial.println("Scanning ...");
|
||||
}
|
||||
|
||||
|
||||
void loop() {
|
||||
Serial.println("-----");
|
||||
|
||||
// Scan.
|
||||
scanner->start(SCAN_TIME_SECONDS, false);
|
||||
|
||||
// Cleanup.
|
||||
scanner->clearResults();
|
||||
forgetOldNotifiers();
|
||||
|
||||
Serial.printf("Count: %d \r\n", seenNotifiers.size());
|
||||
//
|
||||
Serial.printf("Show data on SSD1306 display");
|
||||
display.clear();
|
||||
display.setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display.setFont(ArialMT_Plain_10);
|
||||
display.drawRect(0, 0, 128, 64);
|
||||
display.drawString(64, 2, "Exposure API Counter");
|
||||
display.drawHorizontalLine(0, 16, 128);
|
||||
display.setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
if (seenNotifiers.size() == 1) {
|
||||
display.drawString(64, 48, "Device in last " + String(FORGET_AFTER_MINUTES) +"min.");
|
||||
}
|
||||
else {
|
||||
display.drawString(64, 48, "Devices in last " + String(FORGET_AFTER_MINUTES) +"min.");
|
||||
}
|
||||
//display.drawString(64, 48, "Device(s) in last " + String(FORGET_AFTER_MINUTES) +"min.");
|
||||
display.setFont(ArialMT_Plain_24);
|
||||
display.drawString(64, 20, String(seenNotifiers.size()));
|
||||
display.display();
|
||||
}
|
||||
34
README.md
34
README.md
@@ -1,3 +1,35 @@
|
||||
# ESP32-ExposureAPI-Scanner
|
||||
|
||||
This Repository contains code to scan the official COVID19 Exposure API around an ESP32 using Bluetooth LE and an SSD1306 OLED display.
|
||||
This repository contains code to scan the official COVID19 Exposure API with an ESP32 using Bluetooth LE and shows a counter on an SSD1306 OLED display.
|
||||
|
||||
|
||||

|
||||
|
||||
Code is based on [BLE Exposure Notification Beeper](https://github.com/kmetz/BLEExposureNotificationBeeper) which uses an LED and a piezo buzzer to notify about new detected devices but would not show the number of devices detected so I added the display to it. You can still use the LED/Buzzer because this part is not touched in the code. In case you want to do so use this wiring:
|
||||
|
||||
| ESP32 pin | goes to |
|
||||
|:----------|:--------------------|
|
||||
| GPIO 0 | LED (+) |
|
||||
| GPIO 2 | Buzzer (+) |
|
||||
| GND | LED (-), Buzzer (-) |
|
||||
|
||||
## How it works
|
||||
The official Exposure API used by Apple and Google generates new device keys approx. every 15 Minutes. The scanner remembers these codes and won't notify you again if they're detected multiple times. After 20 minutes these devices will be removed from the counter because the keys are no longer valid.
|
||||
|
||||
You can adjust this timespan in the code by changing `FORGET_AFTER_MINUTES`.
|
||||
|
||||
## Required Hardware
|
||||
+ [ESP32 with in-built SSD1306 Display](https://www.aliexpress.com/item/4000217873035.html?spm=a2g0o.productlist.0.0.3cbb3f174QDsKe&algo_pvid=19b9389d-6613-4aef-bc52-59f1a4f8dfcd&algo_expid=19b9389d-6613-4aef-bc52-59f1a4f8dfcd-1&btsid=0b0a01f815926828747758917ec2d0&ws_ab_test=searchweb0_0,searchweb201602_,searchweb201603_)
|
||||
|
||||
This ESP32 model uses these internal I²C and pins for the SSD1306 display:
|
||||
|
||||
| SSD1306 | goes to |
|
||||
|:-------------|:---------|
|
||||
| I²C ADDRESS | 0x3c |
|
||||
| SDA | 5 |
|
||||
| SDC | 4 |
|
||||
|
||||
You can also use a separate SSD1306 but keep in mind to adjust the I²C adress and the pins in the code.
|
||||
|
||||
## Used Libraries
|
||||
+ [ESP8266-oled-ssd1306 by ThingPulse](https://github.com/ThingPulse/esp8266-oled-ssd1306)
|
||||
|
||||
21
Tone32.cpp
Normal file
21
Tone32.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "Tone32.h"
|
||||
|
||||
void tone(uint8_t pin, unsigned int frequency, unsigned long duration, uint8_t channel)
|
||||
{
|
||||
if (ledcRead(channel)) {
|
||||
log_e("Tone channel %d is already in use", ledcRead(channel));
|
||||
return;
|
||||
}
|
||||
ledcAttachPin(pin, channel);
|
||||
ledcWriteTone(channel, frequency);
|
||||
if (duration) {
|
||||
delay(duration);
|
||||
noTone(pin, channel);
|
||||
}
|
||||
}
|
||||
|
||||
void noTone(uint8_t pin, uint8_t channel)
|
||||
{
|
||||
ledcDetachPin(pin);
|
||||
ledcWrite(channel, 0);
|
||||
}
|
||||
13
Tone32.h
Executable file
13
Tone32.h
Executable file
@@ -0,0 +1,13 @@
|
||||
#ifndef _TONE32_h
|
||||
#define _TONE32_h
|
||||
|
||||
#define TONE_CHANNEL 15
|
||||
#include "Arduino.h"
|
||||
#include "pitches.h"
|
||||
|
||||
void tone(uint8_t pin, unsigned int frequency, unsigned long duration = 0, uint8_t channel = TONE_CHANNEL);
|
||||
void noTone(uint8_t pin, uint8_t channel = TONE_CHANNEL);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
BIN
img/esp32_scanner.jpg
Normal file
BIN
img/esp32_scanner.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.8 MiB |
95
pitches.h
Normal file
95
pitches.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*************************************************
|
||||
* Public Constants
|
||||
*************************************************/
|
||||
|
||||
#define NOTE_B0 31
|
||||
#define NOTE_C1 33
|
||||
#define NOTE_CS1 35
|
||||
#define NOTE_D1 37
|
||||
#define NOTE_DS1 39
|
||||
#define NOTE_E1 41
|
||||
#define NOTE_F1 44
|
||||
#define NOTE_FS1 46
|
||||
#define NOTE_G1 49
|
||||
#define NOTE_GS1 52
|
||||
#define NOTE_A1 55
|
||||
#define NOTE_AS1 58
|
||||
#define NOTE_B1 62
|
||||
#define NOTE_C2 65
|
||||
#define NOTE_CS2 69
|
||||
#define NOTE_D2 73
|
||||
#define NOTE_DS2 78
|
||||
#define NOTE_E2 82
|
||||
#define NOTE_F2 87
|
||||
#define NOTE_FS2 93
|
||||
#define NOTE_G2 98
|
||||
#define NOTE_GS2 104
|
||||
#define NOTE_A2 110
|
||||
#define NOTE_AS2 117
|
||||
#define NOTE_B2 123
|
||||
#define NOTE_C3 131
|
||||
#define NOTE_CS3 139
|
||||
#define NOTE_D3 147
|
||||
#define NOTE_DS3 156
|
||||
#define NOTE_E3 165
|
||||
#define NOTE_F3 175
|
||||
#define NOTE_FS3 185
|
||||
#define NOTE_G3 196
|
||||
#define NOTE_GS3 208
|
||||
#define NOTE_A3 220
|
||||
#define NOTE_AS3 233
|
||||
#define NOTE_B3 247
|
||||
#define NOTE_C4 262
|
||||
#define NOTE_CS4 277
|
||||
#define NOTE_D4 294
|
||||
#define NOTE_DS4 311
|
||||
#define NOTE_E4 330
|
||||
#define NOTE_F4 349
|
||||
#define NOTE_FS4 370
|
||||
#define NOTE_G4 392
|
||||
#define NOTE_GS4 415
|
||||
#define NOTE_A4 440
|
||||
#define NOTE_AS4 466
|
||||
#define NOTE_B4 494
|
||||
#define NOTE_C5 523
|
||||
#define NOTE_CS5 554
|
||||
#define NOTE_D5 587
|
||||
#define NOTE_DS5 622
|
||||
#define NOTE_E5 659
|
||||
#define NOTE_F5 698
|
||||
#define NOTE_FS5 740
|
||||
#define NOTE_G5 784
|
||||
#define NOTE_GS5 831
|
||||
#define NOTE_A5 880
|
||||
#define NOTE_AS5 932
|
||||
#define NOTE_B5 988
|
||||
#define NOTE_C6 1047
|
||||
#define NOTE_CS6 1109
|
||||
#define NOTE_D6 1175
|
||||
#define NOTE_DS6 1245
|
||||
#define NOTE_E6 1319
|
||||
#define NOTE_F6 1397
|
||||
#define NOTE_FS6 1480
|
||||
#define NOTE_G6 1568
|
||||
#define NOTE_GS6 1661
|
||||
#define NOTE_A6 1760
|
||||
#define NOTE_AS6 1865
|
||||
#define NOTE_B6 1976
|
||||
#define NOTE_C7 2093
|
||||
#define NOTE_CS7 2217
|
||||
#define NOTE_D7 2349
|
||||
#define NOTE_DS7 2489
|
||||
#define NOTE_E7 2637
|
||||
#define NOTE_F7 2794
|
||||
#define NOTE_FS7 2960
|
||||
#define NOTE_G7 3136
|
||||
#define NOTE_GS7 3322
|
||||
#define NOTE_A7 3520
|
||||
#define NOTE_AS7 3729
|
||||
#define NOTE_B7 3951
|
||||
#define NOTE_C8 4186
|
||||
#define NOTE_CS8 4435
|
||||
#define NOTE_D8 4699
|
||||
#define NOTE_DS8 4978
|
||||
|
||||
|
||||
Reference in New Issue
Block a user