vCard over cdc_msg
mod_vcard is a regular consumer of the
cdc_msg message-transfer framework. It owns no
BLE service of its own: it registers a handler for one MIME type and sends its
own card through the framework. The old custom ble_vcard GATT service has been
deleted.
MIME type
Section titled “MIME type”The vCard payload uses the MIME type text/vcard. The payload is raw
vCard 4.0 text.
Handler registration
Section titled “Handler registration”In VcardModule::init() the module registers itself as the text/vcard
handler:
cdc::msg::MessageTransfer::instance().registerHandler( "text/vcard", // MIME type "mod_vcard.received", // consent-prompt i18n key -> "Contact (vCard)" deliverVcard); // delivery callbackVcardModule::stop() unregisters it again (unregisterHandler("text/vcard")).
The descriptor key mod_vcard.received resolves through the module’s own English
i18n table to “Contact (vCard)”, which is what the receiver sees in the
Incoming transfer consent prompt.
Delivery (receiving)
Section titled “Delivery (receiving)”deliverVcard(data, len, mime, peerName) is the DeliverFn. The incoming bytes
are untrusted and not NUL-terminated, so the handler:
- Rejects empty payloads and anything larger than
VCARD_MAX_LEN(768 bytes). - Copies the bytes into a bounded PSRAM buffer and NUL-terminates them.
- Calls
vcard_store_add()to validate and store the card.
It returns true only when the card was stored. Storage deduplicates on exact
text, so receiving an identical card twice is a no-op upsert.
Sending
Section titled “Sending”The vCards menu Send vCard action loads the own card and hands it to the framework’s interactive sender:
cdc::msg::MessageTransfer::instance().beginInteractiveSend( "text/vcard", reinterpret_cast<const uint8_t*>(own), static_cast<uint32_t>(len));The framework then owns the peer picker, the numeric-comparison pairing, the consent on the far side, encryption and the progress UI. If no own card is stored, the action shows a “No vCard set” hint and does not send.
Payload format
Section titled “Payload format”The card is vCard 4.0 text. The own-card editor maps to these vCard fields
(via vcard_data_t, generated by vcard_generate_from_struct):
| Editor field | vCard line |
|---|---|
| First name / Last name | N: (family;given) |
| Display name | FN: (falls back to “given family” if empty) |
| Organization | ORG: |
| Position | TITLE: |
EMAIL: | |
| Phone (Home / Mobile / Work) | TEL;TYPE=HOME / CELL / WORK |
| Website | URL: |
| Telegram | IMPP:telegram: |
| Signal | IMPP:signal: |
| Matrix | IMPP:matrix: |
| Threema | IMPP:threema: |
| Social Profile | X-SOCIALPROFILE: |
| Note | NOTE: |
Empty fields are omitted from the generated card. The whole card is bounded to
VCARD_MAX_LEN (768 bytes), which is also the receive-side cap, well within the
framework’s 4096-byte payload limit.
Storage and backup
Section titled “Storage and backup”Received cards go into the vCard store (up to VCARD_MAX_CARDS = 100), separate
from the own card. The module’s exportBackup/importBackup include the own
card (own) and a received array of raw card texts under schema_ver 1;
import upserts by exact text.