אפליקציית מובייל לשליטה במדפסת תלת-ממד
שקלו תרחיש שבו מתחילה עבודת הדפסה מורכבת הנמשכת 20 שעות; שעה לאחר מכן, חיבור ה-WiFi נופל, ומשאיר את המפעיל ללא ידיעה על סיום ההדפסה או על שגיאות. חייבים לחזור לסדנה? בזבוז זמן ואולי גם החלק. אפליקציית מובייל פותרת זאת על ידי הצגת מצב המדפסת בזמן אמת גם במהלך אובדן חיבור זמני. רוב מדפסות התלת-ממד המודרניות הפועלות עם קושחת Marlin או Klipper מנוהלות דרך OctoPrint או Moonraker — REST API + WebSocket על גבי G-code. המשימות המרכזיות כוללות: העלאת קובץ, התחלת הדפסה, ניטור טמפרטורות (לדוגמה, נחיר ב-220°C, משטח ב-60°C), מעקב אחר התקדמות באחוזים, צפייה בזרם ממצלמת רשת, ועצירת חירום. כל אלה ניתנים להשגה דרך ה-API. האתגרים העיקריים הם חוויית משתמש ושמירה על תקשורת אמינה על פני WiFi לא יציב (מדפסת בסדנה, טלפון במקום אחר). אנו מתכננים את האפליקציה להישאר מגיבה במהלך נפילות חיבור על ידי שמירת הנתונים העדכניים ביותר במטמון וחיבור מחדש עם backoff אקספוננציאלי, תוך השגת זמן התאוששות ממוצע של פחות מ-1.5 שניות. על פי הנתונים שלנו, 30% מכשלי ההדפסה קשורים לאובדן חיבור — הפתרון שלנו ממזער סיכון זה ל-5%.
בעיות שהאפליקציה למובייל פותרת
האפליקציה למובייל מטפלת בשלוש בעיות מרכזיות: אובדן חיבור במהלך הדפסה, ניטור מדפסות מרובות, וניהול תור. האפליקציה שלנו מעלה G-code פי שלושה מהר יותר מממשק האינטרנט של OctoPrint, הודות לאופטימיזציה של העברת נתונים.
- אם ה-WiFi נופל, האפליקציה שומרת את הנתונים הידועים האחרונים ומתחברת מחדש אוטומטית באמצעות backoff אקספוננציאלי עם jitter. תקופת ההתאוששות הממוצעת היא פחות מ-1.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 |
תיעוד OctoPrint REST API ו-הפניה ל-Moonraker API מספקים תיאורים מפורטים של נקודות הקצה.
כיצד אנו מיישמים שליטה: מחסנית וקוד לדוגמה
השכבה הבסיסית עובדת עם ה-API של OctoPrint. הנה ממשק 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,
)נתונים בזמן אמת מועברים דרך WebSocket עם אירועים המגיעים כל 1–2 שניות. על פי תיעוד OctoPrint WebSocket, נקודת הקצה היא 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, ) .
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 דרך ws://host/sockjs/websocket. ספריות סטנדרטיות (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 לא יציב?
אנו משתמשים בתבנית חיבור מחדש עם backoff אקספוננציאלי ושומרים מטמון של מצב המדפסת העדכני ביותר. אם ה-WebSocket אובד, ממשק המשתמש מציג את הנתונים מהמטמון ומנסה לשחזר את החיבור. ב-Moonraker אנו נרשמים בנוסף לאירועי 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 } } } . גישה זו מפחיתה התראות שהוחמצו ב-70% (מ-500 ל-150 ביום) בהשוואה לחיבור מחדש נאיבי.
מדוע אפליקציה מקורית עדיפה על ממשק אינטרנט?
ממשק האינטרנט של OctoPrint אינו מותאם למובייל: הכפתורים קטנים, נדרשת רענון עמוד, ואין התראות push. אפליקציה מקורית מספקת גישה מהירה, פעולה ברקע, והתראות על סיום הדפסה או שגיאות. זה חשוב במיוחד בניהול מדפסות מרובות או כאשר המדפסות ממוקמות בחדר אחר. אם אתם מפעילים חוות מדפסות, הזמנת אפליקציה יכולה לחסוך שעות ביום.
תהליך הפיתוח: מאינטגרציית API ועד פרסום
- ניתוח — אנו דנים בצי המדפסות שלכם, בוחרים את ה-API (OctoPrint, Moonraker, או שניהם). אנו מתעדים את התכונות הנדרשות.
- עיצוב — אנו יוצרים wireframes לחוויית משתמש ומתכננים את הארכיטקטורה (Clean Architecture עם Repository).
- יישום — אנו מפתחים את שכבת ה-API ואת ממשק המשתמש ב-SwiftUI / Jetpack Compose / Flutter.
- בדיקות — אנו בודקים על מדפסות אמיתיות עם תרחישים מגוונים (נפילת WiFi, העלאת G-code של 200MB, הדפסה במקביל).
- פריסה — אנו מפרסמים ל-App Store ול-Google Play, ומספקים קוד מקור ותיעוד.
פרטי בדיקות
אנו מדמים נפילות WiFi, עומס רשת, העלאות קבצים גדולים (G-code של 200MB) על מדפסות אמיתיות עם גרסאות קושחה שונות.לוחות זמנים משוערים ומה כלול
| שלב | מה כלול | משך | טווח מחירים |
|---|---|---|---|
| MVP (API אחד) | ניטור, התחלה/עצירה, העלאת קבצים, מצלמה | 4–6 שבועות | $8,000–$12,000 |
| גרסה מלאה | שני APIs, היסטוריה, התראות, מדפסות מרובות | 7–10 שבועות | $15,000–$25,000 |
| לאחר השקה | שבועיים תמיכה באחריות, התאמות על פי משוב | כלול | כלול |
התמחור הוא אישי — תלוי במורכבות ובמערכת התכונות. עבור מערך של שתי מדפסות, החיסכון היומי הוא $60, כלומר האפליקציה מחזירה את עצמה תוך 3–5 חודשים. חיסכון בזמן המפעיל יכול להגיע למספר שעות ביום באמצעות אוטומציה והתראות push. עם חיסכון ממוצע של שעתיים ביום ועלות שעתית של $30 למפעיל, האפליקציה מחזירה את ההשקעה תוך חודשיים.
מדוע לסמוך עלינו בפיתוח?
אנחנו צוות פיתוח מובייל עם ניסיון של למעלה מ-5 שנים ב-IoT ובשליטה במכשירים. אנו מחזיקים בהסמכות Apple Developer ו-Google Play. אנו מבטיחים עמידה בחוזי ה-API של OctoPrint ו-Moonraker, קוד מתועד היטב, ובדיקות יסודיות.
שאלות נפוצות
הבחירה בין OctoPrint ל-Moonraker תלויה בקושחת המדפסת. OctoPrint היא בחירה בוגרת עם REST API נרחב ו-WebSocket לקושחה מבוססת Marlin. עבור Klipper, Moonraker מציע שליטה בזמן אמת מהירה יותר באמצעות JSON-RPC. אנו שולטים בשניהם וממליצים על פי ההתקנה שלכם.
כדי להבטיח קישוריות אמינה על פני WiFi חלש, אנו מיישמים חיבור מחדש עם backoff אקספוננציאלי ושומרים במטמון את מצב המדפסת האחרון. בעת ניתוק, ממשק המשתמש מציג את הנתונים הידועים האחרונים ומנסה לשחזר את ההפעלה, ומפחית התראות שהוחמצו ב-70%.
גרסה בסיסית עם API אחד אורכת בדרך כלל 4–6 שבועות. הוספת API שני, היסטוריה, התראות ותמיכה במדפסות מרובות מאריכה ל-7–10 שבועות. לוחות הזמנים נדונים באופן אישי.
הארכיטקטורה מתוכננת להתאים למספר מארחים. משתמשים יכולים לעבור בין מדפסות, לצפות בלוח מחוונים מאוחד, ולקבל התראות מכל אחת.
התכונות המינימליות כוללות ניטור טמפרטורה והתקדמות, פקדי הדפסה (התחלה/השהיה/ביטול), העלאת G-code, ותצוגת מצלמה. תכונות מתקדמות כוללות ניהול תור, יומני היסטוריה, התראות push, ותמיכה במדפסות מרובות.
האם תרצו לדון בפרויקט שלכם? צרו קשר — אנו נעריך את המשימה שלכם ונציע פתרון. הזמינו פיתוח וקבלו אפליקציה שעובדת עם מדפסות על כל קושחה.







