Разработка мобильного приложения для управления 3D-принтером
Большинство современных 3D-принтеров на Marlin или Klipper прошивке управляются через OctoPrint или Moonraker — REST API + WebSocket поверх G-code. Задача мобильного приложения: загрузить G-code файл, запустить печать, следить за температурами и прогрессом, смотреть в веб-камеру и останавливать при проблемах. Всё это — уже решённые задачи на уровне API, основная сложность в UX и надёжной обработке нестабильного WiFi-соединения (принтер в мастерской, телефон — где угодно).
OctoPrint API: основные эндпоинты
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
Температуры и прогресс — через OctoPrint WebSocket (/sockjs/websocket). События приходят каждые 1-2 секунды:
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) {
// Авторизация через первый message
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 {
// Типы событий: "current", "history", "event", "plugin"
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
}
}
}
Камера: MJPEG-стрим
OctoPrint транслирует MJPEG через /webcam/?action=stream. Coil и Glide не умеют MJPEG — нужен специальный компонент:
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)
}
Moonraker (Klipper) как альтернатива
Принтеры на Klipper используют Moonraker API — более современный REST + WebSocket (/websocket с JSON-RPC). Концепции те же, эндпоинты другие: /printer/objects/query для состояния, /printer/gcode/script для G-code команд, Fluidd/Mainsail как веб-интерфейс.
Загрузка большого G-code файла (100+ MB) через Moonraker с прогрессом:
Future<void> uploadGCode(File file, void Function(double) onProgress) async {
final stream = http.ByteStream(file.openRead());
final length = await file.length();
final request = http.MultipartRequest('POST',
Uri.parse('$baseUrl/server/files/upload'));
request.files.add(http.MultipartFile('file', stream, length,
filename: file.path.split('/').last));
final response = await request.send();
// Прогресс по Content-Length и bytes received из stream
}
Разработка мобильного приложения для управления 3D-принтером (OctoPrint/Moonraker): 4–6 недель. Поддержка нескольких принтеров, исторические данные температур и уведомления о завершении: 7–10 недель. Стоимость рассчитывается индивидуально.







