From b356a66bd123a1378f64ff1cab06e59ad869231b Mon Sep 17 00:00:00 2001 From: uvok Date: Sun, 3 May 2026 19:53:05 +0200 Subject: Add way to send text --- include/badge/ble.h | 54 +++++++++++++++++++++++++++++++------ src/ble.cpp | 78 ++++++++++++++++++++++++++++++++++++++--------------- src/main.cpp | 2 +- 3 files changed, 103 insertions(+), 31 deletions(-) diff --git a/include/badge/ble.h b/include/badge/ble.h index 27b4749..1373d30 100644 --- a/include/badge/ble.h +++ b/include/badge/ble.h @@ -1,7 +1,8 @@ #pragma once #include "indicator.h" -#include +#include +#include namespace de::uvok::badge { @@ -9,21 +10,58 @@ namespace de::uvok::badge { None, Template, - Indicator + Indicator, + TextAsQrCode }; - typedef struct + struct BlePollResult { BleActionType action_type; union { uint8_t new_template; DisplayIndicator new_indicator; }; - } ble_poll_result_t; + std::string new_text; - void ble_init(void); - void ble_advertise(void); - bool ble_is_active(void); - ble_poll_result_t ble_poll(void); + static BlePollResult MakeTemplate(uint8_t val) + { + BlePollResult p{}; + p.action_type = BleActionType::Template; + p.new_template = val; + return p; + } + + static BlePollResult MakeText(const std::string &val) + { + BlePollResult p{}; + p.action_type = BleActionType::TextAsQrCode; + p.new_text = val; + return p; + } + + static BlePollResult MakeEmpty() + { + BlePollResult p{}; + return p; + } + + static BlePollResult MakeIndicator(DisplayIndicator ind) + { + BlePollResult p{}; + p.action_type = BleActionType::Indicator; + p.new_indicator = ind; + return p; + } + + private: + BlePollResult() : action_type(BleActionType::None), new_indicator(DisplayIndicator::None) + { + } + }; + + void ble_init(); + void ble_advertise(); + bool ble_is_active(); + BlePollResult ble_poll(); void ble_set_image(uint8_t image); } // namespace de::uvok::badge diff --git a/src/ble.cpp b/src/ble.cpp index 552055d..6fdc6bc 100644 --- a/src/ble.cpp +++ b/src/ble.cpp @@ -5,17 +5,23 @@ #include "badge/config.h" #include "badge/log.h" +#include + using de::uvok::badge::DisplayIndicator; static NimBLEServer *server; static NimBLEAdvertising *pAdvertising; -static NimBLECharacteristic *selectorCharacteristic; +static NimBLECharacteristic *selectImageCharacteristic; +static NimBLECharacteristic *selectTextCharacteristic; -static volatile struct +template struct CharacteristicPoll { - bool changed; - uint8_t value; -} value_changed; + volatile bool changed; + T value; +}; + +static CharacteristicPoll select_image_value_changed; +static CharacteristicPoll select_text_changed; static volatile DisplayIndicator ble_indicator = DisplayIndicator::Uninit; @@ -23,6 +29,7 @@ static volatile DisplayIndicator ble_indicator = DisplayIndicator::Uninit; const char *templates[] = { #include "./images.cfg" + }; class BadgeServerCallbacks : public NimBLEServerCallbacks @@ -47,7 +54,7 @@ class BadgeSelectorCallbacks : public NimBLECharacteristicCallbacks { NimBLECharacteristicCallbacks::onWrite(pCharacteristic, connInfo); - if (pCharacteristic == selectorCharacteristic) + if (pCharacteristic == selectImageCharacteristic) { LOG_F("Write!"); const char *val = pCharacteristic->getValue().c_str(); @@ -58,21 +65,34 @@ class BadgeSelectorCallbacks : public NimBLECharacteristicCallbacks { LOG_F("Error parsing value\n"); pCharacteristic->setValue(defVal); - (void) pCharacteristic->notify(defVal, BLE_HS_CONN_HANDLE_NONE); + (void)pCharacteristic->notify(defVal, BLE_HS_CONN_HANDLE_NONE); } else if (newVal >= ARRAY_SIZE(templates)) { LOG_F("Value out of range: %ld\n", newVal); pCharacteristic->setValue(defVal); - (void) pCharacteristic->notify(defVal, BLE_HS_CONN_HANDLE_NONE); + (void)pCharacteristic->notify(defVal, BLE_HS_CONN_HANDLE_NONE); } else { LOG_F("Value set to %ld\n", newVal); - value_changed.value = newVal; - value_changed.changed = true; + select_image_value_changed.value = newVal; + select_image_value_changed.changed = true; } } + else if (pCharacteristic == selectTextCharacteristic) + { + // + auto val = pCharacteristic->getValue().c_str(); + if (val == nullptr) + { + return; + } + std::string newText = val; + select_text_changed.value = newText; + select_text_changed.changed = true; + LOG_F("Received new text: %s\n", newText.c_str()); + } } } badgeSelectorCallbacks; @@ -85,11 +105,11 @@ void de::uvok::badge::ble_init() NimBLEService *service = new NimBLEService("ca260000-b4bb-46b2-bd06-b7b7a61ea990"); // read/write current - selectorCharacteristic = + selectImageCharacteristic = service->createCharacteristic("ca260001-b4bb-46b2-bd06-b7b7a61ea990", NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY); - selectorCharacteristic->setValue("0"); - selectorCharacteristic->setCallbacks(&badgeSelectorCallbacks); + selectImageCharacteristic->setValue("0"); + selectImageCharacteristic->setCallbacks(&badgeSelectorCallbacks); // get pictures auto call = service->createCharacteristic("ca260002-b4bb-46b2-bd06-b7b7a61ea990", NIMBLE_PROPERTY::READ); @@ -102,6 +122,14 @@ void de::uvok::badge::ble_init() s.concat(tmp); } call->setValue(s.c_str()); + + // write QR code + selectTextCharacteristic = + service->createCharacteristic("ca260003-b4bb-46b2-bd06-b7b7a61ea990", + NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY); + selectTextCharacteristic->setValue(""); + selectTextCharacteristic->setCallbacks(&badgeSelectorCallbacks); + const uint16_t mtuLen = max(s.length() + 16, 256); NimBLEDevice::setMTU(mtuLen); @@ -134,27 +162,33 @@ void de::uvok::badge::ble_advertise() } } -de::uvok::badge::ble_poll_result_t de::uvok::badge::ble_poll() +de::uvok::badge::BlePollResult de::uvok::badge::ble_poll() { - if (value_changed.changed) + if (select_image_value_changed.changed) + { + const uint8_t val = select_image_value_changed.value; + select_image_value_changed.changed = false; + return BlePollResult::MakeTemplate(val); + } + if (select_text_changed.changed) { - const uint8_t val = value_changed.value; - value_changed.changed = false; - return (ble_poll_result_t){.action_type = BleActionType::Template, .new_template = val}; + const std::string val = select_text_changed.value; + select_text_changed.changed = false; + return BlePollResult::MakeText(val); } if (ble_indicator != DisplayIndicator::Uninit) { DisplayIndicator ind = ble_indicator; ble_indicator = DisplayIndicator::Uninit; - return (ble_poll_result_t){.action_type = BleActionType::Indicator, .new_indicator = ind}; + return BlePollResult::MakeIndicator(ind); } - return (ble_poll_result_t){.action_type = BleActionType::None}; + return BlePollResult::MakeEmpty(); } 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); - (void) selectorCharacteristic->notify(s, BLE_HS_CONN_HANDLE_NONE); + selectImageCharacteristic->setValue(s); + (void)selectImageCharacteristic->notify(s, BLE_HS_CONN_HANDLE_NONE); } \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 0f6af57..6d41820 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -82,7 +82,7 @@ void loop() } { - de::uvok::badge::ble_poll_result_t pollres = de::uvok::badge::ble_poll(); + de::uvok::badge::BlePollResult pollres = de::uvok::badge::ble_poll(); switch (pollres.action_type) { case de::uvok::badge::BleActionType::Template: { -- cgit v1.2.3