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







