logo elektroda
logo elektroda
X
logo elektroda

Internet radio and audio file player on ESP32-S3

MAJSTER XXL 162312 1755
ADVERTISEMENT
Treść została przetłumaczona polish » english Zobacz oryginalną wersję tematu
📢 Listen (AI):
  • #1681 21759937
    MAJSTER XXL
    Level 29  
    @robgold
    robgold wrote:
    @MAJSTER XXL can you check?
    Unfortunately it croaks like most others.
  • ADVERTISEMENT
  • #1682 21759955
    simw
    Level 27  
    MAJSTER XXL wrote:
    Unfortunately it rattles like most others.

    What do you mean by it croaking? All the time during playback?
    In my case only the first 2 seconds. After that it sounds ok.
  • #1683 21759969
    DJCheester
    Level 26  
    ejcon wrote:
    Hello,
    I have a version with a real equalizer and eqalizer but there is a problem with the standard AAC and FLAC on these programs the equalizer is turned on it cuts transfers and jerks .
    There is a prefix for automatic operation when ACC and FLAC are on, the equalizer switches itself off or cuts the bands so that it does not jerk, but there is still a problem. Operation of the equalizer via the website .
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>EVO – Radiobrowser + EQ16</title>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <style>
            :root {
                --bg: #111;
                --card: #181818;
                --fg: #eee;
                --accent: #4caf50;
                --accent-soft: #2e7d32;
                --border: #333;
            }
            * { box-sizing: border-box; }
            body {
                margin: 0;
                padding: 0;
                font-family: system-ui, -apple-system, BlinkMacSystemFont,
                             "Segoe UI", sans-serif;
                background: var(--bg);
                color: var(--fg);
            }
            header {
                padding: 10px 16px;
                background: #222;
                border-bottom: 1px solid var(--border);
                display: flex;
                justify-content: space-between;
                align-items: center;
                flex-wrap: wrap;
                gap: 8px;
            }
            header h1 {
                margin: 0;
                font-size: 18px;
            }
            header .host {
                font-size: 12px;
                color: #aaa;
            }
    
            main {
                min-height: calc(100vh - 46px);
                display: flex;
                justify-content: center;
                align-items: flex-start;
                padding: 18px 8px 40px;
            }
            .card {
                width: 100%;
                max-width: 960px;
                background: var(--card);
                border-radius: 12px;
                border: 1px solid var(--border);
                padding: 14px 14px 18px;
                box-shadow: 0 0 12px rgba(0,0,0,0.6);
            }
    
            h2 {
                margin: 0 0 6px;
                font-size: 15px;
            }
    
            /* ANALIZATOR */
    
            .analyzer-header {
                margin-bottom: 4px;
            }
            .analyzer-desc {
                font-size: 11px;
                color: #aaa;
                margin-bottom: 6px;
            }
            #spectrumCanvas {
                width: 100%;
                height: 160px;          /* wy sze okno analizatora */
                background: #000;
                border-radius: 6px;
                border: 1px solid #444;
                display: block;
            }
            .an-freq-labels {
                display: flex;
                justify-content: space-between;
                margin-top: 4px;
                font-size: 10px;
                color: #ccc;
            }
            .an-freq-label {
                min-width: 0;
                text-align: center;
                flex: 1 1 auto;
            }
    
            /* Korektor */
    
            .eq-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                gap: 8px;
                margin-top: 18px;
            }
            .eq-toggle-group {
                display: flex;
                align-items: center;
                gap: 8px;
            }
            .eq-toggle {
                display: flex;
                align-items: center;
                gap: 4px;
                font-size: 12px;
            }
            .eq-toggle input {
                width: 16px;
                height: 16px;
                cursor: pointer;
            }
            .eq-info {
                font-size: 11px;
                color: #aaa;
                margin-top: 4px;
                margin-bottom: 8px;
            }
    
            .eq-sliders {
                display: flex;
                gap: 6px;
                justify-content: space-between;
                overflow-x: auto;
                padding: 6px 2px 4px;
            }
            .eq-band {
                display: flex;
                flex-direction: column;
                align-items: center;
                font-size: 10px;
                min-width: 24px;
            }
            .eq-slider-wrap {
                height: 120px;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            .eq-slider {
                -webkit-appearance: none;
                appearance: none;
                width: 120px;
                height: 6px;
                transform: rotate(-90deg);
                background: #333;
                border-radius: 4px;
                outline: none;
            }
            .eq-slider::-webkit-slider-thumb {
                -webkit-appearance: none;
                appearance: none;
                width: 10px;
                height: 16px;
                border-radius: 3px;
                background: var(--accent);
                cursor: pointer;
            }
            .eq-slider::-moz-range-thumb {
                width: 10px;
                height: 16px;
                border-radius: 3px;
                background: var(--accent);
                cursor: pointer;
                border: none;
            }
            .eq-band-label {
                margin-top: 2px;
            }
            .eq-band-gain {
                margin-top: 2px;
                color: #ccc;
            }
    
            .eq-buttons {
                margin-top: 8px;
                display: flex;
                flex-wrap: wrap;
                gap: 6px;
                font-size: 12px;
            }
            .eq-buttons button {
                padding: 4px 8px;
                border-radius: 6px;
                border: 1px solid var(--border);
                background: #333;
                color: var(--fg);
                cursor: pointer;
            }
            .eq-buttons button:hover {
                background: #444;
            }
    
            /* Volume */
    
            .vol-section {
        margin-top: 14px;
        font-size: 12px;
    }
    
    .vol-label-row {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 4px;
    }
    
    /* KRÓTSZY I WYŚRODKOWANY SUWAK GŁOŚNOŚCI */
    .vol-slider {
        -webkit-appearance: none;
        appearance: none;
        width: 40%;          /* <-- tu ustaw długość, np. 40–60% */
        height: 10px;
        background: #4caf50;
        border-radius: 5px;
        outline: none;
        display: block;
        margin: 0 auto;      /* wyśrodkowanie w rzędzie */
    }
    
    
            /* Radiobrowser */
    
            .search-bar {
                margin-top: 18px;
                display: flex;
                flex-wrap: wrap;
                gap: 6px;
            }
            .search-bar input[type="text"],
            .search-bar input[type="number"],
            .search-bar select {
                padding: 5px 7px;
                border-radius: 6px;
                border: 1px solid var(--border);
                background: #000;
                color: var(--fg);
                font-size: 13px;
            }
            .search-bar button {
                padding: 6px 10px;
                border-radius: 6px;
                border: 1px solid var(--border);
                background: var(--accent-soft);
                color: var(--fg);
                cursor: pointer;
                font-size: 13px;
                white-space: nowrap;
            }
            .search-bar button:hover {
                background: var(--accent);
            }
            .status {
                font-size: 12px;
                color: #aaa;
                margin-top: 6px;
            }
            .results {
                max-height: 260px;
                overflow-y: auto;
                border-top: 1px solid var(--border);
                margin-top: 6px;
                padding-top: 4px;
            }
            .station {
                display: flex;
                gap: 8px;
                align-items: center;
                padding: 6px 4px;
                border-bottom: 1px solid #222;
            }
            .station:nth-child(even) {
                background: #151515;
            }
            .station-meta-box {
                flex: 1;
            }
            .station-name {
                font-size: 13px;
                font-weight: 600;
            }
            .station-meta {
                font-size: 11px;
                color: #aaa;
            }
            .station-url {
                font-size: 11px;
                word-break: break-all;
                color: #ccc;
            }
            .station-actions {
                display: flex;
                flex-direction: column;
                gap: 3px;
            }
            .btn-small {
                font-size: 11px;
                padding: 3px 6px;
                border-radius: 5px;
                border: 1px solid var(--border);
                background: #333;
                color: var(--fg);
                cursor: pointer;
                white-space: nowrap;
            }
            .btn-small:hover { background: #444; }
    
            @media (max-width: 640px) {
                .eq-slider-wrap { height: 100px; }
            }
        </style>
    </head>
    <body>
    <header>
        <h1>EVO – Radiobrowser + EQ16</h1>
        <div class="host">Host: <span id="hostInfo">?</span></div>
    </header>
    
    <main>
        <div class="card">
    
            <!-- ANALIZATOR -->
            <div class="analyzer-header">
                <h2>Analizator widma (wewn trzny EQ16)</h2>
            </div>
            <div class="analyzer-desc">
                Pasek pokazuje aktualne pasma z analizatora w firmware
                (odczyt z <code>/api/eq/spectrum</code>). Po bokach skala w dB.
            </div>
            <canvas id="spectrumCanvas"></canvas>
            <div id="anFreqLabels" class="an-freq-labels"></div>
    
            <!-- EQ -->
            <div class="eq-header">
                <h2>EQ16 – korektor graficzny</h2>
                <div class="eq-toggle-group">
                    <label class="eq-toggle">
                        <input type="checkbox" id="eqEnabled">
                        <span>Korektor</span>
                    </label>
                    <label class="eq-toggle">
                        <input type="checkbox" id="anEnabled">
                        <span>Analizator</span>
                    </label>
                </div>
            </div>
            <div class="eq-info">
                16 pasm 20 Hz – 20 kHz, zakres -18 … +18 dB.
                Zmiany wysy ane przez API do EVO (<code>/api/eq/*</code>).
            </div>
    
            <div id="eqSliders" class="eq-sliders"></div>
    
            <div class="eq-buttons">
                <button type="button" onclick="setAll(0)">P asko (0 dB)</button>
                <button type="button" onclick="setAll(-6)">Wyciszenie pasm (-6 dB)</button>
                <button type="button" onclick="setAll(6)">Podbicie pasm (+6 dB)</button>
                <button type="button" onclick="applyPreset('rock')">Preset ROCK</button>
                <button type="button" onclick="applyPreset('disco')">Preset DISCO</button>
                <button type="button" onclick="applyPreset('pop')">Preset POP</button>
                <button type="button" onclick="reloadFromDevice()">Odczytaj z EVO</button>
                <button type="button" onclick="saveEQToDevice()">Zapisz EQ</button>
            </div>
    
            <!-- Volume -->
            <div class="vol-section">
                <div class="vol-label-row">
                    <span>G o no  </span>
                    <span id="volValue">--</span>
                </div>
                <input type="range"
                       id="volSlider"
                       class="vol-slider"
                       min="1" max="42" step="1" value="1">
            </div>
    
            <!-- Radiobrowser -->
            <h2 style="margin-top:18px;">Radiobrowser – wyszukiwarka stacji</h2>
            <div class="search-bar">
                <input id="q" type="text" placeholder="Nazwa / kraj">
                <input id="tag" type="text" placeholder="Tag (rock, 80s...)">
                <input id="minBr" type="number" placeholder="Min kbps" min="0" step="32">
                <select id="limit">
                    <option value="20">20</option>
                    <option value="50" selected>50</option>
                    <option value="100">100</option>
                </select>
                <button id="btnSearch">Szukaj</button>
            </div>
            <div id="status" class="status">Podaj fraz  i kliknij „Szukaj”.</div>
            <div id="results" class="results"></div>
    
        </div>
    </main>
    
    <script>
        // Host info
        document.getElementById('hostInfo').textContent =
            window.location.hostname || 'localhost';
    
        // ------- ANALIZATOR -------
    
        const specCanvas = document.getElementById('spectrumCanvas');
        const specCtx = specCanvas.getContext('2d');
    
        // zakres wy wietlania skali
        const DISPLAY_MIN_DB = -60;
        const DISPLAY_MAX_DB = 6;
    
        // peak-hold
        let peakLevels = [];
        let peakHold   = [];
        let peakAlpha  = [];
    
        const PEAK_HOLD_FRAMES = 1;
        const PEAK_FALL_STEP   = 0.12;
        const PEAK_FADE_FACTOR = 0.6;
    
        function resizeSpectrumCanvas() {
            const rect = specCanvas.getBoundingClientRect();
            specCanvas.width  = rect.width;
            specCanvas.height = rect.height;
        }
        window.addEventListener('resize', resizeSpectrumCanvas);
        resizeSpectrumCanvas();
    
        function drawSpectrum(levels) {
            const ctx = specCtx;
            const w = specCanvas.width;
            const h = specCanvas.height;
    
            ctx.fillStyle = '#000';
            ctx.fillRect(0, 0, w, h);
    
            const marginLeft = 32;
            const marginRight = 32;
            const paddingTop = 10;
            const paddingBottom = 20;
    
            const innerW = w - marginLeft - marginRight;
            const innerH = h - paddingTop - paddingBottom;
    
            // siatka dB (  cznie dodatnie warto ci)
            const dbTicks = [6, 0, -10, -20, -30, -40, -50, -60];
            ctx.strokeStyle = '#333';
            ctx.lineWidth = 1;
            ctx.font = '9px system-ui, sans-serif';
            ctx.fillStyle = '#888';
            ctx.textBaseline = 'middle';
    
            dbTicks.forEach(db => {
                let norm = (db - DISPLAY_MIN_DB) / (DISPLAY_MAX_DB - DISPLAY_MIN_DB);
                if (norm < 0) norm = 0;
                if (norm > 1) norm = 1;
                const y = paddingTop + innerH - norm * innerH;
    
                ctx.beginPath();
                ctx.moveTo(marginLeft, y);
                ctx.lineTo(w - marginRight, y);
                ctx.stroke();
    
                const label = (db > 0 ? '+' : '') + db + ' dB';
    
                ctx.textAlign = 'right';
                ctx.fillText(label, marginLeft - 4, y);
                ctx.textAlign = 'left';
                ctx.fillText(label, w - marginRight + 4, y);
            });
    
            if (!levels || !levels.length) return;
    
            const n = levels.length;
    
            if (peakLevels.length !== n) {
                peakLevels = new Array(n).fill(0);
                peakHold   = new Array(n).fill(0);
                peakAlpha  = new Array(n).fill(0);
            }
    
            const barGap = 3;
            const totalGap = barGap * (n + 1);
            const barWidth = Math.max(3, (innerW - totalGap) / n);
            const steps = 18;
    
            for (let i = 0; i < n; i++) {
                // z firmware dostajemy 0..1 odpowiadaj ce -60..0 dB
                const vIn = levels[i];
                let dbVal = vIn * 60 - 60; // -60..0
                if (dbVal < -60) dbVal = -60;
                if (dbVal > 0) dbVal = 0;
    
                // przeskalowanie do zakresu -60..+6 (wizualnie)
                let v = (dbVal - DISPLAY_MIN_DB) / (DISPLAY_MAX_DB - DISPLAY_MIN_DB);
                if (v < 0) v = 0;
                if (v > 1) v = 1;
    
                // peak-hold
                if (v >= peakLevels[i]) {
                    peakLevels[i] = v;
                    peakHold[i]   = 0;
                    peakAlpha[i]  = 1.0;
                } else {
                    if (peakHold[i] < PEAK_HOLD_FRAMES) {
                        peakHold[i]++;
                    } else {
                        peakLevels[i] = Math.max(0, peakLevels[i] - PEAK_FALL_STEP);
                        peakAlpha[i]  = Math.max(0, peakAlpha[i] * PEAK_FADE_FACTOR);
                    }
                }
    
                const activeSteps = Math.round(v * steps);
                const x = marginLeft + barGap + i * (barWidth + barGap);
                const stepH = innerH / steps;
    
                // kostki s upka
                for (let s = 0; s < steps; s++) {
                    const y = paddingTop + innerH - (s + 1) * stepH;
    
                    if (s < activeSteps) {
                        const frac = (s + 1) / steps;
                        if (frac < 0.6) {
                            ctx.fillStyle = '#2e7d32';
                        } else if (frac < 0.85) {
                            ctx.fillStyle = '#f9a825';
                        } else {
                            ctx.fillStyle = '#e53935';
                        }
                    } else {
                        ctx.fillStyle = '#111';
                    }
    
                    ctx.fillRect(x, y, barWidth, stepH - 1);
                }
    
                // niebieski peak z wygaszaniem
                const peakV = peakLevels[i];
                const alpha = peakAlpha[i];
    
                if (peakV > 0.001 && alpha > 0.01) {
                    const peakY = paddingTop + innerH - peakV * innerH;
                    ctx.save();
                    ctx.globalAlpha = alpha;
                    ctx.fillStyle = '#2196f3';
                    ctx.fillRect(x, peakY - 2, barWidth, 3);
                    ctx.restore();
                }
            }
    
            ctx.fillStyle = '#1b5e20';
            ctx.fillRect(marginLeft, h - paddingBottom + 4, innerW, 2);
        }
    
        async function spectrumLoop() {
            const POLL_MS = 120;
            while (true) {
                let bands = [];
                try {
                    const resp = await fetch('/api/eq/spectrum');
                    if (resp.ok) {
                        const data = await resp.json();
                        if (data && Array.isArray(data.bands)) {
                            bands = data.bands;
                        }
                    }
                } catch (e) {
                    // brak endpointu – nic nie rysujemy
                }
                drawSpectrum(bands);
                await new Promise(r => setTimeout(r, POLL_MS));
            }
        }
        spectrumLoop();
    
        // ------- EQ16 + cz stotliwo ci -------
    
        const EQ_BANDS = 16;
        const freqs = [];
        (function computeFreqs() {
            const f0 = 20, f1 = 20000;
            for (let i = 0; i < EQ_BANDS; i++) {
                const t = i / (EQ_BANDS - 1);
                const f = f0 * Math.pow(f1 / f0, t);
                freqs.push(f);
            }
        })();
    
        const anFreqLabelsDiv = document.getElementById('anFreqLabels');
        function createAnalyzerLabels() {
            anFreqLabelsDiv.innerHTML = '';
            for (let i = 0; i < EQ_BANDS; i++) {
                const div = document.createElement('div');
                div.className = 'an-freq-label';
                const f = freqs[i];
                div.textContent = f < 1000 ? Math.round(f) + ' Hz'
                                           : (f / 1000).toFixed(1) + ' k';
                anFreqLabelsDiv.appendChild(div);
            }
        }
    
        const eqSlidersBox = document.getElementById('eqSliders');
        const eqEnabledCheck = document.getElementById('eqEnabled');
        const anEnabledCheck = document.getElementById('anEnabled');
        let eqGains = new Array(EQ_BANDS).fill(0);
    
        function createEqUI() {
            eqSlidersBox.innerHTML = '';
            for (let i = 0; i < EQ_BANDS; i++) {
                const bandDiv = document.createElement('div');
                bandDiv.className = 'eq-band';
    
                const wrap = document.createElement('div');
                wrap.className = 'eq-slider-wrap';
    
                const slider = document.createElement('input');
                slider.type = 'range';
                slider.min = '-18';
                slider.max = '18';
                slider.step = '0.5';
                slider.value = '0';
                slider.className = 'eq-slider';
                slider.dataset.band = String(i);
    
                slider.addEventListener('input', onSliderInput);
                slider.addEventListener('change', onSliderChange);
    
                wrap.appendChild(slider);
                bandDiv.appendChild(wrap);
    
                const label = document.createElement('div');
                label.className = 'eq-band-label';
                const f = freqs[i];
                label.textContent = f < 1000 ? Math.round(f) + ' Hz'
                                             : (f/1000).toFixed(1) + ' k';
                bandDiv.appendChild(label);
    
                const gain = document.createElement('div');
                gain.className = 'eq-band-gain';
                gain.id = 'eqGain' + i;
                gain.textContent = '0 dB';
                bandDiv.appendChild(gain);
    
                eqSlidersBox.appendChild(bandDiv);
            }
        }
    
        function onSliderInput(e) {
            const band = parseInt(e.target.dataset.band, 10);
            const val = parseFloat(e.target.value);
            const gainLabel = document.getElementById('eqGain' + band);
            gainLabel.textContent = (val > 0 ? '+' : '') + val.toFixed(1) + ' dB';
        }
    
        function onSliderChange(e) {
            const band = parseInt(e.target.dataset.band, 10);
            const val = parseFloat(e.target.value);
            eqGains[band] = val;
            sendBandToDevice(band, val);
        }
    
        function setAll(v) {
            eqGains = eqGains.map(() => v);
            const sliders = eqSlidersBox.querySelectorAll('.eq-slider');
            sliders.forEach((sl, i) => {
                sl.value = v;
                const lbl = document.getElementById('eqGain' + i);
                lbl.textContent = (v > 0 ? '+' : '') + Number(v).toFixed(1) + ' dB';
            });
            sendAllToDevice();
        }
    
        // Presety ROCK / DISCO / POP
        function applyPreset(name) {
            let curve = new Array(EQ_BANDS).fill(0);
    
            if (name === 'rock') {
                curve = [5, 4, 3, 2, 1, 0, -1, -2, -2, -1, 0, 1, 2, 3, 4, 5];
            } else if (name === 'disco') {
                curve = [6, 5, 4, 3, 2, 1, 0, -1, -1, 0, 1, 2, 3, 4, 5, 6];
            } else if (name === 'pop') {
                curve = [3, 2, 1, 0, 0, 0, -1, -1, -1, 0, 1, 2, 3, 3, 3, 3];
            }
    
            eqGains = curve.slice(0, EQ_BANDS);
    
            const sliders = eqSlidersBox.querySelectorAll('.eq-slider');
            sliders.forEach((sl, i) => {
                const g = eqGains[i] || 0;
                sl.value = g;
                const lbl = document.getElementById('eqGain' + i);
                lbl.textContent = (g > 0 ? '+' : '') + g.toFixed(1) + ' dB';
            });
    
            sendAllToDevice();
            saveEQToDevice();
        }
    
        async function reloadFromDevice() {
            try {
                const resp = await fetch('/api/eq/state');
                if (!resp.ok) throw new Error('HTTP ' + resp.status);
                const data = await resp.json();
    
                eqEnabledCheck.checked = !!data.enabled;
                if ('analyzer' in data) {
                    anEnabledCheck.checked = !!data.analyzer;
                }
    
                if (Array.isArray(data.gains)) {
                    eqGains = data.gains.slice(0, EQ_BANDS).map(g => parseFloat(g) || 0);
                    const sliders = eqSlidersBox.querySelectorAll('.eq-slider');
                    sliders.forEach((sl, i) => {
                        const g = eqGains[i] || 0;
                        sl.value = g;
                        const lbl = document.getElementById('eqGain' + i);
                        lbl.textContent = (g > 0 ? '+' : '') + g.toFixed(1) + ' dB';
                    });
                }
            } catch (e) {
                alert('Nie uda o si  odczyta  EQ z /api/eq/state');
            }
        }
    
        function sendBandToDevice(band, gain) {
            const url = '/api/eq/band?band=' + encodeURIComponent(band) +
                        '&gain=' + encodeURIComponent(gain.toFixed(2));
            fetch(url).catch(() => {});
        }
    
        function sendAllToDevice() {
            const params = new URLSearchParams();
            eqGains.forEach((g, i) => {
                params.append('g' + i, g.toFixed(2));
            });
            fetch('/api/eq/set?' + params.toString()).catch(() => {});
        }
    
        async function saveEQToDevice() {
            try {
                const resp = await fetch('/api/eq/save');
                if (!resp.ok) throw new Error('HTTP ' + resp.status);
                alert('EQ zapisany do pliku.');
            } catch (e) {
                alert('B  d zapisu EQ: ' + e.message);
            }
        }
    
        eqEnabledCheck.addEventListener('change', () => {
            const on = eqEnabledCheck.checked ? 1 : 0;
            fetch('/api/eq/enable?on=' + on).catch(() => {});
        });
    
        anEnabledCheck.addEventListener('change', () => {
            const on = anEnabledCheck.checked ? 1 : 0;
            fetch('/api/eq/analyzer?on=' + on).catch(() => {});
        });
    
        createAnalyzerLabels();
        createEqUI();
        reloadFromDevice();
    
        // ------- VOLUME przez WebSocket -------
    
        const volSlider = document.getElementById('volSlider');
        const volValue  = document.getElementById('volValue');
        let websocket = null;
    
        function setVolumeLabel(v) {
            volValue.textContent = v;
        }
    
        function updateSliderVolume() {
            const sliderValue = volSlider.value;
            setVolumeLabel(sliderValue);
    
            if (websocket && websocket.readyState === WebSocket.OPEN) {
                websocket.send("volume:" + sliderValue);
            } else {
                console.warn("WebSocket niepo  czony");
            }
        }
    
        function connectWebSocket() {
            websocket = new WebSocket('ws://' + window.location.hostname + '/ws');
    
            websocket.onopen = function () {
                console.log("WebSocket po  czony (EQ panel)");
            };
    
            websocket.onclose = function () {
                console.log("WebSocket zamkni ty – reconnect za 3s");
                setTimeout(connectWebSocket, 3000);
            };
    
            websocket.onerror = function (error) {
                console.error("B  d WebSocket:", error);
                websocket.close();
            };
    
            websocket.onmessage = function (event) {
                if (event.data.startsWith("volume:")) {
                    const vol = parseInt(event.data.split(":")[1]);
                    if (!isNaN(vol)) {
                        volSlider.value = vol;
                        setVolumeLabel(vol);
                    }
                }
            };
        }
    
        volSlider.addEventListener('input', () => {
            setVolumeLabel(volSlider.value);
        });
    
        volSlider.addEventListener('change', () => {
            updateSliderVolume();
        });
    
        volSlider.addEventListener("wheel", function (event) {
            event.preventDefault();
            let currentValue = parseInt(volSlider.value);
            const step = parseInt(volSlider.step) || 1;
            const max = parseInt(volSlider.max);
            const min = parseInt(volSlider.min);
    
            if (event.deltaY < 0) {
                volSlider.value = Math.min(currentValue + step, max);
            } else {
                volSlider.value = Math.max(currentValue - step, min);
            }
            updateSliderVolume();
        });
    
        connectWebSocket();
    
        // ------- Radiobrowser -------
    
        const statusEl = document.getElementById('status');
        const resultsEl = document.getElementById('results');
        const API_RB = 'https://de1.api.radio-browser.info/json/stations/search';
    
        document.getElementById('btnSearch').addEventListener('click', searchStations);
        document.getElementById('q').addEventListener('keydown', e => {
            if (e.key === 'Enter') searchStations();
        });
    
        async function searchStations() {
            const q = document.getElementById('q').value.trim();
            const tag = document.getElementById('tag').value.trim();
            const minBr = document.getElementById('minBr').value.trim();
            const limit = document.getElementById('limit').value;
    
            if (!q && !tag) {
                statusEl.textContent = 'Podaj przynajmniej nazw  lub tag.';
                return;
            }
            statusEl.textContent = 'Szukam...';
            resultsEl.innerHTML = '';
    
            const params = new URLSearchParams();
            if (q) params.append('name', q);
            if (tag) params.append('tag', tag);
            params.append('hidebroken', 'true');
            params.append('limit', limit);
            if (minBr) params.append('bitrate_min', minBr);
    
            try {
                const resp = await fetch(API_RB + '?' + params.toString());
                if (!resp.ok) throw new Error('HTTP ' + resp.status);
                const data = await resp.json();
                if (!Array.isArray(data) || data.length === 0) {
                    statusEl.textContent = 'Brak wynik w.';
                    return;
                }
                statusEl.textContent = 'Znaleziono ' + data.length + ' stacji.';
                data.forEach(addStationRow);
            } catch (e) {
                statusEl.textContent = 'B  d pobierania: ' + e.message;
            }
        }
    
        function addStationRow(s) {
            const div = document.createElement('div');
            div.className = 'station';
    
            const metaBox = document.createElement('div');
            metaBox.className = 'station-meta-box';
    
            const name = document.createElement('div');
            name.className = 'station-name';
            name.textContent = s.name || '(bez nazwy)';
    
            const meta = document.createElement('div');
            meta.className = 'station-meta';
            const country = s.country || '';
            const br = s.bitrate ? s.bitrate + ' kbps' : '';
            const codec = s.codec || '';
            meta.textContent = [country, br, codec].filter(Boolean).join(' • ');
    
            const url = document.createElement('div');
            url.className = 'station-url';
            url.textContent = s.url_resolved || s.url || '';
    
            metaBox.appendChild(name);
            metaBox.appendChild(meta);
            metaBox.appendChild(url);
    
            const actions = document.createElement('div');
            actions.className = 'station-actions';
    
            const btnSet = document.createElement('button');
            btnSet.className = 'btn-small';
            btnSet.textContent = 'Ustaw w EVO';
            btnSet.onclick = () => sendToEvo(s);
            actions.appendChild(btnSet);
    
            const btnCopy = document.createElement('button');
            btnCopy.className = 'btn-small';
            btnCopy.textContent = 'Kopiuj URL';
            btnCopy.onclick = () => copyUrl(url.textContent);
            actions.appendChild(btnCopy);
    
            div.appendChild(metaBox);
            div.appendChild(actions);
            resultsEl.appendChild(div);
        }
    
        function copyUrl(url) {
            if (!url) return;
            navigator.clipboard.writeText(url)
                .then(() => statusEl.textContent = 'Skopiowano URL.')
                .catch(() => statusEl.textContent = 'Nie uda o si  skopiowa .');
        }
    
        function sendToEvo(st) {
            const url = st.url_resolved || st.url || '';
            if (!url) return;
            const name = st.name || 'Radio';
            const href = '/update?url=' + encodeURIComponent(url) +
                         '&name=' + encodeURIComponent(name);
            statusEl.textContent = 'Wysy am URL do EVO...';
            fetch(href)
                .then(() => statusEl.textContent = 'URL wys any.')
                .catch(() => statusEl.textContent = 'B  d wysy ania URL.');
        }
    </script>
    </body>
    </html>
    
    Corrected version of the FLAC equaliser Correction of the auto level input analyser . Added automation with display of equalizer status .



    Tell me what to enter here to make it work ?


    Settings panel with 16-band graphic equalizer and audio parameters

    What frequencies do you have and db ??

    Greetings ...

    Added after 7 [minutes]:

    Mi after winning the software via Arduino IDE (as before) the radio started to play very quietly normally I listen to 3-4 volume now on 15-20 I have this volume, in addition it plays only on one channel.

    And when displaying the analyser there is something like this

    LCD module displaying “Radio Złote Przeboje” and the song “Careless Whisper”.

    And immediately after firing up the mute is switched on I have to mute with the remote control.

    Greetings ...
  • #1684 21760009
    ejcon
    Level 14  
    Hello I am sending a slightly corrected version with the proofreader
    Support continues on page
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>EVO – Radiobrowser + EQ16</title>
        <meta name="viewport" content="width=device-width,initial-scale=1">
        <style>
            :root {
                --bg: #111;
                --card: #181818;
                --fg: #eee;
                --accent: #4caf50;
                --accent-soft: #2e7d32;
                --border: #333;
            }
            * { box-sizing: border-box; }
            body {
                margin: 0;
                padding: 0;
                font-family: system-ui, -apple-system, BlinkMacSystemFont,
                             "Segoe UI", sans-serif;
                background: var(--bg);
                color: var(--fg);
            }
            header {
                padding: 10px 16px;
                background: #222;
                border-bottom: 1px solid var(--border);
                display: flex;
                justify-content: space-between;
                align-items: center;
                flex-wrap: wrap;
                gap: 8px;
            }
            header h1 {
                margin: 0;
                font-size: 18px;
            }
            header .host {
                font-size: 12px;
                color: #aaa;
            }
    
            main {
                min-height: calc(100vh - 46px);
                display: flex;
                justify-content: center;
                align-items: flex-start;
                padding: 18px 8px 40px;
            }
            .card {
                width: 100%;
                max-width: 960px;
                background: var(--card);
                border-radius: 12px;
                border: 1px solid var(--border);
                padding: 14px 14px 18px;
                box-shadow: 0 0 12px rgba(0,0,0,0.6);
            }
    
            h2 {
                margin: 0 0 6px;
                font-size: 15px;
            }
    
            /* ANALIZATOR */
    
            .analyzer-header {
                margin-bottom: 4px;
            }
            .analyzer-desc {
                font-size: 11px;
                color: #aaa;
                margin-bottom: 6px;
            }
            #spectrumCanvas {
                width: 100%;
                height: 160px;          /* wy sze okno analizatora */
                background: #000;
                border-radius: 6px;
                border: 1px solid #444;
                display: block;
            }
            .an-freq-labels {
                display: flex;
                justify-content: space-between;
                margin-top: 4px;
                font-size: 10px;
                color: #ccc;
            }
            .an-freq-label {
                min-width: 0;
                text-align: center;
                flex: 1 1 auto;
            }
    
            /* Korektor */
    
            .eq-header {
                display: flex;
                justify-content: space-between;
                align-items: center;
                gap: 8px;
                margin-top: 18px;
            }
            .eq-toggle-group {
                display: flex;
                align-items: center;
                gap: 8px;
            }
            .eq-toggle {
                display: flex;
                align-items: center;
                gap: 4px;
                font-size: 12px;
            }
            .eq-toggle input {
                width: 16px;
                height: 16px;
                cursor: pointer;
            }
            .eq-info {
                font-size: 11px;
                color: #aaa;
                margin-top: 4px;
                margin-bottom: 8px;
            }
    
            .eq-sliders {
                display: flex;
                gap: 6px;
                justify-content: space-between;
                overflow-x: auto;
                padding: 6px 2px 4px;
            }
            .eq-band {
                display: flex;
                flex-direction: column;
                align-items: center;
                font-size: 10px;
                min-width: 24px;
            }
            .eq-slider-wrap {
                height: 120px;
                display: flex;
                align-items: center;
                justify-content: center;
            }
            .eq-slider {
                -webkit-appearance: none;
                appearance: none;
                width: 120px;
                height: 6px;
                transform: rotate(-90deg);
                background: #333;
                border-radius: 4px;
                outline: none;
            }
            .eq-slider::-webkit-slider-thumb {
                -webkit-appearance: none;
                appearance: none;
                width: 10px;
                height: 16px;
                border-radius: 3px;
                background: var(--accent);
                cursor: pointer;
            }
            .eq-slider::-moz-range-thumb {
                width: 10px;
                height: 16px;
                border-radius: 3px;
                background: var(--accent);
                cursor: pointer;
                border: none;
            }
            .eq-band-label {
                margin-top: 2px;
            }
            .eq-band-gain {
                margin-top: 2px;
                color: #ccc;
            }
    
            .eq-buttons {
                margin-top: 8px;
                display: flex;
                flex-wrap: wrap;
                gap: 6px;
                font-size: 12px;
            }
            .eq-buttons button {
                padding: 4px 8px;
                border-radius: 6px;
                border: 1px solid var(--border);
                background: #333;
                color: var(--fg);
                cursor: pointer;
            }
            .eq-buttons button:hover {
                background: #444;
            }
    
            /* Volume */
    
            .vol-section {
        margin-top: 14px;
        font-size: 12px;
    }
    
    .vol-label-row {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 4px;
    }
    
    /* KRÓTSZY I WYŚRODKOWANY SUWAK GŁOŚNOŚCI */
    .vol-slider {
        -webkit-appearance: none;
        appearance: none;
        width: 40%;          /* <-- tu ustaw długość, np. 40–60% */
        height: 10px;
        background: #4caf50;
        border-radius: 5px;
        outline: none;
        display: block;
        margin: 0 auto;      /* wyśrodkowanie w rzędzie */
    }
    
    
            /* Radiobrowser */
    
            .search-bar {
                margin-top: 18px;
                display: flex;
                flex-wrap: wrap;
                gap: 6px;
            }
            .search-bar input[type="text"],
            .search-bar input[type="number"],
            .search-bar select {
                padding: 5px 7px;
                border-radius: 6px;
                border: 1px solid var(--border);
                background: #000;
                color: var(--fg);
                font-size: 13px;
            }
            .search-bar button {
                padding: 6px 10px;
                border-radius: 6px;
                border: 1px solid var(--border);
                background: var(--accent-soft);
                color: var(--fg);
                cursor: pointer;
                font-size: 13px;
                white-space: nowrap;
            }
            .search-bar button:hover {
                background: var(--accent);
            }
            .status {
                font-size: 12px;
                color: #aaa;
                margin-top: 6px;
            }
            .results {
                max-height: 260px;
                overflow-y: auto;
                border-top: 1px solid var(--border);
                margin-top: 6px;
                padding-top: 4px;
            }
            .station {
                display: flex;
                gap: 8px;
                align-items: center;
                padding: 6px 4px;
                border-bottom: 1px solid #222;
            }
            .station:nth-child(even) {
                background: #151515;
            }
            .station-meta-box {
                flex: 1;
            }
            .station-name {
                font-size: 13px;
                font-weight: 600;
            }
            .station-meta {
                font-size: 11px;
                color: #aaa;
            }
            .station-url {
                font-size: 11px;
                word-break: break-all;
                color: #ccc;
            }
            .station-actions {
                display: flex;
                flex-direction: column;
                gap: 3px;
            }
            .btn-small {
                font-size: 11px;
                padding: 3px 6px;
                border-radius: 5px;
                border: 1px solid var(--border);
                background: #333;
                color: var(--fg);
                cursor: pointer;
                white-space: nowrap;
            }
            .btn-small:hover { background: #444; }
    
            @media (max-width: 640px) {
                .eq-slider-wrap { height: 100px; }
            }
        </style>
    </head>
    <body>
    <header>
        <h1>EVO – Radiobrowser + EQ16</h1>
        <div class="host">Host: <span id="hostInfo">?</span></div>
    </header>
    
    <main>
        <div class="card">
    
            <!-- ANALIZATOR -->
            <div class="analyzer-header">
                <h2>Analizator widma (wewn trzny EQ16)</h2>
            </div>
            <div class="analyzer-desc">
                Pasek pokazuje aktualne pasma z analizatora w firmware
                (odczyt z <code>/api/eq/spectrum</code>). Po bokach skala w dB.
            </div>
            <canvas id="spectrumCanvas"></canvas>
            <div id="anFreqLabels" class="an-freq-labels"></div>
    
            <!-- EQ -->
            <div class="eq-header">
                <h2>EQ16 – korektor graficzny</h2>
                <div class="eq-toggle-group">
                    <label class="eq-toggle">
                        <input type="checkbox" id="eqEnabled">
                        <span>Korektor</span>
                    </label>
                    <label class="eq-toggle">
                        <input type="checkbox" id="anEnabled">
                        <span>Analizator</span>
                    </label>
                </div>
            </div>
            <div class="eq-info">
                16 pasm 20 Hz – 20 kHz, zakres -18 … +18 dB.
                Zmiany wysy ane przez API do EVO (<code>/api/eq/*</code>).
            </div>
    
            <div id="eqSliders" class="eq-sliders"></div>
    
            <div class="eq-buttons">
                <button type="button" onclick="setAll(0)">P asko (0 dB)</button>
                <button type="button" onclick="setAll(-6)">Wyciszenie pasm (-6 dB)</button>
                <button type="button" onclick="setAll(6)">Podbicie pasm (+6 dB)</button>
                <button type="button" onclick="applyPreset('rock')">Preset ROCK</button>
                <button type="button" onclick="applyPreset('disco')">Preset DISCO</button>
                <button type="button" onclick="applyPreset('pop')">Preset POP</button>
                <button type="button" onclick="reloadFromDevice()">Odczytaj z EVO</button>
                <button type="button" onclick="saveEQToDevice()">Zapisz EQ</button>
            </div>
    
            <!-- Volume -->
            <div class="vol-section">
                <div class="vol-label-row">
                    <span>G o no  </span>
                    <span id="volValue">--</span>
                </div>
                <input type="range"
                       id="volSlider"
                       class="vol-slider"
                       min="1" max="42" step="1" value="1">
            </div>
    
            <!-- Radiobrowser -->
            <h2 style="margin-top:18px;">Radiobrowser – wyszukiwarka stacji</h2>
            <div class="search-bar">
                <input id="q" type="text" placeholder="Nazwa / kraj">
                <input id="tag" type="text" placeholder="Tag (rock, 80s...)">
                <input id="minBr" type="number" placeholder="Min kbps" min="0" step="32">
                <select id="limit">
                    <option value="20">20</option>
                    <option value="50" selected>50</option>
                    <option value="100">100</option>
                </select>
                <button id="btnSearch">Szukaj</button>
            </div>
            <div id="status" class="status">Podaj fraz  i kliknij „Szukaj”.</div>
            <div id="results" class="results"></div>
    
        </div>
    </main>
    
    <script>
        // Host info
        document.getElementById('hostInfo').textContent =
            window.location.hostname || 'localhost';
    
        // ------- ANALIZATOR -------
    
        const specCanvas = document.getElementById('spectrumCanvas');
        const specCtx = specCanvas.getContext('2d');
    
        // zakres wy wietlania skali
        const DISPLAY_MIN_DB = -60;
        const DISPLAY_MAX_DB = 6;
    
        // peak-hold
        let peakLevels = [];
        let peakHold   = [];
        let peakAlpha  = [];
    
        const PEAK_HOLD_FRAMES = 1;
        const PEAK_FALL_STEP   = 0.12;
        const PEAK_FADE_FACTOR = 0.6;
    
        function resizeSpectrumCanvas() {
            const rect = specCanvas.getBoundingClientRect();
            specCanvas.width  = rect.width;
            specCanvas.height = rect.height;
        }
        window.addEventListener('resize', resizeSpectrumCanvas);
        resizeSpectrumCanvas();
    
        function drawSpectrum(levels) {
            const ctx = specCtx;
            const w = specCanvas.width;
            const h = specCanvas.height;
    
            ctx.fillStyle = '#000';
            ctx.fillRect(0, 0, w, h);
    
            const marginLeft = 32;
            const marginRight = 32;
            const paddingTop = 10;
            const paddingBottom = 20;
    
            const innerW = w - marginLeft - marginRight;
            const innerH = h - paddingTop - paddingBottom;
    
            // siatka dB (  cznie dodatnie warto ci)
            const dbTicks = [6, 0, -10, -20, -30, -40, -50, -60];
            ctx.strokeStyle = '#333';
            ctx.lineWidth = 1;
            ctx.font = '9px system-ui, sans-serif';
            ctx.fillStyle = '#888';
            ctx.textBaseline = 'middle';
    
            dbTicks.forEach(db => {
                let norm = (db - DISPLAY_MIN_DB) / (DISPLAY_MAX_DB - DISPLAY_MIN_DB);
                if (norm < 0) norm = 0;
                if (norm > 1) norm = 1;
                const y = paddingTop + innerH - norm * innerH;
    
                ctx.beginPath();
                ctx.moveTo(marginLeft, y);
                ctx.lineTo(w - marginRight, y);
                ctx.stroke();
    
                const label = (db > 0 ? '+' : '') + db + ' dB';
    
                ctx.textAlign = 'right';
                ctx.fillText(label, marginLeft - 4, y);
                ctx.textAlign = 'left';
                ctx.fillText(label, w - marginRight + 4, y);
            });
    
            if (!levels || !levels.length) return;
    
            const n = levels.length;
    
            if (peakLevels.length !== n) {
                peakLevels = new Array(n).fill(0);
                peakHold   = new Array(n).fill(0);
                peakAlpha  = new Array(n).fill(0);
            }
    
            const barGap = 3;
            const totalGap = barGap * (n + 1);
            const barWidth = Math.max(3, (innerW - totalGap) / n);
            const steps = 18;
    
            for (let i = 0; i < n; i++) {
                // z firmware dostajemy 0..1 odpowiadaj ce -60..0 dB
                const vIn = levels[i];
                let dbVal = vIn * 60 - 60; // -60..0
                if (dbVal < -60) dbVal = -60;
                if (dbVal > 0) dbVal = 0;
    
                // przeskalowanie do zakresu -60..+6 (wizualnie)
                let v = (dbVal - DISPLAY_MIN_DB) / (DISPLAY_MAX_DB - DISPLAY_MIN_DB);
                if (v < 0) v = 0;
                if (v > 1) v = 1;
    
                // peak-hold
                if (v >= peakLevels[i]) {
                    peakLevels[i] = v;
                    peakHold[i]   = 0;
                    peakAlpha[i]  = 1.0;
                } else {
                    if (peakHold[i] < PEAK_HOLD_FRAMES) {
                        peakHold[i]++;
                    } else {
                        peakLevels[i] = Math.max(0, peakLevels[i] - PEAK_FALL_STEP);
                        peakAlpha[i]  = Math.max(0, peakAlpha[i] * PEAK_FADE_FACTOR);
                    }
                }
    
                const activeSteps = Math.round(v * steps);
                const x = marginLeft + barGap + i * (barWidth + barGap);
                const stepH = innerH / steps;
    
                // kostki s upka
                for (let s = 0; s < steps; s++) {
                    const y = paddingTop + innerH - (s + 1) * stepH;
    
                    if (s < activeSteps) {
                        const frac = (s + 1) / steps;
                        if (frac < 0.6) {
                            ctx.fillStyle = '#2e7d32';
                        } else if (frac < 0.85) {
                            ctx.fillStyle = '#f9a825';
                        } else {
                            ctx.fillStyle = '#e53935';
                        }
                    } else {
                        ctx.fillStyle = '#111';
                    }
    
                    ctx.fillRect(x, y, barWidth, stepH - 1);
                }
    
                // niebieski peak z wygaszaniem
                const peakV = peakLevels[i];
                const alpha = peakAlpha[i];
    
                if (peakV > 0.001 && alpha > 0.01) {
                    const peakY = paddingTop + innerH - peakV * innerH;
                    ctx.save();
                    ctx.globalAlpha = alpha;
                    ctx.fillStyle = '#2196f3';
                    ctx.fillRect(x, peakY - 2, barWidth, 3);
                    ctx.restore();
                }
            }
    
            ctx.fillStyle = '#1b5e20';
            ctx.fillRect(marginLeft, h - paddingBottom + 4, innerW, 2);
        }
    
        async function spectrumLoop() {
            const POLL_MS = 120;
            while (true) {
                let bands = [];
                try {
                    const resp = await fetch('/api/eq/spectrum');
                    if (resp.ok) {
                        const data = await resp.json();
                        if (data && Array.isArray(data.bands)) {
                            bands = data.bands;
                        }
                    }
                } catch (e) {
                    // brak endpointu – nic nie rysujemy
                }
                drawSpectrum(bands);
                await new Promise(r => setTimeout(r, POLL_MS));
            }
        }
        spectrumLoop();
    
        // ------- EQ16 + cz stotliwo ci -------
    
        const EQ_BANDS = 16;
        const freqs = [];
        (function computeFreqs() {
            const f0 = 20, f1 = 20000;
            for (let i = 0; i < EQ_BANDS; i++) {
                const t = i / (EQ_BANDS - 1);
                const f = f0 * Math.pow(f1 / f0, t);
                freqs.push(f);
            }
        })();
    
        const anFreqLabelsDiv = document.getElementById('anFreqLabels');
        function createAnalyzerLabels() {
            anFreqLabelsDiv.innerHTML = '';
            for (let i = 0; i < EQ_BANDS; i++) {
                const div = document.createElement('div');
                div.className = 'an-freq-label';
                const f = freqs[i];
                div.textContent = f < 1000 ? Math.round(f) + ' Hz'
                                           : (f / 1000).toFixed(1) + ' k';
                anFreqLabelsDiv.appendChild(div);
            }
        }
    
        const eqSlidersBox = document.getElementById('eqSliders');
        const eqEnabledCheck = document.getElementById('eqEnabled');
        const anEnabledCheck = document.getElementById('anEnabled');
        let eqGains = new Array(EQ_BANDS).fill(0);
    
        function createEqUI() {
            eqSlidersBox.innerHTML = '';
            for (let i = 0; i < EQ_BANDS; i++) {
                const bandDiv = document.createElement('div');
                bandDiv.className = 'eq-band';
    
                const wrap = document.createElement('div');
                wrap.className = 'eq-slider-wrap';
    
                const slider = document.createElement('input');
                slider.type = 'range';
                slider.min = '-18';
                slider.max = '18';
                slider.step = '0.5';
                slider.value = '0';
                slider.className = 'eq-slider';
                slider.dataset.band = String(i);
    
                slider.addEventListener('input', onSliderInput);
                slider.addEventListener('change', onSliderChange);
    
                wrap.appendChild(slider);
                bandDiv.appendChild(wrap);
    
                const label = document.createElement('div');
                label.className = 'eq-band-label';
                const f = freqs[i];
                label.textContent = f < 1000 ? Math.round(f) + ' Hz'
                                             : (f/1000).toFixed(1) + ' k';
                bandDiv.appendChild(label);
    
                const gain = document.createElement('div');
                gain.className = 'eq-band-gain';
                gain.id = 'eqGain' + i;
                gain.textContent = '0 dB';
                bandDiv.appendChild(gain);
    
                eqSlidersBox.appendChild(bandDiv);
            }
        }
    
        function onSliderInput(e) {
            const band = parseInt(e.target.dataset.band, 10);
            const val = parseFloat(e.target.value);
            const gainLabel = document.getElementById('eqGain' + band);
            gainLabel.textContent = (val > 0 ? '+' : '') + val.toFixed(1) + ' dB';
        }
    
        function onSliderChange(e) {
            const band = parseInt(e.target.dataset.band, 10);
            const val = parseFloat(e.target.value);
            eqGains[band] = val;
            sendBandToDevice(band, val);
        }
    
        function setAll(v) {
            eqGains = eqGains.map(() => v);
            const sliders = eqSlidersBox.querySelectorAll('.eq-slider');
            sliders.forEach((sl, i) => {
                sl.value = v;
                const lbl = document.getElementById('eqGain' + i);
                lbl.textContent = (v > 0 ? '+' : '') + Number(v).toFixed(1) + ' dB';
            });
            sendAllToDevice();
        }
    
        // Presety ROCK / DISCO / POP
        function applyPreset(name) {
            let curve = new Array(EQ_BANDS).fill(0);
    
            if (name === 'rock') {
                curve = [5, 4, 3, 2, 1, 0, -1, -2, -2, -1, 0, 1, 2, 3, 4, 5];
            } else if (name === 'disco') {
                curve = [6, 5, 4, 3, 2, 1, 0, -1, -1, 0, 1, 2, 3, 4, 5, 6];
            } else if (name === 'pop') {
                curve = [3, 2, 1, 0, 0, 0, -1, -1, -1, 0, 1, 2, 3, 3, 3, 3];
            }
    
            eqGains = curve.slice(0, EQ_BANDS);
    
            const sliders = eqSlidersBox.querySelectorAll('.eq-slider');
            sliders.forEach((sl, i) => {
                const g = eqGains[i] || 0;
                sl.value = g;
                const lbl = document.getElementById('eqGain' + i);
                lbl.textContent = (g > 0 ? '+' : '') + g.toFixed(1) + ' dB';
            });
    
            sendAllToDevice();
            saveEQToDevice();
        }
    
        async function reloadFromDevice() {
            try {
                const resp = await fetch('/api/eq/state');
                if (!resp.ok) throw new Error('HTTP ' + resp.status);
                const data = await resp.json();
    
                eqEnabledCheck.checked = !!data.enabled;
                if ('analyzer' in data) {
                    anEnabledCheck.checked = !!data.analyzer;
                }
    
                if (Array.isArray(data.gains)) {
                    eqGains = data.gains.slice(0, EQ_BANDS).map(g => parseFloat(g) || 0);
                    const sliders = eqSlidersBox.querySelectorAll('.eq-slider');
                    sliders.forEach((sl, i) => {
                        const g = eqGains[i] || 0;
                        sl.value = g;
                        const lbl = document.getElementById('eqGain' + i);
                        lbl.textContent = (g > 0 ? '+' : '') + g.toFixed(1) + ' dB';
                    });
                }
            } catch (e) {
                alert('Nie uda o si  odczyta  EQ z /api/eq/state');
            }
        }
    
        function sendBandToDevice(band, gain) {
            const url = '/api/eq/band?band=' + encodeURIComponent(band) +
                        '&gain=' + encodeURIComponent(gain.toFixed(2));
            fetch(url).catch(() => {});
        }
    
        function sendAllToDevice() {
            const params = new URLSearchParams();
            eqGains.forEach((g, i) => {
                params.append('g' + i, g.toFixed(2));
            });
            fetch('/api/eq/set?' + params.toString()).catch(() => {});
        }
    
        async function saveEQToDevice() {
            try {
                const resp = await fetch('/api/eq/save');
                if (!resp.ok) throw new Error('HTTP ' + resp.status);
                alert('EQ zapisany do pliku.');
            } catch (e) {
                alert('B  d zapisu EQ: ' + e.message);
            }
        }
    
        eqEnabledCheck.addEventListener('change', () => {
            const on = eqEnabledCheck.checked ? 1 : 0;
            fetch('/api/eq/enable?on=' + on).catch(() => {});
        });
    
        anEnabledCheck.addEventListener('change', () => {
            const on = anEnabledCheck.checked ? 1 : 0;
            fetch('/api/eq/analyzer?on=' + on).catch(() => {});
        });
    
        createAnalyzerLabels();
        createEqUI();
        reloadFromDevice();
    
        // ------- VOLUME przez WebSocket -------
    
        const volSlider = document.getElementById('volSlider');
        const volValue  = document.getElementById('volValue');
        let websocket = null;
    
        function setVolumeLabel(v) {
            volValue.textContent = v;
        }
    
        function updateSliderVolume() {
            const sliderValue = volSlider.value;
            setVolumeLabel(sliderValue);
    
            if (websocket && websocket.readyState === WebSocket.OPEN) {
                websocket.send("volume:" + sliderValue);
            } else {
                console.warn("WebSocket niepo  czony");
            }
        }
    
        function connectWebSocket() {
            websocket = new WebSocket('ws://' + window.location.hostname + '/ws');
    
            websocket.onopen = function () {
                console.log("WebSocket po  czony (EQ panel)");
            };
    
            websocket.onclose = function () {
                console.log("WebSocket zamkni ty – reconnect za 3s");
                setTimeout(connectWebSocket, 3000);
            };
    
            websocket.onerror = function (error) {
                console.error("B  d WebSocket:", error);
                websocket.close();
            };
    
            websocket.onmessage = function (event) {
                if (event.data.startsWith("volume:")) {
                    const vol = parseInt(event.data.split(":")[1]);
                    if (!isNaN(vol)) {
                        volSlider.value = vol;
                        setVolumeLabel(vol);
                    }
                }
            };
        }
    
        volSlider.addEventListener('input', () => {
            setVolumeLabel(volSlider.value);
        });
    
        volSlider.addEventListener('change', () => {
            updateSliderVolume();
        });
    
        volSlider.addEventListener("wheel", function (event) {
            event.preventDefault();
            let currentValue = parseInt(volSlider.value);
            const step = parseInt(volSlider.step) || 1;
            const max = parseInt(volSlider.max);
            const min = parseInt(volSlider.min);
    
            if (event.deltaY < 0) {
                volSlider.value = Math.min(currentValue + step, max);
            } else {
                volSlider.value = Math.max(currentValue - step, min);
            }
            updateSliderVolume();
        });
    
        connectWebSocket();
    
        // ------- Radiobrowser -------
    
        const statusEl = document.getElementById('status');
        const resultsEl = document.getElementById('results');
        const API_RB = 'https://de1.api.radio-browser.info/json/stations/search';
    
        document.getElementById('btnSearch').addEventListener('click', searchStations);
        document.getElementById('q').addEventListener('keydown', e => {
            if (e.key === 'Enter') searchStations();
        });
    
        async function searchStations() {
            const q = document.getElementById('q').value.trim();
            const tag = document.getElementById('tag').value.trim();
            const minBr = document.getElementById('minBr').value.trim();
            const limit = document.getElementById('limit').value;
    
            if (!q && !tag) {
                statusEl.textContent = 'Podaj przynajmniej nazw  lub tag.';
                return;
            }
            statusEl.textContent = 'Szukam...';
            resultsEl.innerHTML = '';
    
            const params = new URLSearchParams();
            if (q) params.append('name', q);
            if (tag) params.append('tag', tag);
            params.append('hidebroken', 'true');
            params.append('limit', limit);
            if (minBr) params.append('bitrate_min', minBr);
    
            try {
                const resp = await fetch(API_RB + '?' + params.toString());
                if (!resp.ok) throw new Error('HTTP ' + resp.status);
                const data = await resp.json();
                if (!Array.isArray(data) || data.length === 0) {
                    statusEl.textContent = 'Brak wynik w.';
                    return;
                }
                statusEl.textContent = 'Znaleziono ' + data.length + ' stacji.';
                data.forEach(addStationRow);
            } catch (e) {
                statusEl.textContent = 'B  d pobierania: ' + e.message;
            }
        }
    
        function addStationRow(s) {
            const div = document.createElement('div');
            div.className = 'station';
    
            const metaBox = document.createElement('div');
            metaBox.className = 'station-meta-box';
    
            const name = document.createElement('div');
            name.className = 'station-name';
            name.textContent = s.name || '(bez nazwy)';
    
            const meta = document.createElement('div');
            meta.className = 'station-meta';
            const country = s.country || '';
            const br = s.bitrate ? s.bitrate + ' kbps' : '';
            const codec = s.codec || '';
            meta.textContent = [country, br, codec].filter(Boolean).join(' • ');
    
            const url = document.createElement('div');
            url.className = 'station-url';
            url.textContent = s.url_resolved || s.url || '';
    
            metaBox.appendChild(name);
            metaBox.appendChild(meta);
            metaBox.appendChild(url);
    
            const actions = document.createElement('div');
            actions.className = 'station-actions';
    
            const btnSet = document.createElement('button');
            btnSet.className = 'btn-small';
            btnSet.textContent = 'Ustaw w EVO';
            btnSet.onclick = () => sendToEvo(s);
            actions.appendChild(btnSet);
    
            const btnCopy = document.createElement('button');
            btnCopy.className = 'btn-small';
            btnCopy.textContent = 'Kopiuj URL';
            btnCopy.onclick = () => copyUrl(url.textContent);
            actions.appendChild(btnCopy);
    
            div.appendChild(metaBox);
            div.appendChild(actions);
            resultsEl.appendChild(div);
        }
    
        function copyUrl(url) {
            if (!url) return;
            navigator.clipboard.writeText(url)
                .then(() => statusEl.textContent = 'Skopiowano URL.')
                .catch(() => statusEl.textContent = 'Nie uda o si  skopiowa .');
        }
    
        function sendToEvo(st) {
            const url = st.url_resolved || st.url || '';
            if (!url) return;
            const name = st.name || 'Radio';
            const href = '/update?url=' + encodeURIComponent(url) +
                         '&name=' + encodeURIComponent(name);
            statusEl.textContent = 'Wysy am URL do EVO...';
            fetch(href)
                .then(() => statusEl.textContent = 'URL wys any.')
                .catch(() => statusEl.textContent = 'B  d wysy ania URL.');
        }
    </script>
    </body>
    </html>
    


    With me it starts normally, then I have to connect the mode on the page EQ and equalizer on the screen program of the page into Page 1 and there edit and paste the contents of the code runScreenshot of Evo Web Radio interface with green menu buttons with Screenshot of EVO Web Radio interface showing SD card file list with actions naciskasz podgląd i msza stronkę jak na skrenie.Web interface showing EQ16 spectrum analyzer, graphic equalizer, and volume slider
  • #1685 21760010
    DJCheester
    Level 26  
    Okay I'll upload it soon but tell me how to display it on the screen can you send me the config.txt file or tell me how to set it up ???

    And why do I have silence on one channel and no sound at all on the other ???

    Greetings ...

    Added after 3 [minutes]:

    How to get to the page that you show ??
    Umnie is only this ??


    Evo Web Radio settings panel with various display and sound configuration options
  • ADVERTISEMENT
  • #1686 21760018
    ejcon
    Level 14  
    >>21760010 Download the latter, as the earlier one was with errors. Check this one with the screens, it should be ok.
  • #1687 21760023
    DJCheester
    Level 26  
    Well, I am loading but tell me how to get this page what screen you have attached the code you gave in the box where to use it??

    Can you provide me with this browser eq html?
  • #1688 21760039
    ejcon
    Level 14  
    You have the code for the equalizer and the equalizer on the code.


    expand copy and paste into page1 then preview and you have two selections one for the equalizer runs the equalizer and the other for the analyzer if it is checked then uncheck and check again and the analyzer runs the display and on the page

    Here is the code Screenshot from a forum showing HTML code fragment with CSS styling
  • #1689 21760045
    DJCheester
    Level 26  
    Well it is ;)

    But tell me because I use low volume because I have a PAM8403 audio amplifier on my PCB so the volume is in the range of 3-6 on 21 and the analyzer shows poorly, ie low are the bars can you somehow raise it?

    And the refresh rate, but I don't think there is much I can do about that...

    Cool feature, I'll play with it...
  • ADVERTISEMENT
  • #1690 21760069
    ejcon
    Level 14  
    DJCheester wrote:
    No is ;)

    But tell me, because I use low volume because I have a PAM8403 audio amplifier on my PCB so the volume is from 3-6 to 21 and the analyzer shows poorly, ie low bars can you somehow raise it?

    And the refresh rate, but I don't think there is much I can do about that...

    I'll play around with it...





    Thought for 17s

    Okay, let's put this together, because in this project you have two places that behave as "PID / AGC" for the analyser:

    AGC of the analyser in AudioRuntimeEQ_Evo.* - this now actually controls the "gain" of the bars.

    the (optional) PID in main.cpp, which used to spin up eq_set_analyzer_sensitivity() - we disabled it so it would stop freaking out.

    I'll show you where to change the gain and what works as "setpoint"/"gain".

    1. AGC of the analyser - the easiest place to adjust "gain"

    In the AudioRuntimeEQ_Evo.h file you have these constants (in your case already slightly changed):

    // Level limit before normalisation (prevents clipping in the analyser)
    static const float ANALYZER_MAX_INPUT_LEVEL = 0.5f;

    // Target max level after AGC (something like setpoint for bars)
    static const float ANALYZER_AGC_TARGET = 0.4f;

    // AGC response rate (smoothing filter)
    static const float ANALYZER_AGC_SPEED = 0.95f;


    These three things act as the 'gain / PID' of the analyser:

    (a) ANALYZER_AGC_TARGET - your main "setpoint"

    This is the target peak level after the AGC.

    Roughly speaking: "as far as the highest bar should reach".

    If you want:

    tighter (higher bars) → increase, e.g. from 0.4f to 0.5f or 0.6f.

    less aggressively (more reserve so it doesn't hit 1.0) → decrease, e.g. 0.35f.

    Example:

    static const float ANALYZER_AGC_TARGET = 0.55f; // bars higher, more "hot"

    b) ANALYZER_MAX_INPUT_LEVEL - "hard limit" on input

    This is the limit before normalisation / AGC:

    static const float ANALYZER_MAX_INPUT_LEVEL = 0.5f;


    cuts the input level to max 0.5 so that the FFT/analyser does not overdrive,

    the lower you give this threshold, the more attenuated the analyser input will be.

    If at low volume you still have a full 1.0 in the bands, you can go down to 0.4f for example:

    static const float ANALYZER_MAX_INPUT_LEVEL = 0.4f; // stronger input attenuation

    c) ANALYZER_AGC_SPEED - response (a bit like "I" in PID)
    static const float ANALYZER_AGC_SPEED = 0.95f;


    In AudioRuntimeEQ_Evo.cpp there is logic more or less in the style of (I'm simplifying):

    // pseudo-code
    float desiredGain = ANALYZER_AGC_TARGET / analyzerPeakLevel;
    analyzerAGCGain = ANALYZER_AGC_SPEED * analyzerAGCGain
    + (1.0f - ANALYZER_AGC_SPEED) * desiredGain;


    closer to 1.0 (e.g. 0.97) → slower, more stable response,

    closer to 0.0 (e.g. 0.8) → fast bar jumping, strong "pumping".

    If the AGC reacts too slowly or too quickly, you can play with this.

    2. "PID" in main.cpp - if you want to spin it up again

    At one point we had something like this in main.cpp:

    // Target max level of one band
    const float eqPidTargetMax = 0.75f;

    float eqPidIntegral = 0.0f;
    float eqPidPrevError = 0.0f;


    And in a function of type updateEQ16CodecOptimization():

    float sum = 0.0f;
    float maxLevel = 0.0f;
    eq_get_analyzer_stats(&sum, &maxLevel);

    // PI controller that spins up the sensitivity of the analyser
    float error = eqPidTargetMax - maxLevel;
    eqPidIntegral += error * 0.05f;
    if (eqPidIntegral > 50.0f) eqPidIntegral = 50.0f;
    if (eqPidIntegral < -50.0f) eqPidIntegral = -50.0f;

    const float Kp = 0.03f;
    const float Ki = 0.005f;
    float adjust = Kp * error + Ki * eqPidIntegral;

    float currentSens = eq_get_analyzer_sensitivity();
    float newSens = currentSens + adjust;
    if (newSens < 0.1f) newSens = 0.1f;
    if (newSens > 2.0f) newSens = 2.0f;
    eq_set_analyzer_sensitivity(newSens;)


    If you want to control this like a PID:

    visual "amplification":

    increase eqPidTargetMax (e.g. from 0.75 to 0.85) → bars average higher,

    decrease eqPidTargetMax → bars lower.

    "strength" of adjustment:

    increase Kp → quicker to respond, but easier to overdrive / oscillate,

    increase Ki → quicker to reach setpoint, but can 'overdrive',

    as it starts to float / pump → decrease both.

    I turned that PID off, because you had dual control (AGC + PID) and everything was going off.
    If you only want to use one place to shoot:

    safest: leave the PID off in main.cpp,

    adjust the "gain" of the analyser only in AudioRuntimeEQ_Evo.h:

    ANALYZER_AGC_TARGET - how high the bars go on average,

    ANALYZER_MAX_INPUT_LEVEL - how much signal you are letting into the input.

    What I would do practically

    If now:

    at low volume the bars are too low → raise ANALYZER_AGC_TARGET by +0.05...+0.1,

    at low volume they are already almost 1.0 → lower ANALYZER_AGC_TARGET and/or ANALYZER_MAX_INPUT_LEVEL.

    Example of test set:

    static const float ANALYZER_MAX_INPUT_LEVEL = 0.45f;
    static const float ANALYZER_AGC_TARGET = 0.50f;
    static constat ANALYZER_AGC_SPEED = 0.95f;
  • #1691 21760082
    khoam
    Level 42  
    @ejcon
    https://docs.espressif.com/projects/esp-idf/e...5.1/esp32s3/api-guides/performance/speed.html

    Quote:
    Avoid using floating point arithmetic float. Even though ESP32-S3 has a single precision hardware floating point unit, floating point calculations are always slower than integer calculations. If possible then use fixed point representations, a different method of integer representation, or convert part of the calculation to be integer only before switching to floating point.
  • #1693 21760266
    efi222
    Level 20  
    In the pictures a bit of data from the radio. The software is completely different, but based on rogbold's libraries
    You can compare how it behaves at different bitrates:

    Screenshot of radio system diagnostics at 128 kb/s bitrate
    Debug screen showing ESP memory, WiFi signal, and 320 kB/s bitrate in three readings
    Screenshot of diagnostic data at 1411 kb/s bitrate, showing memory and buffer usage
  • #1694 21760267
    irysbox
    Level 13  
    >>21759613 he didn't mess up the radio, that screen fits like a pin in the eye :) somewhere on the internet I saw an option that they modified a scale radio so that the arduino reads the capacity of the aggregate and then depending on the capacity it changes the station, then it would make sense because you can make a list and with the appropriate capacity you can set the stations for yourself
  • #1695 21760319
    khoam
    Level 42  
    efi222 wrote:
    In the pictures a little data from the radio.


    Interesting that the second case decreases the size of the free rudder, and in all cases the size of the free space in the PSRAM.
    I assume that the measurement was done inside loop().
  • #1696 21760762
    DJCheester
    Level 26  
    Hello @MAJSTER XXL

    I ran your radio a bit today

    Apart from the fact that it takes me a long time to connect (but I already know why) I checked the RSS Data and weather and it works, also the menu you wrote about works.

    I am looking for the original files for flac core version 3.2.0 can someone share.

    I would like you to answer the questions I have asked before

    DJCheester wrote:
    Master I have a couple of questions, is it possible to generally make the option in the MP3 player to make the file time count from the end ? Or do you just let the timer go as it starts playing and reset when it goes to the next file ?

    Is it possible in the ESP to scroll through a file, i.e. when you hold down, for example, the next song, so that it does not skip to the next one but skips forward every, say, 5 seconds, something like a "seek" ?

    And the last thing that bothers me is that when I don't have a hotspot on (I use my phone and for example forget my phone), the radio is useless. Is it possible to run an MP3 player in this mode when there is no net?

    On Monday I will upload a new version of your code and test.


    Please write what these extra buttons do on the radio and MP3

    #define rcCmdStop 0x000C // STOP button - station list in radio - folder list in player

    #define rcCmdProg 0x0042 // PROG button - menu also works (radio) - does nothing in the player

    #define rcCmdMemory 0x004A // MEMORY button - recording on the radio - does nothing in the player

    #define rcCmdRandom 0x005E // RANDOM button - here in player - random it appears and works - in radio it does nothing

    Because I'm lost as to whether these buttons do anything else and where.

    And then there is the issue of the numerical buttons 0-9 (I don't have them at the moment because I have a poor remote control). Do they work on the radio and in the player in two digits, i.e. you can select e.g. the 37th station or in the case of the 37th player the 37th song?

    In my radio and in the player the mute button does not work either

    #define rcCmdMute 0x0047 // MUTE button - muting

    In the same way, the button does not work in the opener

    #define rcCmdPauseResume 0x001C // Play / Pause button

    These buttons are read by the serial monitor

    Oh, and a more detailed issue of wifi connections

    Sometimes it won't even let me near the router to connect to the wifi - from the serial it says it can't see my network and puts out an AP

    Code: Text
    Log in, to see the code


    Even when the radio near the router is there - possibly a problem with the mains charger too close - I'll work it out I'll power it from a powerbank on a longer cable etc

    On screen only Hello listener

    The next log is when it hangs on connected to wifi

    Code: Text
    Log in, to see the code


    Here you can see that it has connected and there is a message connected to the network

    And the last log where it hangs here

    Close-up of a touchscreen showing “Connecting, please wait…” for RMF FM

    And the log

    Code: Text
    Log in, to see the code


    Here it always stops at the same place

    Oh and a question about how to add your area to this weather link ?

    These are probably the most important points...



    Added after 40 [minutes]:

    I found these files (after all, ardiuno has them zipped in this location) - I had forgotten ;)

    C:AppDataLocalArduino15packages

    Where xxx is the name of your computer.

    Added after 4 [minutes]:

    Well now MAJSTER XXL this radio of yours runs smoothly and ;)

    However, swapping to the original files fixes the issue

    Files like this for core version 3.2.0

    Well everything quickly music and stream starts playing nicely almost immediately ;) Thanks MAJSTER XXL ;)

    Question because it hangs me on Calling wait but music is playing
    something with these weather ans as if you could tell me how to upload your area and why it sometimes waits so long ...

    Greetings ...
  • #1697 21760849
    DJCheester
    Level 26  
    Well, now I'm playing with the second radio - soft by kol Robgold modified by kol ejcon

    You have described it to me scientifically I will try to do something with it to start with I give the values you have given for the test.
    I will upload the revised last soft with the analyser.

    And here I've already noticed yesterday that in order to run the spectrum analyzer as a screen after a power reset, you have to go to the website uncheck the analyzer and equalizer and then check the analyzer then only the analyzer shows up on the radio screen before there is such a thing.

    Is it possible to do something about it, so that it saves somewhere (preferably on SD card or (I use SPIFFS)) the status, because I often use the remote control and I hardly enter the page unless I change some configuration.

    Another thing is also from the remote control when the analyser is playing and we make the volume lower, then for a split second the volume screen enters but quickly disappears even you can not see the bar (volume responds) but as if on top is always the analyser ???

    I know you wrote that you operate through a server, do you plan to integrate this with the remote control and save settings ?


    Radio display showing station Radio Złote Przeboje and song George Michael

    ejcon wrote:
    Thought for 17s
    - what's that because I don't get it ?

    I'm about to play around with these variables and see how it works.

    Greetings ...

    Added after 16 [minutes]:

    ejcon wrote:
    Example of test set:

    static const float ANALYZER_MAX_INPUT_LEVEL = 0.45f;
    static const float ANALYZER_AGC_TARGET = 0.50f;
    static constat ANALYZER_AGC_SPEED = 0.95f;



    Ok let's focus on that, because what you wrote above is, for me, a higher level of driving.

    After uploading with these settings - the radio plays quietly i.e. not as before (vol3-6) the same volume i have (vol15-20)

    Well, but the analyser is higher now and it is possible to raise it still you can give other values in the direction of raising the bars because currently they come at full volume to this value

    Bar graph spectrum analyzer EQ16 with “Analyzer” checkbox selected

    Oh and just by using the remote control to change the view and going back to the analyser it is no longer there is this screen I have attached again marking and ticking makes the analozator come back.

    Unchecking and selecting on the page makes the analyser appear from virtually every view.

    Greetings ...
  • #1698 21760867
    ejcon
    Level 14  
    >>21760849 Unzip the file you have attached download it with Evo Web Radio SD card file manager interface showing file list and actions http:/ip of the radio /eq you already have running you can check if it can't be started from the remote control under Audio

    For me it looks like this at 10 Volume on this set what you have attached EQ16 graphic equalizer interface with spectrum analyzer and frequency sliders What remote you have because maybe you could send me a file to operate if it is not the one from the original because I'm waiting for shipment to be able to run it from the remote and see if it works .
  • #1699 21760875
    DJCheester
    Level 26  
    It's like this, after uploading and resetting the radio the uV meters died and on the eq page there is another screen but also as undesirable as before everything is scattered and the analyser is not there.

    I did not recompile anything but rebooted

    And as for the altitude they are higher so at -20 db

    and the issue of volume, I think I understand you have an extended range there, because on the slider I can do 30 and more volume, where with the remote control I could only do 21 - well, on 30 it plays loud as before on 10 or 12 about

    Greetings ...

    Added after 19 [minutes]:

    I have a different remote from some Mp3, What file this remote.txt rather mine will not work.

    I will compile I will let you know and tell me whether this slider from the volume page can be made to 1-21 to coincide with the remote control, because now I think you have this extended range 0-42 volume something was once mentioned on the occasion of which update

    Best regards ....
  • #1700 21760894
    ejcon
    Level 14  
    >>21760875 Show scans of what you have on the radio and know the site
  • ADVERTISEMENT
  • #1701 21760899
    DJCheester
    Level 26  
    Video on PW I sent you/ old uvmeters came back after a power reset.
    I am now uploading these new files
    Greetings ...

    Added after 42 [minutes]:

    The level of posts is acceptable with these settings


    C++ code snippet defining audio analyzer parameters including sensitivity and AGC limits
    Bar chart from EQ16 spectrum analyzer showing frequency levels in dB

    Maybe I'll pick up a bit more but that's tomorrow

    Thanks a lot ...

    Regards ...
  • #1702 21761106
    Jarosław 1808
    Level 11  
    Hello.
    I have such a problem everything compiles nicely without any errors but at the very end I find that after uploading the device resets all the time when trying to find the network.
    I have already checked on two Arduino 2.3.6 and 1.18.19 the effect is the same.
    Fully functional device won from another computer at work everything works fine only from my home laptop unfortunately such a lime.


    in




    Added after 2 [minutes]:

    *wm:AutoConnect: SUCCESS
    *wm:STA IP Address: 192.168.100.103
    Connected to WiFi network
    mDNS started, address: evoradio.local in browser
    debug SD -> The bank file /bank16.txt already exists.
    Banu file exists on SD card, we read ONLY from the card
    1 BDPST Rock Radio Bank 16 Station 1 https://s2.audiostream.hu/bdpstrock_FLAC
    2 Youventus Radio Bank 16 Station 2 https://s2.audiostream.hu/juventus_FLAC
    3 Roxy Radio Bank 16 Station 3 https://s2.audiostream.hu/roxy_FLAC
    4 Danubius Radio Bank 16 Station 4 https://danubiusradio.hu/live_HiFi.mp3
    5 Progressieve Rock Bank 16 Station 5 https://progressieverock.nl:8443/flac
    6 JVR CaribbeanVarietyMix Bank 16 Station 6 https://online.jamminvibezradio.com/listen/caribbean/live.flac
    7 JVR Classic Oldies Mix Bank 16 Station 7 https://online.jamminvibezradio.com/listen/oldies/live.flac
    8 JVC Reggae Classics Mix Bank 16 Station 8 https://online.jamminvibezradio.com/listen/reggae/live.flac
    9 Easy Radio Bank 16 Station 9 https://live.easyradio.bg/flac
    10 Radio Linn Mix1 Bank 16 Station 10 http://streams.radiomast.io/d0105650-c51c-4842-ae39-f97b329acd2a?token=UU9QJvCG
    11 Radio Linn Mix2 Bank 16 Station 11 http://streams.radiomast.io/4482dfb3-d502-42ef-8184-fc56b0f3ed44?token=UU9QJvCG
    12 Radio Linn Dreams Bank 16 Station 12 http://streams.radiomast.io/1769696b-ae90-417f-b007-584c1b6edef8?token=UU9QJvCG
    13 Radio Linn Gems Bank 16 Station 13 http://streams.radiomast.io/71cb1023-3c47-4c06-847d-5b15a3ddefee?token=UU9QJvCG
    14 Radio Linn Smooth Bank 16 Station 14 http://streams.radiomast.io/1614c7bc-e84e-4f64-8d22-1226d17cd681?token=UU9QJvCG
    15 Naim Radio Bank 16 Station 15 https://mscp3.live-streams.nl:8362/flac.flac
    16 Naim Classical Bank 16 Station 16 https://mscp3.live-streams.nl:8252/class-flac.flac
    17 Naim Jazz Bank 16 Station 17 http://mscp3.live-streams.nl:8340/jazz-flac.flac
    18 Rondo Classic Klasu Pro Bank 16 Station 18 https://iradio.fi/klasupro.flac
    19 Rondo Classic Klasu Bank 16 Station 19 https://iradio.fi/klasu.flac
    20 Dance Wave Bank 16 Station 20 http://dancewave.online/dance.flac.ogg
    21 Dance Wave Retro! Bank 16 Station 21 http://retro.dancewave.online/retrodance.flac.ogg
    22 Radio Paradise Global MixBank 16 Station 22 http://stream.radioparadise.com/global-flac
    23 Radio Paradise Main Mix Bank 16 Station 23 http://stream.radioparadise.com/flac
    24 Radio Paradise Mellow MixBank 16 Station 24 http://stream.radioparadise.com/mellow-flac
    25 Radio Paradise Rock Mix Bank 16 Station 25 http://stream.radioparadise.com/rock-flac
    26 Futura.FM Bank 16 Station 26 https://futura.fm/stream.ogg?ts=1699656027.325
    27 Sweet FM Bank 16 Station 27 http://stream.sweetfm.fr:8000/dab-forever-lms
    28 ForEver La Radio-BordeauxBank 16 Station 28 http://stream.sweetfm.fr:8000/flac-forever-bdx
    29 95bFM Bank 16 Station 29 https://streams.95bfm.com/stream112
    30 C9 Radio Bank 16 Station 30 https://stream.c9.fr/c9radio-flac.ogg.m3u
    31 Calamar Rradio Bank 16 Station 31 http://www.calamarrradio.org:8000/stream
    32 60North Radio Bank 16 Station 32 http://r5.zetcast.net/flac
    33 Club Retro Hits Radio Bank 16 Station 33 http://v4.tustreaming.cl:18156/stream
    34 Dance Classics Radio Bank 16 Station 34 http://vriezenet.nl:11051/dcflac
    35 DHECTAR Radio Bank 16 Station 35 http://manager.dhectar.fr:1300/stream
    36 Djam Radio Bank 16 Station 36 https://stream10.xdevel.com/audio15s976748-2280/stream/icecast.audio
    37 Frequence 3 Bank 16 Station 37 https://frequence3.net-radio.fr/frequence3.flac
    38 Frequence 3 - Loir-et-CheBank 16 Station 38 https://frequence3.net-radio.fr/frequence3-fm41.flac
    39 Frequence 3-Touraine Bank 16 Station 39 https://frequence3.net-radio.fr/frequence3-fm37.flac
    40 Frequence 3 Dance Bank 16 Station 40 https://frequence3.net-radio.fr/frequence3dance.flac
    41 Frequence 3 Gold Bank 16 Station 41 https://frequence3.net-radio.fr/frequence3gold.flac
    42 Frequence 3 Urban Bank 16 Station 42 https://frequence3.net-radio.fr/frequence3urban.flac
    43 Frequence 3 World Bank 16 Station 43 https://frequence3.net-radio.fr/frequence3world.flac
    44 H2O Bank 16 Station 44 http://ice2.ikoula.net-radio.fr/h2oradio.flac
    45 Haarlem Shuffle Radio Bank 16 Station 45 https://stream.tbmp.nl:8000/haarlemshuffle.flac
    46 Incondicionalmente Retro Bank 16 Station 46 http://v4.tustreaming.cl:18128/stream
    47 Insanity Radio Bank 16 Station 47 https://stream.cor.insanityradio.com/insanity.flac
    48 Intense Radio Bank 16 Station 48 http://secure.live-streams.nl/flac.flac
    49 JB Radio-2 Bank 16 Station 49 https://mediacp.jb-radio.net:8001/flac
    50 Joy Hits Bank 16 Station 50 http://joyhits.online/joyhits.flac.ogg
    51 Le Bon Mix Bank 16 Station 51 https://stream10.xdevel.com/audio17s976748-2218/stream/icecast.audio
    52 Le Son Parisien Bank 16 Station 52 http://stream.lesonparisien.com/live.flac
    53 openbroadcast Bank 16 Station 53 http://stream.openbroadcast.ch/16bit.flac
    54 Open Sky Radio Bank 16 Station 54 https://audio.opensky.radio:8082/flac?_res_tag_=audio
    55 Pi ano Bank 16 Station 55 http://stream.p-node.org/piano.flac
    56 Punkrockers Radio Bank 16 Station 56 https://stream.punkrockers-radio.de:8443/prr.flac
    57 Radio Blues Flac Bank 16 Station 57 https://streams.radiomast.io/radioblues-flac
    58 Radio G! Bank 16 Station 58 http://46.105.110.80:8000/fluxradiogHD
    59 Radio Jeunes Rheims Bank 16 Station 59 http://stream.rjrradio.fr:8000/rjr-dab.flac
    60 Radio Kemonia Bank 16 Station 60 http://kemoniastreaming2.net:8060/stream
    61 Radio Random Bank 16 Station 61 http://radiorandom.org:8000/WBJM-FLAC
    62 Radio Remusica Bank 16 Station 62 http://v4.tustreaming.cl:18144/stream
    63 Radio Sputnik Bank 16 Station 63 https://radiosputnik.nl:8443/flac
    64 Zappa Stream Radio Bank 16 Station 64 https://streams.norbert.de/zappa.flac
    65 BIG BANG RADIO HQ Bank 16 Station 65 https://audio-edge-cmc51.fra.h.radiomast.io/2b410ada-8653-40aa-a654-3e8954cf4a4d
    66 Calamar Rradio Bank 16 Station 66 http://www.calamarrradio.org:8000/stream
    67 Danubius Radio Bank 16 Station 67 https://danubiusradio.hu/live_HiFi.mp3
    68 DHECTAR Radio Bank 16 Station 68 http://manager.dhectar.fr:1300/stream
    69 DHECTAR Hits Bank 16 Station 69 http://manager.dhectar.fr:1110/stream
    70 Djam Radio Bank 16 Station 70 https://stream10.xdevel.com/audio15s976748-2280/stream/icecast.audio
    71 ForEver La Radio Bank 16 Station 71 http://stream.sweetfm.fr:8000/flac-forever-arc
    72 Futura.FM Bank 16 Station 72 https://futura.fm/stream.ogg?ts=1699656027.325
    73 TrancePulse Bank 16 Station 73 https://stream.trance.ie/tpmixes
    74 AIDAradio Bank 16 Station 74 https://cdn05.radio.cloud:8128/AIDA-OMNIA-GAI
    75 High Fi Dream Radio Bank 16 Station 75 https://cdn06-us-east.radio.cloud/80ba63862a97bd69c593cc7a2ccaab1c_hq
    76 VFR '80s Thrash Bank 16 Station 76 https://tuneintoradio1.com/listen/vfr_80s/stream.flac
    77 VFR General Thrash Bank 16 Station 77 https://tuneintoradio1.com/listen/violent_forces_radio/stream.flac
    78 TEKnival Radio Bank 16 Station 78 https://listen.teknivalradio.com/listen/teknivalradio/radio.flac
    79 Magic Radio Bank 16 Station 79 https://mp3.magic-radio.net/flac
    80 Smoothjazz.com.pl Bank 16 Station 80 http://bcast.vigormultimedia.com:8888/sjcomplflac
    81 81.Czech Radio CRo D major http://amp.cesnet.cz:8000/cro-d-dur.flac
    82 82.Czech Radio CRo Jazz http://amp.cesnet.cz:8000/cro-jazz.flac
    83.Czech Radio CRo RadioWave http://amp.cesnet.cz:8000/cro-radio-wave.flac
    84 84.Czech Radio CRo Vltava http://amp.cesnet.cz:8000/cro3.flac
    85 85.RadioSEGA https://icecast.radiosega.net/rs-flac.ogg
    We close the bankFile to the value currentLine:100
    debug changeStation -> Read station from PSRAM
    debug changeStation -> Station name: Naim Radio Bank 16 Station 1
    debug changeStation -> Currently selected station: 15
    debug changeStation -> Link to station: https://mscp3.live-streams.nl:8362/flac.flac
    info: ....... inputBufferSize: 655349 bytes
    info: ....... connect to: "mscp3.live-streams.nl" on port 8362 path "/flac.flac"
    info: ....... SSL has been established in 683 ms
    last URL: ... https://mscp3.live-streams.nl:8362/flac.flac
    debug SD -> We save the bank: 16
    debug SD -> We save the stations: 15
    debug SD -> File station_nr.txt already exists.
    debug SD -> Updating station_nr.txt on SD card
    debug SD -> File bank_nr.txt already exists.
    debug SD -> Update bank_nr.txt on SD card.

    assert failed: tcp_alloc /IDF/components/lwip/lwip/src/core/tcp.c:1854 (Required to lock TCPIP core functionality!)


    Backtrace: 0x40376fa5:0x3fcbddc0 0x40380855:0x3fcbdde0 0x403873ea:0x3fcbde00 0x420b4a9f:0x3fcbdf40 0x420b4c05:0x3fcbdf60 0x4208d279:0x3fcbdf80 0x4208c561:0x3fcbdfd0 0x4201b737:0x3fcbdff0 0x42095a27:0x3fcbe070 0x4038158a:0x3fcbe090




    ELF file SHA256: 64b8b226f

    Rebooting...
    ⸮⸮⸮ESP-ROM:esp32s3-20210327
    Build:Mar 27 2021
    rst:0xc (RTC_SW_CPU_RST),boot:0x1a (SPI_FAST_FLASH_BOOT)
    Saved PC:0x4037cf0e
    SPIWP:0xee
    mode:D IO, clock div:1
    load:0x3fce2810,len:0x1078
    load:0x403c8700,len:0x4
    load:0x403c8704,len:0xaf0
    load:0x403cb700,len:0x2e54
    entry 0x403c889c
    E (19) sleep: Incorrect wakeup source (7) to disa
    ------------------ START of Evo Web Radio --------------------
    - -
    --------- ESP32 SN: 6062929E139C, FW Ver.: v3.19.33 ---------
    - Code source: https://github.com/dzikakuna/ESP32_radio_evo3 -
    --------------------------------------------------------------
    debug--PSRAM initialised correctly
    Available PSRAM: 8388608
    Free PSRAM: 8362032
    info: ....... audioI2S Version 3.4.2
    SD card initialised successfully.
    Reading the config.txt file from the card
    Read configuration variable number:0 value:180
    Read configuration variable number:1 value:10
    Read configuration variable number:2 value:5
    Configuration variable number:3 value:0 read
    Configuration variable number:4 value:1 read
    Configuration variable number:5 value:0 read
    Configuration variable number:6 value:1 read
    Configuration variable number:7 value:0 read
    Configuration variable number:8 value:1 read
    Configuration variable number:9 value:50 read
    Configuration variable number:10 value:50 read
    Configuration variable number:11 value:0 read
    Configuration variable number:12 value:0 read
    Configuration variable number:13 value:30 read
    Configuration variable number:14 value:0 read
    Configuration variable number:15 value:1 read
    Configuration variable number:16 value:1 read
    Configuration variable number:17 value:24 read
    Configuration variable number:18 value:6 read
    Configuration variable number:19 value:1 read
    Configuration variable number:20 value:1 read
    Configuration variable number:21 value:0 read
    Read configuration variable number:22 value:
    Closed config file at value currentLine:23
    debug SD -> Station_nr from SD card read: 15
    debug SD -> Read bank_nr from SD card: 16
    debug SD -> File equalizer.txt does not exist.
    debug SD -> File volume.txt does not exist, value Volume default: 10
    Reading ADC keyboard configuration file adckbd.txt from the card
    Error: File does not exist.
    IR - Config, read IR remote.txt configuration file from card
    IR - Config, error, IR remote control configuration file IR remote.txt does not exist.
    IR Config - Remote control configuration file exists, configIrExist: 0
    IR Config - IR remote control configuration file does not exist, default values assigned
    debug1...value bank_nr:16
    debug1...previous_bank_nr value: 16
    debug1...value station_nr:15
    *wm:AutoConnect
    *wm:Connecting to SAVED AP: NETIASPOT-f5Pj 2.4G
    *wm:connectTimeout not set, ESP waitForConnectResult...
  • #1703 21761135
    robgold
    Level 21  
    To start with, change the contents of the bank and station files on the card by setting them to 1. Sometimes there are stations especially in bank 16, FLAC where if you do not have the ESP WiFi patch they can reset the radio. You can also hold down the encoder and press reset until the radio enters so-called recovery mode, which allows you to reset these values to 1.
  • #1704 21761157
    ejcon
    Level 14  
    The newer version of the equalizer and equalizer the bars look better
    here we can adjust the input signal of the analyser whether it is with or without an amplifier C++ code snippet configuring an audio analyzer with Polish comments Look of the bars on the analyser . the equalizer is started from the eq file still only supported by the website upload the file from RAR http://ip of the radio /eq to the sd file
  • #1705 21761212
    Jarosław 1808
    Level 11  
    What files should be on the sd card?
  • #1706 21761221
    robgold
    Level 21  
    >>21761212 In fact, there may be nothing. For commissioning I recommend this option. The radio will create everything for itself. Only Bank Editor and Radiobrowser won't work, because these are html files that should be on the card. And what are your settings in Arduino? PSRAM correctly selected? Module? Have you done a FLASH memory erase ?
    I'm still wondering if WiFi Manager has a problem with the hotspot name: "NETIASPOT-f5Pj 2.4G" and a comma in the name.
  • #1707 21761222
    Jarosław 1808
    Level 11  
    Tools menu in Arduino IDE 1.8.19 showing settings for ESP32 board

    Added after 2 [minutes]:

    Screenshot of file explorer showing SD card KARTA WV (G with HTML, TXT, and image files Added at 2 [minutes]:
    Sd card
  • #1708 21761233
    robgold
    Level 21  
    >>21761222 Was your radio working on this WIFi before ? Because it's quite unusual to put a comma in the name of a WiFi but maybe I'm a DOS man and even spaces I prefer to replace _ ;)
  • #1709 21761236
    Jarosław 1808
    Level 11  
    Yes, everything worked without a problem when I uploaded from my laptop, but I recently got a hankering for bars, because when I had the version without everything worked, but I uploaded from another computer at work

    Added after 1 [minute]:

    I will say more when I upload a compiled version by another program it also works but no one has somehow made the compiled version available to me with posts hi hi.

    Added after 2 [minutes]:

    If you can send the marged file in this version I would appreciate it.

    Added after 17 [minutes]:

    OLED display showing Radio Pogoda and track info with bitrate and codec details

    Added after 51 [seconds]:

    Winnings from your file
  • #1710 21761288
    robgold
    Level 21  
    >>21761236 I don't have this in any code. I only had it in my test version but we are talking all the time about this pseudo-analyser not the full one with equalizer.
  • 📢 Listen (AI):

    Topic summary

    The discussion centers on the development of an internet radio and audio file player based on the ESP32-S3-WROOM-1 module, featuring a custom-designed prototype PCB with OLED display and user controls including rotary encoders and buttons. Key challenges addressed include pin spacing discrepancies in the ESP32-S3 module footprint, integration of Wi-Fi connectivity with dynamic station list updates, and handling of Polish character encoding on the OLED display. The project uses Arduino IDE (version 2.3.2) with ESP-IDF support and requires enabling PSRAM. Audio playback supports MP3, AAC, and FLAC streams, with the ESP32-audioI2S library recommended over the incompatible Audio library. Users reported issues with SPI MISO pin assignment causing bootloader conflicts, resolved by reassigning MISO to pin 35. The project incorporates WiFiManager for network configuration, EEPROM and SD card storage for saving last played station and settings, and includes plans for tone control via an external KA2107 equalizer and a CS8673 amplifier module. Problems with encoder input stability and memory limitations for Bluetooth A2DP on ESP32-S3 were noted. The community suggested alternatives like KaRadio and ESP32-MiniWebRadio projects. Debugging tips include serial terminal logs for HTTP errors and flash memory erasure to resolve boot loops. The project is open-source on GitHub, encouraging forks and modifications. Additional features under development include browser-based updates, directory navigation, and potential audio recording to SD card.
    Summary generated by the language model.
    ADVERTISEMENT