citruz / beacontools

A Python beacon scanning library.
MIT License
159 stars 59 forks source link

Question - Eddystone TLM - Reading temperature with decimal #22

Closed ghost closed 6 years ago

ghost commented 6 years ago

Hello,

Sorry to bother you but I'm trying to read a decimal temperature from a Minew eddyston beacon.

I have the constructor documentation but I don't understand how to modify your code to read correctly the temperature. My question is probably basic but my knowledges are also very limited

Below is the data format, may I ask you if you have a few time to take a look and give me some hints on how to be able to read the temperature and maybe the humidity.

Thanks a lot anyway for your beacontools is great

image

citruz commented 6 years ago

Given the format description in the picture, it should be easy to add the new format. You need to add a new frame type with the type identifier 0xA1 to the ServiceData struct in structs/eddystone.py and implement the new frame struct (version number, battery level, temp, hum and mac). Then implement a new wrapper class in packet_types/eddystone.py and add it to parser.py. I could also implement it myself, but I am quite busy at the moment. However, I am happy to assist you if you have further questions.

ghost commented 6 years ago

Thank you very much for your help, I will try tonight and come back if needed

ghost commented 6 years ago

For the moment I'm a struggling at creating the new frame structure with correct data type but I will try again during the week

ghost commented 6 years ago

After a couple of hours, I'm still blocked at some point, it seems that my frame is filtered at some points but I'm not able to understand why.

Here is what I have done, if you could give it a look it would be greate

I've added the new frame type in ServiceData:

ServiceData = Struct(
    "eddystone_identifier" / Const(EDDYSTONE_UUID),
    "frame_type" / Byte,
    "frame" / Switch(lambda ctx: ctx.frame_type,
                     {
                         EDDYSTONE_UID_FRAME: EddystoneUIDFrame,
                         EDDYSTONE_URL_FRAME: EddystoneURLFrame,
                         EDDYSTONE_TLM_FRAME: EddystoneTLMFrame,
                         EDDYSTONE_EID_FRAME: EddystoneEIDFrame,
                         EDDYSTONE_HT_FRAME: HTFrame,
                        }
                    )
)

creating the frame type itself (types are probably wrong) :

HTFrame = Struct(
    "version" / Int8ul,
    "battery" / Int8ul,
    "temperature" / Int16ub,
    "humidity" / Int16ub,
    "mac" / Array(16, Byte),
)

I've created the new frame struct in Packet_typeeddystone.py :

class HTFrame(object):

    def __init__(self, data):
        self._version = data['version']
        self._battery = data['battery']
        self._temperature = data['temperature']
        self._humidity = data['humidity']
        self._mac = data['mac']

    @property
    def version(self):
        return self._version

    @property
    def battery(self):
        return self._battery

    @property
    def temperature(self):
        return self._temperature

    @property
    def humidity(self):
        return self._humidity

    @property
    def mac(self):
        return self._mac

    def __str__(self):
        return "HTFrame test : " + str(self._temperature)

I've modified the parser to include the new frame type :

def parse_eddystone_packet(packet):
    """Parse an eddystone beacon advertisement packet."""
    try:
        frame = EddystoneFrame.parse(packet)
        for tlv in frame:
            if tlv['type'] == SERVICE_DATA_TYPE:
                data = tlv['value']
                if data['frame_type'] == EDDYSTONE_UID_FRAME:
                    return EddystoneUIDFrame(data['frame'])
                elif data['frame_type'] == 161:
                    return HTFrame(data['frame'])
                elif data['frame_type'] == EDDYSTONE_TLM_FRAME:
                    if data['frame']['tlm_version'] == EDDYSTONE_TLM_ENCRYPTED:
                        return EddystoneEncryptedTLMFrame(data['frame']['data'])
                    elif data['frame']['tlm_version'] == EDDYSTONE_TLM_UNENCRYPTED:
                        return EddystoneTLMFrame(data['frame']['data'])
                elif data['frame_type'] == EDDYSTONE_URL_FRAME:
                    return EddystoneURLFrame(data['frame'])
                elif data['frame_type'] == EDDYSTONE_EID_FRAME:
                    return EddystoneEIDFrame(data['frame'])
    except ConstructError:
        return None

    return None
citruz commented 6 years ago

Looks good so far. To calculate the real temperature and humidity you need to convert it to a fixed point decimal number in the getter methods. The reason why your packets are still filtered could be that your beacon uses another UUID. Currently, 0xaafe is hardcoded but yours seems to use 0xe1ff. Try adding a “ or Const(b”\xe1\xff”)” everywhere the uuid is used in eddystone.py. You also always dump the packet bytes and inspect them manually.

ghost commented 6 years ago

Thank you !

It works :)

citruz commented 6 years ago

Great! Please submit your changes as a pull request so that others can use them as well.

ghost commented 6 years ago

Hi I will do it next week

citruz commented 6 years ago

Any news on this?

ravirajpspl2023 commented 1 year ago

this is wonderful but in case beacon ACC packet which changes do inside your repo...