feat: 보드 선택에 언어 옵션 추가 + 아두이노 힌트 범용화
- 보드 드롭다운 상단에 Python / C++(일반) / 기타 언어 옵션 추가 - _boardSpecificHints 리팩터: 모든 하드웨어 보드에 Arduino C++ 기본 규칙 적용 - arch별 추가 규칙 (esp32/esp8266/stm32/avr) 유지 - STM32 힌트 "Python 절대 사용 금지" → "PC 앱 필요 시 병행 가능"으로 완화 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+69
-43
@@ -1584,49 +1584,53 @@ function _boardSpecificHints(code = '') {
|
||||
const fqbn = _csbSelBoard.fqbn || '';
|
||||
const arch = fqbn.split(':')[1] || '';
|
||||
const usesWifi = /WiFi|ESP8266WiFi|HTTPClient|WebServer/i.test(code);
|
||||
if (arch === 'esp32') {
|
||||
const lines = [
|
||||
'ESP32 코딩 규칙 (반드시 준수):',
|
||||
'- analogWrite() 없음 → ledcAttach(pin, freq, res) + ledcWrite(pin, duty) 사용',
|
||||
'- loop()에서 오래 blocking하면 watchdog(TG1WDT) 리셋 발생 → delay() 또는 yield() 필수',
|
||||
'- SPIFFS 대신 LittleFS 권장 (esp32 3.x 코어)',
|
||||
];
|
||||
if (usesWifi) lines.splice(1, 0,
|
||||
'- WiFi는 SoC 내장 — #include <SPI.h> 절대 추가하지 말 것',
|
||||
'- #include <WiFi.h> 하나로 충분 (esp32 코어 제공)',
|
||||
'- WiFi.firmwareVersion() 없음 (WiFiNINA 전용)');
|
||||
return '\n' + lines.join('\n');
|
||||
}
|
||||
if (arch === 'esp8266') {
|
||||
const lines = [
|
||||
'ESP8266 코딩 규칙 (반드시 준수):',
|
||||
'- analogWrite() 있음 (PWM, 0-1023)',
|
||||
'- loop()에서 yield() 또는 delay() 호출 필수 (watchdog 방지)',
|
||||
'- SPI.h / ESP32Servo.h / ledcAttach 등 ESP32 전용 API 사용 금지',
|
||||
];
|
||||
if (usesWifi) lines.splice(1, 0,
|
||||
'- WiFi: #include <ESP8266WiFi.h> — #include <WiFi.h> 절대 사용 금지',
|
||||
'- WiFi 암호화 상수: ENC_TYPE_NONE / ENC_TYPE_CCMP / ENC_TYPE_TKIP 사용 (WIFI_AUTH_OPEN 등 ESP32 상수 금지)',
|
||||
'- WiFi 스캔: WiFi.scanNetworks() / WiFi.getNetworkInfo() / WiFi.getScanInfoByIndex() / bss_info 사용');
|
||||
return '\n' + lines.join('\n');
|
||||
}
|
||||
if (arch === 'avr') {
|
||||
const lines = ['AVR(Arduino) 코딩 규칙:'];
|
||||
if (usesWifi) lines.push('- WiFi 실드: #include <SPI.h> + #include <WiFiNINA.h> 또는 #include <WiFi.h>');
|
||||
return '\n' + lines.join('\n');
|
||||
}
|
||||
if (arch === 'stm32') {
|
||||
|
||||
// 언어 직접 선택 (하드웨어 보드 아님)
|
||||
if (fqbn === 'lang:python') {
|
||||
return '\n' + [
|
||||
'STM32 (Arduino) 코딩 규칙 (반드시 준수):',
|
||||
'- 언어: C++ (Arduino 스케치), Python 절대 사용 금지',
|
||||
'- 핀 이름: PB_3·PA_0 등 STM32 PinName 상수 사용. Arduino D-number와 혼용 주의',
|
||||
'- Serial = USART1 (PA9/PA10), Serial.begin(115200) 필수',
|
||||
'- analogWrite() 없음 → HardwareTimer + PWM 채널 사용',
|
||||
'- Wire(I2C1): SDA=PB7, SCL=PB6 (블루필 기본)',
|
||||
'- SPI1: MOSI=PA7, MISO=PA6, SCK=PA5',
|
||||
'언어: Python',
|
||||
'- 순수 Python 코드로 작성하세요. Arduino/C++ 문법 사용 금지',
|
||||
'- 스크립트는 표준 Python 환경에서 실행됩니다',
|
||||
].join('\n');
|
||||
}
|
||||
return '';
|
||||
if (fqbn === 'lang:cpp') {
|
||||
return '\n' + [
|
||||
'언어: C++ (일반)',
|
||||
'- 표준 C++17 코드로 작성하세요. Arduino 전용 API 사용 금지',
|
||||
].join('\n');
|
||||
}
|
||||
if (fqbn === 'lang:other') return '';
|
||||
|
||||
// 하드웨어 보드: 공통 Arduino 규칙 + arch별 추가 규칙
|
||||
if (!arch) return '';
|
||||
const base = ['Arduino (C++) 코딩 규칙:', '- 언어: C++ (Arduino 스케치). PC 전용 앱이 필요하면 해당 언어 병행 가능'];
|
||||
const extra = [];
|
||||
|
||||
if (arch === 'esp32') {
|
||||
extra.push('- analogWrite() 없음 → ledcAttach(pin, freq, res) + ledcWrite(pin, duty)');
|
||||
extra.push('- loop() 장시간 blocking → watchdog 리셋. delay() 또는 yield() 필수');
|
||||
extra.push('- SPIFFS 대신 LittleFS 권장 (esp32 3.x 코어)');
|
||||
if (usesWifi) extra.push(
|
||||
'- WiFi 내장 — #include <WiFi.h> 하나로 충분. SPI.h 추가 금지',
|
||||
'- WiFi.firmwareVersion() 없음 (WiFiNINA 전용)');
|
||||
} else if (arch === 'esp8266') {
|
||||
extra.push('- analogWrite() 있음 (PWM, 0-1023)');
|
||||
extra.push('- loop()에서 yield() 또는 delay() 필수 (watchdog 방지)');
|
||||
extra.push('- ledcAttach 등 ESP32 전용 API 사용 금지');
|
||||
if (usesWifi) extra.push(
|
||||
'- WiFi: #include <ESP8266WiFi.h>. #include <WiFi.h> 사용 금지',
|
||||
'- 암호화 상수: ENC_TYPE_NONE / ENC_TYPE_CCMP / ENC_TYPE_TKIP');
|
||||
} else if (arch === 'stm32') {
|
||||
extra.push('- 핀 이름: PB_3·PA_0 등 STM32 PinName 상수 사용');
|
||||
extra.push('- Serial = USART1 (PA9/PA10), Serial.begin(115200) 필수');
|
||||
extra.push('- analogWrite() 없음 → HardwareTimer + PWM 채널 사용');
|
||||
extra.push('- Wire(I2C1): SDA=PB7, SCL=PB6 / SPI1: MOSI=PA7, MISO=PA6, SCK=PA5');
|
||||
} else if (arch === 'avr') {
|
||||
if (usesWifi) extra.push('- WiFi 실드: #include <SPI.h> + #include <WiFiNINA.h>');
|
||||
}
|
||||
// sam, samd, megaavr 등은 base만 적용
|
||||
|
||||
return '\n' + [...base, ...extra].join('\n');
|
||||
}
|
||||
|
||||
async function csbCompileOnly() {
|
||||
@@ -2875,8 +2879,9 @@ function _buildCodeSystemPrompt() {
|
||||
const summaryBlock = _historySummary ? `\n[이전 대화 요약]\n${_historySummary}\n` : '';
|
||||
const _activeCode = (() => { try { const f = codeFiles.find(f => f.id === activeCodeFileId); return f?.model?.getValue() || f?.content || ''; } catch { return ''; } })();
|
||||
const boardHints = _boardSpecificHints(_activeCode);
|
||||
const isLangOnly = _csbSelBoard?.fqbn?.startsWith('lang:');
|
||||
const boardBlock = _csbSelBoard?.fqbn
|
||||
? `\n## 대상 보드: ${_csbSelBoard.name} (${_csbSelBoard.fqbn})${boardHints}\n`
|
||||
? `\n## ${isLangOnly ? '코딩 언어' : '대상 보드'}: ${_csbSelBoard.name}${isLangOnly ? '' : ` (${_csbSelBoard.fqbn})`}${boardHints}\n`
|
||||
: '';
|
||||
|
||||
// 에디터 파일 컨텍스트 — 로컬 폴더와 분리하여 강조
|
||||
@@ -4840,16 +4845,37 @@ function csbBoardSelClose() {
|
||||
if (dd) dd.style.display = 'none';
|
||||
}
|
||||
|
||||
const _LANG_BOARDS = [
|
||||
{ fqbn: 'lang:python', name: '🐍 Python', desc: 'Python 스크립트' },
|
||||
{ fqbn: 'lang:cpp', name: '⚡ C++ (일반)', desc: '표준 C++, Arduino 아님' },
|
||||
{ fqbn: 'lang:other', name: '📝 기타 언어', desc: 'AI가 언어를 판단' },
|
||||
];
|
||||
|
||||
async function csbBoardSelSearch() {
|
||||
const q = (document.getElementById('csb-board-sel-q')?.value || '').trim();
|
||||
const list = document.getElementById('csb-board-sel-list');
|
||||
if (!list) return;
|
||||
list.innerHTML = '<div style="padding:10px;color:var(--muted);font-size:11px;text-align:center">불러오는 중...</div>';
|
||||
try {
|
||||
// 언어 옵션 고정 섹션
|
||||
const langFiltered = q ? _LANG_BOARDS.filter(b => b.name.includes(q) || b.desc.includes(q)) : _LANG_BOARDS;
|
||||
const langHtml = langFiltered.length ? [
|
||||
`<div style="padding:4px 10px 2px;font-size:9px;color:var(--muted);font-weight:600;letter-spacing:.05em;text-transform:uppercase">언어</div>`,
|
||||
...langFiltered.map(b => {
|
||||
const sel = b.fqbn === _csbSelBoard.fqbn;
|
||||
return `<div data-fqbn="${_csbEsc(b.fqbn)}" data-name="${_csbEsc(b.name)}" data-qemu="" data-chip=""
|
||||
style="padding:6px 10px;cursor:pointer;font-size:11px;border-bottom:1px solid var(--line);display:flex;flex-direction:column;gap:1px;${sel?'background:var(--brand);color:#fff':''}">
|
||||
<span style="font-weight:600;${sel?'color:#fff':'color:var(--text)'}">${_csbEsc(b.name)}</span>
|
||||
<span style="font-size:10px;${sel?'color:rgba(255,255,255,.7)':'color:var(--muted)'}">${b.desc}</span>
|
||||
</div>`;
|
||||
}),
|
||||
`<div style="padding:4px 10px 2px;font-size:9px;color:var(--muted);font-weight:600;letter-spacing:.05em;text-transform:uppercase;border-top:1px solid var(--line)">보드</div>`,
|
||||
].join('') : '';
|
||||
|
||||
const data = await api('/api/arduino/boards/listall?q=' + encodeURIComponent(q));
|
||||
const boards = data.boards || [];
|
||||
if (!boards.length) { list.innerHTML = '<div style="padding:10px;color:var(--muted);font-size:11px;text-align:center">검색 결과 없음</div>'; return; }
|
||||
list.innerHTML = boards.map(b => {
|
||||
if (!boards.length && !langFiltered.length) { list.innerHTML = '<div style="padding:10px;color:var(--muted);font-size:11px;text-align:center">검색 결과 없음</div>'; return; }
|
||||
list.innerHTML = langHtml + boards.map(b => {
|
||||
const sel = b.fqbn === _csbSelBoard.fqbn;
|
||||
const qemuBadge = b.qemu === 'esp8266-native'
|
||||
? `<span style="font-size:9px;padding:1px 5px;border-radius:3px;margin-left:4px;background:#0c3a4a;color:#67e8f9;border:1px solid #0e7490">🖥 Native</span>`
|
||||
|
||||
Reference in New Issue
Block a user