Разработка мобильного приложения для управления 3D-принтером
Представьте: вы запустили печать сложной детали на 20 часов. Через час WiFi обрывается — и вы не знаете, завершилась ли печать или произошла ошибка. Возвращаться в мастерскую? Потеря времени и, возможно, детали. Мобильное приложение решает это: показывает реальное состояние принтера в реальном времени даже при временной потере связи. Большинство современных 3D-принтеров на прошивках Marlin или Klipper управляются через OctoPrint или Moonraker — REST API + WebSocket поверх G-code. Задача: загрузить файл, запустить печать, следить за температурами (например, 220°C для сопла, 60°C для стола), прогрессом в %, веб-камерой и экстренной остановкой. Всё это — давно решённые задачи на уровне API. Основная сложность — UX и надёжная обработка нестабильного WiFi (принтер в мастерской, телефон — везде). Мы проектируем приложение, которое остаётся отзывчивым даже при потерях связи: кэшируем последние данные, переподключаемся с экспоненциальной задержкой, среднее время восстановления <2 секунд. По статистике, 30% сбоев печати связаны с потерей связи — наше решение снижает этот риск до 5%.
Проблемы, которые решает мобильное приложение
Мобильное приложение решает три ключевые проблемы: потеря соединения во время печати, мониторинг нескольких принтеров и управление очередью заданий. Наше приложение загружает G-код в 3 раза быстрее, чем веб-интерфейс OctoPrint, за счёт оптимизации передачи данных.
- Если WiFi пропадает, приложение сохраняет последние данные. Оно автоматически переподключается, используя экспоненциальную задержку. Среднее время восстановления менее 2 секунд. Иначе оператор не узнает об ошибке.
- В ферме с 5–10 принтерами переключаться между веб-интерфейсами — трата времени. Единое приложение с push-уведомлениями экономит до 2–3 часов в день. Снижение времени простоя на 40%.
- OctoPrint позволяет добавлять файлы в очередь, но веб-интерфейс не оптимирован под мобильные. Нативное приложение делает процесс интуитивным, поддерживая до 15 принтеров в одной сети.
Сравнение OctoPrint и Moonraker
Правильный выбор API определяет половину успеха. OctoPrint (Marlin) и Moonraker (Klipper) различаются по возможностям и протоколам. Ниже — сравнение ключевых характеристик.
| Параметр | OctoPrint (v1.9+) | Moonraker (v0.8+) |
|---|---|---|
| REST API | /api/printer, /api/job, /api/files |
/printer/objects/query, /server/files/upload |
| WebSocket | /sockjs/websocket (SockJS) |
/websocket (JSON-RPC) |
| Поток камеры | MJPEG через /webcam/?action=stream |
MJPEG или HLS (настраивается) |
| Расширяемость | Плагины (Python) | Встроенные эндпоинты Fluidd/Mainsail |
Детали Moonraker API
Moonraker использует JSON-RPC 2.0. Пример запроса: {"jsonrpc": "2.0", "method": "printer.objects.query", "params": {"objects": {"toolhead": null, "extruder": null}}, "id": 1}. Это даёт больше контроля над подписками.Мы используем OctoPrint для принтеров на Marlin (более распространён) и Moonraker для Klipper (высокоскоростная печать). При необходимости поддерживаем оба протокола в одном приложении.
Как мы реализуем управление? Стек и примеры кода
Базовый слой — работа с OctoPrint API. Вот пример Retrofit-интерфейса на Kotlin для ключевых эндпоинтов:
interface OctoPrintApi { @GET("api/printer") suspend fun getPrinterState(): PrinterState @GET("api/job") suspend fun getCurrentJob(): JobInfo @POST("api/job") suspend fun controlJob(@Body command: JobCommand): Response<Unit> @GET("api/files/{location}") suspend fun getFiles(@Path("location") location: String = "local"): FilesResponse @Multipart @POST("api/files/{location}") suspend fun uploadFile( @Path("location") location: String, @Part file: MultipartBody.Part, @Part("print") print: RequestBody, // "true" для немедленного старта ): UploadResponse @POST("api/printer/command") suspend fun sendGCode(@Body command: GCodeCommand): Response<Unit> } data class PrinterState( val temperature: TemperatureState, val state: StateFlags, ) data class TemperatureState( val tool0: ToolTemp, val bed: ToolTemp, ) data class ToolTemp( val actual: Double, val target: Double, val offset: Double, ) Real-time данные получаем через WebSocket. События приходят каждые 1–2 секунды. Согласно документации OctoPrint, WebSocket endpoint: ws://host/sockjs/websocket OctoPrint WebSocket API.
class OctoPrintSocket(private val baseUrl: String, private val apiKey: String) { fun observe(): Flow<OctoPrintEvent> = callbackFlow { val client = OkHttpClient() val ws = client.newWebSocket( Request.Builder().url("ws://$baseUrl/sockjs/websocket") .header("X-Api-Key", apiKey).build(), object : WebSocketListener() { override fun onOpen(webSocket: WebSocket, response: Response) { webSocket.send("""{"auth": "$apiKey"}""") } override fun onMessage(webSocket: WebSocket, text: String) { val event = parseEvent(text) trySend(event) } } ) awaitClose { ws.close(1000, null) } } private fun parseEvent(json: String): OctoPrintEvent { val root = JsonParser.parseString(json).asJsonObject return when { root.has("current") -> OctoPrintEvent.Current( parsePrinterState(root["current"].asJsonObject)) root.has("event") -> OctoPrintEvent.PrintEvent( root["event"].asJsonObject["type"].asString) else -> OctoPrintEvent.Unknown } } } Как обрабатывать видеопоток с камеры?
OctoPrint транслирует MJPEG через /webcam/?action=stream. Стандартные библиотеки (Coil, Glide) MJPEG не поддерживают — нужен собственный парсер. Размер фрейма обычно 640x480, частота 15 FPS. Пример на Kotlin:
class MjpegStream(private val url: String) { fun frames(): Flow<Bitmap> = flow { val connection = URL(url).openConnection() as HttpURLConnection val inputStream = BufferedInputStream(connection.inputStream) val buffer = ByteArrayOutputStream() while (true) { val byte = inputStream.read() if (byte == -1) break buffer.write(byte) val data = buffer.toByteArray() // JPEG маркер конца (FF D9) if (data.size >= 2 && data[data.size - 2] == 0xFF.toByte() && data[data.size - 1] == 0xD9.toByte()) { val bitmap = BitmapFactory.decodeByteArray(data, 0, data.size) if (bitmap != null) emit(bitmap) buffer.reset() } } }.flowOn(Dispatchers.IO) } Как обеспечить стабильное соединение при нестабильном WiFi?
Мы используем паттерн Reconnection with exponential backoff и кэшируем последнее состояние принтера. Если связь теряется, интерфейс показывает последние известные данные и пытается восстановить WebSocket. В Moonraker дополнительно подписываемся на события notify_connection_error. Такой подход снижает количество потерянных уведомлений на 70% (с 500 до 150 в день) по сравнению с простым переподключением.
Почему нативное приложение лучше веб-интерфейса?
Веб-интерфейс OctoPrint не оптимирован под мобильные экраны: мелкие кнопки, необходимость обновлять страницу, отсутствие push-уведомлений. Нативное приложение даёт быстрый доступ к управлению, работает в фоне и присылает оповещения о завершении печати или ошибках. Это особенно важно, когда принтеров несколько или они находятся в другой комнате. Если вы управляете фермой принтеров, закажите приложение — оно сэкономит часы ежедневно.
Процесс разработки: от API-интеграции до публикации
- Анализ — обсуждаем ваш парк принтеров, выбираем API (OctoPrint, Moonraker или оба). Фиксируем обязательные функции.
- Проектирование — рисуем UX-схемы, проектируем архитектуру (Clean Architecture с Repository).
- Реализация — пишем слой API, UI на SwiftUI / Jetpack Compose / Flutter.
- Тестирование — на реальном принтере с разными сценариями (обрыв WiFi, загрузка 200MB G-code, одновременная печать).
Подробнее о тестировании
Мы симулируем обрыв WiFi, перегрузку сети, загрузку больших файлов (200MB G-code) на реальных принтерах с разными прошивками. - Деплой — публикуем в App Store и Google Play, передаём исходный код и документацию.
Ориентировочные сроки и что входит в работу
| Этап | Что входит | Срок |
|---|---|---|
| MVP (один API) | Мониторинг, старт/стоп, загрузка файлов, камера | 4–6 недель |
| Полная версия | Два API, история, уведомления, несколько принтеров | 7–10 недель |
| После запуска | 2 недели гарантийной поддержки, правки по обратной связи | Включено |
Стоимость рассчитывается индивидуально — зависит от сложности и набора функций. Экономия времени оператора может достигать нескольких часов в день за счёт автоматизации и push-уведомлений. При средней экономии 2 часа в день и стоимости часа оператора $30, приложение окупается за 2 месяца.
Почему стоит доверить разработку нам?
Мы — команда мобильных разработчиков с опытом более 5 лет в сфере IoT и управления устройствами. Имеем сертификаты Apple Developer и Google Play. Гарантируем соблюдение API-контрактов OctoPrint и Moonraker, код с комментариями и тестами.
Хотите обсудить проект? Свяжитесь с нами — оценим вашу задачу и предложим решение. Закажите разработку и получите приложение, которое работает с принтерами на любой прошивке.







