diff --git a/doc/device-signals.md b/doc/device-signals.md
new file mode 100644
index 0000000..3c12cc8
--- /dev/null
+++ b/doc/device-signals.md
@@ -0,0 +1,390 @@
+# 📡 Taekwondo Scoring System – Device Signals & Communication Protocol
+
+> **Version:** 2.0 (Full Device Coverage)
+> **Last Updated:** July 2025
+> **Target Developer:** Embedded (ESP32) & Android (.NET) Developers
+> **Purpose:** Define **two-way communication** for **all battery-powered devices**: joysticks, hogu, and headgear
+
+---
+
+## 📌 Table of Contents
+
+- [1. Overview](#1-overview)
+- [2. Device Types](#2-device-types)
+- [3. Communication Model (TCP Full-Duplex)](#3-communication-model-tcp-full-duplex)
+- [4. Common Device Signals](#4-common-device-signals)
+- [5. Referee Joystick](#5-referee-joystick)
+- [6. Hogu (Trunk Protector)](#6-hogu-trunk-protector)
+- [7. Headgear](#7-headgear)
+- [8. OLED Display (128x64, I2C 0x3C)](#8-oled-display-128x64-i2c-0x3c)
+- [9. Telemetry: Battery & Health Monitoring](#9-telemetry-battery--health-monitoring)
+- [10. Feedback: Vibration, LED, Buzzer](#10-feedback-vibration-led-buzzer)
+- [11. Command & Response Format](#11-command--response-format)
+- [12. ESP32 Code Structure (Modular)](#12-esp32-code-structure-modular)
+- [13. Android .NET Server Integration](#13-android-net-server-integration)
+- [14. Best Practices](#14-best-practices)
+
+---
+
+## 1. Overview
+
+All **battery-powered devices** in the system support **two-way TCP communication**:
+
+- ✅ **Send**: Sensor data, telemetry, events
+- ✅ **Receive**: Feedback commands, display updates, calibration
+- ✅ **Device Types**:
+ - **Referee Joysticks** (4 units)
+ - **Hogu (Trunk Protector)** – Red & Blue
+ - **Headgear** – Red & Blue
+
+Each device identifies itself with:
+```
+DEVICE:NAME=...,TYPE=...,MAC=...
+```
+
+---
+
+## 2. Device Types
+
+| Device | Type | Key Features |
+|-------|------|-------------|
+| **Joystick** | `JOYSTICK` | Button, OLED, vibration, LED, buzzer, battery telemetry |
+| **Hogu** | `SENSOR` | Force sensor (FSR), MPU6050 (spinning), OLED, vibration, battery |
+| **Headgear** | `SENSOR` | Force sensor, MPU6050, OLED, vibration, battery |
+
+> All use **ESP32-C3**, **LiPo battery**, and **I2C OLED (0x3C)**.
+
+---
+
+## 3. Communication Model (TCP Full-Duplex)
+
+```
+[All Devices] ↔ [Android Tablet: TCP Server (Port 5000)]
+```
+
+- Devices connect to `192.168.4.1:5000` (hotspot mode)
+- First message: `DEVICE:NAME=...,TYPE=...,MAC=...`
+- Subsequent messages: JSON `\n`-terminated
+- Server can send commands at any time
+
+> Full-duplex: **Send and receive on same connection**.
+
+---
+
+## 4. Common Device Signals
+
+### 4.1 Device Identity (Mandatory First Message)
+```
+DEVICE:NAME=HOGU_RED,TYPE=SENSOR,MAC=30:85:A9:12:34:56
+```
+
+### 4.2 Telemetry (Periodic, Every 60 sec)
+```json
+{
+ "type": "telemetry",
+ "device": "HOGU_RED",
+ "battery_level": 88,
+ "battery_voltage": 4.02,
+ "rssi": -65,
+ "uptime": 3420,
+ "timestamp": 1712345679000
+}
+```
+
+### 4.3 Command Acknowledgment
+```json
+{"ack": "vibrate", "time": 1712345679100}
+```
+
+---
+
+## 5. Referee Joystick
+
+### 📥 Outgoing
+- `button` press
+- `telemetry`
+
+### 📤 Incoming Commands
+- `vibrate` – tactile feedback
+- `buzz` – audible alert
+- `led` – status indication
+- `display` – show text
+- `ping` – liveness check
+
+### Example Button Press
+```json
+{"type":"button","device":"JOYSTICK_CORNER_1","pressed":true,"time":1712345678905}
+```
+
+---
+
+## 6. Hogu (Trunk Protector)
+
+### 📥 Outgoing
+
+#### 6.1 Impact Detection
+```json
+{
+ "type": "impact",
+ "device": "hogu_red",
+ "force": 850,
+ "spinning": true,
+ "time": 1712345678901
+}
+```
+
+- `force`: 0–1023 (analog read)
+- `spinning`: from MPU6050 (rotation > 180°)
+
+#### 6.2 Telemetry
+Same as joystick, includes sensor health.
+
+---
+
+### 📤 Incoming Commands
+
+| Command | Effect |
+|--------|--------|
+| `vibrate` | Confirm impact registered |
+| `led` | Flash green/red for status |
+| `display` | Show "HOGU RED", battery, status |
+| `calibrate` | Recalibrate force sensor |
+| `ping` | Liveness check |
+
+#### Example: Calibrate Command
+```json
+{"cmd": "calibrate", "mode": "zero"}
+```
+
+---
+
+## 7. Headgear
+
+### 📥 Outgoing
+
+#### 7.1 Impact Detection
+```json
+{
+ "type": "impact",
+ "device": "head_blue",
+ "force": 720,
+ "spinning": false,
+ "time": 1712345678903
+}
+```
+
+> Lower force threshold (safety).
+
+#### 7.2 Telemetry
+Same format, sent every 60 sec.
+
+---
+
+### 📤 Incoming Commands
+
+| Command | Effect |
+|--------|--------|
+| `vibrate` | Confirm head kick detected |
+| `led` | Status light (e.g., red = fault) |
+| `display` | Show "HEAD BLUE", battery |
+| `ping` | Liveness check |
+
+> No `calibrate` — headgear is sealed.
+
+---
+
+## 8. OLED Display (128x64, I2C 0x3C)
+
+All devices have the same OLED for **status and feedback**.
+
+### 8.1 Display Modes
+
+| Device | Normal | Active | Low Battery | Error |
+|-------|--------|--------|-------------|-------|
+| **Joystick** | `CORNER 1`
`READY` | `PRESSED!` | `⚠️ BATT: 20%` | `NO SIGNAL` |
+| **Hogu** | `HOGU RED`
`OK` | `IMPACT!` | `⚠️ BATT: 15%` | `SENSOR FAULT` |
+| **Headgear** | `HEAD BLUE`
`ACTIVE` | `HIT DETECTED` | `⚠️ BATT: 10%` | `DISCONNECTED` |
+
+---
+
+### 8.2 Server Command
+```json
+{
+ "cmd": "display",
+ "lines": [
+ {"text": "HOGU RED", "y": 0},
+ {"text": "IMPACT!", "y": 30, "color": "green"}
+ ],
+ "refresh": true
+}
+```
+
+---
+
+## 9. Telemetry: Battery & Health Monitoring
+
+### 9.1 Battery Measurement (All Devices)
+```cpp
+float readBatteryVoltage() {
+ int adc = analogRead(BATTERY_PIN);
+ float voltage = (adc / 4095.0) * 3.3 * 2.0; // 2:1 divider
+ return voltage;
+}
+
+int getBatteryLevel(float voltage) {
+ if (voltage >= 4.2) return 100;
+ if (voltage <= 3.3) return 0;
+ return (int)((voltage - 3.3) / 0.9 * 100);
+}
+```
+
+### 9.2 Telemetry Frequency
+- Every **60 seconds**
+- Or on **significant change** (e.g., battery drop >5%)
+
+---
+
+## 10. Feedback: Vibration, LED, Buzzer
+
+### 10.1 Pin Assignments (Shared)
+
+| Component | GPIO |
+|---------|------|
+| Vibration Motor | 4 |
+| Buzzer | 5 |
+| LED (RGB) | 6 (R), 7 (G), 8 (B) |
+| OLED (SDA) | 21 |
+| OLED (SCL) | 22 |
+| FSR (Hogu) | 34 |
+| FSR (Head) | 35 |
+| MPU6050 (SCL/SDA) | 21, 22 (shared with OLED) |
+
+> Use **I2C multiplexer** if needed.
+
+---
+
+### 10.2 Feedback Examples
+
+#### Vibrate (200ms)
+```json
+{"cmd": "vibrate", "duration": 200}
+```
+
+#### LED: Green Blink (3 times)
+```json
+{"cmd": "led", "color": "#00FF00", "mode": "blink", "count": 3}
+```
+
+#### Buzzer: Short beep
+```json
+{"cmd": "buzz", "tone": 1000, "duration": 150}
+```
+
+---
+
+## 11. Command & Response Format
+
+### 11.1 Outgoing (Device → Server)
+```json
+{"type":"impact","device":"head_blue","force":720,"spinning":false,"time":1712345678903}
+```
+```json
+{"type":"telemetry","device":"HOGU_RED","battery_level":88,"rssi":-65,"timestamp":1712345679000}
+```
+
+### 11.2 Incoming (Server → Device)
+```json
+{"cmd":"vibrate","duration":200}
+{"cmd":"led","color":"#0000FF","mode":"solid"}
+{"cmd":"display","lines":[{"text":"IMPACT!","y":30}]}
+{"cmd":"ping"}
+```
+
+> Device responds to `ping` with:
+```json
+{"ack":"ping","time":1712345679100}
+```
+
+---
+
+## 12. ESP32 Code Structure (Modular)
+
+Use a **modular design** with shared components:
+
+```
+/src/
+ ├── main.cpp
+ ├── wifi_manager.cpp
+ ├── tcp_client.cpp
+ ├── oled_display.cpp
+ ├── battery_monitor.cpp
+ ├── sensors/
+ │ ├── fsr.cpp
+ │ └── mpu6050.cpp
+ ├── feedback/
+ │ ├── vibration.cpp
+ │ ├── buzzer.cpp
+ │ └── led.cpp
+ └── device_config.h
+```
+
+### Example: `device_config.h`
+```cpp
+#define DEVICE_NAME "HOGU_RED"
+#define DEVICE_TYPE "SENSOR"
+#define OLED_ADDR 0x3C
+#define BATTERY_PIN 35
+#define VIBRO_PIN 4
+#define BUZZER_PIN 5
+#define LED_PIN 6
+```
+
+---
+
+## 13. Android .NET Server Integration
+
+### Send Command to Any Device
+```csharp
+void SendCommand(string mac, string commandJson)
+{
+ TcpClient client = GetDeviceByMac(mac);
+ if (client?.Connected == true)
+ {
+ var stream = client.GetStream();
+ var writer = new StreamWriter(stream);
+ await writer.WriteLineAsync(commandJson);
+ await writer.FlushAsync();
+ }
+}
+```
+
+### Example: Confirm Impact
+```csharp
+// After auto-scoring
+SendCommand("30:85:A9:12:34:56", "{\"cmd\":\"vibrate\",\"duration\":200}");
+SendCommand("30:85:A9:12:34:56", "{\"cmd\":\"led\",\"color\":\"#00FF00\",\"mode\":\"blink\",\"count\":2}");
+```
+
+### Broadcast to All Sensors
+```csharp
+foreach (var device in GetAllSensors())
+{
+ SendCommand(device.Mac, "{\"cmd\":\"display\",\"lines\":[{\"text\":\"MATCH START\"}]}");
+}
+```
+
+---
+
+## 14. Best Practices
+
+| Practice | Why |
+|--------|-----|
+| Send telemetry every 60 sec | Monitor battery and health |
+| Debounce sensor inputs | Prevent false triggers |
+| Use I2C for OLED & MPU6050 | Save pins |
+| Add `ping` command | Check device liveness |
+| Log all commands | Debugging and auditing |
+| Low-power mode | Extend battery life |
+| Clear OLED on boot | Avoid ghosting |
+| Validate MAC before sending | Prevent errors |
+