Skip to content

Commit 0ecaebd

Browse files
authored
Merge pull request #33 from calliope-edu/new-parser
New parser
2 parents a445b27 + dbc58af commit 0ecaebd

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1244
-1102
lines changed

android-partial-flashing-lib

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit 3a208f4216c705c972d9373acd00e390aa4a49b6

app/build.gradle

+7-8
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ android {
1111
applicationId "cc.calliope.mini"
1212
minSdk 23
1313
targetSdk 34
14-
versionCode 21
15-
versionName '2.0.3'
14+
versionCode 22
15+
versionName '2.0.4'
1616
multiDexEnabled true
1717

1818
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -30,23 +30,22 @@ android {
3030
}
3131
buildFeatures {
3232
viewBinding true
33+
buildConfig true
3334
}
3435
namespace 'cc.calliope.mini'
3536
}
3637

3738
dependencies {
3839
implementation 'no.nordicsemi.android.kotlin.ble:scanner:1.1.0'
39-
implementation 'no.nordicsemi.android:dfu:2.5.0'
40+
implementation 'no.nordicsemi.android:dfu:2.6.0'
4041

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

4445
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
45-
implementation 'androidx.navigation:navigation-fragment-ktx:2.7.7'
46-
implementation 'androidx.navigation:navigation-ui-ktx:2.7.7'
46+
implementation 'androidx.navigation:navigation-fragment-ktx:2.8.3'
47+
implementation 'androidx.navigation:navigation-ui-ktx:2.8.3'
4748
implementation 'androidx.preference:preference-ktx:1.2.1'
4849

49-
testImplementation 'junit:junit:4.13.2'
50-
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
51-
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
50+
implementation 'androidx.browser:browser:1.8.0'
5251
}

app/src/androidTest/java/cc/calliope/mini/ExampleInstrumentedTest.java

-26
This file was deleted.

app/src/main/AndroidManifest.xml

+3-1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@
122122
<service
123123
android:name=".core.service.LegacyDfuService"
124124
android:enabled="true" />
125-
125+
<service
126+
android:name=".core.bluetooth.CheckService"
127+
android:enabled="true" />
126128
</application>
127129
</manifest>

app/src/main/java/cc/calliope/mini/App.java

+18
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
package cc.calliope.mini;
22

3+
import static no.nordicsemi.android.dfu.DfuBaseService.EXTRA_DATA;
4+
35
import android.app.Application;
6+
import android.content.BroadcastReceiver;
7+
import android.content.Context;
8+
import android.content.Intent;
9+
import android.content.IntentFilter;
410
import android.util.Log;
511

12+
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
13+
614
import java.io.File;
715
import java.io.FileOutputStream;
816
import java.io.IOException;
917
import java.io.InputStream;
1018

19+
import cc.calliope.mini.core.service.DfuService;
20+
import cc.calliope.mini.utils.Utils;
21+
import no.nordicsemi.android.dfu.DfuBaseService;
22+
import no.nordicsemi.android.error.GattError;
23+
1124
public class App extends Application {
1225
private static final String FILE_NAME = "one_time_pairing.hex";
1326
private static final String CUSTOM_DIR = "CUSTOM";
@@ -19,6 +32,11 @@ public void onCreate() {
1932
copyFileToInternalStorage();
2033
}
2134

35+
@Override
36+
public void onTerminate() {
37+
super.onTerminate();
38+
}
39+
2240
private void copyFileToInternalStorage() {
2341
File libraryDir = new File(getFilesDir(), CUSTOM_DIR);
2442
if (!libraryDir.exists()) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package cc.calliope.mini
2+
3+
import android.bluetooth.BluetoothAdapter
4+
import android.bluetooth.BluetoothDevice
5+
import android.bluetooth.BluetoothSocket
6+
import android.content.Context
7+
import android.util.Log
8+
import android.widget.Toast
9+
import androidx.annotation.RequiresPermission
10+
import java.io.IOException
11+
import java.io.InputStream
12+
import java.io.OutputStream
13+
import java.util.*
14+
15+
class BluetoothService(private val context: Context) {
16+
17+
private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
18+
private var bluetoothSocket: BluetoothSocket? = null
19+
private var inputStream: InputStream? = null
20+
private var outputStream: OutputStream? = null
21+
22+
private val uuid: UUID = UUID.fromString("0b500100-607f-4151-9091-7d008d6ffc5c")
23+
24+
@RequiresPermission(allOf = [android.Manifest.permission.BLUETOOTH_CONNECT])
25+
fun connect(address: String): Boolean {
26+
if (bluetoothAdapter == null) {
27+
Toast.makeText(context, "Bluetooth не підтримується", Toast.LENGTH_SHORT).show()
28+
return false
29+
}
30+
31+
val device: BluetoothDevice = bluetoothAdapter.getRemoteDevice(address)
32+
33+
try {
34+
bluetoothSocket = device.createRfcommSocketToServiceRecord(uuid)
35+
bluetoothSocket?.connect()
36+
inputStream = bluetoothSocket?.inputStream
37+
outputStream = bluetoothSocket?.outputStream
38+
Toast.makeText(context, "Підключено до $address", Toast.LENGTH_SHORT).show()
39+
return true
40+
} catch (e: IOException) {
41+
e.printStackTrace()
42+
Toast.makeText(context, "Не вдалося підключитися", Toast.LENGTH_SHORT).show()
43+
return false
44+
}
45+
}
46+
47+
fun sendData(data: String) {
48+
try {
49+
outputStream?.write(data.toByteArray())
50+
} catch (e: IOException) {
51+
e.printStackTrace()
52+
}
53+
}
54+
55+
fun receiveData(): String? {
56+
val buffer = ByteArray(1024)
57+
return try {
58+
val bytes = inputStream?.read(buffer)
59+
bytes?.let { String(buffer, 0, it) }
60+
} catch (e: IOException) {
61+
e.printStackTrace()
62+
null
63+
}
64+
}
65+
66+
fun disconnect() {
67+
try {
68+
bluetoothSocket?.close()
69+
inputStream?.close()
70+
outputStream?.close()
71+
Toast.makeText(context, "Роз'єднано", Toast.LENGTH_SHORT).show()
72+
} catch (e: IOException) {
73+
e.printStackTrace()
74+
}
75+
}
76+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package cc.calliope.mini
2+
3+
import java.io.File
4+
5+
class HexParser(private val path: String) {
6+
private fun calculateChecksum(address: UShort, type: Int, data: ByteArray): UByte {
7+
var crc: UInt = data.size.toUByte() +
8+
(address.toUInt() shr 8).toUByte() +
9+
((address.toUInt() and 0xFFu) + type.toUInt()).toUByte()
10+
for (b in data) {
11+
crc = (crc + b.toUByte()).toUByte().toUInt()
12+
}
13+
return ((0x100u - crc) and 0xFFu).toUByte()
14+
}
15+
16+
fun parse(handleDataEntry: (Long, ByteArray, Int, Boolean) -> Unit) {
17+
val file = File(path)
18+
val reader = file.bufferedReader()
19+
20+
var isUniversal = false
21+
var addressHi: UInt = 0u
22+
var dataType = 0
23+
24+
reader.useLines { lines ->
25+
lines.forEach { line ->
26+
var beginIndex = 0
27+
var endIndex = 1
28+
29+
if (line.isEmpty() || line[beginIndex] != ':') return@forEach
30+
beginIndex = endIndex
31+
32+
endIndex = beginIndex + 2
33+
val length = line.substring(beginIndex, endIndex).toInt(16).toUByte()
34+
beginIndex = endIndex
35+
36+
endIndex = beginIndex + 4
37+
val addressLo = line.substring(beginIndex, endIndex).toUInt(16)
38+
beginIndex = endIndex
39+
40+
endIndex = beginIndex + 2
41+
val type = line.substring(beginIndex, endIndex).toInt(16)
42+
beginIndex = endIndex
43+
44+
endIndex = beginIndex + 2 * length.toInt()
45+
if (endIndex > line.length) return@forEach
46+
val payload = line.substring(beginIndex, endIndex)
47+
beginIndex = endIndex
48+
49+
endIndex = beginIndex + 2
50+
if (endIndex > line.length) return@forEach
51+
val checksum = line.substring(beginIndex, endIndex).toUIntOrNull(16)?.toUByte() ?: return@forEach
52+
53+
val data = payload.hexStringToByteArray()
54+
val calculatedChecksum = calculateChecksum(addressLo.toUShort(), type, data)
55+
if (checksum != calculatedChecksum) {
56+
return@forEach
57+
}
58+
59+
when (type) {
60+
0, 13 -> { // Data
61+
val position = addressHi + addressLo
62+
if (data.size == length.toInt()) {
63+
handleDataEntry(position.toLong(), data, dataType, isUniversal)
64+
}
65+
}
66+
1 -> return // EOF
67+
2 -> { // EXT SEGMENT ADDRESS
68+
val segment = payload.toUInt(16)
69+
addressHi = segment shl 4
70+
}
71+
3 -> { /* START SEGMENT ADDRESS */ }
72+
4 -> { // EXT LINEAR ADDRESS
73+
val segment = payload.toUInt(16)
74+
addressHi = segment shl 16
75+
}
76+
5 -> { /* START LINEAR ADDRESS */ }
77+
10 -> { // Block Start Address
78+
isUniversal = true
79+
val dataTypeField = line.substring(9, 13)
80+
dataType = when (dataTypeField) {
81+
"9900" -> 1
82+
"9903" -> 2
83+
else -> dataType
84+
}
85+
}
86+
else -> { /* OTHER */ }
87+
}
88+
}
89+
}
90+
}
91+
}
92+
93+
fun String.hexStringToByteArray(): ByteArray {
94+
val len = this.length
95+
val data = ByteArray(len / 2)
96+
var i = 0
97+
while (i < len) {
98+
val byte = this.substring(i, i + 2).toInt(16).toByte()
99+
data[i / 2] = byte
100+
i += 2
101+
}
102+
return data
103+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package cc.calliope.mini
2+
3+
import java.nio.ByteBuffer
4+
import java.nio.ByteOrder
5+
6+
class InitPacket(private val appSize: Int) {
7+
8+
private val appName: ByteArray = "microbit_app".toByteArray(Charsets.UTF_8)
9+
private val initPacketVersion: Int = 1
10+
private val hashSize: Int = 0
11+
private val hashBytes: ByteArray = ByteArray(32) { 0.toByte() }
12+
13+
fun encode(): ByteArray {
14+
val initPacket = mutableListOf<Byte>()
15+
16+
initPacket.addAll(appName.toList())
17+
initPacket.addAll(initPacketVersion.toLittleEndianByteArray().toList())
18+
initPacket.addAll(appSize.toLittleEndianByteArray().toList())
19+
initPacket.addAll(hashSize.toBigEndianByteArray().toList())
20+
initPacket.addAll(hashBytes.toList())
21+
22+
return initPacket.toByteArray()
23+
}
24+
}
25+
26+
fun Int.toLittleEndianByteArray(): ByteArray {
27+
val buffer = ByteBuffer.allocate(4)
28+
buffer.order(ByteOrder.LITTLE_ENDIAN)
29+
buffer.putInt(this)
30+
return buffer.array()
31+
}
32+
33+
fun Int.toBigEndianByteArray(): ByteArray {
34+
val buffer = ByteBuffer.allocate(4)
35+
buffer.order(ByteOrder.BIG_ENDIAN)
36+
buffer.putInt(this)
37+
return buffer.array()
38+
}

0 commit comments

Comments
 (0)