@toponextech/smartembed-mcp-server
Version:
MCP server for intelligent embedded development with PlatformIO - AI-powered project creation, error diagnosis, and device detection
314 lines • 12.3 kB
JavaScript
/**
* Device Parser for SmartEmbed
* Parses PlatformIO device list output and extracts device information
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DeviceParser = void 0;
// Common board identification patterns
const BOARD_PATTERNS = [
// ESP32 variants
{
patterns: [/CP210[0-9]/i, /Silicon Labs/i],
boards: ['esp32dev', 'esp32-c3-devkitm-1', 'esp32-s2-devkitm-1'],
manufacturer: 'Silicon Labs',
boardType: 'ESP32',
confidence: 'high',
},
{
patterns: [/CH340/i, /CH341/i],
boards: ['esp32dev', 'nodemcu-32s', 'wemos_d1_mini32', 'uno', 'nodemcuv2'],
manufacturer: 'WCH',
boardType: 'ESP32/ESP8266/Arduino',
confidence: 'low', // CH340 is used by many different boards
},
// ESP8266 variants
{
patterns: [/NodeMCU/i, /CP210[0-9].*NodeMCU/i],
boards: ['nodemcuv2', 'nodemcu', 'esp12e'],
boardType: 'ESP8266',
confidence: 'high',
},
{
patterns: [/Wemos.*D1/i, /WEMOS/i],
boards: ['d1_mini', 'd1_mini_pro', 'd1'],
boardType: 'ESP8266',
confidence: 'high',
},
// Arduino boards
{
patterns: [/Arduino.*Uno/i, /2341:0043/i, /2341:0001/i],
boards: ['uno'],
boardType: 'Arduino',
confidence: 'high',
},
{
patterns: [/Arduino.*Mega/i, /2341:0042/i, /2341:0010/i],
boards: ['megaatmega2560', 'megaatmega1280'],
boardType: 'Arduino',
confidence: 'high',
},
{
patterns: [/Arduino.*Nano/i, /Arduino.*Nano.*Every/i],
boards: ['nano', 'nanoatmega328', 'nano_every'],
boardType: 'Arduino',
confidence: 'high',
},
{
patterns: [/Arduino.*Leonardo/i, /2341:8036/i],
boards: ['leonardo'],
boardType: 'Arduino',
confidence: 'high',
},
// STM32 boards
{
patterns: [/STM32.*Nucleo/i, /0483:374[0-9]/i],
boards: ['nucleo_f103rb', 'nucleo_f401re', 'nucleo_f411re'],
boardType: 'STM32',
confidence: 'high',
},
{
patterns: [/STM32/i, /STMicroelectronics/i, /0483:/i],
boards: ['genericSTM32F103C8', 'bluepill_f103c8', 'blackpill_f401cc'],
boardType: 'STM32',
confidence: 'medium',
},
// Raspberry Pi Pico
{
patterns: [/Raspberry.*Pi.*Pico/i, /2e8a:0005/i, /RP2040/i],
boards: ['pico', 'nanorp2040connect'],
boardType: 'RP2040',
confidence: 'high',
},
// Generic FTDI
{
patterns: [/FTDI/i, /FT232/i, /0403:6001/i],
boards: ['uno', 'pro16MHzatmega328', 'pro8MHzatmega328'],
boardType: 'Generic',
confidence: 'low',
connectionTips: ['This appears to be an FTDI adapter. Make sure it\'s connected to your board correctly.'],
},
];
// VID:PID database for quick lookup
const VID_PID_DATABASE = {
'2341:0043': { boards: ['uno'], confidence: 'high' },
'2341:0042': { boards: ['megaatmega2560'], confidence: 'high' },
'2341:8036': { boards: ['leonardo'], confidence: 'high' },
'1a86:7523': { boards: ['uno', 'esp32dev', 'nodemcuv2'], confidence: 'low' },
'10c4:ea60': { boards: ['esp32dev', 'esp32-c3-devkitm-1'], confidence: 'medium' },
'0403:6001': { boards: ['uno', 'pro16MHzatmega328'], confidence: 'low' },
'2e8a:0005': { boards: ['pico'], confidence: 'high' },
'0483:374b': { boards: ['nucleo_f103rb'], confidence: 'high' },
};
class DeviceParser {
/**
* Parse PlatformIO device list output
*/
parseDeviceList(output) {
const devices = [];
const lines = output.split('\n');
let currentDevice = null;
for (const line of lines) {
const trimmedLine = line.trim();
// Windows format: COM3 - USB Serial Device (COM3)
const windowsMatch = trimmedLine.match(/^(COM\d+)\s*-\s*(.+)$/);
if (windowsMatch) {
if (currentDevice) {
devices.push(this.finalizeDevice(currentDevice));
}
currentDevice = {
port: windowsMatch[1],
description: windowsMatch[2],
hwid: '',
};
continue;
}
// Unix format: /dev/ttyUSB0 - CP2102 USB to UART Bridge Controller
const unixMatch = trimmedLine.match(/^(\/dev\/[^\s]+)\s*-\s*(.+)$/);
if (unixMatch) {
if (currentDevice) {
devices.push(this.finalizeDevice(currentDevice));
}
currentDevice = {
port: unixMatch[1],
description: unixMatch[2],
hwid: '',
};
continue;
}
// Hardware ID line
if (trimmedLine.startsWith('HWID:') || trimmedLine.startsWith('Hardware ID:')) {
const hwid = trimmedLine.replace(/^(HWID:|Hardware ID:)\s*/i, '');
if (currentDevice) {
currentDevice.hwid = hwid;
this.parseHwid(hwid, currentDevice);
}
}
// Description line (indented) - but skip HWID continuation lines
if ((trimmedLine.startsWith('desc:') || /^\s+/.test(line)) &&
!trimmedLine.startsWith('SER:') &&
!trimmedLine.startsWith('LOCATION:') &&
!trimmedLine.startsWith('HWID:') &&
!trimmedLine.startsWith('Hardware ID:')) {
const desc = trimmedLine.replace(/^desc:\s*/i, '').trim();
if (currentDevice && desc && desc !== currentDevice.description) {
// Only update description if it's different
currentDevice.description = desc;
}
}
}
// Don't forget the last device
if (currentDevice) {
devices.push(this.finalizeDevice(currentDevice));
}
return devices;
}
/**
* Parse hardware ID to extract VID/PID and other info
*/
parseHwid(hwid, device) {
// Extract VID:PID - handle both formats
let vidPidMatch = hwid.match(/VID:PID=([0-9A-Fa-f]{4}):([0-9A-Fa-f]{4})/i);
if (!vidPidMatch) {
// Try Windows format: VID_1234&PID_5678
vidPidMatch = hwid.match(/VID_([0-9A-Fa-f]{4})&PID_([0-9A-Fa-f]{4})/i);
}
if (vidPidMatch) {
device.vid = vidPidMatch[1].toUpperCase();
device.pid = vidPidMatch[2].toUpperCase();
}
// Extract serial number
const serialMatch = hwid.match(/SER=([^\s\\]+)/i);
if (serialMatch) {
device.serialNumber = serialMatch[1];
}
// Extract location (for uniqueness)
const locationMatch = hwid.match(/LOCATION=([^\s\\]+)/i);
if (locationMatch) {
// Store location in serialNumber if no serial found
if (!device.serialNumber) {
device.serialNumber = `LOC:${locationMatch[1]}`;
}
}
}
/**
* Finalize device object with defaults
*/
finalizeDevice(device) {
return {
port: device.port || 'Unknown',
description: device.description || 'Unknown Device',
hwid: device.hwid || '',
manufacturer: device.manufacturer,
product: device.product,
serialNumber: device.serialNumber,
vid: device.vid,
pid: device.pid,
boardType: device.boardType,
suggestedBoard: device.suggestedBoard,
};
}
/**
* Identify board type based on device information
*/
identifyBoard(device) {
let bestMatch = null;
// First, try VID:PID lookup
if (device.vid && device.pid) {
const vidPid = `${device.vid}:${device.pid}`.toLowerCase();
const match = VID_PID_DATABASE[vidPid];
if (match) {
bestMatch = {
board: match.boards[0],
confidence: match.confidence,
alternatives: match.boards.slice(1),
tips: [],
};
}
}
// Then try pattern matching on description and HWID
for (const pattern of BOARD_PATTERNS) {
const matchText = `${device.description} ${device.hwid}`;
const matches = pattern.patterns.some(p => p.test(matchText));
if (matches) {
// If we already have a high confidence match, skip
if (bestMatch && bestMatch.confidence === 'high' && pattern.confidence !== 'high') {
continue;
}
bestMatch = {
board: pattern.boards[0],
confidence: pattern.confidence,
alternatives: pattern.boards.slice(1),
tips: pattern.connectionTips || [],
};
// Store board type in device
device.boardType = pattern.boardType;
device.suggestedBoard = pattern.boards[0];
// If high confidence, we're done
if (pattern.confidence === 'high') {
break;
}
}
}
// If no match found, provide generic suggestions
if (!bestMatch) {
return {
detectedBoard: 'unknown',
confidence: 'low',
alternativeBoards: ['uno', 'esp32dev', 'nodemcuv2', 'nucleo_f103rb'],
connectionTips: [
'Could not identify the board automatically.',
'Try specifying the board manually with: pio boards',
'Make sure the board is properly connected and drivers are installed.',
],
};
}
// Generate connection tips based on board type
const tips = [...bestMatch.tips];
if (device.boardType === 'ESP32' || device.boardType === 'ESP8266') {
tips.push('Make sure you have the correct USB drivers installed (CP2102, CH340, etc.)');
tips.push('Some boards require holding the BOOT button when uploading');
}
if (device.boardType === 'STM32') {
tips.push('STM32 boards may require STM32CubeProgrammer or DFU mode for first upload');
tips.push('Check the BOOT0 jumper position if upload fails');
}
return {
detectedBoard: bestMatch.board,
confidence: bestMatch.confidence,
alternativeBoards: bestMatch.alternatives,
connectionTips: tips,
};
}
/**
* Generate connection suggestions based on device analysis
*/
generateConnectionSuggestions(devices) {
const suggestions = [];
if (devices.length === 0) {
suggestions.push('No devices detected. Please check:');
suggestions.push('- Board is connected via USB');
suggestions.push('- USB cable supports data (not charge-only)');
suggestions.push('- Drivers are installed (check Device Manager on Windows)');
suggestions.push('- Try a different USB port');
return suggestions;
}
if (devices.length > 1) {
suggestions.push(`Found ${devices.length} devices. To specify which one to use:`);
suggestions.push(`- Add to platformio.ini: upload_port = ${devices[0].port}`);
suggestions.push('- Or use: pio run -t upload --upload-port <port>');
}
// Check for common issues
for (const device of devices) {
if (device.description.includes('FTDI') || device.description.includes('FT232')) {
suggestions.push('FTDI adapter detected - ensure TX/RX connections are correct (TX→RX, RX→TX)');
}
if (device.port.includes('Bluetooth')) {
suggestions.push('Bluetooth port detected - for programming, use USB connection instead');
}
}
return suggestions;
}
}
exports.DeviceParser = DeviceParser;
//# sourceMappingURL=device-parser.js.map
;