简体   繁体   English

如何在不更改其他字节的情况下有选择地编辑字节数组中的十六进制字节

[英]How do I selectively edit hex bytes in a byte array without changing other

I'm writing a python program that is sending a byte array to a BLE device using a byte array.我正在编写一个 python 程序,它使用字节数组将字节数组发送到 BLE 设备。 I can manipulate the data sent to the device and that works.我可以操纵发送到设备的数据并且可以正常工作。 The device is an 8 button Bluetooth switch panel.该设备是一个 8 键蓝牙开关面板。 The byte array when read from the device looks like this从设备读取的字节数组如下所示

x08\x00\x00\x00\x00

To clarify what this means in context, x08 I believe is the header which is position 0 of the array.为了阐明这在上下文中的含义,我认为 x08 是位于数组位置 0 的标头。 Position 1 is switch 1 and 2, Position 2 is switch 3 and 4, and so on位置 1 为开关 1 和 2,位置 2 为开关 3 和 4,依此类推

The array appears to be looking at each position as a hex code so if I want to turn on switch number 4 I have to send 01 but if I want to turn on switch 3 I have to send 16 if I want both it would be 17 which would breakdown something like this该数组似乎将每个位置视为十六进制代码,所以如果我想打开开关编号 4,我必须发送 01,但如果我想打开开关 3,我必须发送 16,如果我想要两者,它将是 17这会分解这样的东西

Switch 4 only
switch_status[2] = 01

output:
x08\x00\x01\x00\x00

Switch 3 only
x08\x00\x16\x00\x00

There are other functions like momentary mode and flash but I'm keeping this simple for now.还有其他功能,如瞬时模式和闪光灯,但我暂时保持简单。

I'm trying to avoid writing an entire function that writes the entire array each time as I don't want to manipulate all the switch just to turn one switch on.我试图避免编写一个每次都写入整个数组的完整函数,因为我不想为了打开一个开关而操纵所有开关。 However since each byte in the array controls 2 switches I either have to run a check to determine the state then change the byte for both switches to ensure I'm not turning on or off the wrong switch or I need to find a better way to manipulate the array elements.然而,由于数组中的每个字节控制 2 个开关,我要么必须运行检查以确定状态,然后更改两个开关的字节以确保我没有打开或关闭错误的开关,要么我需要找到更好的方法来操作数组元素。 I'm not sure what the right call is here.我不确定这里的正确调用是什么。 Any advice would be appreciated.任何意见,将不胜感激。 I'm still learning python so I may be using the wrong concepts here.我还在学习 python,所以我可能在这里使用了错误的概念。

I've attempted directly writing the bytes for individual switch pairs and that works.我已经尝试直接为单个开关对写入字节并且有效。 However it makes the code a bit cumbersome when I have to check each pair for current status and write if statements to ensure I'm not overwriting a value I want to keep if another switch is already on.然而,当我必须检查每对当前状态并编写 if 语句以确保我不会覆盖我想保留的值(如果另一个开关已经打开)时,它会使代码有点麻烦。 I can do that but it seems inefficient.我可以这样做,但它似乎效率低下。 Could be that is the correct answer but I thought I would ask.可能那是正确的答案,但我想我会问。 I've considered dumping the array to a string and converting back after a specific edit has been made but that seemed like over handling.我考虑过将数组转储为字符串并在进行特定编辑后转换回来,但这似乎处理过度了。 If statements by comparison seemed more efficient.通过比较,If 语句似乎更有效率。 Then end goal is to turn this into a function call that gets used by another part of the app.然后最终目标是将其变成一个函数调用,供应用程序的另一部分使用。 The ability to reliably push commands over BLE to do what I need the switches to do was the logical first step.通过 BLE 可靠地推送命令以执行我需要交换机执行的操作的能力是合乎逻辑的第一步。

Here is the code I'm working on这是我正在处理的代码

import asyncio
from bleak import BleakClient

#Define the device we are manipulating

address = "{DEVICE HW ADDRESS}"
SWITCH_UUID = "0000fff1-0000-1000-8000-00805f9b34fb"

#Define our Variables. Outside of testing these should all be set to 0
sign = 0
left = 0
right =
rear = 0
bar = 0
cargo = 0
jump = 0
booster = 0

#Convert our variables to a hex string then store them in a byte array. At some point we will need to get current switch status so we can parse and change variables in the array to reflect current configuration.
simplearray = "08" + str(sign) + str(left) + str(right) + str(rear) + str(bar) + str(cargo) + str(jump) + str(booster)
#array = bytes.fromhex("08" + str(sign) + str(left) + str(right) + str(rear) + str(bar) + str(cargo) + str(jump) + str(booster))
array = bytes.fromhex(simplearray)

#Print list of current states for switches for troubleshooting.
print("Switch1: " + simplearray[2])
print("Switch2: " + simplearray[3])
print("Switch3: " + simplearray[4])
print("Switch4: " + simplearray[5])
print("Switch5: " + simplearray[6])
print("Switch6: " + simplearray[7])
print("Switch7: " + simplearray[8])
print("Switch8: " + simplearray[9])

#Use asyncio to connect to our BLE GATT server
async def main(address):
    async with BleakClient(address) as client:

        #verify connection to our server
        print(f"Connected: {client.is_connected}")

        #Get the switch status from the UUID imput above and print current status. Later we will use this to make sure we are only changing desired switches.
        switch_status = await client.read_gatt_char(SWITCH_UUID)
        print("Switch Status:" + str(switch_status))

        #Set security level. What is this for anyway?
        paired = await client.pair(protection_level=2)

        #Make sure we are paired before operating switches. Error handler needed here.
        print(f"Paired: {paired}")

        #Signal lights to turn on and re-read status.
        print("Turning Switches On....")

        #For some reason this doesn't work. Can't manually assign values to a position in simplearray.
        #Would have to use a replace command instead but leaves us sending the entire hex byte array to the unit instead of switching one output like we want
        #will likely read hex value and manipulate for values we want
        #simplearray[5] = 1
        #print(simplearray[5])

        #print output of the array to check our status while testing. Will be removed later
        print(array)
        #This sends the array to the switch. I can send commands to individual pairs but not individual switches. Will need to investigate further.
        await client.write_gatt_char(SWITCH_UUID, array)
        switch_status = await client.read_gatt_char(SWITCH_UUID)
        print(switch_status)
        await asyncio.sleep(5.0)
        print(switch_status[0])
        print(switch_status[1])
        print(switch_status[2])
        print(switch_status[3])
        print(switch_status[4])
        #Signla turn switches off after wait.
        print("Turning Switches Off...")

        #For testing purposes turn all switches off before we exit.
        await client.write_gatt_char(SWITCH_UUID, b"\x08\x00\x00\x00\x00")
        switch_status = await client.read_gatt_char(SWITCH_UUID)
        print(switch_status)
        await asyncio.sleep(1.0)

#Disconnect I guess? Look into this one.
asyncio.run(main(address))

Ok thanks to @ukBaz I found a solution.好的,感谢@ukBaz,我找到了解决方案。 It wasn't what I was hoping for but I'm constrained by the fact that I have to send a command to at least 2 switches.这不是我所希望的,但我必须向至少 2 个开关发送命令这一事实限制了我。 working with bytearray directly may have been a fruitful endeavor but I think this solution will fit my needs better.直接使用 bytearray 可能是一项富有成效的尝试,但我认为该解决方案更符合我的需求。 The benefit I see is that the gui I end up building for this will be able to get status from the bluetooth device in realtime and a simple change of state will be easier and require less code using this as a base.我看到的好处是我最终为此构建的 gui 将能够实时从蓝牙设备获取状态,并且使用它作为基础的简单状态更改将更容易并且需要更少的代码。 I can also use it to automate certain tasks in other areas as well.我也可以使用它来自动执行其他领域的某些任务。

Here is the working code if anyone is interested.如果有人感兴趣,这是工作代码。

And again, thank you @ukBaz, I'm grateful you took the time.再次感谢@ukBaz,感谢您抽出宝贵时间。

import asyncio
from bleak import BleakClient

#Define the device we are manipulating

address = "(MAC ADDRESS TO SWITCH)"
SWITCH_UUID = "CHARACTERISTIC UUID"


def switch_on(switch_status: int, switch_id: int) -> int:
    if switch_id % 2:
        switch_id += 2
    switch_status |= 1 << (4 * switch_id)
    return switch_status

def switch_off(switch_status: int, switch_id: int) -> int:
    if switch_id % 2:
        switch_id += 2
    switch_mask = 1 << (4 * switch_id) ^ 0xFFFFFFFFFF
    return switch_status & switch_mask


#Use asyncio to connect to our BLE GATT server
async def main(address):
   async with BleakClient(address) as client:
     switch_status = await client.read_gatt_char(SWITCH_UUID)
     switch_int = int.from_bytes(switch_status, 'little')  # current value to integer
     print(f"[start]  {switch_int.to_bytes(5, 'little')} ({switch_int})\n")

     print(f"Connected: {client.is_connected}")
     paired = await client.pair(protection_level=2)
     print(f"Paired: {paired}")

     print("Turning Switches On....")

     print("Which switch to operate: 1 - 8")

     switch_id = int(input())

     print("On or Off?")
     state = input()

     if state == 'on':
          state_change = switch_on
     elif state == 'off':
          state_change = switch_off
     print("Switch " + str(switch_id) + " Selected")

     switch_int = state_change(switch_int, switch_id)
     operate = switch_int.to_bytes(5, 'little')
     await client.write_gatt_char(SWITCH_UUID, operate)

     print(
         f"[ON]  ID={switch_id} : {switch_int.to_bytes(5, 'little')} ({switch_int:})"
     )

#Disconnect I guess? Look into this one.
asyncio.run(main(address))

Half a byte is often called a nibble.半字节通常称为半字节。 Using Python bitwise operations to modify the nibble for the switch you want to turn on/off is maybe the way to go.使用 Python按位运算修改要打开/关闭的开关的半字节可能是可行的方法。

I created two functions called swtich_on and switch_off .我创建了两个名为swtich_onswitch_off的函数。 They take the current value from the SWITCH_UUID read after it is converted to an integer.它们从转换为整数后读取的SWITCH_UUID中获取当前值。 Using int.from_bytes to do this.使用int.from_bytes来做到这一点。

To get the value to write to the SWITCH_UUID characteristic, int.to_bytes is used.要获取写入SWITCH_UUID特性的值,使用int.to_bytes

Here is a test I did to turn all the switches on then off again using these two functions:这是我使用这两个函数打开所有开关然后再次关闭的测试:

def switch_on(switch_status: int, switch_id: int) -> int:
    if switch_id % 2:
        switch_id += 2
    switch_status |= 1 << (4 * switch_id)
    return switch_status


def switch_off(switch_status: int, switch_id: int) -> int:
    if switch_id % 2:
        switch_id += 2
    switch_mask = 1 << (4 * switch_id) ^ 0xFFFFFFFFFF
    return switch_status & switch_mask


def main():
    switch_int = int.from_bytes(b'\x08\x00\x00\x00\x00', 'little')  # current value to integer
    print(f"[start]  {switch_int.to_bytes(5, 'little')} ({switch_int})\n")
    for switch_id in range(1, 9):
        switch_int = switch_on(switch_int, switch_id)
        print(
            f"[ON]  ID={switch_id} : {switch_int.to_bytes(5, 'little')} ({switch_int:})"
        )
        switch_int = switch_off(switch_int, switch_id)
        print(
            f"[OFF] ID={switch_id} : {switch_int.to_bytes(5, 'little')} ({switch_int:})\n"
        )


if __name__ == "__main__":
    main()

which gave a transcript of:它提供了以下内容的成绩单:

[start]  b'\x08\x00\x00\x00\x00' (8)

[ON]  ID=1 : b'\x08\x10\x00\x00\x00' (4104)
[OFF] ID=1 : b'\x08\x00\x00\x00\x00' (8)

[ON]  ID=2 : b'\x08\x01\x00\x00\x00' (264)
[OFF] ID=2 : b'\x08\x00\x00\x00\x00' (8)

[ON]  ID=3 : b'\x08\x00\x10\x00\x00' (1048584)
[OFF] ID=3 : b'\x08\x00\x00\x00\x00' (8)

[ON]  ID=4 : b'\x08\x00\x01\x00\x00' (65544)
[OFF] ID=4 : b'\x08\x00\x00\x00\x00' (8)

[ON]  ID=5 : b'\x08\x00\x00\x10\x00' (268435464)
[OFF] ID=5 : b'\x08\x00\x00\x00\x00' (8)

[ON]  ID=6 : b'\x08\x00\x00\x01\x00' (16777224)
[OFF] ID=6 : b'\x08\x00\x00\x00\x00' (8)

[ON]  ID=7 : b'\x08\x00\x00\x00\x10' (68719476744)
[OFF] ID=7 : b'\x08\x00\x00\x00\x00' (8)

[ON]  ID=8 : b'\x08\x00\x00\x00\x01' (4294967304)
[OFF] ID=8 : b'\x08\x00\x00\x00\x00' (8)

If you start with all the switches on, the the switch_on function has no effect because that switch is on already and the switches get turned off one by one with the switch_off function.如果您开始时所有开关都打开,则switch_on函数无效,因为该开关已经打开,并且开关会通过switch_off函数一个接一个地关闭。

[start]  b'\x08\x11\x11\x11\x11' (73300775176)

[ON]  ID=1 : b'\x08\x11\x11\x11\x11' (73300775176)
[OFF] ID=1 : b'\x08\x01\x11\x11\x11' (73300771080)

[ON]  ID=2 : b'\x08\x01\x11\x11\x11' (73300771080)
[OFF] ID=2 : b'\x08\x00\x11\x11\x11' (73300770824)

[ON]  ID=3 : b'\x08\x00\x11\x11\x11' (73300770824)
[OFF] ID=3 : b'\x08\x00\x01\x11\x11' (73299722248)

[ON]  ID=4 : b'\x08\x00\x01\x11\x11' (73299722248)
[OFF] ID=4 : b'\x08\x00\x00\x11\x11' (73299656712)

[ON]  ID=5 : b'\x08\x00\x00\x11\x11' (73299656712)
[OFF] ID=5 : b'\x08\x00\x00\x01\x11' (73031221256)

[ON]  ID=6 : b'\x08\x00\x00\x01\x11' (73031221256)
[OFF] ID=6 : b'\x08\x00\x00\x00\x11' (73014444040)

[ON]  ID=7 : b'\x08\x00\x00\x00\x11' (73014444040)
[OFF] ID=7 : b'\x08\x00\x00\x00\x01' (4294967304)

[ON]  ID=8 : b'\x08\x00\x00\x00\x01' (4294967304)
[OFF] ID=8 : b'\x08\x00\x00\x00\x00' (8)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM