ble: sending the first SP1/SP2 pairing command

from the fake pod logs
```
INFO[0007] Received SP1 SP2 payload 5350313d0004000010912c5350323d000bffc32dbd08030e0100008a
TRAC[0007] Read field: SP1= :: 00001091 :: 4
TRAC[0007] Read field: ,SP2= :: ffc32dbd08030e0100008a :: 11
INFO[0007] Received SP1 SP2: 00001091 :: ffc32dbd08030e0100008a
```
This commit is contained in:
Andrei Vereha 2021-02-26 16:52:55 +01:00
parent 318c0c642c
commit badf8fe24d
6 changed files with 83 additions and 38 deletions

View file

@ -4,7 +4,7 @@ import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.BleIO import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.io.BleIO
data class LTK(val ltk: ByteArray, val noncePrefix: ByteArray) { data class LTK(val ltk: ByteArray, val noncePrefix: ByteArray) {
init{ init {
require(ltk.size == 16) require(ltk.size == 16)
require(noncePrefix.size == 16) require(noncePrefix.size == 16)
} }

View file

@ -1,13 +1,11 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.ltk package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.ltk
import info.nightscout.androidaps.logging.AAPSLogger import info.nightscout.androidaps.logging.AAPSLogger
import info.nightscout.androidaps.logging.LTag
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManagerImpl import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.OmnipodDashBleManagerImpl
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.Id import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.Id
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.MessageIO
import info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message.StringLengthPrefixEncoding
import info.nightscout.androidaps.utils.extensions.hexStringToByteArray import info.nightscout.androidaps.utils.extensions.hexStringToByteArray
import info.nightscout.androidaps.utils.extensions.toHex
import java.nio.ByteBuffer
internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgIO: MessageIO) { internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgIO: MessageIO) {
@ -48,8 +46,10 @@ internal class LTKExchanger(private val aapsLogger: AAPSLogger, private val msgI
} }
fun sp1sp2(sp1: ByteArray, sp2: ByteArray, seq: Byte, controllerId: Id, nodeId: Id): PairMessage { fun sp1sp2(sp1: ByteArray, sp2: ByteArray, seq: Byte, controllerId: Id, nodeId: Id): PairMessage {
val payload = "SP1=".toByteArray() + sp1 val payload = StringLengthPrefixEncoding.formatKeys(
",SP2=".toByteArray() + sp2 arrayOf("SP1=", ",SP2="),
arrayOf(sp1, sp2),
)
return PairMessage( return PairMessage(
sequenceNumber = seq, sequenceNumber = seq,
source = controllerId, source = controllerId,

View file

@ -13,7 +13,6 @@ data class PairMessage(
type=MessageType.PAIRING, type=MessageType.PAIRING,
source=source, source=source,
destination = destination, destination = destination,
encryptedPayload=false,
payload=payload, payload=payload,
sequenceNumber=sequenceNumber, sequenceNumber=sequenceNumber,
sas=true, // TODO: understand why this is true for PairMessages sas=true, // TODO: understand why this is true for PairMessages

View file

@ -26,7 +26,7 @@ class MessageIO(private val aapsLogger: AAPSLogger, private val bleIO: BleIO) {
} }
// TODO: peek for NACKs // TODO: peek for NACKs
val expectSuccess = bleIO.receivePacket(CharacteristicType.CMD) val expectSuccess = bleIO.receivePacket(CharacteristicType.CMD)
if (BleCommand(expectSuccess) != BleCommandCTS()) { if (BleCommand(expectSuccess) != BleCommandSuccess()) {
throw UnexpectedCommandException(BleCommand(expectSuccess)) throw UnexpectedCommandException(BleCommand(expectSuccess))
} }
// TODO: handle NACKS/FAILS/etc // TODO: handle NACKS/FAILS/etc

View file

@ -1,34 +1,10 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message
import java.io.DataOutput
import java.nio.ByteBuffer import java.nio.ByteBuffer
/*** /***
* MessagePacket contains header and raw payload for a message * MessagePacket contains header and raw payload for a message
*/ */
class Flag:Byte{
fun set(idx: Byte, val: Boolean) {
val mask = 1 <lsh (7-idx)
}
func (f *flag) set(index byte, val bool) {
var mask flag = 1 << (7 - index)
if !val {
return
}
*f |= mask
}
func (f flag) get(index byte) byte {
var mask flag = 1 << (7 - index)
if f&mask == 0 {
return 0
}
return 1
}
}
data class MessagePacket( data class MessagePacket(
val type: MessageType, val type: MessageType,
val source: Id, val source: Id,
@ -46,15 +22,51 @@ data class MessagePacket(
val version: Short = 0.toShort()) { val version: Short = 0.toShort()) {
fun asByteArray(): ByteArray { fun asByteArray(): ByteArray {
val v = ByteBuffer.allocate(16+ payload.size) val bb = ByteBuffer.allocate(16 + payload.size)
v.put(MAGIC_PATTERN.toByteArray()) bb.put(MAGIC_PATTERN.toByteArray())
val f1: Byte=0
val f1 = Flag()
f1.set(0, this.version.toInt() and 4 != 0)
f1.set(1, this.version.toInt() and 2 != 0)
f1.set(2, this.version.toInt() and 1 != 0)
f1.set(3, this.sas)
f1.set(4, this.tfs)
f1.set(5, this.eqos.toInt() and 4 != 0)
f1.set(6, this.eqos.toInt() and 2 != 0)
f1.set(7, this.eqos.toInt() and 1 != 0)
return this.payload val f2 = Flag()
f2.set(0, this.ack)
f2.set(1, this.priority)
f2.set(2, this.lastMessage)
f2.set(3, this.gateway)
f2.set(4, this.type.value.toInt() and 8 != 0)
f2.set(5, this.type.value.toInt() and 4 != 0)
f2.set(6, this.type.value.toInt() and 2 != 0)
f2.set(7, this.type.value.toInt() and 1 != 0)
bb.put(f1.value.toByte())
bb.put(f2.value.toByte())
bb.put(this.sequenceNumber)
bb.put(this.ackNumber)
bb.put((this.payload.size ushr 3).toByte())
bb.put((this.payload.size shl 5).toByte())
bb.put(this.source.address)
bb.put(this.destination.address)
bb.put(this.payload)
val ret = ByteArray(bb.position())
bb.flip()
bb.get(ret)
return ret
} }
companion object { companion object {
private val MAGIC_PATTERN = "TW" // all messages start with this string private val MAGIC_PATTERN = "TW" // all messages start with this string
fun parse(payload: ByteArray): MessagePacket { fun parse(payload: ByteArray): MessagePacket {
@ -62,3 +74,18 @@ data class MessagePacket(
} }
} }
} }
private class Flag(var value: Int=0) {
fun set(idx: Byte, set: Boolean) {
val mask = 1 shl (7 - idx)
if (!set)
return
value = value or mask
}
fun get(idx: Byte): Boolean {
val mask = 1 shl (7 - idx)
return value and mask != 0
}
}

View file

@ -1,17 +1,36 @@
package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message package info.nightscout.androidaps.plugins.pump.omnipod.dash.driver.comm.message
import java.nio.ByteBuffer
/*** /***
* String prefix and length encoding and decoding. Example message: * String prefix and length encoding and decoding. Example message:
*/ */
class StringLengthPrefixEncoding { class StringLengthPrefixEncoding {
companion object { companion object {
fun parseKeys(keys: List<String>): List<ByteArray> { fun parseKeys(keys: List<String>): List<ByteArray> {
TODO("not implemented") TODO("not implemented")
} }
fun formatKeys(keys: List<String>, payload: List<ByteArray>, addLength: Boolean): ByteArray { fun formatKeys(keys: Array<String>, payloads: Array<ByteArray>): ByteArray {
TODO("not implemented") val payloadTotalSize = payloads.fold(0) { acc, i -> acc + i.size }
val keyTotalSize = keys.fold(0) { acc, i -> acc + i.length }
val bb = ByteBuffer.allocate(2 * keys.size + keyTotalSize + payloadTotalSize)
for (idx in keys.indices) {
val k = keys[idx]
val payload = payloads[idx]
bb.put(k.toByteArray())
bb.putShort(payload.size.toShort())
bb.put(payload)
}
val ret = ByteArray(bb.position())
bb.flip()
bb.get(ret)
return ret
} }
} }
} }