А какой у вас CNC станок?

это разъемчик только под экран и кнопки, колечко там без надобности

А вот и нет - там стоит китайская копия стм, именно она передает весь код в 328 для ГРБЛ и именно по этому проводу.

Там шлейф плоский. Нет колец. И как его натянуть? Ну диаметр большой я найду :slight_smile:

А почему? читать код с файла и в отрезки его на дисплей.
…я так же сделал на игрушечной рисовалке, правда там координаты концов векторов, а не грбл код.
…и файлы это массивы в самом МК.

Можно все, вопрос в возможностях МК. Обычно для таких задач ставят уже нечто помощнее чем 328 мега и его аналоги. Конечно тот клон стм может и неплох, но сомневаюсь что его ресурсов хватит для быстрой отрисовки. И да, придется файл 3 раза прочитать - сначала определить границы и коэффициенты для отрисовки, затем отрисовать и наконец отправить в станок.

Чёй-та!? “Чистый спирт”(с)

Ну у кого как, я про станок lilik, там вижу GD32F103.

ну ОК, там передача по UART, низкочастотная, зачем там колечки
У меня по подобному кабелю 20 МГц бегает и проблемы начинаются только при длине провода более полуметра

Хорошо, а что будет если из-за помех будет передано не G1 x5 y5, а что-то другое? Там ведь нет никакого контроля четности и достаточно один бит изменить и строка обработана не будет, интерпретатор ее скорее всего проигнорирует. Кстати в теории может быть передано и другое число тогда и хорошо если это будет не Z ось.

Кстати не стоит исключать что проблема в самом пульте. Действительно стоит наверное попробовать управление с ПК.

я все таки думаю не правильно вы рисуете)))
рисовать надо одной линией!

<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>Рисование одной линией из координат TXT</title>
    <style>
        * {
            box-sizing: border-box;
            user-select: none; /* небольшая помощь при перетаскивании, но не блокирует выделение текста в файле */
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: #1a2a32;
            margin: 0;
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }

        .container {
            max-width: 1300px;
            width: 100%;
            background: #2c3e2f;
            border-radius: 2rem;
            box-shadow: 0 20px 35px rgba(0,0,0,0.4);
            overflow: hidden;
            backdrop-filter: blur(0px);
            transition: all 0.2s;
        }

        .header {
            background: #1e2a23;
            padding: 1rem 2rem;
            color: #f0f3e8;
            border-bottom: 2px solid #5b8c5a;
        }

        .header h1 {
            margin: 0;
            font-size: 1.6rem;
            font-weight: 500;
            letter-spacing: -0.3px;
        }

        .header p {
            margin: 0.3rem 0 0;
            font-size: 0.85rem;
            opacity: 0.8;
            font-family: monospace;
        }

        .upload-area {
            background: #24342b;
            padding: 1.5rem 2rem;
            display: flex;
            flex-wrap: wrap;
            gap: 1rem;
            align-items: center;
            justify-content: space-between;
            border-bottom: 1px solid #3f5842;
        }

        .file-label {
            background: #5f8b6f;
            padding: 0.6rem 1.4rem;
            border-radius: 40px;
            color: white;
            font-weight: bold;
            cursor: pointer;
            transition: 0.2s;
            font-size: 0.9rem;
            display: inline-flex;
            align-items: center;
            gap: 8px;
            box-shadow: 0 2px 5px rgba(0,0,0,0.2);
        }

        .file-label:hover {
            background: #7aa77a;
            transform: scale(0.97);
        }

        #fileInput {
            display: none;
        }

        .info-panel {
            background: #1f2e26;
            padding: 0.5rem 1.5rem;
            border-radius: 30px;
            font-family: monospace;
            font-size: 0.9rem;
            color: #cfe6cf;
            display: flex;
            gap: 1.5rem;
            flex-wrap: wrap;
        }

        .info-panel span {
            font-weight: bold;
            color: #ffe5a3;
        }

        .canvas-wrapper {
            background: #eef4ea;
            padding: 1.5rem;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 550px;
        }

        canvas {
            background: #ffffff;
            box-shadow: 0 10px 25px rgba(0,0,0,0.2);
            border-radius: 20px;
            max-width: 100%;
            height: auto;
            border: 2px solid #cbdcc2;
            cursor: crosshair;
        }

        .controls {
            background: #1e2a23;
            padding: 1rem 2rem;
            display: flex;
            gap: 1.2rem;
            flex-wrap: wrap;
            justify-content: space-between;
            align-items: center;
            border-top: 1px solid #3f5842;
        }

        button {
            background: #41644a;
            border: none;
            color: white;
            padding: 0.5rem 1.3rem;
            border-radius: 40px;
            font-weight: bold;
            font-size: 0.85rem;
            cursor: pointer;
            transition: 0.15s;
            font-family: inherit;
            box-shadow: 0 1px 3px black;
        }

        button:hover {
            background: #5e8868;
            transform: translateY(-1px);
        }

        button:active {
            transform: translateY(1px);
        }

        .status {
            background: #2a3b30;
            padding: 0.4rem 1rem;
            border-radius: 30px;
            font-size: 0.8rem;
            font-family: monospace;
            color: #c7e9c7;
        }

        .error {
            color: #ffbc9a;
            background: #4a2a2a;
        }

        footer {
            font-size: 0.7rem;
            text-align: center;
            background: #142018;
            color: #8aa889;
            padding: 0.6rem;
        }

        @media (max-width: 700px) {
            .upload-area {
                flex-direction: column;
                align-items: stretch;
            }
            .info-panel {
                justify-content: center;
            }
            .controls {
                flex-direction: column;
                align-items: stretch;
            }
        }
    </style>
</head>
<body>
<div class="container">
    <div class="header">
        <h1>✍️ Рисование одной линией</h1>
        <p>Формат TXT: первая строка заголовок X Y (или сразу числа) / далее пары X Y через пробел или запятую</p>
    </div>
    <div class="upload-area">
        <label class="file-label">
            📂 Выбрать файл .txt
            <input type="file" id="fileInput" accept=".txt, .csv, .text/plain">
        </label>
        <div class="info-panel" id="infoPanel">
            <div>📊 Точек: <span id="pointCount">0</span></div>
            <div>📏 Диапазон X: <span id="xRange">—</span></div>
            <div>📐 Диапазон Y: <span id="yRange">—</span></div>
        </div>
    </div>
    <div class="canvas-wrapper">
        <canvas id="drawCanvas" width="900" height="600" style="width:100%; height:auto; max-width:900px; aspect-ratio:900/600"></canvas>
    </div>
    <div class="controls">
        <div class="status" id="statusMsg">⚡ Ожидание загрузки TXT с координатами</div>
        <div>
            <button id="btnZoomFit">🔍 Подогнать под холст</button>
            <button id="btnResetView">🔄 Сброс вида</button>
        </div>
    </div>
    <footer>Поддерживаются форматы: "X Y" или "X,Y" в каждой строке. Линия рисуется в порядке следования точек.</footer>
</div>

<script>
    (function(){
        // ------ элементы ------
        const canvas = document.getElementById('drawCanvas');
        const ctx = canvas.getContext('2d');
        const fileInput = document.getElementById('fileInput');
        const pointCountSpan = document.getElementById('pointCount');
        const xRangeSpan = document.getElementById('xRange');
        const yRangeSpan = document.getElementById('yRange');
        const statusDiv = document.getElementById('statusMsg');
        const zoomFitBtn = document.getElementById('btnZoomFit');
        const resetViewBtn = document.getElementById('btnResetView');

        // ------ данные ------
        let points = [];            // массив объектов {x, y} (исходные координаты, числа)
        let originalMinX = 0, originalMaxX = 0, originalMinY = 0, originalMaxY = 0;
        
        // параметры отображения (панорамирование и масштаб)
        let offsetX = 0, offsetY = 0;    // смещение в пикселях (в координатах canvas)
        let scale = 1.0;                 // масштаб (1px = 1 единица пользователя? нет, динамический)
        // но для удобства: мы будем хранить transform: смещение (offsetX, offsetY) и масштаб,
        // но масштаб будет относительно изначальной проекции world -> canvas.
        // Чтобы универсально: определяем функцию worldToCanvas(x, y)
        
        let viewInitialized = false;     // флаг что сделали auto-fit при первой загрузке
        
        // ----- вспомогательные функции для парсинга -----
        function parseNumber(str) {
            // удаляем лишние пробелы, заменяем запятую на точку? но у нас целые/дробные с точкой? 
            // пример: "289,390" — но тут запятая разделитель координат, но возможна дробная часть через точку.
            // На всякий случай: если внутри числа есть запятая как десятичный разделитель - преобразуем.
            let cleaned = str.trim();
            // если есть запятая и она не является разделителем пар (т.е. внутри числа), но у нас пары разделены пробелом или запятой.
            // Но по условию пример: X Y потом 0,0 - но это видимо "0,0" как два числа? на самом деле строка "0,0" => разделитель запятая.
            // В задании: "X Y" затем строки вида "289,390" - это координаты через запятую.
            // Лучше: если видим запятую в строке после обрезания, пробуем сплитнуть её.
            if (cleaned.includes(',')) {
                // замена десятичной запятой на точку? если это десятичное, но у нас похоже целые. 
                // Но если это число типа "289,390" целиком? Неправильно. Лучше: в функции разбора строки.
                // оставим стандартно: просто заменяем запятую на точку для чисел с плавающей точкой, если это одно число.
                // но тут число скорее всего без десятичных. Просто замена запятой на точку для безопасности.
                cleaned = cleaned.replace(',', '.');
            }
            const num = parseFloat(cleaned);
            return isNaN(num) ? null : num;
        }
        
        // парсинг строки вида "289 390" или "289,390" или " 289 , 390 "
        function parseCoordLine(line) {
            // удаляем возможные лишние пробелы
            line = line.trim();
            if (line === "") return null;
            // сначала пробуем разделить пробельными символами
            let parts = line.trim().split(/\s+/);
            if (parts.length >= 2) {
                let xRaw = parts[0];
                let yRaw = parts[1];
                // в xRaw или yRaw может быть запятая в конце? например "289," ? мало вероятно.
                let x = parseNumber(xRaw);
                let y = parseNumber(yRaw);
                if (x !== null && y !== null && !isNaN(x) && !isNaN(y)) return {x, y};
            }
            // иначе пробуем разделить запятыми
            if (line.includes(',')) {
                let commaParts = line.split(',').map(s => s.trim());
                if (commaParts.length >= 2) {
                    let x = parseNumber(commaParts[0]);
                    let y = parseNumber(commaParts[1]);
                    if (x !== null && y !== null && !isNaN(x) && !isNaN(y)) return {x, y};
                }
            }
            return null;
        }
        
        // основной парсер файла: из текста извлекает массив точек
        function parseTxtToPoints(text) {
            const lines = text.split(/\r?\n/);
            let coords = [];
            let headerSkipped = false;
            
            for(let i=0; i<lines.length; i++) {
                let line = lines[i].trim();
                if (line === "") continue;
                
                // проверяем, возможно ли это строка заголовка "X Y" или "X,Y" (игнорируем регистр)
                let lowerLine = line.toLowerCase();
                if (!headerSkipped && (lowerLine === "x y" || lowerLine === "x,y" || lowerLine === "x\ty" || 
                    lowerLine.startsWith("x") && lowerLine.includes("y") && lowerLine.length < 10)) {
                    // пропускаем строку заголовка если она содержит только x и y
                    headerSkipped = true;
                    continue;
                }
                
                const point = parseCoordLine(line);
                if (point !== null) {
                    coords.push(point);
                    headerSkipped = true; // после первой успешной точки считаем что заголовка дальше нет
                } else {
                    // возможно это мусорная строка или неверный формат, но не прерываем, просто игнорируем
                    if (coords.length === 0 && i < 3) {
                        // не ругаемся сильно, просто пропускаем первые строки если они не распознаны
                    }
                }
            }
            return coords;
        }
        
        // обновление статистики и границ
        function updateStats(pointsArray) {
            if (!pointsArray.length) {
                pointCountSpan.innerText = '0';
                xRangeSpan.innerText = '—';
                yRangeSpan.innerText = '—';
                return;
            }
            let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
            for(let p of pointsArray) {
                if(p.x < minX) minX = p.x;
                if(p.x > maxX) maxX = p.x;
                if(p.y < minY) minY = p.y;
                if(p.y > maxY) maxY = p.y;
            }
            originalMinX = minX; originalMaxX = maxX; originalMinY = minY; originalMaxY = maxY;
            pointCountSpan.innerText = pointsArray.length;
            xRangeSpan.innerText = `${minX.toFixed(2)} … ${maxX.toFixed(2)}`;
            yRangeSpan.innerText = `${minY.toFixed(2)} … ${maxY.toFixed(2)}`;
        }
        
        // функция пересчета world координат (исходные x,y) в canvas координаты (px)
        function worldToCanvas(xWorld, yWorld) {
            // применяем текущий масштаб scale и смещение offsetX, offsetY
            // стратегия: сначала масштабируем координаты относительно начала координат (0,0) world
            // но для удобства панорамирования обычно: canvasX = (xWorld - viewCenterWorldX)*scale + canvasCenterX + offsetPixels
            // Но мы сделаем проще: offsetX и offsetY это сдвиг в пикселях после масштаба, а начальное отображение задаётся функцией fitToCanvas.
            // Базовая формула: canvasX = xWorld * scale + offsetX;   canvasY = yWorld * scale + offsetY;
            // однако из-за того что ось Y в canvas направлена вниз, а world Y растет вверх? но координаты из файла могут быть любыми.
            // Для рисования линии в точности как задано, просто отображаем Y зеркально? нет, не зеркалим, иначе рисунок перевернётся.
            // Будем использовать прямое отображение: Y canvas = yWorld * scale + offsetY. Чтобы не инвертировать, потому что пользователь ожидает как в файле.
            // но в SVG/обычной графике ось Y направлена вниз, и если файл содержит координаты как на плоскости, то рисунок может быть перевернут.
            // Для линий без привязки к реальному миру - не страшно, пользователь может повернуть мысленно. Но часто дают координаты как есть.
            // добавим опцию? нет, оставим прямое отображение (Y увеличивается вниз). Можно переключить checkbox? но для простоты не будем усложнять.
            // Если потребуется зеркалирование по Y, то изменим: canvasY = canvas.height - (yWorld * scale + offsetY). Но тогда настройки собьются.
            // Чтобы рисунок выглядел "правильно" относительно привычного просмотра, лучше не инвертировать. Оставим как есть.
            // при необходимости подгонки и панорамирования будет комфортно.
            return {
                x: xWorld * scale + offsetX,
                y: yWorld * scale + offsetY
            };
        }
        
        // рисование одной линии по точкам
        function drawLine() {
            if (!canvas || !ctx) return;
            if (!points.length) {
                // очищаем холст с белым фоном и показываем сообщение
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.fillStyle = "#ffffff";
                ctx.fillRect(0, 0, canvas.width, canvas.height);
                ctx.font = "16px 'Segoe UI'";
                ctx.fillStyle = "#7f8c6d";
                ctx.textAlign = "center";
                ctx.fillText("Нет данных. Загрузите TXT файл с координатами.", canvas.width/2, canvas.height/2);
                return;
            }
            
            // очистка
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.fillStyle = "#ffffff";
            ctx.fillRect(0, 0, canvas.width, canvas.height);
            
            // рисуем линию
            ctx.beginPath();
            ctx.lineWidth = 2.2;
            ctx.lineCap = 'round';
            ctx.lineJoin = 'round';
            ctx.strokeStyle = "#2c5a2e";
            ctx.shadowBlur = 0;
            
            let first = true;
            for(let i=0; i<points.length; i++) {
                const {x, y} = points[i];
                const canvasPos = worldToCanvas(x, y);
                if (first) {
                    ctx.moveTo(canvasPos.x, canvasPos.y);
                    first = false;
                } else {
                    ctx.lineTo(canvasPos.x, canvasPos.y);
                }
            }
            ctx.stroke();
            
            // опционально: рисуем начальную и конечную точки для наглядности (небольшие маркеры)
            if(points.length > 0) {
                const start = worldToCanvas(points[0].x, points[0].y);
                const end = worldToCanvas(points[points.length-1].x, points[points.length-1].y);
                ctx.fillStyle = "#c44b3c";
                ctx.beginPath();
                ctx.arc(start.x, start.y, 4, 0, 2*Math.PI);
                ctx.fill();
                ctx.fillStyle = "#2b7a3e";
                ctx.beginPath();
                ctx.arc(end.x, end.y, 4, 0, 2*Math.PI);
                ctx.fill();
                ctx.fillStyle = "#1f2e1c";
                ctx.font = "bold 12px monospace";
                ctx.shadowBlur = 0;
                ctx.fillText("Старт", start.x+5, start.y-3);
                ctx.fillText("Финиш", end.x+5, end.y-3);
            }
        }
        
        // автоматическое масштабирование и центрирование (fit)
        function fitToCanvas(padding = 40) {
            if (!points.length) return;
            // вычисляем мировые границы
            let minX = originalMinX, maxX = originalMaxX, minY = originalMinY, maxY = originalMaxY;
            if (minX === maxX) { minX -= 0.5; maxX += 0.5; }
            if (minY === maxY) { minY -= 0.5; maxY += 0.5; }
            const worldWidth = maxX - minX;
            const worldHeight = maxY - minY;
            const canvasWidth = canvas.width;
            const canvasHeight = canvas.height;
            // вычисляем масштаб так, чтобы влезло с отступами
            const scaleX = (canvasWidth - padding*2) / worldWidth;
            const scaleY = (canvasHeight - padding*2) / worldHeight;
            let newScale = Math.min(scaleX, scaleY);
            if (!isFinite(newScale) || newScale <= 0) newScale = 1;
            scale = newScale;
            // смещение: центрируем bounding box
            const worldCenterX = (minX + maxX) / 2;
            const worldCenterY = (minY + maxY) / 2;
            // координаты центра мира в холсте
            const canvasCenterX = canvasWidth / 2;
            const canvasCenterY = canvasHeight / 2;
            offsetX = canvasCenterX - worldCenterX * scale;
            offsetY = canvasCenterY - worldCenterY * scale;
            
            drawLine();
        }
        
        // сброс вида: просто заново fit
        function resetView() {
            if (!points.length) {
                statusDiv.innerText = "⚠️ Нет точек для сброса вида";
                return;
            }
            fitToCanvas(45);
            statusDiv.innerText = "🔄 Вид сброшен (fit)";
            setTimeout(() => {
                if(statusDiv.innerText.includes("сброшен")) statusDiv.innerText = "✅ Готово";
            }, 1200);
        }
        
        // загрузка и отрисовка файла
        function loadAndDraw(file) {
            const reader = new FileReader();
            reader.onload = function(e) {
                const content = e.target.result;
                const parsedPoints = parseTxtToPoints(content);
                if (!parsedPoints.length) {
                    statusDiv.innerText = "❌ Ошибка: не найдены координаты. Проверьте формат (X Y или X,Y)";
                    statusDiv.classList.add("error");
                    points = [];
                    updateStats([]);
                    drawLine(); // отрисует пустой холст с сообщением
                    return;
                }
                statusDiv.classList.remove("error");
                points = parsedPoints;
                updateStats(points);
                // при новой загрузке делаем автофит
                fitToCanvas(45);
                statusDiv.innerText = `✅ Загружено ${points.length} точек. Линия построена.`;
                viewInitialized = true;
            };
            reader.onerror = function() {
                statusDiv.innerText = "❌ Ошибка чтения файла";
                statusDiv.classList.add("error");
            };
            reader.readAsText(file, "UTF-8");
        }
        
        // обработчик выбора файла
        fileInput.addEventListener('change', (event) => {
            const file = event.target.files[0];
            if (!file) return;
            if (!file.name.toLowerCase().endsWith('.txt') && !file.type.includes('text')) {
                statusDiv.innerText = "⚠️ Рекомендуется .txt файл, но попробуем прочитать...";
            }
            loadAndDraw(file);
        });
        
        // дополнительная возможность перетаскивания? (опционально, но можно)
        // панорамирование мышкой для удобства
        let isPanning = false;
        let panStartX = 0, panStartY = 0;
        let startOffsetX = 0, startOffsetY = 0;
        
        canvas.addEventListener('mousedown', (e) => {
            if (!points.length) return;
            e.preventDefault();
            const rect = canvas.getBoundingClientRect();
            const mouseX = (e.clientX - rect.left) * (canvas.width / rect.width);
            const mouseY = (e.clientY - rect.top) * (canvas.height / rect.height);
            isPanning = true;
            panStartX = mouseX;
            panStartY = mouseY;
            startOffsetX = offsetX;
            startOffsetY = offsetY;
            canvas.style.cursor = 'grabbing';
        });
        
        window.addEventListener('mousemove', (e) => {
            if (!isPanning || !points.length) return;
            const rect = canvas.getBoundingClientRect();
            const mouseX = (e.clientX - rect.left) * (canvas.width / rect.width);
            const mouseY = (e.clientY - rect.top) * (canvas.height / rect.height);
            const dx = mouseX - panStartX;
            const dy = mouseY - panStartY;
            offsetX = startOffsetX + dx;
            offsetY = startOffsetY + dy;
            drawLine();
        });
        
        window.addEventListener('mouseup', () => {
            if (isPanning) {
                isPanning = false;
                canvas.style.cursor = 'crosshair';
            }
        });
        
        // колесико для зума
        canvas.addEventListener('wheel', (e) => {
            if (!points.length) return;
            e.preventDefault();
            const rect = canvas.getBoundingClientRect();
            const mouseXcanvas = (e.clientX - rect.left) * (canvas.width / rect.width);
            const mouseYcanvas = (e.clientY - rect.top) * (canvas.height / rect.height);
            // находим мировые координаты под курсором до зума
            const worldBefore = (mx, my) => {
                // xWorld = (mx - offsetX) / scale
                return { x: (mx - offsetX) / scale, y: (my - offsetY) / scale };
            };
            const before = worldBefore(mouseXcanvas, mouseYcanvas);
            const delta = e.deltaY > 0 ? 0.9 : 1.1;
            let newScale = scale * delta;
            newScale = Math.min(Math.max(newScale, 0.05), 50);
            scale = newScale;
            // после изменения масштаба корректируем смещение, чтобы точка под курсором осталась на месте
            const after = worldBefore(mouseXcanvas, mouseYcanvas);
            offsetX += (before.x - after.x) * scale;
            offsetY += (before.y - after.y) * scale;
            drawLine();
        }, { passive: false });
        
        zoomFitBtn.addEventListener('click', () => {
            if (points.length) fitToCanvas(45);
            else statusDiv.innerText = "Нет данных для подгонки";
        });
        resetViewBtn.addEventListener('click', resetView);
        
        // начальная отрисовка пустого холста
        drawLine();
        
        // если canvas изначально имеет размер, то хорошо, но при ресайзе окна сохраняем масштаб? не трогаем, но перерисовываем
        window.addEventListener('resize', () => {
            if (points.length) drawLine();
        });
        
        // подсказка про drag & zoom
        const styleStatus = document.createElement('style');
        styleStatus.textContent = `canvas { transition: cursor 0.1s; }`;
        document.head.appendChild(styleStatus);
    })();
</script>
</body>
</html>

вот код для рисования

https://ru.files.fm/u/eqrkvz7m39

только так наверное сверло будет ломаться))) по этому рисовать надо на песке!)))

А сколько лет это рисоваться будет?

очень долго, особенно если медленная скорость, в этом и прелесть, наблюдать как робот пыхтит и рисует на песке это….
но итоговый результат можно посмотреть в html)))

https://aliexpress.ru/item/1005010152320218.html вот на этом, переделав ему мозги управления, надо запускать, на это вроде можно смотреть и любоваться

Может выложишь в проектах хтмл файл на узоры? А потом по подобию сделаешь стол с песком виртуальный, с красивой имитацией катания шарика на экране ПК по генерируемым узорам. На песке ни баба, ни машинка не проскочат из-за грубости инструмента. Мышка как вершина возможностей и всякие орнаменты - всё.
Что касается одной линии. Тут надо понимать принципиальную идею Гайвера. Уход от контурного рисования … к многоконтурному закрашиванию :slight_smile:




Вот его подход на рисовалке.

файлы - примеры не скачиваются.

Он залочил скачивание с веб сервера..Решил проблему с другой темы.

А зачем? Как мы тогда посмотрим работу хтмл страницы из поста 249? Это надо идти в конвертор Гайвера со своей картинкой.

спс, у меня такое часто, выкладываю а потом что то происходит и не качаются…

https://dropmefiles.com/GNpMQ

https://wdfiles.ru/2QOpn

получилось ?)))

или да, проще посмотреть через MagicGyver я от туда тырил(заимствовал частично) код)))

Пришёл пиксельный дисплей, совсем не в тему. Наврал продавец про ws2812b :frowning:


А кто сказал что там на уровне обмена чистый UART ?

Как минимум паркинг строк есть , т.к. системные запросы начинаются с ‘$’, вполне возможно что и crc16 прикручен. Для МК не критично у stm32f103 он аппаратный.

В 3D принтере Элегу эта проблема кое-как решена. По крайней мере что-то вроде эскиза принтер на экране показывает. Подозреваю, что картинку добавляет слайсер как комментарии в G-файл, но проверить это руки не доходят.

Если картинка двумерная - можно, а с 3D есть существенные проблемы.

А что там на самом деле?
А то я уже к нему присматривался.

Там, на обороте ряд микросхем, наверное это типа Р6.