# 🌐 Taekwondo Scoring System – Wi-Fi Connectivity Guide

> **Version:** 1.0  
> **Last Updated:** July 2025  
> **Target Developer:** Embedded & Android developers  
> **Purpose:** Detailed guide on **Wi-Fi connectivity** for single and multi-court setups with **auto-discovery and conflict prevention**

---

## 📌 Table of Contents

- [1. Overview](#1-overview)
- [2. Mode 1: Wi-Fi Hotspot-Based (Single Court)](#2-mode-1-wi-fi-hotspot-based-single-court)
- [3. Mode 2: Wi-Fi Non-Hotspot (Tournament Mode)](#3-mode-2-wi-fi-non-hotspot-tournament-mode)
- [4. Auto-Discovery Mechanism](#4-auto-discovery-mechanism)
- [5. Multi-Court Safety & Conflict Prevention](#5-multi-court-safety--conflict-prevention)
- [6. ESP32 Code Examples](#6-esp32-code-examples)
- [7. Android .NET Server Code](#7-android-net-server-code)
- [8. Best Practices](#8-best-practices)

---

## 1. Overview

This guide details **two Wi-Fi connection modes** for the **Taekwondo Scoring System**:

| Mode | Use Case | Network Control |
|------|---------|----------------|
| **1. Hotspot-Based** | Clubs, single court | Tablet creates Wi-Fi |
| **2. Non-Hotspot (Client)** | Tournaments, multi-court | Tablet joins existing Wi-Fi |

Both modes use **auto-discovery** so devices (sensors, joysticks, TVs) can **find the server without IP input**.

Key goals:
- ✅ Zero-config for non-technical users
- ✅ Reliable auto-discovery
- ✅ Prevent cross-talk between courts
- ✅ Secure and scalable

---

## 2. Mode 1: Wi-Fi Hotspot-Based (Single Court)

Ideal for **training clubs** or **local events** with **one court**.

### 📶 Network Setup
- **Score Control Tablet** creates a Wi-Fi hotspot:
  - **SSID**: `TKD_SCORING_COURT1`
  - **Password**: `taekwondo123`
  - **IP**: `192.168.4.1` (default hotspot IP)
- All devices connect to this network.

### ✅ Advantages
- No dependency on venue Wi-Fi
- Fixed server IP: `192.168.4.1`
- Fast, stable, low-latency
- Simple for staff: "Connect to TKD_SCORING_COURT1"

### 🔄 Auto-Discovery Flow
```
[ESP32 Device] → Connects to TKD_SCORING_COURT1
                 ↓
         Sends UDP broadcast: TKD_DISCOVER
                 ↓
[Tablet] ← Responds: SERVER_IP:192.168.4.1
                 ↓
         Device connects to 192.168.4.1:5000
```

> Since the tablet is the **only router**, it's always the **first IP** (`192.168.4.1`).

---

## 3. Mode 2: Wi-Fi Non-Hotspot (Tournament Mode)

Used in **multi-court tournaments** where multiple score systems run **on the same venue Wi-Fi**.

### 📶 Network Setup
- **Score Control Tablet** connects to **existing Wi-Fi** (e.g., `Venue_Guest`)
- Gets IP via DHCP (e.g., `192.168.1.100`)
- Starts TCP server on Port `5000`
- Devices must **discover** the server IP

### ⚠️ Challenge
- Server IP is **not fixed**
- Devices must **find the correct server** among many

### ✅ Solution: **UDP Broadcast Discovery**

Devices send a broadcast to find any active **Taekwondo scoring server**.

---

## 4. Auto-Discovery Mechanism

To enable auto-discovery in **both modes**, we use **UDP broadcast**.

### 📡 Discovery Protocol

| Step | Message |
|------|--------|
| 1 | Device sends: `TKD_DISCOVER` |
| 2 | Server responds: `SERVER_IP:<ip>:<port>:<court_id>` |
| 3 | Device connects to `ip:port` |

#### Example Response
```
SERVER_IP:192.168.1.100:5000:COURT1
```

This allows devices to:
- Find the server
- Know which court they belong to

---

### 🔄 Discovery Flow (Non-Hotspot Mode)

```
[ESP32 Device] → Joins venue Wi-Fi
                 ↓
         Sends UDP broadcast: TKD_DISCOVER
                 ↓
[Court 1 Server] ← Responds: SERVER_IP:192.168.1.100:5000:COURT1
[Court 2 Server] ← Responds: SERVER_IP:192.168.1.101:5000:COURT2
                 ↓
[Device] → Uses SSID or config to choose COURT1 → connects
```

> Devices only connect to the server matching their **court ID**.

---

## 5. Multi-Court Safety & Conflict Prevention

To prevent **cross-talk** between courts:

### ✅ 1. Unique SSID per Court (Recommended)
- **Court 1**: `TKD_COURT1`
- **Court 2**: `TKD_COURT2`
- Devices are **pre-configured** to connect only to their court’s SSID

> ✅ **Best for tournaments** — no discovery conflicts

### ✅ 2. Court ID in Discovery Response
- Server includes `COURT1`, `COURT2` in response
- Device validates: only connect if `court_id` matches

### ✅ 3. MAC Address Whitelisting
- Each server has a **pre-loaded list** of allowed device MACs
- Blocks devices from other courts

### ✅ 4. Separate UDP Ports (Optional)
- Court 1 listens on UDP `5002`
- Court 2 listens on UDP `5003`
- Prevents broadcast overlap

---

### 🛡️ Safety Summary

| Risk | Solution |
|------|----------|
| Device connects to wrong court | Unique SSID or Court ID check |
| Two tablets respond to discovery | Use Court ID filtering |
| Unauthorized device connects | MAC whitelisting |
| IP conflict | Let DHCP handle it |

---

## 6. ESP32 Code Examples

### 📡 ESP32: Auto-Discovery & Connect (Arduino C++)

```cpp
#include <WiFi.h>
#include <WiFiUdp.h>

const char* ssid = "TKD_SCORING_COURT1";  // Change per court
const char* password = "taekwondo123";
const int udpPort = 5002;
const char* discoverMsg = "TKD_DISCOVER";
const char* expectedCourt = "COURT1";

WiFiUDP udp;

String getServerIP() {
  udp.beginPacket("255.255.255.255", udpPort);
  udp.print(discoverMsg);
  udp.endPacket();

  unsigned long timeout = millis() + 3000;
  while (millis() < timeout) {
    int len = udp.parsePacket();
    if (len > 0) {
      char response[64];
      udp.read(response, len);
      response[len] = '\0';

      // Parse: SERVER_IP:192.168.1.100:5000:COURT1
      String resp = String(response);
      int ipStart = resp.indexOf("SERVER_IP:") + 10;
      int courtStart = resp.lastIndexOf(":") + 1;
      String courtId = resp.substring(courtStart);

      if (courtId == expectedCourt) {
        int portEnd = resp.lastIndexOf(":");
        int ipEnd = resp.lastIndexOf(":", portEnd - 1);
        return resp.substring(ipStart, ipEnd);
      }
    }
    delay(100);
  }
  return "";
}

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  udp.begin(udpPort + 1); // Use different port for listening

  String serverIP = getServerIP();
  if (serverIP != "") {
    Serial.println("Found server: " + serverIP);
    // Now connect to TCP server
    WiFiClient client;
    if (client.connect(serverIP.c_str(), 5000)) {
      client.println("DEVICE:NAME=HOGU_RED,TYPE=SENSOR,MAC=" + WiFi.macAddress());
      // Send data...
    }
  } else {
    Serial.println("No server found!");
  }
}
```

---

## 7. Android .NET Server Code

### 🖥️ Android: UDP Discovery Server (C# .NET MAUI)

```csharp
using System.Net;
using System.Net.Sockets;
using System.Text;

public class UdpDiscoveryServer
{
    private UdpClient _udp;
    private string _serverIp;
    private string _courtId;
    private int _tcpPort = 5000;
    private int _udpPort = 5002;

    public UdpDiscoveryServer(string courtId)
    {
        _courtId = courtId;
        _serverIp = GetLocalIPAddress(); // e.g., 192.168.1.100
    }

    public void Start()
    {
        _udp = new UdpClient(_udpPort);
        Task.Run(async () => {
            while (true)
            {
                var remote = new IPEndPoint(IPAddress.Any, 0);
                try
                {
                    byte[] data = _udp.Receive(ref remote);
                    string request = Encoding.UTF8.GetString(data);
                    if (request.Trim() == "TKD_DISCOVER")
                    {
                        string response = $"SERVER_IP:{_serverIp}:{_tcpPort}:{_courtId}";
                        byte[] responseData = Encoding.UTF8.GetBytes(response);
                        _udp.Send(responseData, responseData.Length, remote);
                    }
                }
                catch (Exception ex)
                {
                    // Handle error
                }
            }
        });
    }

    private string GetLocalIPAddress()
    {
        // Get Wi-Fi IP (simplified)
        // In practice, use ConnectivityManager or WifiManager
        return "192.168.4.1"; // Or get dynamically
    }
}
```

### 🖥️ Android: TCP Server (Score Control App)

```csharp
using System.Net;
using System.Net.Sockets;

public class TcpServer
{
    private TcpListener _listener;
    private Dictionary<string, TcpClient> _clients = new();

    public async Task Start(int port)
    {
        _listener = new TcpListener(IPAddress.Any, port);
        _listener.Start();

        while (true)
        {
            TcpClient client = await _listener.AcceptTcpClientAsync();
            _ = HandleClientAsync(client);
        }
    }

    private async Task HandleClientAsync(TcpClient client)
    {
        using var stream = client.GetStream();
        using var reader = new StreamReader(stream);

        string identity = await reader.ReadLineAsync();
        if (identity?.StartsWith("DEVICE:") == true)
        {
            var parts = ParseIdentity(identity);
            string mac = parts["MAC"];
            string name = parts["NAME"];

            // Validate MAC against whitelist
            if (IsAllowed(mac))
            {
                _clients[mac] = client;
                // Start reading data...
            }
            else
            {
                // Send DENIED and close
                var writer = new StreamWriter(stream);
                await writer.WriteLineAsync("DENIED");
                await writer.FlushAsync();
                client.Close();
            }
        }
    }

    private Dictionary<string, string> ParseIdentity(string line)
    {
        var result = new Dictionary<string, string>();
        if (!line.StartsWith("DEVICE:")) return result;
        var parts = line.Substring(7).Split(',');
        foreach (var part in parts)
        {
            var kv = part.Split('=', 2);
            if (kv.Length == 2) result[kv[0]] = kv[1];
        }
        return result;
    }

    private bool IsAllowed(string mac) => AllowedDevices.Contains(mac);
}
```

---

## 8. Best Practices

| Practice | Why |
|--------|-----|
| **Use unique SSID per court** | Prevents discovery conflicts |
| **Pre-configure device Wi-Fi** | No user setup needed |
| **Include Court ID in discovery** | Safe multi-court operation |
| **Whitelist device MACs** | Prevents spoofing |
| **Use UDP broadcast on 5002+** | Avoids port conflicts |
| **Fixed hotspot IP (192.168.4.1)** | Simplifies client logic |
| **Reconnect logic on ESP32** | Handles Wi-Fi drops |
| **Log connection events** | Debugging and auditing |