Android Seri Bağlantı Noktası (Kotlin)
Kotlin’de yazılmış modern, iş parçacığı güvenli ve reaktif bir Android Seri Bağlantı Noktası kütüphanesi. Bu kütüphane, popüler xmaihh/Android-Serialport kütüphanesinin modernizasyonudur ve eşzamansız veri işleme için Kotlin Coroutines ve Flow’dan yararlanır.
- Modern Yığın: %100 Kotlin, Coroutines ve Flow.
- Reaktif API: Verileri sıcak
Flow<ByteArray>olarak okuyun. - Paket İşleme: Çoklu ayrıştırma stratejileri (delimiter, fixedLength, startEndMarker, lengthPrefixed, customParser) ile “yapışkan paketler” için yerleşik destek.
- Cihaz Keşfi:
SerialPortFindercihazdaki mevcut seri portları listeler. - Root Desteği: Seri bağlantı noktalarına erişmek için root izinleri talep eden yardımcı.
- İş Parçacığı Güvenliği: G/Ç işlemleri
Dispatchers.IO. ‘ya yüklenir.
Kurulum
Seçenek 1: Maven Central (Önerilen)
Bağımlılığı modülünüzün build.gradle.kts dosyasına ekleyin:
dependencies {
implementation("io.github.onatakduman:kserialport:1.1.3")
}Seçenek 2: JitPack
JitPack deposunu settings.gradle.kts‘ye ekleyin:
dependencyResolutionManagement {
depolar {
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}Bağımlılığı ekleyin:
dependencies {
implementation("com.github.onatakduman:kserialport:1.1.3")
}Kullanım
1. Kullanılabilir Cihazları Listele
val finder = SerialPortFinder()
val devices = finder.getAllDevices() // Returns Array<String> örneğin, ["ttyS0 (serial)", ...]
val paths = finder.getAllDevicesPath() // Returns Array<String> örneğin, ["/dev/ttyS0", ...]2. Seri Bağlantı Noktasını Açın
try {
// 8N1 (Varsayılan)
val serialPort = SerialPort("/dev/ttyS1", 9600)
// Özel Yapılandırma: 8E1 (8 Veri biti, Çift Eşlik, 1 Durma biti)
// val serialPort = SerialPort(
// path = "/dev/ttyS1",
// baudRate = 9600,
// dataBits = SerialPort.DATA_BITS_8,
// stopBits = SerialPort.STOP_BITS_1,
// parity = SerialPort.PARITY_EVEN
// )
val bağlantı = serialPort.open()
// Bağlantıyı kullan...
} catch (e: SecurityException) {
// İzin hatasını ele al
} catch (e: IOException) {
// Açma hatasını ele al
}3. Veri Okuma (Reaktif)
Veri almak için readFlow‘u kullanın. Paket işleme operatörlerini doğrudan uygulayabilirsiniz.
// Basit okuma - ham baytları geldikleri gibi alın
lifecycleScope.launch {
connection.readFlow.collect { bytes ->
Log.d("Serial", "Received: ${bytes.toHexString()}")
}
}4. Paket Ayrıştırma (Yapışkan Paket İşleme)
Seri veriler genellikle parçalanmış veya birleştirilmiş olarak gelir. Paketlerin tamamını ayıklamak için bu operatörleri kullanın:
Sınırlayıcı Tabanlı
Paketleri bir sınırlayıcı dizisi (örn. satırsonu, CRLF) ile bölün:
connection.readFlow
.delimiter(byteArrayOf(0x0A)) // \n ile böl
.collect { packet ->
Log.d("Serial", "Line: ${String(packet)}")
}Sabit Uzunluklu Paketler
Paketler her zaman aynı boyuta sahip olduğunda:
connection.readFlow
.fixedLength(32) // 32 baytlık paketler
.collect { packet ->
// 32 baytlık paketi işleyin
}Başlangıç/Bitiş İşaretleyici Çerçeveleri
Verileri işaretleyicilerle çerçeveleyen protokoller için (örn. STX/ETX):
val STX = byteArrayOf(0x02)
val ETX = byteArrayOf(0x03)
connection.readFlow
.startEndMarker(startMarker = STX, endMarker = ETX)
.collect { packet ->
// STX ve ETX arasında paket veri
}Uzunluk Önekli Paketler
Bir başlığın yük uzunluğunu içerdiği ikili protokollerde yaygındır:
// Protokol: [1 bayt uzunluk][yük]
connection.readFlow
.lengthPrefixed(
lengthFieldOffset = 0, // 0 baytındaki uzunluk
lengthFieldSize = 1, // 1 bayt uzunluk alanı
bigEndian = true
)
.collect { packet ->
// Başlık dahil paketin tamamı
}
// Daha karmaşık: [2 bayt başlık][2 bayt uzunluk BE][yük]
connection.readFlow
.lengthPrefixed(
lengthFieldOffset = 2, // Uzunluk alanı bayt 2'de başlar
lengthFieldSize = 2, // 2 bayt uzunluk (big-endian)
headerSize = 4, // Toplam başlık 4 bayttır
bigEndian = true
)
.collect { packet -> }Özel Ayrıştırıcı
Karmaşık protokoller için kendi mantığınızı uygulayın:
connection.readFlow
.customParser { buffer ->
// Henüz tamamlanmış paket yoksa null döndür
if (buffer.size < 4) return@customParser null
// Protokolünüzü ayrıştırın...
val packetEnd = findPacketEnd(buffer)
if (packetEnd == -1) return@customParser null
// Paketi ve kalan baytları döndür
PacketParser.ParseResult(
packet = buffer.copyOfRange(0, packetEnd),
kalan = buffer.copyOfRange(packetEnd, buffer.size)
)
}
.collect { packet -> }5. Veri Yaz
lifecycleScope.launch {
val data = "Merhaba Dünya".toByteArray()
connection.write(data)
}6. Bağlantıyı Kapat
connection.close()Lisans
Apache Lisansı 2.0