Công cụ ESP32

Bộ công cụ phát triển chuyên nghiệp cho ESP32 - Code editor online, nạp firmware, GPIO configurator và nhiều tiện ích khác

Arduino IDE Online

Console Output
ESP32 Code Editor Ready
Chọn board và cổng COM để bắt đầu lập trình

Cấu hình Board

Ví dụ mẫu

Nạp Firmware

Kéo thả file firmware (.bin) vào đây hoặc

Tiến trình nạp

Chuẩn bị firmware 0%
Upload firmware 0%
Xác minh firmware 0%
Firmware Uploader Ready
Chọn file firmware và cấu hình để bắt đầu

GPIO Configurator

Sơ đồ chân ESP32

Chân đã chọn
Chưa có chân nào được chọn
Code sinh ra

Cấu hình chân

Chức năng đặc biệt

Touch Pad GPIO 4, 0, 2, 15, 13, 12, 14, 27, 33, 32
ADC GPIO 32-39
DAC GPIO 25, 26
SPI GPIO 18, 19, 21, 22, 23
I2C GPIO 21, 22

Serial Monitor

Serial Monitor Ready
Chọn cổng COM và baud rate để kết nối

Trạng thái kết nối

Trạng thái: Chưa kết nối
Cổng: -
Baud rate: -
Data received: 0 bytes

Cấu hình hiển thị

Lệnh nhanh

Code Generator

Code sinh ra

Thao tác thành công!
"; server.send(200, "text/html", html); } void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nWiFi connected!"); Serial.println(WiFi.localIP()); server.on("/", handleRoot); server.begin(); } void loop() { server.handleClient(); }` }; let codeEditor; let selectedPins = []; let currentPinConfig = {}; // Initialize page document.addEventListener('DOMContentLoaded', function() { initializeToolsPage(); }); function initializeToolsPage() { initializeCodeEditor(); initializeTabs(); initializeGPIOConfigurator(); initializeFirmwareUploader(); initializeSerialMonitor(); initializeCodeGenerator(); } function initializeCodeEditor() { // Initialize CodeMirror const textarea = document.getElementById('code-editor-textarea'); codeEditor = CodeMirror.fromTextArea(textarea, { mode: 'text/x-c++src', theme: 'monokai', lineNumbers: true, autoCloseBrackets: true, matchBrackets: true, indentUnit: 2, tabSize: 2, lineWrapping: true }); // Example buttons document.querySelectorAll('.example-btn').forEach(btn => { btn.addEventListener('click', function() { const example = this.dataset.example; if (codeExamples[example]) { codeEditor.setValue(codeExamples[example]); addToConsole('info', `Đã tải ví dụ: ${this.textContent.trim()}`); } }); }); // Compile button document.getElementById('compile-btn').addEventListener('click', function() { simulateCompile(); }); // Upload button document.getElementById('upload-btn').addEventListener('click', function() { simulateUpload(); }); // Clear console document.getElementById('clear-console').addEventListener('click', function() { document.getElementById('console-output').innerHTML = ''; }); } function initializeTabs() { const tabButtons = document.querySelectorAll('.tab-button'); const toolSections = document.querySelectorAll('.tool-section'); tabButtons.forEach(button => { button.addEventListener('click', function() { const toolId = this.dataset.tool; // Update active tab tabButtons.forEach(btn => btn.classList.remove('active')); this.classList.add('active'); // Show corresponding section toolSections.forEach(section => { section.classList.remove('active'); if (section.id === toolId) { section.classList.add('active'); } }); }); }); } function initializeGPIOConfigurator() { generateGPIOPins(); document.getElementById('pin-mode').addEventListener('change', function() { const mode = this.value; document.getElementById('pwm-config').style.display = mode === 'pwm' ? 'block' : 'none'; document.getElementById('pull-config').style.display = mode === 'input' ? 'block' : 'none'; }); document.getElementById('apply-config').addEventListener('click', function() { applyPinConfig(); }); } function generateGPIOPins() { const container = document.getElementById('gpio-pins'); const pins = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 21, 22, 23, 25, 26, 27, 32, 33, 34, 35, 36, 37, 38, 39]; container.innerHTML = pins.map(pin => `
${pin}
` ).join(''); } function selectPin(pin) { const pinElement = document.querySelector(`[data-pin="${pin}"]`); if (selectedPins.includes(pin)) { // Deselect pin selectedPins = selectedPins.filter(p => p !== pin); pinElement.classList.remove('selected'); delete currentPinConfig[pin]; } else { // Select pin selectedPins.push(pin); pinElement.classList.add('selected'); currentPinConfig[pin] = { mode: 'input', pull: 'none' }; } updateSelectedPinsDisplay(); generateGPIOCode(); } function updateSelectedPinsDisplay() { const container = document.getElementById('selected-pins'); if (selectedPins.length === 0) { container.innerHTML = '
Chưa có chân nào được chọn
'; return; } container.innerHTML = selectedPins.map(pin => { const config = currentPinConfig[pin]; return `
GPIO ${pin} ${config.mode.toUpperCase()}
`; }).join(''); } function applyPinConfig() { const pin = selectedPins[selectedPins.length - 1]; if (!pin) return; const mode = document.getElementById('pin-mode').value; const pull = document.getElementById('pull-mode').value; const frequency = document.getElementById('pwm-frequency').value; currentPinConfig[pin] = { mode: mode, pull: mode === 'input' ? pull : 'none', frequency: mode === 'pwm' ? frequency : undefined }; // Update pin visual const pinElement = document.querySelector(`[data-pin="${pin}"]`); pinElement.className = `gpio-pin ${mode}`; updateSelectedPinsDisplay(); generateGPIOCode(); showToast(`Đã cấu hình GPIO ${pin} thành ${mode.toUpperCase()}`); } function generateGPIOCode() { if (selectedPins.length === 0) { document.getElementById('gpio-code').value = '// Chọn chân GPIO để sinh code'; return; } let code = '// GPIO Configuration\n#include \n\nvoid setup() {\n Serial.begin(115200);\n'; selectedPins.forEach(pin => { const config = currentPinConfig[pin]; const pinDefine = `#define PIN_${pin} ${pin}`; code += ` ${pinDefine}\n`; if (config.mode === 'input') { if (config.pull === 'up') { code += ` pinMode(PIN_${pin}, INPUT_PULLUP);\n`; } else if (config.pull === 'down') { code += ` pinMode(PIN_${pin}, INPUT_PULLDOWN);\n`; } else { code += ` pinMode(PIN_${pin}, INPUT);\n`; } } else if (config.mode === 'output') { code += ` pinMode(PIN_${pin}, OUTPUT);\n`; } else if (config.mode === 'pwm') { code += ` ledcAttachPin(PIN_${pin}, ${pin});\n`; code += ` ledcSetup(${pin}, ${config.frequency}, 8);\n`; } }); code += '}\n\nvoid loop() {\n // Your code here\n}'; document.getElementById('gpio-code').value = code; } function initializeFirmwareUploader() { const fileInput = document.getElementById('firmware-file'); const dropZone = document.getElementById('firmware-drop-zone'); fileInput.addEventListener('change', function(e) { if (e.target.files.length > 0) { const file = e.target.files[0]; showToast(`Đã chọn file: ${file.name}`); } }); dropZone.addEventListener('dragover', function(e) { e.preventDefault(); dropZone.classList.add('border-red-500'); }); dropZone.addEventListener('dragleave', function() { dropZone.classList.remove('border-red-500'); }); dropZone.addEventListener('drop', function(e) { e.preventDefault(); dropZone.classList.remove('border-red-500'); const files = e.dataTransfer.files; if (files.length > 0) { const file = files[0]; if (file.name.endsWith('.bin')) { showToast(`Đã chọn file: ${file.name}`); } else { showToast('Vui lòng chọn file .bin', 'error'); } } }); document.getElementById('start-flash').addEventListener('click', simulateFirmwareUpload); } function simulateFirmwareUpload() { const button = document.getElementById('start-flash'); button.disabled = true; button.textContent = 'Đang nạp...'; let progress = 0; const interval = setInterval(() => { progress += Math.random() * 10; if (progress < 30) { document.getElementById('prepare-bar').style.width = progress + '%'; document.getElementById('prepare-progress').textContent = Math.round(progress) + '%'; } else if (progress < 80) { document.getElementById('upload-bar').style.width = (progress - 30) + '%'; document.getElementById('upload-progress').textContent = Math.round(progress - 30) + '%'; } else { document.getElementById('verify-bar').style.width = (progress - 80) + '%'; document.getElementById('verify-progress').textContent = Math.round(progress - 80) + '%'; } if (progress >= 100) { clearInterval(interval); button.disabled = false; button.textContent = 'Bắt đầu nạp firmware'; showToast('Nạp firmware thành công!', 'success'); // Reset progress bars setTimeout(() => { document.querySelectorAll('.progress-fill').forEach(bar => bar.style.width = '0%'); document.querySelectorAll('[id$="-progress"]').forEach(label => label.textContent = '0%'); }, 2000); } }, 100); } function initializeSerialMonitor() { let isConnected = false; document.getElementById('connect-serial').addEventListener('click', function() { const port = document.getElementById('serial-port').value; const baud = document.getElementById('baud-rate').value; if (!port) { showToast('Vui lòng chọn cổng COM', 'error'); return; } isConnected = true; this.disabled = true; document.getElementById('disconnect-serial').disabled = false; document.getElementById('connection-status').textContent = 'Đã kết nối'; document.getElementById('connection-status').className = 'text-green-600'; document.getElementById('active-port').textContent = port; document.getElementById('active-baud').textContent = baud; addToSerialOutput('info', `Đã kết nối với ${port} tại ${baud} baud`); // Simulate receiving data startSerialDataSimulation(); }); document.getElementById('disconnect-serial').addEventListener('click', function() { isConnected = false; this.disabled = true; document.getElementById('connect-serial').disabled = false; document.getElementById('connection-status').textContent = 'Chưa kết nối'; document.getElementById('connection-status').className = 'text-red-600'; document.getElementById('active-port').textContent = '-'; document.getElementById('active-baud').textContent = '-'; addToSerialOutput('info', 'Đã ngắt kết nối'); }); document.getElementById('send-serial').addEventListener('click', function() { const input = document.getElementById('serial-input'); const command = input.value.trim(); if (command && isConnected) { addToSerialOutput('sent', command); input.value = ''; // Simulate response setTimeout(() => { addToSerialOutput('received', `Echo: ${command}`); }, 100); } }); document.getElementById('serial-input').addEventListener('keypress', function(e) { if (e.key === 'Enter') { document.getElementById('send-serial').click(); } }); document.getElementById('clear-serial').addEventListener('click', function() { document.getElementById('serial-output').innerHTML = ''; }); // Quick commands document.querySelectorAll('.quick-cmd').forEach(btn => { btn.addEventListener('click', function() { const cmd = this.dataset.cmd; if (isConnected) { addToSerialOutput('sent', cmd); // Simulate responses setTimeout(() => { switch(cmd) { case 'restart': addToSerialOutput('received', 'Restarting ESP32...'); break; case 'info': addToSerialOutput('received', 'ESP32 DevKit v1.0'); addToSerialOutput('received', 'Flash: 4MB'); addToSerialOutput('received', 'PSRAM: 0MB'); break; case 'wifi': addToSerialOutput('received', 'WiFi Status: Connected'); addToSerialOutput('received', 'SSID: ESP32_Network'); addToSerialOutput('received', 'IP: 192.168.1.100'); break; case 'memory': addToSerialOutput('received', 'Free Heap: 234567 bytes'); addToSerialOutput('received', 'Minimum Free Heap: 198765 bytes'); addToSerialOutput('received', 'Largest Free Block: 113456 bytes'); break; } }, 200); } }); }); } function startSerialDataSimulation() { const dataTypes = [ { type: 'Sensor', values: ['Temp: 25.5°C', 'Humidity: 65%', 'Light: 890 lux'] }, { type: 'Status', values: ['System OK', 'WiFi Connected', 'MQTT Connected'] }, { type: 'Debug', values: ['Loop execution time: 45ms', 'Memory free: 234KB', 'Battery: 87%'] } ]; setInterval(() => { if (Math.random() > 0.7) { // 30% chance to receive data const dataType = dataTypes[Math.floor(Math.random() * dataTypes.length)]; const value = dataType.values[Math.floor(Math.random() * dataType.values.length)]; addToSerialOutput('received', `[${dataType.type}] ${value}`); } }, 2000); } function addToSerialOutput(type, message) { const output = document.getElementById('serial-output'); const timestamp = new Date().toLocaleTimeString(); const showTimestamp = document.getElementById('show-timestamp').checked; const autoScroll = document.getElementById('auto-scroll').checked; let prefix = ''; if (showTimestamp) { prefix = `[${timestamp}] `; } const div = document.createElement('div'); div.className = type; div.innerHTML = prefix + message; output.appendChild(div); if (autoScroll) { output.scrollTop = output.scrollHeight; } // Update data count const currentCount = parseInt(document.getElementById('data-count').textContent) || 0; document.getElementById('data-count').textContent = (currentCount + message.length) + ' bytes'; } function initializeCodeGenerator() { const projectType = document.getElementById('project-type'); projectType.addEventListener('change', function() { const type = this.value; // Hide all config sections document.getElementById('wifi-config').style.display = 'none'; document.getElementById('mqtt-config').style.display = 'none'; document.getElementById('sensor-config').style.display = 'none'; document.getElementById('actuator-config').style.display = 'none'; // Show relevant config if (type === 'wifi' || type === 'webserver') { document.getElementById('wifi-config').style.display = 'block'; } else if (type === 'mqtt') { document.getElementById('wifi-config').style.display = 'block'; document.getElementById('mqtt-config').style.display = 'block'; } else if (type === 'sensor') { document.getElementById('sensor-config').style.display = 'block'; } else if (type === 'actuator') { document.getElementById('actuator-config').style.display = 'block'; } }); document.getElementById('generate-code').addEventListener('click', generateProjectCode); document.getElementById('copy-code').addEventListener('click', copyGeneratedCode); document.getElementById('save-code').addEventListener('click', saveGeneratedCode); } function generateProjectCode() { const type = document.getElementById('project-type').value; let code = ''; switch(type) { case 'basic': code = generateBasicCode(); break; case 'wifi': code = generateWiFiCode(); break; case 'webserver': code = generateWebServerCode(); break; case 'mqtt': code = generateMQTTCode(); break; case 'sensor': code = generateSensorCode(); break; case 'actuator': code = generateActuatorCode(); break; } document.getElementById('generated-code').value = code; showToast('Đã sinh code thành công!'); } function generateBasicCode() { return `// ESP32 Basic I/O Example #include #define LED_PIN 2 #define BUTTON_PIN 0 void setup() { Serial.begin(115200); pinMode(LED_PIN, OUTPUT); pinMode(BUTTON_PIN, INPUT_PULLUP); Serial.println("ESP32 Basic I/O Started!"); } void loop() { // Read button state bool buttonState = digitalRead(BUTTON_PIN); // Control LED digitalWrite(LED_PIN, !buttonState); // Print status Serial.print("Button: "); Serial.print(buttonState ? "Released" : "Pressed"); Serial.print(" | LED: "); Serial.println(!buttonState ? "ON" : "OFF"); delay(100); }`; } function generateWiFiCode() { const ssid = document.getElementById('wifi-ssid').value || 'YOUR_SSID'; const password = document.getElementById('wifi-password').value || 'YOUR_PASSWORD'; return `// ESP32 WiFi Connection Example #include const char* ssid = "${ssid}"; const char* password = "${password}"; void setup() { Serial.begin(115200); WiFi.begin(ssid, password); Serial.println("Connecting to WiFi..."); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\\nWiFi connected!"); Serial.print("IP address: "); Serial.println(WiFi.localIP()); } void loop() { if (WiFi.status() != WL_CONNECTED) { Serial.println("WiFi connection lost. Reconnecting..."); WiFi.reconnect(); } delay(5000); }`; } function generateWebServerCode() { const ssid = document.getElementById('wifi-ssid').value || 'YOUR_SSID'; const password = document.getElementById('wifi-password').value || 'YOUR_PASSWORD'; return `// ESP32 Web Server Example #include #include const char* ssid = "${ssid}"; const char* password = "${password}"; WebServer server(80); void handleRoot() { String html = "ESP32 Web Server"; html += "

ESP32 Web Server

"; html += "

Hello from ESP32!

"; html += "

IP Address: " + WiFi.localIP().toString() + "

"; html += "

Uptime: " + String(millis()/1000) + " seconds

"; html += ""; server.send(200, "text/html", html); } void handleAPI() { String json = "{\"status\":\"ok\",\"uptime\":" + String(millis()/1000) + "}"; server.send(200, "application/json", json); } void setup() { Serial.begin(115200); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\\nWiFi connected!"); Serial.println(WiFi.localIP()); server.on("/", handleRoot); server.on("/api", handleAPI); server.begin(); } void loop() { server.handleClient(); }`; } function generateMQTTCode() { const ssid = document.getElementById('wifi-ssid').value || 'YOUR_SSID'; const password = document.getElementById('wifi-password').value || 'YOUR_PASSWORD'; const server = document.getElementById('mqtt-server').value || 'broker.hivemq.com'; const port = document.getElementById('mqtt-port').value || '1883'; return `// ESP32 MQTT Client Example #include #include const char* ssid = "${ssid}"; const char* password = "${password}"; const char* mqtt_server = "${server}"; WiFiClient espClient; PubSubClient client(espClient); void wifiSetup() { WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\\nWiFi connected"); } void mqttCallback(char* topic, byte* payload, unsigned int length) { Serial.print("Message received ["); Serial.print(topic); Serial.print("] "); for (int i = 0; i < length; i++) { Serial.print((char)payload[i]); } Serial.println(); } void mqttReconnect() { while (!client.connected()) { Serial.print("Attempting MQTT connection..."); if (client.connect("ESP32Client")) { Serial.println("connected"); client.subscribe("esp32/command"); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); delay(5000); } } } void setup() { Serial.begin(115200); wifiSetup(); client.setServer(mqtt_server, ${port}); client.setCallback(mqttCallback); } void loop() { if (!client.connected()) { mqttReconnect(); } client.loop(); // Publish sensor data every 10 seconds static unsigned long lastMsg = 0; unsigned long now = millis(); if (now - lastMsg > 10000) { lastMsg = now; String msg = "Hello from ESP32 at " + String(now); client.publish("esp32/status", msg.c_str()); } }`; } function generateSensorCode() { const sensorType = document.getElementById('sensor-type').value || 'dht11'; const sensorPin = document.getElementById('sensor-pin').value || '4'; let sensorInclude = ''; let sensorSetup = ''; let sensorRead = ''; if (sensorType === 'dht11' || sensorType === 'dht22') { sensorInclude = `#include \n#define DHT_PIN ${sensorPin}\n#define DHT_TYPE ${sensorType.toUpperCase()}\nDHT dht(DHT_PIN, DHT_TYPE);`; sensorSetup = ' dht.begin();'; sensorRead = ` float humidity = dht.readHumidity(); float temperature = dht.readTemperature(); if (isnan(humidity) || isnan(temperature)) { Serial.println("Failed to read from DHT sensor!"); return; } Serial.print("Humidity: "); Serial.print(humidity); Serial.print("% Temperature: "); Serial.print(temperature); Serial.println("°C");`; } else if (sensorType === 'bmp280') { sensorInclude = `#include \n#include \nAdafruit_BMP280 bmp;`; sensorSetup = ' bmp.begin(0x76);'; sensorRead = ` float temperature = bmp.readTemperature(); float pressure = bmp.readPressure() / 100.0; float altitude = bmp.readAltitude(1013.25); Serial.print("Temperature: "); Serial.print(temperature); Serial.println("°C"); Serial.print("Pressure: "); Serial.print(pressure); Serial.println(" hPa"); Serial.print("Altitude: "); Serial.print(altitude); Serial.println(" m");`; } return `// ESP32 ${sensorType.toUpperCase()} Sensor Example #include ${sensorInclude} void setup() { Serial.begin(115200); ${sensorSetup} Serial.println("${sensorType.toUpperCase()} Sensor Reading Started!"); } void loop() { ${sensorRead} delay(2000); }`; } function generateActuatorCode() { const actuatorType = document.getElementById('actuator-type').value || 'led'; const actuatorPin = document.getElementById('actuator-pin').value || '2'; let actuatorSetup = ''; let actuatorLoop = ''; if (actuatorType === 'led') { actuatorSetup = ` pinMode(${actuatorPin}, OUTPUT);`; actuatorLoop = ` digitalWrite(${actuatorPin}, HIGH); Serial.println("LED ON"); delay(1000); digitalWrite(${actuatorPin}, LOW); Serial.println("LED OFF"); delay(1000);`; } else if (actuatorType === 'servo') { actuatorSetup = ` #include \n Servo myServo;\n myServo.attach(${actuatorPin});`; actuatorLoop = ` for (int pos = 0; pos <= 180; pos += 1) { myServo.write(pos); delay(15); } for (int pos = 180; pos >= 0; pos -= 1) { myServo.write(pos); delay(15); }`; } return `// ESP32 ${actuatorType.charAt(0).toUpperCase() + actuatorType.slice(1)} Control Example #include void setup() { Serial.begin(115200); ${actuatorSetup} Serial.println("${actuatorType.charAt(0).toUpperCase() + actuatorType.slice(1)} Control Started!"); } void loop() { ${actuatorLoop} }`; } function copyGeneratedCode() { const code = document.getElementById('generated-code'); code.select(); document.execCommand('copy'); showToast('Đã copy code vào clipboard!'); } function saveGeneratedCode() { const code = document.getElementById('generated-code').value; const blob = new Blob([code], { type: 'text/plain' }); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = 'esp32_code.ino'; a.click(); window.URL.revokeObjectURL(url); showToast('Đã lưu file code!'); } function simulateCompile() { addToConsole('info', 'Bắt đầu compile code...'); setTimeout(() => { addToConsole('info', 'Đang kiểm tra cú pháp...'); }, 500); setTimeout(() => { addToConsole('success', 'Compile thành công!'); addToConsole('info', 'Sketch uses 234567 bytes (17%) of program storage space'); addToConsole('info', 'Global variables use 23456 bytes (7%) of dynamic memory'); }, 2000); } function simulateUpload() { const comPort = document.getElementById('com-port').value; if (!comPort) { addToConsole('error', 'Vui lòng chọn cổng COM'); return; } addToConsole('info', `Đang upload lên ${comPort}...`); setTimeout(() => { addToConsole('info', 'Đang kết nối với board...'); }, 500); setTimeout(() => { addToConsole('info', 'Đang upload firmware...'); }, 1000); setTimeout(() => { addToConsole('success', 'Upload thành công!'); }, 3000); } function addToConsole(type, message) { const console = document.getElementById('console-output'); const div = document.createElement('div'); div.className = type; div.textContent = message; console.appendChild(div); console.scrollTop = console.scrollHeight; } function showToast(message, type = 'success') { const toast = document.getElementById('toast'); const toastMessage = document.getElementById('toast-message'); if (toast && toastMessage) { toastMessage.textContent = message; toast.className = toast.className.replace(/bg-\w+-500/, `bg-${type === 'error' ? 'red' : 'green'}-500`); toast.classList.remove('translate-x-full'); toast.classList.add('translate-x-0'); setTimeout(() => { toast.classList.remove('translate-x-0'); toast.classList.add('translate-x-full'); }, 3000); } } // Mobile menu functionality const mobileMenuBtn = document.getElementById('mobile-menu-btn'); const mobileMenu = document.getElementById('mobile-menu'); if (mobileMenuBtn && mobileMenu) { mobileMenuBtn.addEventListener('click', function() { mobileMenu.classList.toggle('hidden'); }); }