Skip to content

New parser #33

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android-partial-flashing-lib
Submodule android-partial-flashing-lib added at 3a208f
15 changes: 7 additions & 8 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ android {
applicationId "cc.calliope.mini"
minSdk 23
targetSdk 34
versionCode 21
versionName '2.0.3'
versionCode 22
versionName '2.0.4'
multiDexEnabled true

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand All @@ -30,23 +30,22 @@ android {
}
buildFeatures {
viewBinding true
buildConfig true
}
namespace 'cc.calliope.mini'
}

dependencies {
implementation 'no.nordicsemi.android.kotlin.ble:scanner:1.1.0'
implementation 'no.nordicsemi.android:dfu:2.5.0'
implementation 'no.nordicsemi.android:dfu:2.6.0'

implementation 'commons-io:commons-io:2.16.1'
implementation 'org.apache.commons:commons-lang3:3.15.0'

implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.7'
implementation 'androidx.navigation:navigation-ui-ktx:2.7.7'
implementation 'androidx.navigation:navigation-fragment-ktx:2.8.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.8.3'
implementation 'androidx.preference:preference-ktx:1.2.1'

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
implementation 'androidx.browser:browser:1.8.0'
}

This file was deleted.

4 changes: 3 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@
<service
android:name=".core.service.LegacyDfuService"
android:enabled="true" />

<service
android:name=".core.bluetooth.CheckService"
android:enabled="true" />
</application>
</manifest>
18 changes: 18 additions & 0 deletions app/src/main/java/cc/calliope/mini/App.java
Original file line number Diff line number Diff line change
@@ -1,13 +1,26 @@
package cc.calliope.mini;

import static no.nordicsemi.android.dfu.DfuBaseService.EXTRA_DATA;

import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;

import androidx.localbroadcastmanager.content.LocalBroadcastManager;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;

import cc.calliope.mini.core.service.DfuService;
import cc.calliope.mini.utils.Utils;
import no.nordicsemi.android.dfu.DfuBaseService;
import no.nordicsemi.android.error.GattError;

public class App extends Application {
private static final String FILE_NAME = "one_time_pairing.hex";
private static final String CUSTOM_DIR = "CUSTOM";
Expand All @@ -19,6 +32,11 @@ public void onCreate() {
copyFileToInternalStorage();
}

@Override
public void onTerminate() {
super.onTerminate();
}

private void copyFileToInternalStorage() {
File libraryDir = new File(getFilesDir(), CUSTOM_DIR);
if (!libraryDir.exists()) {
Expand Down
76 changes: 76 additions & 0 deletions app/src/main/java/cc/calliope/mini/BluetoothService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package cc.calliope.mini

import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import android.content.Context
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresPermission
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.*

class BluetoothService(private val context: Context) {

private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
private var bluetoothSocket: BluetoothSocket? = null
private var inputStream: InputStream? = null
private var outputStream: OutputStream? = null

private val uuid: UUID = UUID.fromString("0b500100-607f-4151-9091-7d008d6ffc5c")

@RequiresPermission(allOf = [android.Manifest.permission.BLUETOOTH_CONNECT])
fun connect(address: String): Boolean {
if (bluetoothAdapter == null) {
Toast.makeText(context, "Bluetooth не підтримується", Toast.LENGTH_SHORT).show()
return false
}

val device: BluetoothDevice = bluetoothAdapter.getRemoteDevice(address)

try {
bluetoothSocket = device.createRfcommSocketToServiceRecord(uuid)
bluetoothSocket?.connect()
inputStream = bluetoothSocket?.inputStream
outputStream = bluetoothSocket?.outputStream
Toast.makeText(context, "Підключено до $address", Toast.LENGTH_SHORT).show()
return true
} catch (e: IOException) {
e.printStackTrace()
Toast.makeText(context, "Не вдалося підключитися", Toast.LENGTH_SHORT).show()
return false
}
}

fun sendData(data: String) {
try {
outputStream?.write(data.toByteArray())
} catch (e: IOException) {
e.printStackTrace()
}
}

fun receiveData(): String? {
val buffer = ByteArray(1024)
return try {
val bytes = inputStream?.read(buffer)
bytes?.let { String(buffer, 0, it) }
} catch (e: IOException) {
e.printStackTrace()
null
}
}

fun disconnect() {
try {
bluetoothSocket?.close()
inputStream?.close()
outputStream?.close()
Toast.makeText(context, "Роз'єднано", Toast.LENGTH_SHORT).show()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
103 changes: 103 additions & 0 deletions app/src/main/java/cc/calliope/mini/HexParser.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package cc.calliope.mini

import java.io.File

class HexParser(private val path: String) {
private fun calculateChecksum(address: UShort, type: Int, data: ByteArray): UByte {
var crc: UInt = data.size.toUByte() +
(address.toUInt() shr 8).toUByte() +
((address.toUInt() and 0xFFu) + type.toUInt()).toUByte()
for (b in data) {
crc = (crc + b.toUByte()).toUByte().toUInt()
}
return ((0x100u - crc) and 0xFFu).toUByte()
}

fun parse(handleDataEntry: (Long, ByteArray, Int, Boolean) -> Unit) {
val file = File(path)
val reader = file.bufferedReader()

var isUniversal = false
var addressHi: UInt = 0u
var dataType = 0

reader.useLines { lines ->
lines.forEach { line ->
var beginIndex = 0
var endIndex = 1

if (line.isEmpty() || line[beginIndex] != ':') return@forEach
beginIndex = endIndex

endIndex = beginIndex + 2
val length = line.substring(beginIndex, endIndex).toInt(16).toUByte()
beginIndex = endIndex

endIndex = beginIndex + 4
val addressLo = line.substring(beginIndex, endIndex).toUInt(16)
beginIndex = endIndex

endIndex = beginIndex + 2
val type = line.substring(beginIndex, endIndex).toInt(16)
beginIndex = endIndex

endIndex = beginIndex + 2 * length.toInt()
if (endIndex > line.length) return@forEach
val payload = line.substring(beginIndex, endIndex)
beginIndex = endIndex

endIndex = beginIndex + 2
if (endIndex > line.length) return@forEach
val checksum = line.substring(beginIndex, endIndex).toUIntOrNull(16)?.toUByte() ?: return@forEach

val data = payload.hexStringToByteArray()
val calculatedChecksum = calculateChecksum(addressLo.toUShort(), type, data)
if (checksum != calculatedChecksum) {
return@forEach
}

when (type) {
0, 13 -> { // Data
val position = addressHi + addressLo
if (data.size == length.toInt()) {
handleDataEntry(position.toLong(), data, dataType, isUniversal)
}
}
1 -> return // EOF
2 -> { // EXT SEGMENT ADDRESS
val segment = payload.toUInt(16)
addressHi = segment shl 4
}
3 -> { /* START SEGMENT ADDRESS */ }
4 -> { // EXT LINEAR ADDRESS
val segment = payload.toUInt(16)
addressHi = segment shl 16
}
5 -> { /* START LINEAR ADDRESS */ }
10 -> { // Block Start Address
isUniversal = true
val dataTypeField = line.substring(9, 13)
dataType = when (dataTypeField) {
"9900" -> 1
"9903" -> 2
else -> dataType
}
}
else -> { /* OTHER */ }
}
}
}
}
}

fun String.hexStringToByteArray(): ByteArray {
val len = this.length
val data = ByteArray(len / 2)
var i = 0
while (i < len) {
val byte = this.substring(i, i + 2).toInt(16).toByte()
data[i / 2] = byte
i += 2
}
return data
}
38 changes: 38 additions & 0 deletions app/src/main/java/cc/calliope/mini/InitPacket.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cc.calliope.mini

import java.nio.ByteBuffer
import java.nio.ByteOrder

class InitPacket(private val appSize: Int) {

private val appName: ByteArray = "microbit_app".toByteArray(Charsets.UTF_8)
private val initPacketVersion: Int = 1
private val hashSize: Int = 0
private val hashBytes: ByteArray = ByteArray(32) { 0.toByte() }

fun encode(): ByteArray {
val initPacket = mutableListOf<Byte>()

initPacket.addAll(appName.toList())
initPacket.addAll(initPacketVersion.toLittleEndianByteArray().toList())
initPacket.addAll(appSize.toLittleEndianByteArray().toList())
initPacket.addAll(hashSize.toBigEndianByteArray().toList())
initPacket.addAll(hashBytes.toList())

return initPacket.toByteArray()
}
}

fun Int.toLittleEndianByteArray(): ByteArray {
val buffer = ByteBuffer.allocate(4)
buffer.order(ByteOrder.LITTLE_ENDIAN)
buffer.putInt(this)
return buffer.array()
}

fun Int.toBigEndianByteArray(): ByteArray {
val buffer = ByteBuffer.allocate(4)
buffer.order(ByteOrder.BIG_ENDIAN)
buffer.putInt(this)
return buffer.array()
}
Loading
Loading