Project 2: Bluetooth Low Energy (BLE) Remote Control
Project 2: Bluetooth Low Energy (BLE) Remote Control
Project Overview
| Attribute | Value |
|---|---|
| Difficulty | Intermediate |
| Time Estimate | 2-3 weeks |
| Main Language | C |
| Alternatives | MicroPython, Rust, Arduino C++ |
| Primary Book | Getting Started with Bluetooth Low Energy by Kevin Townsend |
| Knowledge Areas | Embedded Systems, BLE, GATT, FreeRTOS, Interrupts |
What Youโll Build
A wireless controller that pairs with your phone via BLE, sends button presses and joystick positions, and can control games or applications on your computer/phone.
Physical Setup:
- ESP32 with 4-8 tactile buttons and a dual-axis joystick
- LED indicators for connection status and battery level
- 3.7V LiPo battery for portable operation
- Optional: 3D-printed enclosure
What Youโll See on Your Phone:
Bluetooth Settings (iOS/Android):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Bluetooth Devices โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ ESP32 GamePad [Connected] โ
โ Battery: 87% โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
BLE Scanner App (nRF Connect):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ESP32 GamePad โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Services: โ
โ โ
โ Battery Service (0x180F) โ
โ Battery Level: 87% โ
โ โ
โ Custom Controller Service โ
โ Buttons (Read/Notify) โ
โ Value: 0x01 (A pressed) โ
โ โ
โ Joystick X (Read/Notify) โ
โ Value: 255 (right) โ
โ โ
โ Joystick Y (Read/Notify) โ
โ Value: 128 (centered) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Learning Objectives
By completing this project, you will be able to:
- Implement a BLE GATT server with custom services and characteristics
- Handle hardware interrupts for responsive button input
- Implement button debouncing in software
- Read analog joystick values using ADC with calibration
- Manage BLE connection lifecycle (advertising, pairing, disconnect)
- Optimize power consumption for battery operation
- Use FreeRTOS tasks for concurrent button and BLE handling
Deep Theoretical Foundation
Bluetooth Low Energy vs Classic Bluetooth
BLE and Classic Bluetooth are fundamentally different protocols that happen to share the Bluetooth name and some radio frequencies.
Classic Bluetooth: BLE (Bluetooth Low Energy):
โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ
โ Continuous stream โ โ Intermittent bursts โ
โ Audio, file transferโ โ Sensors, buttons โ
โ High bandwidth โ โ Low bandwidth โ
โ Always connected โ โ Sleep between eventsโ
โ ~30mA active โ โ ~5-15mA active โ
โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ
| Feature | Classic Bluetooth | BLE |
|---|---|---|
| Data Rate | 1-3 Mbps | 125 Kbps - 2 Mbps |
| Range | ~10m | ~10-100m |
| Latency | 100+ ms | 6-20 ms |
| Power | 30-100 mA | 5-15 mA |
| Use Case | Audio, keyboards | Sensors, beacons |
Why BLE for a game controller? Lower latency than you might expect (can achieve <20ms), excellent battery life, and native support on all modern phones without pairing in settings.
The BLE Protocol Stack
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Application โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ GATT โ
โ (Generic Attribute Profile) โ
โ Services โ Characteristics โ Descriptors โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ATT โ
โ (Attribute Protocol) โ
โ Read, Write, Notify โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ GAP โ
โ (Generic Access Profile) โ
โ Advertising, Discovery, Connection โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ L2CAP โ
โ (Logical Link Control) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Link Layer โ
โ Connection state machine, packets โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ Physical Layer โ
โ 2.4 GHz radio, channels โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
For this project, youโll work primarily with GAP (advertising, connections) and GATT (services, characteristics).
GATT: The Heart of BLE Data
GATT (Generic Attribute Profile) defines how BLE devices expose data. Think of it as a structured database of values.
GATT Hierarchy
Server (ESP32)
โโโ Service: Controller (UUID: 0xFF01)
โโโ Characteristic: Buttons
โ โโโ Value: 0x01 (1 byte)
โ โโโ Properties: Read, Notify
โ โโโ Descriptor: CCCD (enable notifications)
โโโ Characteristic: Joystick X
โ โโโ Value: 128 (1 byte, 0-255)
โ โโโ Properties: Read, Notify
โโโ Characteristic: Joystick Y
โโโ Value: 128 (1 byte, 0-255)
โโโ Properties: Read, Notify
โโโ Service: Battery (UUID: 0x180F) [Standard]
โโโ Characteristic: Battery Level
โโโ Value: 87 (1 byte, 0-100)
โโโ Properties: Read, Notify
UUIDs: Identifying Services and Characteristics
| UUID Type | Format | Example | Use |
|---|---|---|---|
| 16-bit | 0xNNNN | 0x180F | Standard services (Battery, Heart Rate) |
| 128-bit | xxxxxxxx-โฆ | Custom | Your own services |
Standard 16-bit UUIDs are defined by the Bluetooth SIG. Using them makes your device compatible with standard clients. For example, the Battery Service (0x180F) automatically shows battery level in phone settings.
Custom UUIDs use the format: 0000XXXX-0000-1000-8000-00805F9B34FB where XXXX is your 16-bit identifier.
Characteristic Properties
| Property | Meaning | Use Case |
|---|---|---|
| Read | Client can request current value | Get joystick position |
| Write | Client can set value | Configure sensitivity |
| Notify | Server pushes updates (no ACK) | Button press events |
| Indicate | Server pushes updates (with ACK) | Critical status changes |
Notify vs Indicate: Notify is faster (no acknowledgment) but less reliable. For button presses, Notify is perfectโif one is lost, the next one comes quickly anyway.
BLE Advertising
Before any connection, the BLE device must advertise its presence. Advertising packets are broadcast on channels 37, 38, and 39.
Advertising Packet (31 bytes max):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Flags โ Name โ Service UUIDs โ Manufacturer Data โ TX Power โ
โ (3B) โ (var)โ (var) โ (var) โ (2B) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Advertising Interval: Time between advertising packets
- Short interval (20-100ms): Faster discovery, higher power
- Long interval (1-10s): Slower discovery, lower power
For a game controller, use ~100ms interval while waiting for connection.
Hardware Interrupts
Polling buttons in a loop wastes CPU cycles. Interrupts allow the CPU to sleep until something happens.
Polling (wasteful): Interrupts (efficient):
while(1) { void IRAM_ATTR button_isr() {
if (button_pressed()) { // Handle immediately
handle_button(); xSemaphoreGiveFromISR(sem);
} }
delay(10);
} // CPU sleeps until interrupt
ESP32 Interrupt Configuration
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << BUTTON_A_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_NEGEDGE, // Trigger on falling edge
};
gpio_config(&io_conf);
gpio_install_isr_service(0);
gpio_isr_handler_add(BUTTON_A_PIN, button_isr_handler, NULL);
IRAM_ATTR: ISR code must be in IRAM (fast memory) for reliable execution.
Button Debouncing
Mechanical buttons โbounceโโthe contacts vibrate when pressed, creating multiple electrical transitions.
Ideal button press: Real button press (bouncing):
โโโโโโ โโโโโโ โโโโโโ โโโ โโโ โโโโโโ
โ โ โโโ โโโ โโโโโ
โโโโโโโ โโ ~10ms โโ
Without debouncing, one physical press might register as 5-10 presses!
Software Debouncing Strategy
volatile uint32_t last_interrupt_time = 0;
void IRAM_ATTR button_isr() {
uint32_t now = xTaskGetTickCountFromISR();
if (now - last_interrupt_time > pdMS_TO_TICKS(50)) {
// Real button press
xSemaphoreGiveFromISR(button_semaphore, NULL);
}
last_interrupt_time = now;
}
The 50ms debounce window ignores rapid transitions while still feeling responsive.
ADC for Joystick Input
Joysticks use potentiometersโvariable resistors that output a voltage proportional to position.
Joystick Internals:
VCC (3.3V)
โ
โค Potentiometer
โโโโโโโโโโ WIPER (to ADC)
โค
โ
GND
Position: Left Center Right
Voltage: 0V 1.65V 3.3V
ADC Value: 0 2048 4095
ADC Calibration
Real joysticks have manufacturing tolerances:
- Center might be at 2100 instead of 2048
- Maximum might be 4000 instead of 4095
- Deadzone exists around center (drift)
typedef struct {
int16_t center_x; // Calibrated center (read at startup)
int16_t center_y;
int16_t deadzone; // Ignore movements this small (e.g., 100)
int16_t max_value; // Actual max (e.g., 4000)
} joystick_calibration_t;
uint8_t calibrate_joystick_axis(int16_t raw, joystick_calibration_t* cal) {
// Apply deadzone
if (abs(raw - cal->center) < cal->deadzone) {
return 128; // Center
}
// Scale to 0-255
if (raw < cal->center) {
return map(raw, 0, cal->center - cal->deadzone, 0, 127);
} else {
return map(raw, cal->center + cal->deadzone, cal->max_value, 128, 255);
}
}
BLE Connection Parameters
Connection parameters significantly affect latency and power consumption.
Connection Interval: 7.5ms to 4s
โโโ Time between data exchanges
Slave Latency: 0 to 500
โโโ Number of connection events peripheral can skip
Supervision Timeout: 100ms to 32s
โโโ Time before connection declared dead
For a game controller:
- Connection Interval: 7.5-15ms (low latency!)
- Slave Latency: 0 (never skip events)
- Supervision Timeout: 2s (detect disconnects quickly)
Project Specification
Hardware Requirements
| Component | Quantity | Purpose |
|---|---|---|
| ESP32 DevKit | 1 | Main microcontroller |
| Tactile buttons | 4-8 | A, B, X, Y buttons |
| Analog joystick | 1-2 | Directional control |
| LED (green) | 1 | Connected indicator |
| LED (red) | 1 | Low battery indicator |
| 220ฮฉ Resistor | 2 | LED current limiting |
| 3.7V LiPo battery | 1 | Portable power |
| TP4056 module | 1 | Battery charging |
Wiring Diagram
ESP32 DevKit Buttons (active LOW)
โโโโโโโโโโโโโโโ โโโโโโโโโโ
โ GPIO14 โโโโโโโโโโโโโโโโโโโ BTN_A โโโGND
โ GPIO27 โโโโโโโโโโโโโโโโโโโ BTN_B โโโGND
โ GPIO26 โโโโโโโโโโโโโโโโโโโ BTN_X โโโGND
โ GPIO25 โโโโโโโโโโโโโโโโโโโ BTN_Y โโโGND
โ โ โโโโโโโโโโ
โ โ
โ โ Joystick
โ GPIO34 โโโโโโโโโโโโโโโโโโโ VRx (X-axis)
โ GPIO35 โโโโโโโโโโโโโโโโโโโ VRy (Y-axis)
โ 3.3V โโโโโโโโโโโโโโโโโโโ VCC
โ GND โโโโโโโโโโโโโโโโโโโ GND
โ โ
โ โ LEDs
โ GPIO2 โโโโโ[220ฮฉ]โโโโโโโโ Green LED โโโGND
โ GPIO4 โโโโโ[220ฮฉ]โโโโโโโโ Red LED โโโGND
โ โ
โ โ Battery (via TP4056)
โ VIN โโโโโโโโโโโโโโโโโโโ OUT+
โ GND โโโโโโโโโโโโโโโโโโโ OUT-
โโโโโโโโโโโโโโโ
GATT Service Design
ESP32 GamePad GATT Server
โ
โโโ Generic Access Service (0x1800) [Standard]
โ โโโ Device Name: "ESP32 GamePad"
โ โโโ Appearance: 0x03C4 (Gamepad)
โ
โโโ Battery Service (0x180F) [Standard]
โ โโโ Battery Level (0x2A19)
โ โโโ Value: uint8 (0-100%)
โ โโโ Properties: Read, Notify
โ
โโโ Controller Service (0xFF01) [Custom]
โโโ Buttons (0xFF02)
โ โโโ Value: uint8 (bitmask)
โ โ Bit 0: A, Bit 1: B, Bit 2: X, Bit 3: Y
โ โโโ Properties: Read, Notify
โ
โโโ Joystick X (0xFF03)
โ โโโ Value: uint8 (0=left, 128=center, 255=right)
โ โโโ Properties: Read, Notify
โ
โโโ Joystick Y (0xFF04)
โโโ Value: uint8 (0=up, 128=center, 255=down)
โโโ Properties: Read, Notify
Functional Requirements
- BLE Advertising
- Advertise as โESP32 GamePadโ when not connected
- Include battery service UUID in advertising data
- LED blinks during advertising
- Button Input
- Detect button presses via hardware interrupts
- Debounce with 50ms window
- Send BLE notification within 10ms of press
- Joystick Input
- Sample joystick every 20ms (50 Hz)
- Apply deadzone (10% around center)
- Only notify on significant change (>5 units)
- Battery Monitoring
- Read battery voltage via ADC
- Update characteristic every 60 seconds
- Light red LED when <20%
- Connection Management
- Request 15ms connection interval
- Solid green LED when connected
- Resume advertising on disconnect
Solution Architecture
System Block Diagram
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ESP32 System โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ Button โ โ Joystick โ โ BLE โ โ
โ โ Handler โ โ Task โ โ Stack โ โ
โ โ โ โ โ โ โ โ
โ โ - ISR โ โ - Sample ADC โ โ - Advertisingโ โ
โ โ - Debounce โ โ - Calibrate โ โ - GATT serverโ โ
โ โ - Update โโโโโโ - Notify โโโโโโ - Notify โ โ
โ โ bitmask โ โ โ โ clients โ โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโ โ
โ โ Shared โ โ
โ โ State โ โ
โ โ โ โ
โ โ - buttons โ โ
โ โ - joy_x, y โ โ
โ โ - battery โ โ
โ โ - connected โ โ
โ โโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Task Distribution
| Task | Core | Priority | Stack | Purpose |
|---|---|---|---|---|
| Button Handler | Any | High (10) | 2KB | Respond to interrupts |
| Joystick Sample | 0 | Medium (5) | 2KB | ADC sampling loop |
| BLE Event Loop | 1 | Medium (5) | 4KB | Handle BLE stack |
| Battery Monitor | 0 | Low (2) | 1KB | Periodic ADC read |
Notification Flow
Button Press Event:
โโโโโโโโโโโ ISR โโโโโโโโโโโ Semaphore โโโโโโโโโโโ Queue โโโโโโโโโโโ
โ GPIO โโโโโโโโโโโโ Debounceโโโโโโโโโโโโโโโ Update โโโโโโโโโโโ BLE โ
โ Hardwareโ โ Timer โ โ Bitmask โ โ Notify โ
โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ
โ โ โ โ
0ms 50ms 51ms 55ms
(debounce) (process) (transmit)
Total latency: ~55ms (well under perceptible threshold of 100ms)
Phased Implementation Guide
Phase 1: BLE Advertising (Day 1-2)
Goal: Phone discovers ESP32 in Bluetooth settings
- Initialize BLE Stack
- Enable Bluetooth controller and host
- Register GATT and GAP callbacks
- Create GATT database
- Configure Advertising
- Set device name: โESP32 GamePadโ
- Include battery service UUID
- Set advertising interval: 100ms
- Verify Discovery
- Open Bluetooth settings on phone
- Should see โESP32 GamePadโ
- Use nRF Connect to inspect advertising data
Checkpoint: Phone discovers โESP32 GamePadโ
Phase 2: GATT Services (Day 3-4)
Goal: nRF Connect shows all services and characteristics
- Create Battery Service
- UUID: 0x180F (standard)
- Add Battery Level characteristic (0x2A19)
- Enable read and notify
- Create Controller Service
- UUID: 0xFF01 (custom)
- Add Buttons characteristic (0xFF02)
- Add Joystick X/Y characteristics (0xFF03, 0xFF04)
- Enable read and notify on all
- Handle Connections
- Log connect/disconnect events
- Light green LED on connect
- Resume advertising on disconnect
Checkpoint: nRF Connect shows all services and can read values
Phase 3: Button Input (Day 5-7)
Goal: Button presses appear in nRF Connect
- Configure GPIO Interrupts
- Set buttons as input with pull-up
- Trigger on falling edge (button press)
- Register ISR handlers
- Implement Debouncing
- Track last interrupt time
- Ignore interrupts within 50ms
- Use semaphore to signal main task
- Update Characteristics
- Update button bitmask on press/release
- Send BLE notification
- Log to serial for debugging
Checkpoint: nRF Connect shows button bitmask changing
Phase 4: Joystick Input (Day 8-9)
Goal: Joystick movement appears in nRF Connect
- Sample ADC
- Configure ADC for GPIO34, GPIO35
- Sample every 20ms (50 Hz)
- Apply 11dB attenuation for 0-3.3V range
- Calibrate Readings
- Read center value on startup
- Calculate deadzone (10%)
- Map raw values to 0-255 range
- Notify on Change
- Only notify if value changed by >5
- Update characteristic value
- Send notification
Checkpoint: Joystick movement updates in real-time in nRF Connect
Phase 5: Battery and Polish (Day 10-14)
Goal: Production-quality controller
- Battery Monitoring
- Read battery voltage via ADC
- Calculate percentage (4.2V=100%, 3.3V=0%)
- Update battery characteristic every minute
- Connection Parameters
- Request 15ms connection interval
- Handle parameter update response
- Verify latency with timing measurements
- Power Optimization
- Reduce advertising power when connected
- Use light sleep between samples
- Measure actual current draw
Testing Strategy
Unit Tests
| Component | Test | Expected Result |
|---|---|---|
| GPIO | Read button state | LOW when pressed |
| ADC | Read joystick center | ~2048 (ยฑ100) |
| BLE | Start advertising | nRF Connect sees device |
| BLE | Connect | Connection callback fires |
| GATT | Read battery | Value 0-100 |
| GATT | Enable notify | Notifications received |
Integration Tests
- Latency Measurement
- Press button, measure time until phone receives notification
- Target: <100ms for gaming applications
- Concurrent Input
- Press multiple buttons while moving joystick
- All inputs should register without missed events
- Reconnection
- Disconnect phone, verify advertising resumes
- Reconnect, verify all services work
Stress Testing
- Rapid Button Presses
- Press button 10 times per second
- All presses should register (after debounce)
- Long Duration
- Run for 8 hours on battery
- Monitor for memory leaks, disconnects
Common Pitfalls and Debugging
BLE Issues
Problem: Phone doesnโt discover device
- Ensure advertising is started
- Check advertising data isnโt too long (31 bytes max)
- Try restarting Bluetooth on phone
- Clear phoneโs Bluetooth cache
Problem: Connection drops frequently
- Increase supervision timeout
- Check for WiFi interference (same 2.4GHz band)
- Reduce distance to phone
Problem: Notifications not received
- Verify client enabled notifications (wrote 0x0001 to CCCD)
- Check characteristic has Notify property
- Verify MTU is sufficient for data
Button Issues
Problem: Button presses missed
- ISR might be too slow (use IRAM_ATTR)
- Debounce time too long (reduce to 20ms)
- Semaphore queue full (increase queue size)
Problem: Ghost button presses
- Electrical noise (add 0.1ยตF capacitor)
- Debounce time too short (increase to 50ms)
- Floating input (ensure pull-up enabled)
Joystick Issues
Problem: Joystick drifts when idle
- Increase deadzone size
- Calibrate center on startup
- Add hysteresis to prevent oscillation
Problem: Joystick range limited
- Calibrate actual min/max values
- Check potentiometer voltage range
- Verify ADC attenuation setting
Extensions and Challenges
Beginner Extensions
- Haptic Feedback
- Add vibration motor
- Trigger on button press
- Control via BLE write characteristic
- Multiple Profiles
- Store different button mappings
- Switch with long-press combo
- Save to NVS
Intermediate Challenges
- HID Gamepad Profile
- Implement standard HID over GATT
- Works as native gamepad on any device
- No custom app needed
- iOS/Android App
- Write companion app using React Native
- Display controller state
- Configure button mappings
Advanced Challenges
- Dual Controller Support
- Two ESP32 controllers paired to one hub
- Multiplayer gaming support
- Synchronized timing
- Motion Controls
- Add MPU6050 accelerometer/gyroscope
- Send orientation data
- Gesture recognition
Real-World Connections
Commercial Products
| Product | BLE Feature | Your Project Skill |
|---|---|---|
| Nintendo Joy-Con | BLE gamepad | GATT design, low latency |
| AirPods | Proximity sensing | BLE connection management |
| Tile Tracker | BLE beacon | Advertising, battery life |
| Fitbit | BLE sync | Notification characteristics |
Industry Applications
- Medical Devices: Heart rate monitors, glucose meters
- Industrial IoT: Sensor beacons, asset tracking
- Consumer Electronics: Remotes, wearables, smart home
Resources
Official Documentation
| Resource | URL |
|---|---|
| ESP-IDF BLE API | docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/bluetooth/ |
| Bluetooth SIG GATT | bluetooth.com/specifications/specs/gatt-specification/ |
| nRF Connect App | nordicsemi.com/Products/Development-tools/nrf-connect-for-mobile |
Books
| Book | Author | Relevant Chapters |
|---|---|---|
| Getting Started with BLE | Kevin Townsend | All chapters |
| Making Embedded Systems | Elecia White | Ch. 5 (GPIO), Ch. 8 (Interrupts) |
| The Art of Electronics | Horowitz & Hill | Section 10.5 (Debouncing) |
Self-Assessment Checklist
Fundamentals
- I can explain the GATT hierarchy (Services, Characteristics, Descriptors)
- I understand the difference between Read, Write, and Notify operations
- I can describe why button debouncing is necessary
- I know why interrupts are preferred over polling
Implementation
- Phone discovers my device in Bluetooth settings
- nRF Connect shows all my services correctly
- Button presses trigger notifications within 100ms
- Joystick movements are smooth and calibrated
- Battery percentage updates and shows correctly
Code Quality
- ISRs are minimal (just set flags/semaphores)
- No busy-waiting in main code
- Clean separation between input handling and BLE
- Reconnection works reliably
Interview Preparation
Be ready to answer these questions:
- โExplain how BLE GATT works. Whatโs the difference between a service and characteristic?โ
- Service groups related data; characteristic is a single value with properties
- โWhy use interrupts instead of polling for buttons?โ
- CPU can sleep, lower power, more responsive, no wasted cycles
- โHow do you handle button debouncing in software?โ
- Ignore transitions within time window (20-50ms) after first edge
- โWhat are BLE connection parameters and how do they affect latency?โ
- Connection interval, slave latency, supervision timeout; shorter interval = lower latency but higher power
- โHow would you implement the standard HID profile for a gamepad?โ
- Use HID over GATT service (0x1812), define report descriptor, handle report characteristic
Next Project: P03-multi-sensor-deep-sleep-logger.md - Multi-Sensor Data Logger with Deep Sleep