Initial commit: Next.js UWB positioning webapp
- Complete Next.js 14 app with TypeScript and Tailwind CSS - ESP32 serial communication via API routes - Real-time UWB positioning visualization - Interactive 2D warehouse mapping with Canvas - Device connection interface with auto-detection - AT command parsing for UWBHelper library integration - Clean project structure with comprehensive documentation
This commit is contained in:
commit
fa75faa69d
20 changed files with 7600 additions and 0 deletions
71
lib/serial-utils.ts
Normal file
71
lib/serial-utils.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
// Utility functions for serial communication and UWB data processing
|
||||
|
||||
export interface SerialPortInfo {
|
||||
path: string
|
||||
manufacturer?: string
|
||||
serialNumber?: string
|
||||
pnpId?: string
|
||||
locationId?: string
|
||||
productId?: string
|
||||
vendorId?: string
|
||||
}
|
||||
|
||||
export class UWBDataProcessor {
|
||||
private static instance: UWBDataProcessor
|
||||
private dataBuffer: string = ''
|
||||
|
||||
static getInstance(): UWBDataProcessor {
|
||||
if (!UWBDataProcessor.instance) {
|
||||
UWBDataProcessor.instance = new UWBDataProcessor()
|
||||
}
|
||||
return UWBDataProcessor.instance
|
||||
}
|
||||
|
||||
// Process incoming serial data stream
|
||||
processDataStream(chunk: string): string[] {
|
||||
this.dataBuffer += chunk
|
||||
const lines: string[] = []
|
||||
|
||||
// Split on line endings
|
||||
const parts = this.dataBuffer.split(/\r?\n/)
|
||||
|
||||
// Keep last incomplete line in buffer
|
||||
this.dataBuffer = parts.pop() || ''
|
||||
|
||||
// Return complete lines
|
||||
return parts.filter(line => line.trim().length > 0)
|
||||
}
|
||||
|
||||
// Check if ESP32 device based on port info
|
||||
static isESP32Device(port: SerialPortInfo): boolean {
|
||||
const esp32Vendors = ['10C4', '1A86', '0403'] // Silicon Labs, QinHeng, FTDI
|
||||
const esp32Manufacturers = ['Silicon Labs', 'FTDI', 'QinHeng Electronics']
|
||||
|
||||
return (
|
||||
(port.vendorId && esp32Vendors.includes(port.vendorId)) ||
|
||||
(port.manufacturer && esp32Manufacturers.some(m =>
|
||||
port.manufacturer?.includes(m)
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
// Validate UWB command format
|
||||
static validateUWBCommand(command: string): boolean {
|
||||
const validCommands = [
|
||||
'AT?', 'AT+GETVER?', 'AT+RESTART', 'AT+RESTORE', 'AT+SAVE',
|
||||
'AT+SETCFG=', 'AT+GETCFG?', 'AT+SETANT=', 'AT+GETANT?',
|
||||
'AT+SETCAP=', 'AT+GETCAP?', 'AT+SETRPT=', 'AT+GETRPT?',
|
||||
'AT+SLEEP=', 'AT+SETPOW=', 'AT+GETPOW?', 'AT+DATA=',
|
||||
'AT+RDATA', 'AT+SETPAN=', 'AT+GETPAN?'
|
||||
]
|
||||
|
||||
return validCommands.some(cmd =>
|
||||
command.startsWith(cmd) || command === cmd
|
||||
)
|
||||
}
|
||||
|
||||
// Clear data buffer
|
||||
clearBuffer(): void {
|
||||
this.dataBuffer = ''
|
||||
}
|
||||
}
|
||||
67
lib/uwb-types.ts
Normal file
67
lib/uwb-types.ts
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// UWB Device Data Types (from ESP32 project context)
|
||||
|
||||
export interface DeviceData {
|
||||
deviceId: number;
|
||||
distance: number;
|
||||
rssi: number;
|
||||
lastUpdate: number;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export interface AnchorPosition {
|
||||
anchorId: number;
|
||||
x: number;
|
||||
y: number;
|
||||
confidence: number;
|
||||
valid: boolean;
|
||||
}
|
||||
|
||||
export interface RangeResult {
|
||||
tagId: number;
|
||||
mask: number;
|
||||
sequence: number;
|
||||
ranges: number[]; // 8 elements
|
||||
rssi: number[]; // 8 elements
|
||||
anchorIds: number[]; // 8 elements
|
||||
timer: number;
|
||||
timerSys: number;
|
||||
}
|
||||
|
||||
export interface TagPosition {
|
||||
x: number;
|
||||
y: number;
|
||||
timestamp: number;
|
||||
confidence: number;
|
||||
}
|
||||
|
||||
// Configuration constants from ESP32 project
|
||||
export const UWB_CONFIG = {
|
||||
MAX_ANCHORS: 8,
|
||||
UWB_TAG_COUNT: 64,
|
||||
NETWORK_ID: 1234,
|
||||
DEVICE_TIMEOUT: 5000,
|
||||
BAUD_RATE: 115200,
|
||||
DATA_RATE: 1, // 6.8Mbps
|
||||
RANGE_FILTER: 1
|
||||
} as const;
|
||||
|
||||
// Serial data parsing types
|
||||
export interface SerialData {
|
||||
type: 'range' | 'config' | 'status';
|
||||
raw: string;
|
||||
parsed?: RangeResult | DeviceData[];
|
||||
timestamp: number;
|
||||
}
|
||||
|
||||
// File upload types for offline analysis
|
||||
export interface PositioningSession {
|
||||
rawData: RangeResult[];
|
||||
anchors: AnchorPosition[];
|
||||
tagPath: TagPosition[];
|
||||
sessionInfo: {
|
||||
startTime: number;
|
||||
endTime: number;
|
||||
duration: number;
|
||||
totalPoints: number;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue