/
spaceswimmer
/
perf-source
Обзор
Документация
Войти
/
spaceswimmer
/
perf-source
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
materials/perf_orig.cpp
783 строки
27 KB
spaceswimmer
fixed all the issues
04 мар 2026, 17:39
04 мар 2026, 17:39
cd29e6e
Код
Авторство
О чём код?
#include <WiFi.h> #include <ESPAsyncWebServer.h> #include <DNSServer.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <math.h> #include <SPI.h> // === ИЗМЕНЕНО: Используем пины 5 и 10 вместо 3 и 4 === #define CONTROL_PIN_1 5 // Свободный пин GPIO5 #define CONTROL_PIN_2 10 // Свободный пин GPIO10 #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); const char* ssid = "Sweep"; const char* password = "SonikaSweep"; AsyncWebServer server(80); DNSServer dnsServer; float fMin_exact = 24.66, fMax_exact = 48.8; int fMin_display = 25, fMax_display = 49; float tMax = 10.0; String law = "linear"; bool isRunning = false; unsigned long startTime = 0; float currentProgress = 0.0; uint8_t currentPotValue = 0; // === Настройки SPI для потенциометра TPL0501 === const int POT_CS = 2; // Chip Select для TPL0501 const int POT_SCK = 6; // Serial Clock const int POT_SDI = 7; // Serial Data Input const int POT_SDO = -1; // Serial Data Output (не используется) uint8_t lastWrittenPotValue = 0; uint8_t startPotValue = 0; // Начальное значение при запуске развертки const unsigned char bitmap_128x64[] PROGMEM = { // 'IMG-20240328-WA0004', 128x64px 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xfc,0x07,0xff,0xff,0xff,0xff,0xff,0xff, // ... (остальной массив без изменений) 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, }; struct SchedulePoint { unsigned long timeMs; uint8_t potValue; }; SchedulePoint schedule[600]; int scheduleSize = 0; // === Функция управления пинами === void setControlPins(bool state) { digitalWrite(CONTROL_PIN_1, state ? HIGH : LOW); digitalWrite(CONTROL_PIN_2, state ? HIGH : LOW); Serial.printf("Control pins %d/%d set to: %s\n", CONTROL_PIN_1, CONTROL_PIN_2, state ? "HIGH" : "LOW"); // Дополнительная проверка int actual1 = digitalRead(CONTROL_PIN_1); int actual2 = digitalRead(CONTROL_PIN_2); Serial.printf("Actual state: pin %d=%d, pin %d=%d\n", CONTROL_PIN_1, actual1, CONTROL_PIN_2, actual2); } // === Функция для инициализации потенциометра === void setupDigitalPot() { pinMode(POT_CS, OUTPUT); digitalWrite(POT_CS, HIGH); SPI.begin(POT_SCK, POT_SDO, POT_SDI, POT_CS); SPI.setFrequency(1000000); SPI.setBitOrder(MSBFIRST); SPI.setDataMode(SPI_MODE0); // Устанавливаем начальное значение потенциометра (минимальное) writePotentiometer(1); lastWrittenPotValue = 1; Serial.println("Digital potentiometer initialized"); } // === Функция записи значения в потенциометр === void writePotentiometer(uint8_t value) { digitalWrite(POT_CS, LOW); SPI.transfer(0x00); // Команда записи (адрес регистра 0x00) SPI.transfer(value); // Отправляем значение как есть (1-235) digitalWrite(POT_CS, HIGH); Serial.print("Potentiometer set to: "); Serial.println(value); } void setup() { Serial.begin(115200); Serial.println("\n=== Frequency Sweep Controller ==="); // === Инициализация пинов управления === Serial.println("Initializing control pins..."); pinMode(CONTROL_PIN_1, OUTPUT); pinMode(CONTROL_PIN_2, OUTPUT); // Явная установка в LOW с задержкой для проверки digitalWrite(CONTROL_PIN_1, LOW); digitalWrite(CONTROL_PIN_2, LOW); delay(100); // Проверяем начальное состояние Serial.printf("Initial state: pin %d=%d, pin %d=%d\n", CONTROL_PIN_1, digitalRead(CONTROL_PIN_1), CONTROL_PIN_2, digitalRead(CONTROL_PIN_2)); // === 1. Инициализация OLED === Serial.println("Initializing OLED..."); Wire.begin(8, 9); Wire.setClock(400000); bool oledOK = false; if(display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println("OLED: OK at 0x3C"); oledOK = true; } else if(display.begin(SSD1306_SWITCHCAPVCC, 0x3D)) { Serial.println("OLED: OK at 0x3D"); oledOK = true; } else { Serial.println("OLED: FAILED"); } if(oledOK) { display.clearDisplay(); display.drawBitmap(0, 0, bitmap_128x64, 128, 64, SSD1306_WHITE); display.display(); delay(2000); } if(oledOK) { display.clearDisplay(); display.setTextSize(2); display.setTextColor(SSD1306_WHITE); display.setCursor(0,45); display.print("by Sonica"); display.display(); delay(1500); } // === 2. Инициализация цифрового потенциометра === Serial.println("Initializing digital potentiometer..."); setupDigitalPot(); // === 3. Настройка Wi-Fi === Serial.println("Initializing Wi-Fi AP..."); WiFi.persistent(false); WiFi.mode(WIFI_AP); bool apStarted = WiFi.softAP(ssid, password, 10, 0, 4); if(apStarted) { Serial.println(" ✅ Access point started!"); Serial.print(" SSID: "); Serial.println(ssid); Serial.print(" IP: "); Serial.println(WiFi.softAPIP()); WiFi.setTxPower(WIFI_POWER_8_5dBm); delay(500); } else { Serial.println(" ❌ ERROR: Failed to start AP!"); } // === 4. Настройка DNS === dnsServer.start(53, "*", WiFi.softAPIP()); // === 5. Показываем системный экран === if(oledOK) { displayParametersScreen(); } // === 6. Настройка веб-сервера === setupWebServer(); server.begin(); Serial.println("HTTP server started on port 80"); generateSchedule(); } void loop() { dnsServer.processNextRequest(); if(isRunning && scheduleSize > 0) { unsigned long elapsed = millis() - startTime; unsigned long total = tMax * 1000; if(elapsed >= total) { // Развертка завершена - сбрасываем потенциометр на начальное значение isRunning = false; currentProgress = 100.0; // Выключаем пины при завершении развертки setControlPins(false); // Возвращаем потенциометр к начальному значению writePotentiometer(startPotValue); currentPotValue = startPotValue; lastWrittenPotValue = startPotValue; displayParametersScreen(); Serial.print("Sweep completed! Potentiometer reset to start value: "); Serial.println(startPotValue); } else { currentProgress = (float)elapsed / (float)total * 100.0; int index = findCurrentPoint(elapsed); if(index >= 0 && index < scheduleSize) { uint8_t newPotValue = schedule[index].potValue; if(newPotValue != currentPotValue) { currentPotValue = newPotValue; // Записываем новое значение в потенциометр writePotentiometer(currentPotValue); lastWrittenPotValue = currentPotValue; Serial.print("Time: "); Serial.print(elapsed/1000.0, 1); Serial.print("s, Pot: "); Serial.print(currentPotValue); Serial.print(", Freq: "); Serial.print(potToFrequency(currentPotValue), 1); Serial.println(" Hz"); } } static unsigned long lastDisplayUpdate = 0; if(millis() - lastDisplayUpdate > 100) { lastDisplayUpdate = millis(); displayProgressScreen(); } } } delay(50); } // === Остальные функции БЕЗ изменений === int findCurrentPoint(unsigned long elapsed) { for(int i = 0; i < scheduleSize; i++) { if(schedule[i].timeMs >= elapsed) { return i > 0 ? i-1 : 0; } } return scheduleSize - 1; } float potToFrequency(uint8_t potValue) { return fMin_exact + (float)(potValue - 1) * (fMax_exact - fMin_exact) / 234.0; } uint8_t frequencyToPot(float frequency) { float value = 1 + (frequency - fMin_exact) * 234.0 / (fMax_exact - fMin_exact); if(value < 1) value = 1; if(value > 235) value = 235; return (uint8_t)value; } void displayParametersScreen() { display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0,0); display.println("Sweep Controller"); display.drawLine(0, 9, 127, 9, SSD1306_WHITE); display.setCursor(0,12); display.print("F: "); display.print(fMin_display); display.print("-"); display.print(fMax_display); display.println(" Hz"); display.print("T: "); display.print(tMax, 1); display.println(" s"); display.print("Law: "); display.println(law); display.setCursor(0,45); display.print("Wi-Fi: "); display.print(ssid); display.setCursor(0,55); display.print("GPIO: "); display.print(digitalRead(CONTROL_PIN_1)); display.print("/"); display.print(digitalRead(CONTROL_PIN_2)); display.display(); } void displayProgressScreen() { display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0,0); display.println("Sweep Progress:"); display.drawLine(0, 9, 127, 9, SSD1306_WHITE); int barWidth = map((int)currentProgress, 0, 100, 0, 120); display.drawRect(0, 12, 120, 10, SSD1306_WHITE); display.fillRect(2, 14, barWidth, 6, SSD1306_WHITE); display.setCursor(0, 25); display.print("Progress: "); display.print(currentProgress, 1); display.println("%"); display.print("Time: "); display.print((millis() - startTime) / 1000.0, 1); display.print(" / "); display.print(tMax, 1); display.println(" s"); float currentFreq = potToFrequency(currentPotValue); display.setCursor(0, 45); display.print("Pot: "); display.print(currentPotValue); display.print(" Freq: "); display.print(currentFreq, 1); display.println(" Hz"); display.setCursor(0, 55); display.print("GPIO: "); display.print(digitalRead(CONTROL_PIN_1)); display.print("/"); display.print(digitalRead(CONTROL_PIN_2)); display.display(); } void generateSchedule(uint8_t pot_min, uint8_t pot_max) { scheduleSize = 0; unsigned long totalTimeMs = (unsigned long)(tMax * 1000); unsigned long stepTimeMs = 100; for(unsigned long t = 0; t <= totalTimeMs && scheduleSize < 600; t += stepTimeMs) { uint8_t potValue; if(law == "linear") { float normalizedTime = (float)t / (float)totalTimeMs; potValue = 1 + (uint8_t)(normalizedTime * 234); } else { float normalizedTime = (float)t / (float)totalTimeMs; float I = exp(normalizedTime * log(236.0)) - 1.0; if(I > 235.0) I = 235.0; if(I < 1.0) I = 1.0; potValue = (uint8_t)I; } schedule[scheduleSize].timeMs = t; schedule[scheduleSize].potValue = potValue; scheduleSize++; } Serial.print("Generated "); Serial.print(scheduleSize); Serial.println(" points"); } void setupWebServer() { server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ String html = R"rawliteral( <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Sweep Controller</title> <style> body { font-family: Arial; padding: 20px; max-width: 800px; margin: auto; } .container { background: white; padding: 20px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } h1 { color: #2c3e50; text-align: center; } .info-box { background: #e8f4fc; padding: 15px; border-radius: 8px; margin: 15px 0; border-left: 4px solid #3498db; } .control-group { margin: 20px 0; } label { display: block; margin: 10px 0 5px; font-weight: 600; } input, select { padding: 10px; margin: 5px 0 15px; width: 100%; max-width: 300px; } .button-group { display: flex; flex-wrap: wrap; gap: 10px; margin: 20px 0; } button { padding: 10px 20px; border: none; border-radius: 6px; font-size: 16px; cursor: pointer; flex: 1; min-width: 150px; } #startBtn { background: #2ecc71; color: white; } #stopBtn { background: #e74c3c; color: white; } #stopBtn:disabled { background: #95a5a6; } button:not(#startBtn):not(#stopBtn) { background: #3498db; color: white; } .status { margin: 20px 0; padding: 15px; background: #f8f9fa; border-radius: 8px; font-family: monospace; } #chartContainer { margin: 20px 0; padding: 15px; border: 2px solid #ddd; border-radius: 8px; background: #fafafa; } canvas { width: 100%; height: 300px; background: white; } </style> </head> <body> <div class="container"> <h1>🎛️ Sweep Controller</h1> <div class="info-box"> <strong>Физическое соответствие фиксировано:</strong><br> Потенциометр 1-235 ↔ Частота 24.66-48.8 Гц<br> Вводимые Fmin и Fmax определяют только диапазон отображения </div> <div class="control-group"> <label>Display Fmin (25-49 Hz):</label> <input type="number" id="fmin" value="25" min="25" max="49"> <label>Display Fmax (25-49 Hz):</label> <input type="number" id="fmax" value="49" min="25" max="49"> <label>Time (5-60 seconds):</label> <input type="number" id="tmax" value="10" min="5" max="60"> <label>Sweep Law:</label> <select id="law"> <option value="linear">Linear</option> <option value="exponential">Exponential</option> </select> </div> <div class="button-group"> <button onclick="updateChart()">📈 Update Graph</button> <button onclick="sendToESP()">📤 Send to ESP32</button> <button onclick="startSweep()" id="startBtn">▶️ Start Sweep</button> <button onclick="stopSweep()" id="stopBtn" disabled>⏹️ Stop Sweep</button> </div> <div class="status" id="status"> Status: Ready </div> <div id="chartContainer"> <canvas id="chart"></canvas> </div> <div style="margin-top: 20px; font-size: 12px; color: #666;"> <strong>Physical correspondence (fixed):</strong><br> Potentiometer: 1-235 ↔ Frequency: 24.66-48.8 Hz<br> Display values only affect visualization range </div> </div> <script> function drawSimpleChart(labels, data, minY, maxY) { const canvas = document.getElementById('chart'); const ctx = canvas.getContext('2d'); const width = canvas.width; const height = canvas.height; const padding = 40; ctx.clearRect(0, 0, width, height); const chartWidth = width - 2 * padding; const chartHeight = height - 2 * padding; ctx.strokeStyle = '#eee'; ctx.lineWidth = 1; for(let i = 0; i <= 5; i++) { const y = padding + (i * chartHeight / 5); ctx.beginPath(); ctx.moveTo(padding, y); ctx.lineTo(width - padding, y); ctx.stroke(); ctx.fillStyle = '#666'; ctx.font = '12px Arial'; ctx.textAlign = 'right'; const value = maxY - (maxY - minY) * i / 5; ctx.fillText(value.toFixed(0), padding - 10, y + 4); } for(let i = 0; i <= 5; i++) { const x = padding + (i * chartWidth / 5); ctx.beginPath(); ctx.moveTo(x, padding); ctx.lineTo(x, height - padding); ctx.stroke(); ctx.fillStyle = '#666'; ctx.font = '12px Arial'; ctx.textAlign = 'center'; const timeValue = (labels[labels.length-1] * i / 5).toFixed(1); ctx.fillText(timeValue, x, height - padding + 20); } ctx.strokeStyle = '#000'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(padding, padding); ctx.lineTo(padding, height - padding); ctx.lineTo(width - padding, height - padding); ctx.stroke(); ctx.fillStyle = '#000'; ctx.font = '14px Arial'; ctx.textAlign = 'center'; ctx.fillText('Time (seconds)', width / 2, height - 5); ctx.save(); ctx.translate(15, height / 2); ctx.rotate(-Math.PI / 2); ctx.fillText('Frequency (Hz)', 0, 0); ctx.restore(); if(data.length < 2) return; ctx.strokeStyle = '#3498db'; ctx.lineWidth = 3; ctx.beginPath(); const xScale = chartWidth / (labels.length - 1); const yScale = chartHeight / (maxY - minY); for(let i = 0; i < data.length; i++) { const x = padding + i * xScale; const y = height - padding - (data[i] - minY) * yScale; if(i === 0) { ctx.moveTo(x, y); } else { ctx.lineTo(x, y); } } ctx.stroke(); ctx.fillStyle = 'rgba(52, 152, 219, 0.1)'; ctx.lineTo(padding + (data.length - 1) * xScale, height - padding); ctx.lineTo(padding, height - padding); ctx.closePath(); ctx.fill(); } const fMin_exact_fixed = 24.66; const fMax_exact_fixed = 48.8; function frequencyToPot(frequency) { let value = 1 + (frequency - fMin_exact_fixed) * 234 / (fMax_exact_fixed - fMin_exact_fixed); return Math.max(1, Math.min(235, Math.round(value))); } function potToFrequency(potValue) { return fMin_exact_fixed + (potValue - 1) * (fMax_exact_fixed - fMin_exact_fixed) / 234; } function updateChart() { const fmin_display = parseInt(document.getElementById('fmin').value) || 25; const fmax_display = parseInt(document.getElementById('fmax').value) || 49; const tmax = parseFloat(document.getElementById('tmax').value) || 10; const law = document.getElementById('law').value; const pot_min = frequencyToPot(fmin_display); const pot_max = frequencyToPot(fmax_display); const steps = 50; const labels = []; const data = []; for(let i = 0; i <= steps; i++) { const t = (i / steps) * tmax; labels.push(t); let potValue; if(law === 'linear') { const normalizedTime = i / steps; potValue = pot_min + normalizedTime * (pot_max - pot_min); } else { const normalizedTime = i / steps; const I = pot_min + (pot_max - pot_min) * (Math.exp(normalizedTime * Math.log(236)) - 1) / 235; potValue = Math.max(pot_min, Math.min(pot_max, I)); } const freq = potToFrequency(potValue); data.push(freq); } drawSimpleChart(labels, data, 25, 49); document.getElementById('status').innerHTML = 'Graph updated | Display: ' + fmin_display + '-' + fmax_display + ' Hz | ' + 'Potentiometer: ' + pot_min + '-' + pot_max + ' | ' + 'Actual frequency: ' + potToFrequency(pot_min).toFixed(1) + '-' + potToFrequency(pot_max).toFixed(1) + ' Hz'; } async function sendToESP() { const params = { fmin: document.getElementById('fmin').value, fmax: document.getElementById('fmax').value, tmax: document.getElementById('tmax').value, law: document.getElementById('law').value }; try { const form = new FormData(); form.append('fmin', params.fmin); form.append('fmax', params.fmax); form.append('tmax', params.tmax); form.append('law', params.law); const response = await fetch('/set_params', { method: 'POST', body: form }); const text = await response.text(); document.getElementById('status').innerHTML = '✅ ' + text; } catch(e) { document.getElementById('status').innerHTML = '❌ Error: ' + e; } } async function startSweep() { const response = await fetch('/start', {method: 'POST'}); if(response.ok) { document.getElementById('startBtn').disabled = true; document.getElementById('stopBtn').disabled = false; document.getElementById('status').innerHTML = '▶️ Sweep started!'; checkStatus(); } } async function stopSweep() { const response = await fetch('/stop', {method: 'POST'}); if(response.ok) { document.getElementById('startBtn').disabled = false; document.getElementById('stopBtn').disabled = true; document.getElementById('status').innerHTML = '⏹️ Sweep stopped!'; } } async function checkStatus() { try { const response = await fetch('/status'); const text = await response.text(); if(text.includes("Running")) { setTimeout(checkStatus, 1000); } else { document.getElementById('startBtn').disabled = false; document.getElementById('stopBtn').disabled = true; document.getElementById('status').innerHTML = '✅ Sweep completed!'; } } catch(e) { setTimeout(checkStatus, 2000); } } window.onload = function() { updateChart(); ['fmin', 'fmax', 'tmax', 'law'].forEach(id => { document.getElementById(id).addEventListener('change', updateChart); }); const canvas = document.getElementById('chart'); canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; updateChart(); }; window.addEventListener('resize', function() { const canvas = document.getElementById('chart'); canvas.width = canvas.offsetWidth; canvas.height = canvas.offsetHeight; updateChart(); }); </script> </body> </html> )rawliteral"; request->send(200, "text/html", html); }); server.on("/set_params", HTTP_POST, [](AsyncWebServerRequest *request){ if(request->hasParam("fmin", true)) { fMin_display = request->getParam("fmin", true)->value().toInt(); fMax_display = request->getParam("fmax", true)->value().toInt(); tMax = request->getParam("tmax", true)->value().toFloat(); law = request->getParam("law", true)->value(); fMin_exact = (float)fMin_display; fMax_exact = (float)fMax_display; if(fMin_display < 25) fMin_display = 25; if(fMax_display > 49) fMax_display = 49; if(tMax < 5) tMax = 5; if(tMax > 60) tMax = 60; generateSchedule(); displayParametersScreen(); uint8_t pot_min = frequencyToPot(fMin_exact); uint8_t pot_max = frequencyToPot(fMax_exact); Serial.print("Display F="); Serial.print(fMin_display); Serial.print("-"); Serial.print(fMax_display); Serial.print(" Hz -> Pot="); Serial.print(pot_min); Serial.print("-"); Serial.print(pot_max); Serial.print(" (Actual F="); Serial.print(potToFrequency(pot_min), 1); Serial.print("-"); Serial.print(potToFrequency(pot_max), 1); Serial.print(" Hz), T="); Serial.print(tMax); Serial.print(" s, Law="); Serial.println(law); request->send(200, "text/plain", "OK. Pot range: " + String(pot_min) + "-" + String(pot_max)); } else { request->send(400, "text/plain", "Missing params"); } }); server.on("/start", HTTP_POST, [](AsyncWebServerRequest *request){ if(scheduleSize == 0) { request->send(400, "text/plain", "No schedule generated"); return; } isRunning = true; startTime = millis(); currentProgress = 0.0; currentPotValue = schedule[0].potValue; // Сохраняем начальное значение для сброса после завершения startPotValue = schedule[0].potValue; writePotentiometer(currentPotValue); lastWrittenPotValue = currentPotValue; // Включаем пины при старте развертки setControlPins(true); Serial.print("Sweep started! Initial pot value: "); Serial.println(startPotValue); request->send(200, "text/plain", "Sweep started"); }); server.on("/stop", HTTP_POST, [](AsyncWebServerRequest *request){ isRunning = false; currentProgress = 0.0; // Выключаем пины при остановке setControlPins(false); // При остановке также сбрасываем на начальное значение if (startPotValue > 0) { writePotentiometer(startPotValue); currentPotValue = startPotValue; lastWrittenPotValue = startPotValue; Serial.print("Sweep stopped. Potentiometer reset to start value: "); Serial.println(startPotValue); } displayParametersScreen(); Serial.println("Sweep stopped by user"); request->send(200, "text/plain", "Sweep stopped"); }); server.on("/status", HTTP_GET, [](AsyncWebServerRequest *request){ String status = isRunning ? "Running: " + String(currentProgress, 1) + "%" : "Ready. Points: " + String(scheduleSize); request->send(200, "text/plain", status); }); server.begin(); }