mcolyer/hacklet

`network_id` may be misidentified in `SamplesRequest`

Closed this issue · 10 comments

I'm in the process of converting this hacklet script to python because I want to integrate it into some continuous energy monitoring tools that I'm tinkering with, and I'm somewhat confused in the SamplesRequest packet structure:

The SamplesRequest message describes:

        uint16 :network_id
        uint16 :channel_id

However, the network ID that is shoved into the uint16 is a 64-bit variable. I don't know exactly how ruby handles this, but in my application, it looks like it's being clamped to 0<=n<=2**16, so in the debug trace, I'm seeing 0xFFFF being used as the network ID.

I'm not sure how this works, but it does seem to work.

This isn't the only place that the 64-bit network ID is shoved into a 16-bit variable, either.

Furthermore, I can confirm that I can use random network IDs > 2**16, and successfully read the power draw on my modlet (I'm only interested in monitoring power usage). Is it possible the field identified as network_id is for something else, and it has to be 0xFFFF for the script to work as expected?

Yep, it's entirely possible the network_id isn't the correct name for that variable. I reversed all of this without any documentation.

However I'm confused as to why the network id is a 64 bit variable? Every place that I use the field in this library it's 16 bit. Also it's necessary for me to use different values for that field to select different modlets on my network.

As for the binary munging, I use the bindata library, http://rubydoc.info/gems/bindata/1.8.1/frames

Huh, I had assumed it was related to the hardware MAC address of something, which is, IIRC, 64 bits.

It's also printed as 64 bits when commissioning the network (I think), which is why I though it was 64 bits. If it's really 16 bit, a lot of things make more sense.

It might be a decent idea to check the command line parameters before just sending them out the serial port. Apparently you can give arbitrarily large values as the network_id on the command line without the script complaining (and the somehow work too!).

FWIW, I know this was all done without docs, and I'm not trying to come across as an asshole (I seem to do that by accident). I'm basically entirely just exploiting the fact that you have a working script, even if the underlying hardware isn't fully understood. I'm just trying to provide some (possibly) useful commentary from someone else's perspective.

Anyways, I'm currently dealing with the insane mixed-endianess (seriously, thinkeco: WTF?). The bindata docs seem to be down atm, but I can figure out how it works from just the code. It looks like it's basically similar to packed C structs.

OTOH, this might mean that the network address of 0xFFFF is actually a broadcast address, and everything responds to it, which could be of use. I wonder if it's possible to query multiple modlets in one packet?

Unfortunately, I only have one modlet. Would you be interested in testing that?

Holy crap! Is there a reason you're throwing away all the fractional wattage readings?

(s/13.0).round Whryyyy?

Sure it's probably rather noisy, but you can oversample the snot out of it.

You're right that the command line parsing part should warn if you give a larger than expected value. However the reason it works is that bindata will clamp the data to fit the specified field size.

I did try using 0xFFFF as the network identifier and did receive a response, so it probably is a broadcast thing. However other random values don't seem to return anything. To properly handle the broadcast response the code probably needs some updating (currently it just receives a single response but that may just be due to the fact that is all I expected that code to receive).

The reason I converted using (s/13.0) is that I wanted the displayed values to be in watts for my purposes. As best as I can tell that's the conversion factor, I plugged a kill-a-watt in to check the values.

It's not the conversion to wats I was commenting on, it is the ".round" call which is dropping fractional watts when displayed. AFICT, the data is fully valid, and I simply removed the ".round" from the local copy I'm using.

(It may not be round, this is from memory (I'm not at my PC) in any event, the number is being truncated to an int after the division by 13.0)

Sent from my phone. Please excuse my brevity.


Edit: Yeah, it's round: samples.map { |s| t += 10; [Time.at(t), (s/13.0).round] }

Ok, correction, it looks like I've been mostly doing a stupid. I've been confusing the network ID and the deviceID.

I assumed the network ID was the ID of the whole network, rather then then something like the address of a known device on the local network, and that was why I was confused.

I guess it would be most analogous to say the network ID is like an IP address, and the deviceID is like a MAC address?

More notes:

    class SamplesResponse < Message
        endian :big

        uint8    :header, :check_value => lambda { value == 0x02 }
        uint16   :command, :check_value => lambda { value == 0x40A4 }
        uint8    :payload_length

        uint16   :network_id
        uint16   :channel_id

        # TODO: Confirm this is actually the device id
        uint16   :data

        uint32le :time
        uint8    :sample_count
        uint24le :stored_sample_count
        array    :samples, :type => [:uint16le], :initial_length => :sample_count

        uint8 :checksum, :check_value => lambda { calculate_checksum == checksum }

        def converted_samples
            t = time - 10
            samples.map { |s| t += 10; [Time.at(t), (s/13.0).round] }
        end
    end

I think channel_id isn't really 16 bits. It looks like it's something that is 8-bit (and constant), and then the 8-bit channel_id.

The something is a constant 128 on my modlet, so it's either actually 128, or -128, depending on whether it's signed or not.

I'm also occationally getting a response of a packet with the command value of 0xA0:58, and the content of either 0x15:00:00:00, 0x15:00:01:00, or 0x15:00:02:00, which is interesting.

I'm polling for new data at 1 Hz, so this might be a "too busy to respond* message, though I see it roughly once every 10 seconds (the actual modlet update interval),

Closing because my Modlet spontaneously stopped working.

TL:DR don't buy a modlet for basically anything.