Разработка мобильного приложения для управления 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, код с комментариями и тестами.
Хотите обсудить проект? Свяжитесь с нами — оценим вашу задачу и предложим решение. Закажите разработку и получите приложение, которое работает с принтерами на любой прошивке.







