#include "badge/ble.h" #include #include "badge/config.h" #include "badge/log.h" static NimBLEServer *server; static NimBLEAdvertising *pAdvertising; static NimBLECharacteristic *selectorCharacteristic; static volatile struct { bool changed; uint8_t value; } value_changed; #define IMAGE_DATA(_, display) display const char *templates[] = { #include "./images.cfg" }; class BadgeServerCallbacks : public NimBLEServerCallbacks { void onConnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo) override { NimBLEServerCallbacks::onConnect(pServer, connInfo); } void onDisconnect(NimBLEServer *pServer, NimBLEConnInfo &connInfo, int reason) override { NimBLEServerCallbacks::onDisconnect(pServer, connInfo, reason); } } badgeServerCallbacks; class BadgeSelectorCallbacks : public NimBLECharacteristicCallbacks { void onWrite(NimBLECharacteristic *pCharacteristic, NimBLEConnInfo &connInfo) override { NimBLECharacteristicCallbacks::onWrite(pCharacteristic, connInfo); if (pCharacteristic == selectorCharacteristic) { LOG_F("Write!"); const char *val = pCharacteristic->getValue().c_str(); char *end = NULL; long newVal = strtol(val, &end, 10); String defVal("0"); if (end == NULL || end == val) { LOG_F("Error parsing value\n"); pCharacteristic->setValue(defVal); pCharacteristic->notify(defVal, BLE_HS_CONN_HANDLE_NONE); } else if (newVal >= ARRAY_SIZE(templates)) { LOG_F("Value out of range: %d\n", newVal); pCharacteristic->setValue(defVal); pCharacteristic->notify(defVal, BLE_HS_CONN_HANDLE_NONE); } else { LOG_F("Value set to %d\n", newVal); value_changed.value = newVal; value_changed.changed = true; } } } } badgeSelectorCallbacks; void de::uvok::badge::ble_init(void) { NimBLEDevice::init("Espadge"); server = NimBLEDevice::createServer(); server->setCallbacks(&badgeServerCallbacks); NimBLEService *service = new NimBLEService("ca260000-b4bb-46b2-bd06-b7b7a61ea990"); // read/write current selectorCharacteristic = service->createCharacteristic("ca260001-b4bb-46b2-bd06-b7b7a61ea990", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY); selectorCharacteristic->setValue("0"); selectorCharacteristic->setCallbacks(&badgeSelectorCallbacks); // get pictures auto call = service->createCharacteristic("ca260002-b4bb-46b2-bd06-b7b7a61ea990", NIMBLE_PROPERTY::READ); String s{}; for (int i = 0; i < ARRAY_SIZE(templates); i++) { char tmp[32]; snprintf(tmp, sizeof(tmp), "%d-%s;", i, templates[i]); s.concat(tmp); } call->setValue(s.c_str()); service->start(); server->addService(service); pAdvertising = NimBLEDevice::getAdvertising(); pAdvertising->setName("NimBLE"); } bool de::uvok::badge::ble_is_active(void) { return pAdvertising->isAdvertising() || server->getConnectedCount() > 0; // ???? NimBLEDevice::getConnectedClients().size() > 0; } void de::uvok::badge::ble_advertise(void) { if (!pAdvertising->isAdvertising()) { Serial.println("Long press detected. Starting advertising..."); pAdvertising->start(10000); } } de::uvok::badge::ble_poll_result_t de::uvok::badge::ble_poll(void) { if (value_changed.changed) { const uint8_t val = value_changed.value; value_changed.changed = false; return (ble_poll_result_t){.has_data = true, .new_template = val}; } return (ble_poll_result_t){.has_data = false}; } void de::uvok::badge::ble_set_image(uint8_t image) { LOG_F("Notify BLE: set image to %d\n", image); String s(image); selectorCharacteristic->setValue(s); selectorCharacteristic->notify(s, BLE_HS_CONN_HANDLE_NONE); }