Skip to content

필사 모드: FingerScore Hardware 4 — Designing BLE Wireless Communication (From GATT to Mobile Integration)

English
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

Introduction

FingerScore is a smart scoring platform for racket sports (tennis, badminton, table tennis, squash). There is a tiny BLE ring you wear on a finger, and a simple gesture (a light tap of the finger, or pressing a button) bumps the score, which is then transmitted wirelessly to a phone or web app. No more running to the scoreboard or scribbling on paper mid-match — a single finger motion records the point.

This series teaches you how to actually build that hardware, step by step. Parts 1 through 3 covered choosing a microcontroller, sensing (detecting gestures with an accelerometer), and firmware basics. This Part 4 is about **wireless communication**. No matter how cleverly the ring detects a gesture, it is useless if it cannot reliably get that signal to the phone.

Why BLE (Bluetooth Low Energy) specifically? Why not Wi-Fi or classic Bluetooth? How do we convey "the score went up by one" to the phone? Is pairing secure? This article explains all of it from scratch. You do not need an electrical engineering degree to follow along — we pair analogies with concrete code skeletons.

1. Why BLE — the Philosophy of Low Power

The wireless candidates

A finger-worn ring has a very small battery — a single coin cell (CR2032) or a fingernail-sized LiPo, at most. So the single most important criterion when choosing a wireless method is **power**.

| Method | Avg. power draw | Range | Data volume | Fit for FingerScore |

| --- | --- | --- | --- | --- |

| Wi-Fi | Very high (hundreds of mA) | Long | Very large | Poor (battery lasts hours) |

| Classic Bluetooth | High (tens of mA) | Medium | Large | Poor |

| BLE | Very low (tens of uA avg.) | Medium (~10m) | Small (a few bytes) | Ideal |

| ANT+ | Low | Medium | Small | Possible, weak phone support |

| LoRa | Low | Very long | Very small | Overkill (range unneeded) |

The data FingerScore sends is tiny events: "score went up," "set ended." A few bytes per event is plenty. There is no need to push large data quickly. Meanwhile the battery must last days to weeks. BLE fits these constraints best.

How BLE saves power

The core idea is **"sleep most of the time, wake up only for a moment."** The BLE radio chip is mostly off and only wakes during a short, scheduled window (the connection interval) to exchange data. Often it is awake less than 1% of the time.

BLE radio time usage (conceptual)

power

^

| . . . .

| | | | | <- short TX/RX windows (a few ms)

| | | | |

|___|_________|_________|_________|____> time

<--------> <- mostly sleep (tens-hundreds of ms)

interval

This "wake-up period" is the connection interval, covered in detail later. The longer it is, the more power you save but the slower the response. A classic tradeoff.

2. The Two Pillars of BLE — GAP and GATT

The two acronyms that confuse newcomers most are GAP and GATT. Let us settle them with an analogy.

- **GAP (Generic Access Profile)**: the rules for "who connects and how." Think of discovering each other at a party, saying hello, and pairing up.

- **GATT (Generic Attribute Profile)**: the rules for "what data is exchanged, and how, once connected." This is the format of the actual conversation after the greeting.

GAP — discovery and connection

A BLE device usually plays one of two roles.

- **Peripheral**: the side that advertises its presence. The FingerScore ring is a Peripheral.

- **Central**: the side that scans advertisements and initiates connections. Usually the phone.

The ring periodically broadcasts a small advertising packet: "I'm here, I'm a FingerScore ring." The phone, while scanning, discovers this packet and attempts a connection.

[FingerScore ring] --- advertising ---> (air) <--- scanning --- [phone]

Peripheral Central

After discovery:

[ring] <========= connection established =========> [phone]

The advertising packet can carry the device name, a service UUID (explained below), TX power, and so on. There is a size limit (31 bytes for legacy advertising), so include only what is essential.

GATT — the structure of data

Once connected, we are in GATT's world. GATT organizes data into a hierarchy: **Service → Characteristic → Descriptor**.

- **Service**: a bundle of related data. e.g., "Score Service," "Battery Service."

- **Characteristic**: a single piece of actual data. e.g., "current score," "set number," "battery level."

- **Descriptor**: metadata about a characteristic. e.g., a human-readable description, notification settings.

By analogy, a Service is a dresser, a Characteristic is one drawer, and a Descriptor is the label on the drawer.

GATT server (FingerScore ring) structure

Service: Battery Service (standard, UUID 0x180F)

└─ Characteristic: Battery Level (0x2A19) [Read, Notify]

Service: FingerScore Score Service (custom 128-bit UUID)

├─ Characteristic: Score State [Read, Notify]

├─ Characteristic: Match Control [Write]

└─ Characteristic: Device Config [Read, Write]

An important point: typically the **Peripheral (ring) is the GATT server** and the **Central (phone) is the GATT client**. The side that stores and serves the data is the ring.

3. How Characteristics Behave — Read, Write, Notify

A characteristic has several properties. Here are the three that matter most for FingerScore.

| Property | Direction | Meaning | FingerScore example |

| --- | --- | --- | --- |

| Read | phone asks the ring | phone reads the value | one-shot query of current score |

| Write | phone sends to the ring | phone writes a value | send a "reset match" command |

| Notify | ring pushes to the phone | ring alerts when value changes | notify instantly when score rises |

The heart of FingerScore is **Notify**. Having the phone constantly ask "did the score change?" (polling) wastes power. Far more efficient: the ring tells the phone "it just changed!" only when the score actually changes.

How Notify works

After connecting, the phone writes to a special descriptor of the characteristic it wants alerts from — the **CCCD (Client Characteristic Configuration Descriptor)**. Writing 1 there means "turn notifications on." From then on, whenever the ring updates the score characteristic, it is delivered to the phone automatically.

1. phone: write 0x0001 to the Score State characteristic's CCCD (subscribe)

2. ring: detect gesture -> score +1 -> update Score State value

3. ring: immediately Notify the updated value to the phone

4. phone: receive new score in callback -> refresh UI

There is also Indicate, similar to Notify. The difference is **acknowledgement (ACK)**. Notify is fire-and-forget (fast, can be lost); Indicate requires the phone to confirm receipt before the next one is sent (slower, more reliable). For data like a score that must not be lost, Indicate can be safer, but usually we use Notify with a sequence number to detect loss. More on that in the packet design section.

4. Designing a Custom GATT Service — the Score Event

Standard services (battery, heart rate, etc.) have fixed UUIDs, but the FingerScore score service is our own, so we mint a **128-bit custom UUID**. The convention is to draw one random base UUID from a generator and vary just a few bytes per service/characteristic.

Base UUID: 6E40XXXX-B5A3-F393-E0A9-E50E24DCCA9E (example)

Score Service : 6E400001-...

├ Score State char : 6E400002-... [Notify, Read]

├ Match Control char: 6E400003-... [Write]

└ Device Config char: 6E400004-... [Read, Write]

Designing the score packet

How do we express "the score went up" in bytes? If we naively send just the score number, the phone has no way to know when a packet was lost. So we design a small but smart packet structure.

Score State packet (8 bytes)

Offset Size Field description

0 1 seq sequence number (0-255 wrapping). for loss detection

1 1 event_type 0=score, 1=set end, 2=match end, 3=reset

2 1 player 0=me, 1=opponent

3 1 score_self my current score

4 1 score_oppo opponent current score

5 1 set_self my set count

6 1 set_oppo opponent set count

7 1 battery_pct battery level (%)

Advantages of this design:

- The **sequence number** lets the phone detect "I got 7 right after 5? Number 6 was lost." It then re-reads the current state with Read to resync.

- We send the score as an absolute (current) value, so even if one or two packets are lost, the next packet auto-recovers the truth (absolute, not incremental, so it is robust).

- Eight bytes fit comfortably within BLE's default MTU (23 bytes, 20-byte effective payload).

> Design principle: on small IoT devices, sending "the full current state" beats sending "an increment (+1)" for loss resilience. Even if one packet disappears, the next carries the truth.

5. Firmware Code Skeleton (Peripheral Side)

The SDK differs per chip (e.g., Nordic nRF52, ESP32), but the concept is similar. Here is a near-pseudocode C skeleton showing the flow. All braces and pointers live inside code fences, so they are safe.

// struct holding the score state

typedef struct {

uint8_t seq;

uint8_t event_type;

uint8_t player;

uint8_t score_self;

uint8_t score_oppo;

uint8_t set_self;

uint8_t set_oppo;

uint8_t battery_pct;

} score_packet_t;

static score_packet_t g_score;

static uint16_t score_char_handle; // GATT characteristic handle

// gesture detection callback (called from accelerometer interrupt)

void on_gesture_score(uint8_t which_player) {

g_score.seq++; // increment sequence

g_score.event_type = 0; // score event

g_score.player = which_player;

if (which_player == 0) g_score.score_self++;

else g_score.score_oppo++;

g_score.battery_pct = battery_read_percent();

// update characteristic value + send Notify

ble_notify(score_char_handle,

(uint8_t*)&g_score,

sizeof(g_score));

}

The essence is simply: when a gesture interrupt arrives, update the struct and call `ble_notify()`. Normally the MCU stays asleep; when the accelerometer senses a "tap!" impact, it wakes via interrupt, runs this function, and goes back to sleep.

Handling Write (phone -> ring commands)

When the phone sends a command like "reset match," a Write callback fires.

// when a Write arrives on the Match Control characteristic

void on_match_control_write(const uint8_t *data, uint16_t len) {

if (len < 1) return;

uint8_t cmd = data[0];

switch (cmd) {

case 0x01: // reset match

memset(&g_score, 0, sizeof(g_score));

break;

case 0x02: // start set

g_score.score_self = 0;

g_score.score_oppo = 0;

break;

default:

break; // ignore unknown commands

}

}

6. Pairing and Security — LE Secure Connections

We do not want a stranger's phone intercepting our score, or a fake ring attaching to the phone. BLE provides security procedures for this.

The three stages of security

1. **Pairing**: the two devices exchange temporary keys to establish an encrypted channel.

2. **Bonding**: after pairing, keys are stored so the devices trust each other automatically next time.

3. **Encryption**: the actual data is encrypted to prevent eavesdropping.

Modern BLE (4.2 and up) supports **LE Secure Connections**, which uses ECDH (elliptic-curve Diffie-Hellman) key exchange so that even a man-in-the-middle (MITM) snooping all packets cannot derive the key. Far safer than the older LE Legacy Pairing.

Choosing a pairing method

The FingerScore ring has no display and no keypad. Such devices usually use one of these:

| Method | UI needed | MITM defense | FingerScore |

| --- | --- | --- | --- |

| Just Works | none | weak | default (convenience first) |

| Passkey Entry | numeric input | strong | impossible (no UI) |

| Numeric Comparison | two screens | strong | impossible (no UI) |

| OOB | side channel (NFC etc.) | strong | possible if NFC is added |

With no UI, the ring usually starts with **Just Works**; since score data has low sensitivity, this is pragmatically fine. For sensitive operations like firmware updates, though, layer on extra authentication (an app-account-based token, for example).

7. Mobile and Web Integration

Web Bluetooth (web app)

Chromium-based browsers can access BLE devices directly via the Web Bluetooth API. The user must explicitly pick the device by pressing a button (for security).

const SCORE_SERVICE = '6e400001-b5a3-f393-e0a9-e50e24dcca9e';

const SCORE_CHAR = '6e400002-b5a3-f393-e0a9-e50e24dcca9e';

async function connectRing() {

// show device-picker popup to the user

const device = await navigator.bluetooth.requestDevice({

filters: [{ services: [SCORE_SERVICE] }],

});

const server = await device.gatt.connect();

const service = await server.getPrimaryService(SCORE_SERVICE);

const ch = await service.getCharacteristic(SCORE_CHAR);

// subscribe to Notify

await ch.startNotifications();

ch.addEventListener('characteristicvaluechanged', (e) => {

const v = e.target.value; // DataView

const seq = v.getUint8(0);

const scoreSelf = v.getUint8(3);

const scoreOppo = v.getUint8(4);

updateScoreboard(seq, scoreSelf, scoreOppo);

});

}

Android (Kotlin) overview

Android native uses the `BluetoothGatt` API. It is callback-based, so the flow is analogous.

private val gattCallback = object : BluetoothGattCallback() {

override fun onConnectionStateChange(g: BluetoothGatt, status: Int, newState: Int) {

if (newState == BluetoothProfile.STATE_CONNECTED) {

g.discoverServices() // start service discovery

}

}

override fun onServicesDiscovered(g: BluetoothGatt, status: Int) {

val ch = g.getService(SCORE_SERVICE_UUID)

.getCharacteristic(SCORE_CHAR_UUID)

g.setCharacteristicNotification(ch, true)

// write the enable-notification value to the CCCD

val cccd = ch.getDescriptor(CCCD_UUID)

cccd.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE

g.writeDescriptor(cccd)

}

override fun onCharacteristicChanged(g: BluetoothGatt, ch: BluetoothGattCharacteristic) {

val data = ch.value

val scoreSelf = data[3].toInt() and 0xFF

val scoreOppo = data[4].toInt() and 0xFF

// update the scoreboard on the UI thread

}

}

iOS covers nearly identical concepts via CoreBluetooth (`CBCentralManager`, `CBPeripheral`).

8. Power Saving — Connection Interval and Packet Frequency

You can save more power even while the connection is held. The key parameters:

| Parameter | Meaning | Tradeoff |

| --- | --- | --- |

| Connection Interval | wake-and-communicate period (7.5ms-4s) | short = fast, more power; long = slow, less power |

| Slave Latency | how many intervals the peripheral may skip if it has nothing to send | higher = less power, responsiveness preserved |

| Supervision Timeout | declare the connection dropped after this idle time | too short = false drops, too long = slow drop detection |

FingerScore's score does not change often (once every few to tens of seconds). So a **long connection interval + high slave latency** is ideal. Sleep nearly all the time, and wake to Notify the instant the score changes.

power vs responsiveness (conceptual)

interval short (15ms) : instant response, battery a few days

interval medium (100ms): near-instant, battery 1-2 weeks

interval long (500ms) : 0.5s lag, battery several weeks

a 0.1-0.5s delay is fine for scoring -> pick the long interval

In firmware you can request a `connection parameter update` after connecting, proposing to the phone "let's switch to a more relaxed interval."

9. Antenna and Radio — 2.4GHz and the Human Body

BLE uses the 2.4GHz band. This frequency is readily absorbed by water, and **the human body is mostly water**. Since the ring sits on a finger, the antenna is very close to skin — a primary cause of reduced range.

Mitigations:

- **Antenna placement**: route the PCB so the antenna faces outward (toward air), not the inner finger surface (against flesh).

- **Ground keep-out**: clear the copper (ground) around the antenna so it radiates well. We dig into this in Part 6 (PCB design).

- **Output tuning**: raise TX power appropriately, but too high drains the battery.

As for human exposure: BLE transmit power is very low (typically 0dBm = 1mW, far weaker than a phone call). No health issues are reported for normal use, and it operates within regulators' SAR (specific absorption rate) limits. Certification (FCC/KC) is covered in Part 6.

10. Debugging — Seeing the Invisible Radio

Radio is invisible, which makes debugging hard. Fortunately good tools exist.

- **nRF Connect (mobile app)**: scan nearby BLE devices from your phone, expand services/characteristics, and try Read/Write/Notify by hand. Essential in early firmware development. Even without an app, use it to verify the ring's GATT structure is built correctly.

- **BLE sniffer**: an nRF52840 dongle plus Wireshark captures over-the-air packets directly. Use it to chase deep issues — why a connection drops, which packet is lost.

- **Logging (RTT/UART)**: pull serial logs from the chip to inspect internal firmware state.

debugging tools by stage

stage 1: verify GATT structure with the nRF Connect app (do services show?)

stage 2: subscribe Notify in nRF Connect -> does the value change on a gesture?

stage 3: if not, check internal state via RTT logs

stage 4: if still not, capture over-the-air packets with a sniffer

11. Common Pitfalls

Mines that nearly everyone steps on at least once when building BLE firmware.

- **The connection keeps dropping**: supervision timeout too short, or the ring keeps the radio off too long going into sleep and misses responses. Balance sleep against the connection interval.

- **MTU problems**: default MTU is 23 bytes (20-byte effective). To send more, negotiate MTU after connecting. FingerScore's 8-byte packet needs no negotiation — the deliberately small packet design pays off here.

- **Expecting Notify without enabling the CCCD**: if the phone never subscribed (CCCD write) and you only update the value, the phone receives nothing. This is where beginners get stuck most.

- **Advertising too often / too rarely**: too long an advertising interval makes the phone slow to discover the ring; too short wastes battery. Usually compromise between 100ms and 1s.

- **Tangled bonding info**: if the phone deleted the pairing but the ring still remembers it, reconnection fails. It helps to add a "clear bonding" action on the ring (e.g., long-press).

12. Closing — What's Next

In this Part 4 we designed FingerScore's wireless nervous system, BLE, from scratch: the GAP/GATT structure, Notify-based score delivery, a loss-resilient packet design, LE Secure Connections security, Web Bluetooth and Android integration, plus power saving and debugging. The ring can now send the score to the phone smartly and sparingly.

But there is one big assumption baked in: **the ring must have power**. How do you fit a battery into a finger-sized device, make it last for days, and charge it safely?

The next Part 5, **Power, Battery, and Circuit Design**, covers everything that actually keeps the ring on — from a power budget calculation, to battery choice (coin cell vs. LiPo), charging ICs and protection circuits, LDOs and buck converters, and heat, ESD, and safety.

References

- Bluetooth Core Specification — [https://www.bluetooth.com/specifications/specs/](https://www.bluetooth.com/specifications/specs/)

- Nordic Semiconductor nRF Connect SDK docs — [https://docs.nordicsemi.com/](https://docs.nordicsemi.com/)

- Web Bluetooth API (MDN) — [https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API)

- Android BLE guide — [https://developer.android.com/develop/connectivity/bluetooth/ble/ble-overview](https://developer.android.com/develop/connectivity/bluetooth/ble/ble-overview)

- Apple Core Bluetooth — [https://developer.apple.com/documentation/corebluetooth](https://developer.apple.com/documentation/corebluetooth)

- nRF Connect for Mobile — [https://www.nordicsemi.com/Products/Development-tools/nRF-Connect-for-mobile](https://www.nordicsemi.com/Products/Development-tools/nRF-Connect-for-mobile)

- Adafruit Bluefruit LE learning material — [https://learn.adafruit.com/introduction-to-bluetooth-low-energy](https://learn.adafruit.com/introduction-to-bluetooth-low-energy)

- ESP32 BLE (ESP-IDF) docs — [https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/bluetooth/index.html](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/bluetooth/index.html)

현재 단락 (1/230)

FingerScore is a smart scoring platform for racket sports (tennis, badminton, table tennis, squash)....

작성 글자: 0원문 글자: 17,203작성 단락: 0/230