kplindegaard / smbus2

A drop-in replacement for smbus-cffi/smbus-python in pure Python
MIT License
243 stars 68 forks source link

read_i2c_block_data response contains length instead of last byte of data #68

Open mefistotelis opened 3 years ago

mefistotelis commented 3 years ago

I need help understanding a response I'm getting from read_i2c_block_data(). I'm using I2C bus on RPI.

When I block read certain offset from the device, read_i2c_block_data() gets the following content of msg.data.contents.block:

0f 0d 05 50 00 36 00 34 00 03 80 00 01 00 03 c5 00
    ^length                                   ^PEC

The smbus2 library cuts the first byte, and gives my python code 13 bytes, as this is the size my script requested. So my script receives:

0d 05 50 00 36 00 34 00 03 80 00 01 00

In other word, I don't receive the last byte of data (03), and instead I receive the size byte (0d).

In order to receive the whole message, I need to actually read 14 bytes, and remove the length from my script. Which is unnecessary, but not that bad until I get to 32-byte block. For 32-byte one, there is no way for me to retrieve the last byte. So the smbus2 library effectively allows me to retrieve message of max 31 bytes.

Where is the issue? Is the library implementation flawed? or is it unexpected that the buffer contains the 0f at start?

kplindegaard commented 3 years ago

Can you share a code example?

mefistotelis commented 3 years ago

Here is the script I'm developing, and the read function which denies me last byte on 32-byte read: https://github.com/mefistotelis/smart-battery-system-bq-experiments/blob/main/comm_sbs_bqctrl.py#L5522

Though it might be a bit overcomplicated for an example.

Within the smbus_read_block_for_basecmd() function, I'm adding "\0" at end of the packet to be able to receive 32-bit packets at all.

mefistotelis commented 3 years ago

Here is simple one:

#!/usr/bin/env python3

import smbus2

ManufacturerInfo = 0x70

dev_addr = 0x0b
with smbus2.SMBus(1) as bus:
    print("Sending ManufacturerInfo block read, expected result: 'abcdefghijklmnopqrstuvwzxy012345'")
    b = bus.read_i2c_block_data(dev_addr, ManufacturerInfo, 32)
    print("Raw response hex: {}".format(" ".join('{:02x}'.format(x) for x in b)))
    print("As bytes: {}".format(bytes(b)))

pass

Output:

pi@raspberrypi:~/dji-firmware-tools $ ./test.py
Sending ManufacturerInfo block read, expected result: 'abcdefghijklmnopqrstuvwzxy012345'
Raw response hex: 20 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 7a 78 79 30 31 32 33 34
As bytes: b' abcdefghijklmnopqrstuvwzxy01234'
pi@raspberrypi:~/dji-firmware-tools $
kplindegaard commented 3 years ago

Wild guess since I haven't used the recently added PEC switch myself. None of my devices use that, but since you referred to trailing PEC info in the first post I'll take a wild guess and say you should enable PEC.

....
with smbus2.SMBus(1) as bus:
    bus.pec = True
    ....

Have no idea if it works but maybe worth a shot?

mefistotelis commented 3 years ago

Tested - no change. I have PEC enabled in the big script, just the test didn't had it enabled.

mefistotelis commented 3 years ago

I workarounded the issue by using 'i2c_msg' instead of normal smbus API. When reading this way, I don't see the additional 0x0f byte at start.

Which also suggests that this additional byte doesn't come from the transfer.