193 lines
4.5 KiB
C++
193 lines
4.5 KiB
C++
/*
|
|
EddystoneURL beacon by BeeGee
|
|
EddystoneURL frame specification https://github.com/google/eddystone/blob/master/eddystone-url/README.md
|
|
|
|
*/
|
|
|
|
/*
|
|
Create a BLE server that will send periodic Eddystone URL frames.
|
|
The design of creating the BLE server is:
|
|
1. Create a BLE Server
|
|
2. Create advertising data
|
|
3. Start advertising.
|
|
4. wait
|
|
5. Stop advertising.
|
|
6. deep sleep
|
|
|
|
*/
|
|
#include "sys/time.h"
|
|
|
|
#include <Arduino.h>
|
|
|
|
#include "BLEDevice.h"
|
|
#include "BLEUtils.h"
|
|
#include "BLEBeacon.h"
|
|
#include "BLEAdvertising.h"
|
|
#include "BLEEddystoneURL.h"
|
|
|
|
#include "esp_sleep.h"
|
|
|
|
#define GPIO_DEEP_SLEEP_DURATION 10 // sleep x seconds and then wake up
|
|
RTC_DATA_ATTR static time_t last; // remember last boot in RTC Memory
|
|
RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory
|
|
|
|
// See the following for generating UUIDs:
|
|
// https://www.uuidgenerator.net/
|
|
BLEAdvertising *pAdvertising;
|
|
struct timeval now;
|
|
|
|
#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/)
|
|
|
|
static const char *eddystone_url_prefix_subs[] = {
|
|
"http://www.",
|
|
"https://www.",
|
|
"http://",
|
|
"https://",
|
|
"urn:uuid:",
|
|
NULL
|
|
};
|
|
|
|
static const char *eddystone_url_suffix_subs[] = {
|
|
".com/",
|
|
".org/",
|
|
".edu/",
|
|
".net/",
|
|
".info/",
|
|
".biz/",
|
|
".gov/",
|
|
".com",
|
|
".org",
|
|
".edu",
|
|
".net",
|
|
".info",
|
|
".biz",
|
|
".gov",
|
|
NULL
|
|
};
|
|
|
|
static int string_begin_with(const char *str, const char *prefix)
|
|
{
|
|
int prefix_len = strlen(prefix);
|
|
if (strncmp(prefix, str, prefix_len) == 0)
|
|
{
|
|
return prefix_len;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void setBeacon()
|
|
{
|
|
BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
|
|
BLEAdvertisementData oScanResponseData = BLEAdvertisementData();
|
|
|
|
const char url[] = "https://d.giesecke.tk";
|
|
|
|
int scheme_len, ext_len = 1, i, idx, url_idx;
|
|
char *ret_data;
|
|
int url_len = strlen(url);
|
|
|
|
ret_data = (char *)calloc(1, url_len + 13);
|
|
|
|
ret_data[0] = 2; // Len
|
|
ret_data[1] = 0x01; // Type Flags
|
|
ret_data[2] = 0x06; // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04
|
|
ret_data[3] = 3; // Len
|
|
ret_data[4] = 0x03; // Type 16-Bit UUID
|
|
ret_data[5] = 0xAA; // Eddystone UUID 2 -> 0xFEAA LSB
|
|
ret_data[6] = 0xFE; // Eddystone UUID 1 MSB
|
|
ret_data[7] = 19; // Length of Beacon Data
|
|
ret_data[8] = 0x16; // Type Service Data
|
|
ret_data[9] = 0xAA; // Eddystone UUID 2 -> 0xFEAA LSB
|
|
ret_data[10] = 0xFE; // Eddystone UUID 1 MSB
|
|
ret_data[11] = 0x10; // Eddystone Frame Type
|
|
ret_data[12] = 0xF4; // Beacons TX power at 0m
|
|
|
|
i = 0, idx = 13, url_idx = 0;
|
|
|
|
//replace prefix
|
|
scheme_len = 0;
|
|
while (eddystone_url_prefix_subs[i] != NULL)
|
|
{
|
|
if ((scheme_len = string_begin_with(url, eddystone_url_prefix_subs[i])) > 0)
|
|
{
|
|
ret_data[idx] = i;
|
|
idx++;
|
|
url_idx += scheme_len;
|
|
break;
|
|
}
|
|
i++;
|
|
}
|
|
while (url_idx < url_len)
|
|
{
|
|
i = 0;
|
|
ret_data[idx] = url[url_idx];
|
|
ext_len = 1;
|
|
while (eddystone_url_suffix_subs[i] != NULL)
|
|
{
|
|
if ((ext_len = string_begin_with(&url[url_idx], eddystone_url_suffix_subs[i])) > 0)
|
|
{
|
|
ret_data[idx] = i;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ext_len = 1; //inc 1
|
|
}
|
|
i++;
|
|
}
|
|
url_idx += ext_len;
|
|
idx++;
|
|
}
|
|
ret_data[7] = idx - 8;
|
|
|
|
Serial.printf("struct size %d url size %d reported len %d\n",
|
|
url_len + 13,
|
|
url_len, ret_data[7]);
|
|
|
|
Serial.printf("URL in data %s\n", &ret_data[13]);
|
|
|
|
std::string eddyStoneData(ret_data);
|
|
|
|
oAdvertisementData.addData(eddyStoneData);
|
|
oScanResponseData.setName("URLBeacon");
|
|
pAdvertising->setAdvertisementData(oAdvertisementData);
|
|
pAdvertising->setScanResponseData(oScanResponseData);
|
|
}
|
|
|
|
void setup()
|
|
{
|
|
|
|
Serial.begin(115200);
|
|
gettimeofday(&now, NULL);
|
|
|
|
Serial.printf("start ESP32 %d\n", bootcount++);
|
|
|
|
Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n", now.tv_sec, now.tv_sec - last);
|
|
|
|
last = now.tv_sec;
|
|
|
|
// Create the BLE Device
|
|
BLEDevice::init("URLBeacon");
|
|
|
|
BLEDevice::setPower(ESP_PWR_LVL_N12);
|
|
|
|
// Create the BLE Server
|
|
// BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage
|
|
|
|
pAdvertising = BLEDevice::getAdvertising();
|
|
|
|
setBeacon();
|
|
// Start advertising
|
|
pAdvertising->start();
|
|
Serial.println("Advertizing started...");
|
|
delay(10000);
|
|
pAdvertising->stop();
|
|
Serial.printf("enter deep sleep\n");
|
|
esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION);
|
|
Serial.printf("in deep sleep\n");
|
|
}
|
|
|
|
void loop()
|
|
{
|
|
}
|