@@ -18,6 +18,7 @@ board_build.partitions = default.csv
|
|||||||
build_flags =
|
build_flags =
|
||||||
-DARDUINO_USB_CDC_ON_BOOT=1
|
-DARDUINO_USB_CDC_ON_BOOT=1
|
||||||
-DBOARD_HAS_PSRAM
|
-DBOARD_HAS_PSRAM
|
||||||
|
-DCORE_DEBUG_LEVEL=0
|
||||||
|
|
||||||
lib_deps =
|
lib_deps =
|
||||||
h2zero/NimBLE-Arduino
|
h2zero/NimBLE-Arduino
|
||||||
|
|||||||
@@ -5,9 +5,14 @@
|
|||||||
#include "freertos/queue.h"
|
#include "freertos/queue.h"
|
||||||
#include <HTTPClient.h>
|
#include <HTTPClient.h>
|
||||||
#include <PubSubClient.h>
|
#include <PubSubClient.h>
|
||||||
|
#include <Preferences.h>
|
||||||
|
#include "esp_system.h"
|
||||||
|
|
||||||
|
Preferences prefs;
|
||||||
|
Config config;
|
||||||
CB cb;
|
CB cb;
|
||||||
char MQTT_PUB_TOPIC[sizeof(MQTT_TOPIC) + sizeof(DEV_NAME) - 1];
|
String MQTT_PUB_TOPIC;
|
||||||
|
String serial_buffer = "";
|
||||||
std::unordered_map<std::string, Tag> tags;
|
std::unordered_map<std::string, Tag> tags;
|
||||||
std::unordered_map<std::string, UUIDCacheEntry> uuid_cache;
|
std::unordered_map<std::string, UUIDCacheEntry> uuid_cache;
|
||||||
SemaphoreHandle_t uuid_mtx;
|
SemaphoreHandle_t uuid_mtx;
|
||||||
@@ -27,7 +32,7 @@ void cleanupCache()
|
|||||||
{
|
{
|
||||||
uint32_t age = now - it->second.last_check;
|
uint32_t age = now - it->second.last_check;
|
||||||
|
|
||||||
if (age > UUID_CACHE_CLEAN_TIMEOUT)
|
if (age > config.interval.uuid_cache_clean_timeout)
|
||||||
{
|
{
|
||||||
Serial.printf("Erasing %s from cache after being inactive for %dms.\n",
|
Serial.printf("Erasing %s from cache after being inactive for %dms.\n",
|
||||||
it->first.c_str(),
|
it->first.c_str(),
|
||||||
@@ -47,7 +52,7 @@ void cleanupTags()
|
|||||||
|
|
||||||
for (auto it = tags.begin(); it != tags.end();)
|
for (auto it = tags.begin(); it != tags.end();)
|
||||||
{
|
{
|
||||||
if ((now - it->second.getLastSeen()) >= TAG_TIMEOUT)
|
if ((now - it->second.getLastSeen()) >= config.interval.tag_timeout)
|
||||||
{
|
{
|
||||||
Serial.printf("Erasing %s from memory after being inactive for %dms.\n",
|
Serial.printf("Erasing %s from memory after being inactive for %dms.\n",
|
||||||
it->first.c_str(),
|
it->first.c_str(),
|
||||||
@@ -69,7 +74,7 @@ void httpTask(void *)
|
|||||||
{
|
{
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
|
|
||||||
String url = UUID_CHECK_ENDPOINT + String(ev.uuid);
|
String url = config.api.url + config.api.uuid_check_endpoint + String("/") + String(ev.uuid);
|
||||||
|
|
||||||
http.setTimeout(3000);
|
http.setTimeout(3000);
|
||||||
http.setReuse(false);
|
http.setReuse(false);
|
||||||
@@ -82,6 +87,19 @@ void httpTask(void *)
|
|||||||
|
|
||||||
int code = http.GET();
|
int code = http.GET();
|
||||||
|
|
||||||
|
if (code < 0)
|
||||||
|
{
|
||||||
|
xSemaphoreTake(uuid_mtx, portMAX_DELAY);
|
||||||
|
uuid_cache[ev.uuid] = {
|
||||||
|
UUIDState::RETRY,
|
||||||
|
millis()};
|
||||||
|
|
||||||
|
xSemaphoreGive(uuid_mtx);
|
||||||
|
|
||||||
|
http.end();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
bool valid = (code == 200);
|
bool valid = (code == 200);
|
||||||
|
|
||||||
http.end();
|
http.end();
|
||||||
@@ -110,8 +128,8 @@ void mqttTask(void *)
|
|||||||
{
|
{
|
||||||
while (!mqtt.connected())
|
while (!mqtt.connected())
|
||||||
{
|
{
|
||||||
Serial.printf("MQTT Disconnected. Connecting to %s:%d\n", MQTT_HOST, MQTT_PORT);
|
Serial.printf("MQTT Disconnected. Connecting to %s:%d\n", config.mqtt.host.c_str(), config.mqtt.port);
|
||||||
if (mqtt.connect(DEV_NAME, MQTT_USER, MQTT_PASS))
|
if (mqtt.connect(config.dev_name.c_str(), config.mqtt.username.c_str(), config.mqtt.password.c_str()))
|
||||||
Serial.println("MQTT connected.");
|
Serial.println("MQTT connected.");
|
||||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||||
}
|
}
|
||||||
@@ -125,7 +143,7 @@ void mqttTask(void *)
|
|||||||
ev.uuid,
|
ev.uuid,
|
||||||
ev.ema);
|
ev.ema);
|
||||||
|
|
||||||
bool ok = mqtt.publish(MQTT_PUB_TOPIC, payload);
|
bool ok = mqtt.publish(MQTT_PUB_TOPIC.c_str(), payload);
|
||||||
|
|
||||||
if (!ok)
|
if (!ok)
|
||||||
Serial.println("Failed to publish on MQTT.");
|
Serial.println("Failed to publish on MQTT.");
|
||||||
@@ -143,7 +161,7 @@ void processEvent(const BleEvent &event)
|
|||||||
|
|
||||||
uint32_t now = millis();
|
uint32_t now = millis();
|
||||||
|
|
||||||
if (it == uuid_cache.end())
|
if (it == uuid_cache.end() || (it->second.state == UUIDState::RETRY))
|
||||||
{
|
{
|
||||||
uuid_cache[event.uuid] = {UUIDState::PENDING, now};
|
uuid_cache[event.uuid] = {UUIDState::PENDING, now};
|
||||||
|
|
||||||
@@ -158,7 +176,7 @@ void processEvent(const BleEvent &event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (it->second.state == UUIDState::INVALID &&
|
if (it->second.state == UUIDState::INVALID &&
|
||||||
now - it->second.last_check > UUID_CACHE_RETRY_TIMEOUT)
|
now - it->second.last_check > config.interval.uuid_cache_retry_timeout)
|
||||||
{
|
{
|
||||||
it->second.state = UUIDState::PENDING;
|
it->second.state = UUIDState::PENDING;
|
||||||
it->second.last_check = now;
|
it->second.last_check = now;
|
||||||
@@ -202,9 +220,9 @@ void CB::onResult(const NimBLEAdvertisedDevice *d)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
NimBLEUUID uuid = d->getServiceUUID(0);
|
NimBLEUUID uuid = d->getServiceUUID(0);
|
||||||
const std::string &name = d->getName();
|
const String name(d->getName().c_str());
|
||||||
|
|
||||||
if ((uuid.bitSize() != 128) || (name != TAG_NAME))
|
if ((uuid.bitSize() != 128) || (name != config.tag_name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
BleEvent event;
|
BleEvent event;
|
||||||
@@ -215,23 +233,170 @@ void CB::onResult(const NimBLEAdvertisedDevice *d)
|
|||||||
xQueueSend(ble_queue, &event, pdMS_TO_TICKS(5));
|
xQueueSend(ble_queue, &event, pdMS_TO_TICKS(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void saveConfig()
|
||||||
|
{
|
||||||
|
prefs.begin("config", false);
|
||||||
|
|
||||||
|
prefs.putUInt(CLEANUP_INTERVAL_CONFIG_KEY, config.interval.cleanup);
|
||||||
|
prefs.putUInt(TAG_TIMEOUT_CONFIG_KEY, config.interval.tag_timeout);
|
||||||
|
prefs.putUInt(UUID_CACHE_RETRY_TIMEOUT_CONFIG_KEY, config.interval.uuid_cache_retry_timeout);
|
||||||
|
prefs.putUInt(UUID_CACHE_CLEAN_TIMEOUT_CONFIG_KEY, config.interval.uuid_cache_clean_timeout);
|
||||||
|
|
||||||
|
prefs.putString(API_URL_CONFIG_KEY, config.api.url);
|
||||||
|
prefs.putString(UUID_CHECK_ENDPOINT_CONFIG_KEY, config.api.uuid_check_endpoint);
|
||||||
|
|
||||||
|
prefs.putString(MQTT_HOST_CONFIG_KEY, config.mqtt.host);
|
||||||
|
prefs.putUShort(MQTT_PORT_CONFIG_KEY, config.mqtt.port);
|
||||||
|
prefs.putString(MQTT_USER_CONFIG_KEY, config.mqtt.username);
|
||||||
|
prefs.putString(MQTT_PASS_CONFIG_KEY, config.mqtt.password);
|
||||||
|
prefs.putString(MQTT_TOPIC_CONFIG_KEY, config.mqtt.topic);
|
||||||
|
|
||||||
|
prefs.putString(WIFI_SSID_CONFIG_KEY, config.wifi.ssid);
|
||||||
|
prefs.putString(WIFI_PASS_CONFIG_KEY, config.wifi.password);
|
||||||
|
|
||||||
|
prefs.putString(DEV_NAME_CONFIG_KEY, config.dev_name);
|
||||||
|
prefs.putString(TAG_NAME_CONFIG_KEY, config.tag_name);
|
||||||
|
|
||||||
|
prefs.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadConfig()
|
||||||
|
{
|
||||||
|
prefs.begin("config", true);
|
||||||
|
|
||||||
|
config.interval.cleanup = prefs.getUInt(CLEANUP_INTERVAL_CONFIG_KEY, CLEANUP_INTERVAL);
|
||||||
|
config.interval.tag_timeout = prefs.getUInt(TAG_TIMEOUT_CONFIG_KEY, TAG_TIMEOUT);
|
||||||
|
config.interval.uuid_cache_retry_timeout = prefs.getUInt(UUID_CACHE_RETRY_TIMEOUT_CONFIG_KEY, UUID_CACHE_RETRY_TIMEOUT);
|
||||||
|
config.interval.uuid_cache_clean_timeout = prefs.getUInt(UUID_CACHE_CLEAN_TIMEOUT_CONFIG_KEY, UUID_CACHE_CLEAN_TIMEOUT);
|
||||||
|
|
||||||
|
config.api.url = prefs.getString(API_URL_CONFIG_KEY, API_URL);
|
||||||
|
config.api.uuid_check_endpoint = prefs.getString(UUID_CHECK_ENDPOINT_CONFIG_KEY, UUID_CHECK_ENDPOINT);
|
||||||
|
|
||||||
|
config.mqtt.host = prefs.getString(MQTT_HOST_CONFIG_KEY, MQTT_HOST);
|
||||||
|
config.mqtt.port = prefs.getUShort(MQTT_PORT_CONFIG_KEY, MQTT_PORT);
|
||||||
|
config.mqtt.username = prefs.getString(MQTT_USER_CONFIG_KEY, MQTT_USER);
|
||||||
|
config.mqtt.password = prefs.getString(MQTT_PASS_CONFIG_KEY, MQTT_PASS);
|
||||||
|
config.mqtt.topic = prefs.getString(MQTT_TOPIC_CONFIG_KEY, MQTT_TOPIC);
|
||||||
|
|
||||||
|
config.wifi.ssid = prefs.getString(WIFI_SSID_CONFIG_KEY, WIFI_SSID);
|
||||||
|
config.wifi.password = prefs.getString(WIFI_PASS_CONFIG_KEY, WIFI_PASS);
|
||||||
|
|
||||||
|
config.dev_name = prefs.getString(DEV_NAME_CONFIG_KEY, DEV_NAME);
|
||||||
|
config.tag_name = prefs.getString(TAG_NAME_CONFIG_KEY, TAG_NAME);
|
||||||
|
|
||||||
|
prefs.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleCommand(String line)
|
||||||
|
{
|
||||||
|
if (line.startsWith("set "))
|
||||||
|
{
|
||||||
|
int p1 = line.indexOf(' ', 4);
|
||||||
|
String key = line.substring(4, p1);
|
||||||
|
String value = line.substring(p1 + 1);
|
||||||
|
|
||||||
|
if (key.length() == 0 || value.length() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (key == CLEANUP_INTERVAL_CONFIG_KEY)
|
||||||
|
config.interval.cleanup = (uint32_t)value.toInt();
|
||||||
|
else if (key == TAG_TIMEOUT_CONFIG_KEY)
|
||||||
|
config.interval.tag_timeout = (uint32_t)value.toInt();
|
||||||
|
else if (key == UUID_CACHE_RETRY_TIMEOUT_CONFIG_KEY)
|
||||||
|
config.interval.uuid_cache_retry_timeout = (uint32_t)value.toInt();
|
||||||
|
else if (key == UUID_CACHE_CLEAN_TIMEOUT_CONFIG_KEY)
|
||||||
|
config.interval.uuid_cache_clean_timeout = (uint32_t)value.toInt();
|
||||||
|
else if (key == API_URL_CONFIG_KEY)
|
||||||
|
config.api.url = value;
|
||||||
|
else if (key == UUID_CHECK_ENDPOINT_CONFIG_KEY)
|
||||||
|
config.api.uuid_check_endpoint = value;
|
||||||
|
else if (key == MQTT_HOST_CONFIG_KEY)
|
||||||
|
config.mqtt.host = value;
|
||||||
|
else if (key == MQTT_PORT_CONFIG_KEY)
|
||||||
|
config.mqtt.port = value.toInt();
|
||||||
|
else if (key == MQTT_USER_CONFIG_KEY)
|
||||||
|
config.mqtt.username = value;
|
||||||
|
else if (key == MQTT_PASS_CONFIG_KEY)
|
||||||
|
config.mqtt.password = value;
|
||||||
|
else if (key == MQTT_TOPIC_CONFIG_KEY)
|
||||||
|
config.mqtt.topic = value;
|
||||||
|
else if (key == WIFI_SSID_CONFIG_KEY)
|
||||||
|
config.wifi.ssid = value;
|
||||||
|
else if (key == WIFI_PASS_CONFIG_KEY)
|
||||||
|
config.wifi.password = value;
|
||||||
|
else if (key == DEV_NAME_CONFIG_KEY)
|
||||||
|
config.dev_name = value;
|
||||||
|
else if (key == TAG_NAME_CONFIG_KEY)
|
||||||
|
config.tag_name = value;
|
||||||
|
}
|
||||||
|
else if (line == "save")
|
||||||
|
saveConfig();
|
||||||
|
else if (line.startsWith("show "))
|
||||||
|
{
|
||||||
|
String key = line.substring(5);
|
||||||
|
|
||||||
|
if (key.length() == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (key == CLEANUP_INTERVAL_CONFIG_KEY)
|
||||||
|
Serial.println(config.interval.cleanup);
|
||||||
|
else if (key == TAG_TIMEOUT_CONFIG_KEY)
|
||||||
|
Serial.println(config.interval.tag_timeout);
|
||||||
|
else if (key == UUID_CACHE_RETRY_TIMEOUT_CONFIG_KEY)
|
||||||
|
Serial.println(config.interval.uuid_cache_retry_timeout);
|
||||||
|
else if (key == UUID_CACHE_CLEAN_TIMEOUT_CONFIG_KEY)
|
||||||
|
Serial.println(config.interval.uuid_cache_clean_timeout);
|
||||||
|
else if (key == API_URL_CONFIG_KEY)
|
||||||
|
Serial.println(config.api.url);
|
||||||
|
else if (key == UUID_CHECK_ENDPOINT_CONFIG_KEY)
|
||||||
|
Serial.println(config.api.uuid_check_endpoint);
|
||||||
|
else if (key == MQTT_HOST_CONFIG_KEY)
|
||||||
|
Serial.println(config.mqtt.host);
|
||||||
|
else if (key == MQTT_PORT_CONFIG_KEY)
|
||||||
|
Serial.println(config.mqtt.port);
|
||||||
|
else if (key == MQTT_USER_CONFIG_KEY)
|
||||||
|
Serial.println(config.mqtt.username);
|
||||||
|
else if (key == MQTT_PASS_CONFIG_KEY)
|
||||||
|
Serial.println(config.mqtt.password);
|
||||||
|
else if (key == MQTT_TOPIC_CONFIG_KEY)
|
||||||
|
Serial.println(config.mqtt.topic);
|
||||||
|
else if (key == WIFI_SSID_CONFIG_KEY)
|
||||||
|
Serial.println(config.wifi.ssid);
|
||||||
|
else if (key == WIFI_PASS_CONFIG_KEY)
|
||||||
|
Serial.println(config.wifi.password);
|
||||||
|
else if (key == DEV_NAME_CONFIG_KEY)
|
||||||
|
Serial.println(config.dev_name);
|
||||||
|
else if (key == TAG_NAME_CONFIG_KEY)
|
||||||
|
Serial.println(config.tag_name);
|
||||||
|
}
|
||||||
|
else if (line == "reboot")
|
||||||
|
{
|
||||||
|
Serial.println("Rebooting...");
|
||||||
|
Serial.flush();
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
|
else if (line == "help")
|
||||||
|
{
|
||||||
|
Serial.println("Commands:");
|
||||||
|
Serial.println(" set <key> <value> - Set a configuration value");
|
||||||
|
Serial.println(" show <key> - Show a configuration value");
|
||||||
|
Serial.println(" save - Save configuration to non-volatile storage");
|
||||||
|
Serial.println(" reboot - Reboot the device");
|
||||||
|
Serial.println(" help - Show this help message");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setup()
|
void setup()
|
||||||
{
|
{
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
|
|
||||||
WiFi.begin(WIFI_SSID, WIFI_PASS);
|
loadConfig();
|
||||||
|
|
||||||
while (WiFi.status() != WL_CONNECTED)
|
WiFi.begin(config.wifi.ssid.c_str(), config.wifi.password.c_str());
|
||||||
{
|
|
||||||
delay(500);
|
|
||||||
Serial.print(".");
|
|
||||||
}
|
|
||||||
|
|
||||||
Serial.println("\nWiFi connected");
|
MQTT_PUB_TOPIC = config.mqtt.topic + config.dev_name;
|
||||||
|
|
||||||
snprintf(MQTT_PUB_TOPIC, sizeof(MQTT_PUB_TOPIC), "%s%s", MQTT_TOPIC, DEV_NAME);
|
mqtt.setServer(config.mqtt.host.c_str(), config.mqtt.port);
|
||||||
|
|
||||||
mqtt.setServer(MQTT_HOST, MQTT_PORT);
|
|
||||||
mqtt.setKeepAlive(60);
|
mqtt.setKeepAlive(60);
|
||||||
mqtt.setSocketTimeout(5);
|
mqtt.setSocketTimeout(5);
|
||||||
|
|
||||||
@@ -244,7 +409,7 @@ void setup()
|
|||||||
xTaskCreatePinnedToCore(mqttTask, "mqtt", 4096, NULL, 1, NULL, 1);
|
xTaskCreatePinnedToCore(mqttTask, "mqtt", 4096, NULL, 1, NULL, 1);
|
||||||
xTaskCreatePinnedToCore(httpTask, "http", 4096, NULL, 1, NULL, 1);
|
xTaskCreatePinnedToCore(httpTask, "http", 4096, NULL, 1, NULL, 1);
|
||||||
|
|
||||||
NimBLEDevice::init(DEV_NAME);
|
NimBLEDevice::init(config.dev_name.c_str());
|
||||||
|
|
||||||
NimBLEScan *scan = NimBLEDevice::getScan();
|
NimBLEScan *scan = NimBLEDevice::getScan();
|
||||||
|
|
||||||
@@ -264,10 +429,45 @@ void loop()
|
|||||||
|
|
||||||
static uint32_t last_cleanup = 0;
|
static uint32_t last_cleanup = 0;
|
||||||
|
|
||||||
if (millis() - last_cleanup >= CLEANUP_INTERVAL)
|
if (millis() - last_cleanup >= config.interval.cleanup)
|
||||||
{
|
{
|
||||||
cleanupTags();
|
cleanupTags();
|
||||||
cleanupCache();
|
cleanupCache();
|
||||||
last_cleanup = millis();
|
last_cleanup = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (Serial.available())
|
||||||
|
{
|
||||||
|
char c = Serial.read();
|
||||||
|
|
||||||
|
if (c == '\r')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (c == '\n')
|
||||||
|
{
|
||||||
|
Serial.print(c);
|
||||||
|
serial_buffer.trim();
|
||||||
|
|
||||||
|
if (serial_buffer.length() > 0)
|
||||||
|
handleCommand(serial_buffer);
|
||||||
|
|
||||||
|
serial_buffer = "";
|
||||||
|
|
||||||
|
Serial.printf("%s> ", config.dev_name.c_str());
|
||||||
|
}
|
||||||
|
else if (c == '\b')
|
||||||
|
{
|
||||||
|
if (serial_buffer.length() > 0)
|
||||||
|
{
|
||||||
|
serial_buffer.remove(serial_buffer.length() - 1);
|
||||||
|
|
||||||
|
Serial.print("\b \b");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.print(c);
|
||||||
|
serial_buffer += c;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,12 +5,29 @@
|
|||||||
#include <NimBLEDevice.h>
|
#include <NimBLEDevice.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#define CLEANUP_INTERVAL_CONFIG_KEY "int.cln"
|
||||||
|
#define TAG_TIMEOUT_CONFIG_KEY "int.tagto"
|
||||||
|
#define UUID_CACHE_RETRY_TIMEOUT_CONFIG_KEY "int.urtry"
|
||||||
|
#define UUID_CACHE_CLEAN_TIMEOUT_CONFIG_KEY "int.ucclean"
|
||||||
|
#define API_URL_CONFIG_KEY "api.url"
|
||||||
|
#define UUID_CHECK_ENDPOINT_CONFIG_KEY "api.uuidchk"
|
||||||
|
#define MQTT_HOST_CONFIG_KEY "mq.host"
|
||||||
|
#define MQTT_PORT_CONFIG_KEY "mq.port"
|
||||||
|
#define MQTT_USER_CONFIG_KEY "mq.user"
|
||||||
|
#define MQTT_PASS_CONFIG_KEY "mq.pass"
|
||||||
|
#define MQTT_TOPIC_CONFIG_KEY "mq.topic"
|
||||||
|
#define WIFI_SSID_CONFIG_KEY "wf.ssid"
|
||||||
|
#define WIFI_PASS_CONFIG_KEY "wf.pass"
|
||||||
|
#define DEV_NAME_CONFIG_KEY "dev.name"
|
||||||
|
#define TAG_NAME_CONFIG_KEY "tag.name"
|
||||||
|
|
||||||
#define CLEANUP_INTERVAL 5 * 1000 // ms
|
#define CLEANUP_INTERVAL 5 * 1000 // ms
|
||||||
#define TAG_TIMEOUT 60 * 1000 // ms
|
#define TAG_TIMEOUT 60 * 1000 // ms
|
||||||
#define UUID_CACHE_RETRY_TIMEOUT 5 * 60 * 1000 // ms
|
#define UUID_CACHE_RETRY_TIMEOUT 5 * 60 * 1000 // ms
|
||||||
#define UUID_CACHE_CLEAN_TIMEOUT 60 * 60 * 1000 // ms
|
#define UUID_CACHE_CLEAN_TIMEOUT 60 * 60 * 1000 // ms
|
||||||
|
#define API_URL "http://192.168.1.2"
|
||||||
|
#define UUID_CHECK_ENDPOINT "/check"
|
||||||
#define TAG_NAME "X6TAG"
|
#define TAG_NAME "X6TAG"
|
||||||
#define UUID_CHECK_ENDPOINT "http://192.168.1.2/check/"
|
|
||||||
#define MQTT_HOST "192.168.1.2"
|
#define MQTT_HOST "192.168.1.2"
|
||||||
#define MQTT_PORT 1883
|
#define MQTT_PORT 1883
|
||||||
#define MQTT_USER "user"
|
#define MQTT_USER "user"
|
||||||
@@ -20,6 +37,41 @@
|
|||||||
#define WIFI_PASS "x32hbh54673ngccdsfa9"
|
#define WIFI_PASS "x32hbh54673ngccdsfa9"
|
||||||
#define DEV_NAME "Scanner_Room_A"
|
#define DEV_NAME "Scanner_Room_A"
|
||||||
|
|
||||||
|
struct Config
|
||||||
|
{
|
||||||
|
struct Interval
|
||||||
|
{
|
||||||
|
uint32_t cleanup;
|
||||||
|
uint32_t tag_timeout;
|
||||||
|
uint32_t uuid_cache_retry_timeout;
|
||||||
|
uint32_t uuid_cache_clean_timeout;
|
||||||
|
} interval;
|
||||||
|
|
||||||
|
struct API
|
||||||
|
{
|
||||||
|
String url;
|
||||||
|
String uuid_check_endpoint;
|
||||||
|
} api;
|
||||||
|
|
||||||
|
struct MQTT
|
||||||
|
{
|
||||||
|
String host;
|
||||||
|
uint16_t port;
|
||||||
|
String username;
|
||||||
|
String password;
|
||||||
|
String topic;
|
||||||
|
} mqtt;
|
||||||
|
|
||||||
|
struct WiFi
|
||||||
|
{
|
||||||
|
String ssid;
|
||||||
|
String password;
|
||||||
|
} wifi;
|
||||||
|
|
||||||
|
String dev_name;
|
||||||
|
String tag_name;
|
||||||
|
};
|
||||||
|
|
||||||
struct BleEvent
|
struct BleEvent
|
||||||
{
|
{
|
||||||
char uuid[37];
|
char uuid[37];
|
||||||
@@ -40,6 +92,7 @@ struct HttpEvent
|
|||||||
enum class UUIDState
|
enum class UUIDState
|
||||||
{
|
{
|
||||||
PENDING,
|
PENDING,
|
||||||
|
RETRY,
|
||||||
VALID,
|
VALID,
|
||||||
INVALID
|
INVALID
|
||||||
};
|
};
|
||||||
|
|||||||
+246
-19
@@ -1,21 +1,13 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
|
|
||||||
#include "nimble/nimble_port.h"
|
#include "nimble/nimble_port.h"
|
||||||
#include "nimble/nimble_port_freertos.h"
|
#include "nimble/nimble_port_freertos.h"
|
||||||
|
|
||||||
#include "host/ble_hs.h"
|
|
||||||
#include "services/gap/ble_svc_gap.h"
|
#include "services/gap/ble_svc_gap.h"
|
||||||
|
#include "driver/usb_serial_jtag.h"
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
static const char *TAG = "BLE";
|
static config_t config;
|
||||||
|
|
||||||
static const ble_uuid128_t service_uuid =
|
|
||||||
BLE_UUID128_INIT(
|
|
||||||
0xF0, 0xDE, 0xBC, 0x9A,
|
|
||||||
0x78, 0x56, 0x34, 0x12,
|
|
||||||
0xF0, 0xDE, 0xBC, 0x9A,
|
|
||||||
0x78, 0x56, 0x34, 0x12);
|
|
||||||
|
|
||||||
static void start_adv(void)
|
static void start_adv(void)
|
||||||
{
|
{
|
||||||
@@ -24,19 +16,18 @@ static void start_adv(void)
|
|||||||
|
|
||||||
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
|
fields.flags = BLE_HS_ADV_F_DISC_GEN | BLE_HS_ADV_F_BREDR_UNSUP;
|
||||||
|
|
||||||
const char *name = "X6TAG";
|
fields.name = (uint8_t *)config.name;
|
||||||
fields.name = (uint8_t *)name;
|
fields.name_len = strlen(config.name);
|
||||||
fields.name_len = strlen(name);
|
|
||||||
fields.name_is_complete = 1;
|
fields.name_is_complete = 1;
|
||||||
|
|
||||||
fields.uuids128 = (ble_uuid128_t *)&service_uuid;
|
fields.uuids128 = (ble_uuid128_t *)&config.uuid;
|
||||||
fields.num_uuids128 = 1;
|
fields.num_uuids128 = 1;
|
||||||
fields.uuids128_is_complete = 1;
|
fields.uuids128_is_complete = 1;
|
||||||
|
|
||||||
int rc = ble_gap_adv_set_fields(&fields);
|
int rc = ble_gap_adv_set_fields(&fields);
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
{
|
{
|
||||||
ESP_LOGE(TAG, "adv set fields failed: %d", rc);
|
ESP_LOGE(config.name, "adv set fields failed: %d", rc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,17 +49,17 @@ static void start_adv(void)
|
|||||||
|
|
||||||
if (rc != 0)
|
if (rc != 0)
|
||||||
{
|
{
|
||||||
ESP_LOGE(TAG, "adv start failed: %d", rc);
|
ESP_LOGE(config.name, "adv start failed: %d", rc);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAG, "advertising started");
|
ESP_LOGI(config.name, "advertising started");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void on_sync(void)
|
static void on_sync(void)
|
||||||
{
|
{
|
||||||
ESP_LOGI(TAG, "BLE synced");
|
ESP_LOGI(config.name, "BLE synced");
|
||||||
|
|
||||||
start_adv();
|
start_adv();
|
||||||
}
|
}
|
||||||
@@ -78,9 +69,237 @@ static void host_task(void *param)
|
|||||||
nimble_port_run();
|
nimble_port_run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool uuid_from_string(const char *str, ble_uuid128_t *uuid)
|
||||||
|
{
|
||||||
|
unsigned int b[16];
|
||||||
|
|
||||||
|
int rc = sscanf(str,
|
||||||
|
"%02x%02x%02x%02x-"
|
||||||
|
"%02x%02x-"
|
||||||
|
"%02x%02x-"
|
||||||
|
"%02x%02x-"
|
||||||
|
"%02x%02x%02x%02x%02x%02x",
|
||||||
|
|
||||||
|
&b[15], &b[14], &b[13], &b[12],
|
||||||
|
&b[11], &b[10],
|
||||||
|
&b[9], &b[8],
|
||||||
|
&b[7], &b[6],
|
||||||
|
&b[5], &b[4], &b[3],
|
||||||
|
&b[2], &b[1], &b[0]);
|
||||||
|
|
||||||
|
if (rc != 16)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
uuid->u.type = BLE_UUID_TYPE_128;
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
uuid->value[i] = (uint8_t)b[i];
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t reset_config()
|
||||||
|
{
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
|
||||||
|
esp_err_t err = nvs_open("config", NVS_READWRITE, &nvs);
|
||||||
|
|
||||||
|
if (err != ESP_OK)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = nvs_erase_all(nvs);
|
||||||
|
|
||||||
|
if (err != ESP_OK)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
err = nvs_commit(nvs);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
nvs_close(nvs);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t save_config()
|
||||||
|
{
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
|
||||||
|
esp_err_t err = nvs_open("config", NVS_READWRITE, &nvs);
|
||||||
|
|
||||||
|
if (err != ESP_OK)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = nvs_set_str(nvs, "uuid", config.uuid_str);
|
||||||
|
|
||||||
|
if (err != ESP_OK)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
err = nvs_set_str(nvs, "name", config.name);
|
||||||
|
|
||||||
|
if (err != ESP_OK)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
err = nvs_set_u32(nvs, "adv_int", config.adv_interval);
|
||||||
|
|
||||||
|
if (err != ESP_OK)
|
||||||
|
goto exit;
|
||||||
|
|
||||||
|
err = nvs_commit(nvs);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
nvs_close(nvs);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t load_config()
|
||||||
|
{
|
||||||
|
nvs_handle_t nvs;
|
||||||
|
|
||||||
|
esp_err_t err = nvs_open("config", NVS_READONLY, &nvs);
|
||||||
|
|
||||||
|
strcpy(config.uuid_str, DEFAULT_UUID);
|
||||||
|
strcpy(config.name, DEFAULT_NAME);
|
||||||
|
config.adv_interval = DEFAULT_ADV_INTERVAL;
|
||||||
|
|
||||||
|
uuid_from_string(config.uuid_str, &config.uuid);
|
||||||
|
|
||||||
|
if (err != ESP_OK)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
len = sizeof(config.uuid_str);
|
||||||
|
|
||||||
|
nvs_get_str(nvs, "uuid", config.uuid_str, &len);
|
||||||
|
|
||||||
|
len = sizeof(config.name);
|
||||||
|
|
||||||
|
nvs_get_str(nvs, "name", config.name, &len);
|
||||||
|
|
||||||
|
nvs_get_u32(nvs, "adv_int", &config.adv_interval);
|
||||||
|
|
||||||
|
nvs_close(nvs);
|
||||||
|
|
||||||
|
uuid_from_string(config.uuid_str, &config.uuid);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void serial_task(void *arg)
|
||||||
|
{
|
||||||
|
char line[128];
|
||||||
|
char buf[64];
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
uint8_t ch;
|
||||||
|
|
||||||
|
int len = usb_serial_jtag_read_bytes(&ch, 1, portMAX_DELAY);
|
||||||
|
|
||||||
|
if (len > 0)
|
||||||
|
{
|
||||||
|
if (ch == '\n' || ch == '\r')
|
||||||
|
{
|
||||||
|
usb_serial_jtag_write_bytes("\n", 1, 20 / portTICK_PERIOD_MS);
|
||||||
|
line[pos] = '\0';
|
||||||
|
|
||||||
|
if (pos > 0)
|
||||||
|
{
|
||||||
|
if (strncmp(line, "set uuid ", 9) == 0)
|
||||||
|
{
|
||||||
|
if (uuid_from_string(line + 9, &config.uuid))
|
||||||
|
strcpy(config.uuid_str, line + 9);
|
||||||
|
}
|
||||||
|
else if (strncmp(line, "set name ", 9) == 0)
|
||||||
|
{
|
||||||
|
strncpy(config.name, line + 9, sizeof(config.name) - 1);
|
||||||
|
config.name[sizeof(config.name) - 1] = '\0';
|
||||||
|
}
|
||||||
|
else if (strncmp(line, "set adv_interval ", 17) == 0)
|
||||||
|
{
|
||||||
|
uint32_t interval = atoi(line + 17);
|
||||||
|
if (interval > 0)
|
||||||
|
config.adv_interval = interval;
|
||||||
|
}
|
||||||
|
else if (strcmp(line, "show uuid") == 0)
|
||||||
|
{
|
||||||
|
usb_serial_jtag_write_bytes(config.uuid_str, strlen(config.uuid_str), 20 / portTICK_PERIOD_MS);
|
||||||
|
usb_serial_jtag_write_bytes("\n", 1, 20 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
else if (strcmp(line, "show name") == 0)
|
||||||
|
{
|
||||||
|
usb_serial_jtag_write_bytes(config.name, strlen(config.name), 20 / portTICK_PERIOD_MS);
|
||||||
|
usb_serial_jtag_write_bytes("\n", 1, 20 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
else if (strcmp(line, "show adv_interval") == 0)
|
||||||
|
{
|
||||||
|
int len = snprintf(buf, sizeof(buf), "%lu\n", (unsigned long)config.adv_interval);
|
||||||
|
usb_serial_jtag_write_bytes(buf, len, 20 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
else if (strcmp(line, "save") == 0)
|
||||||
|
save_config();
|
||||||
|
else if (strcmp(line, "reset") == 0)
|
||||||
|
{
|
||||||
|
reset_config();
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
|
else if (strcmp(line, "reboot") == 0)
|
||||||
|
{
|
||||||
|
usb_serial_jtag_write_bytes("Rebooting...\n", 13, 20 / portTICK_PERIOD_MS);
|
||||||
|
esp_restart();
|
||||||
|
}
|
||||||
|
else if (strcmp(line, "help") == 0)
|
||||||
|
{
|
||||||
|
usb_serial_jtag_write_bytes("Commands:\n"
|
||||||
|
" set uuid <uuid>\n"
|
||||||
|
" set name <name>\n"
|
||||||
|
" set adv_interval <ms>\n"
|
||||||
|
" show uuid\n"
|
||||||
|
" show name\n"
|
||||||
|
" show adv_interval\n"
|
||||||
|
" save\n"
|
||||||
|
" reset\n"
|
||||||
|
" reboot\n"
|
||||||
|
" help\n",
|
||||||
|
145, 20 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos = 0;
|
||||||
|
int len = snprintf(buf, sizeof(buf), "%s> ", config.name);
|
||||||
|
usb_serial_jtag_write_bytes(buf, len, 20 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
else if (ch == 0x08)
|
||||||
|
{
|
||||||
|
if (pos > 0)
|
||||||
|
{
|
||||||
|
pos--;
|
||||||
|
usb_serial_jtag_write_bytes("\b \b", 3, 20 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (pos < sizeof(line) - 1)
|
||||||
|
{
|
||||||
|
line[pos++] = ch;
|
||||||
|
usb_serial_jtag_write_bytes((char *)&ch, 1, 20 / portTICK_PERIOD_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void app_main(void)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
ESP_ERROR_CHECK(nvs_flash_init());
|
ESP_ERROR_CHECK(nvs_flash_init());
|
||||||
|
|
||||||
|
usb_serial_jtag_driver_config_t cfg = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT();
|
||||||
|
usb_serial_jtag_driver_install(&cfg);
|
||||||
|
|
||||||
|
load_config();
|
||||||
|
|
||||||
nimble_port_init();
|
nimble_port_init();
|
||||||
|
|
||||||
ble_svc_gap_init();
|
ble_svc_gap_init();
|
||||||
@@ -88,5 +307,13 @@ void app_main(void)
|
|||||||
|
|
||||||
ble_hs_cfg.sync_cb = on_sync;
|
ble_hs_cfg.sync_cb = on_sync;
|
||||||
|
|
||||||
|
xTaskCreate(
|
||||||
|
serial_task,
|
||||||
|
"serial_task",
|
||||||
|
4096,
|
||||||
|
NULL,
|
||||||
|
5,
|
||||||
|
NULL);
|
||||||
|
|
||||||
nimble_port_freertos_init(host_task);
|
nimble_port_freertos_init(host_task);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef MAIN_H
|
||||||
|
#define MAIN_H
|
||||||
|
|
||||||
|
#include "host/ble_hs.h"
|
||||||
|
|
||||||
|
#define DEFAULT_UUID "12345678-9abc-def0-1234-56789abcdef0"
|
||||||
|
#define DEFAULT_NAME "X6TAG"
|
||||||
|
#define DEFAULT_ADV_INTERVAL 0x80
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
ble_uuid128_t uuid;
|
||||||
|
char uuid_str[37];
|
||||||
|
char name[32];
|
||||||
|
uint32_t adv_interval;
|
||||||
|
} config_t;
|
||||||
|
|
||||||
|
#endif
|
||||||
Reference in New Issue
Block a user