0

I am using a Python library for ANT+ wireless communication with sensors. Communication is done by pairing with an ID, which is a number 4-5 digits long. It was all working fine until one of the devices I tested had an ID of "00625".

Tracking the code, the source of the issue is the struct pack/unpack function used to encode and decode the bytes for the ANT+ protocol message.

>>> from struct import *
>>> pack(b'<H', 11977)
'\xc9.'
>>> unpack(b'<H', '\xc9.')
(11977,)
>>> pack(b'<H', 625)
'q\x02'
>>> unpack(b'<H', 'q\x02')
(625,)
>>> pack(b'<H', 00625)
'\x95\x01'
>>> unpack(b'<H', '\x95\x01')
(405,)

So, when I pack the ANT ID's 11977 or 625 and then unpack them, I get exactly what I packed.

However, when I pack ANT ID 00625 and then unpack it, I get 405 back. Not sure how I can go about dealing with this, so that I can pack 00625 correctly.

martineau
  • 119,623
  • 25
  • 170
  • 301
jftorre
  • 3
  • 2
  • Is it possible to pack 00625 as a string '00625' first, then unpack the result? – JYCH Mar 13 '20 at 17:42
  • @JYCH no, throws out `struct.error: cannot convert argument to integer`. – jftorre Mar 13 '20 at 17:50
  • In Python, leading zeros in decimal integer literals aren't permitted. You will need to strip them off somehow. What is the source of the code with the `00625` literal in it? – martineau Mar 13 '20 at 18:10
  • You must be using Python 2. Integer literals with a leading zero are treated as **octal**. In Python 3 this was removed, but a `0o` prefix still exists. Anyway, how did you get this integer into your code in the first place? Ultimately that's the problem, not `struct.pack`. If it's coming in as a string, you should be using `int()`, not `eval()`, and `raw_input()` instead of `input()`. – wjandrea Mar 13 '20 at 18:36
  • Related: [What do numbers starting with 0 mean in python?](https://stackoverflow.com/q/11620151/4518341) – wjandrea Mar 13 '20 at 19:15

1 Answers1

0

Yes, I'm using Python 2, which means the leading zero will make the int be interpreted as octal. Diving into the how the message is sent, I figured it out, instead of passing the leading zeros, I just pass '625' as the ID, and changed the pack function arguments to pad with two pad bytes. That did the trick, just had to add a condition where any int passed with less than 3 digits gets padded, like so:

>>> from struct import *
>>> pack(b'<H', 625)
'q\x02'
>>> pack(b'<xxH', 625)
'\x00\x00q\x02'

So I created a condition on the message module to use one or the other depending on the size of ID:

    def deviceNumber(self, device_number):
        if len(str(device_number)) > 3 :
            self._payload[1:3] = pack(b'<H', device_number)
        else :
            self._payload[1:3] = pack(b'<xxH', device_number)

Maybe there's a better way, but it's working, that's good enough for now!

Dharman
  • 30,962
  • 25
  • 85
  • 135
jftorre
  • 3
  • 2