Add doc/WiFiConnectivity.md
This commit is contained in:
parent
17a1dbb784
commit
e03d71a163
411
doc/WiFiConnectivity.md
Normal file
411
doc/WiFiConnectivity.md
Normal file
@ -0,0 +1,411 @@
|
||||
# 🌐 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 |
|
||||
|
||||
---
|
||||
|
||||
🎯 **This guide ensures reliable, secure, and user-friendly Wi-Fi connectivity** for both **single-court clubs** and **multi-court tournaments**.
|
||||
You now have everything needed to implement **auto-discovery** and **prevent cross-talk**.
|
||||
|
||||
📬 For help: Contact project lead.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### ✅ How to Use
|
||||
|
||||
1. Save as `WIFI_CONNECTIVITY_GUIDE.md`
|
||||
2. Place in `/docs` folder
|
||||
3. Commit to GitHub
|
||||
|
||||
---
|
||||
|
||||
Let me know if you want:
|
||||
- A **diagram (Mermaid)** of the network flow
|
||||
- A **PDF version**
|
||||
- A **device configuration template**
|
||||
- A **tournament setup checklist**
|
||||
|
||||
You're now fully equipped to build a **rock-solid, scalable Wi-Fi system**. 🏆
|
||||
Loading…
x
Reference in New Issue
Block a user