Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Gridstream RF protocol from Landis & Gyr meters #2616

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

krvmk
Copy link

@krvmk krvmk commented Sep 2, 2023

This PR is to add initial support for the Gridstream RF protocol as discussed in #2531.

@merbanan
Copy link
Owner

merbanan commented Sep 2, 2023

Can you add relevant information from here:
https://usermanual.wiki/Landis-Gyr-Technology/IWRS4/html

src/devices/gridstream.c Outdated Show resolved Hide resolved
src/devices/gridstream.c Outdated Show resolved Hide resolved
src/devices/gridstream.c Outdated Show resolved Hide resolved
src/devices/gridstream.c Outdated Show resolved Hide resolved
src/devices/gridstream.c Outdated Show resolved Hide resolved
src/devices/gridstream.c Outdated Show resolved Hide resolved
src/devices/gridstream.c Outdated Show resolved Hide resolved
NULL,
};

r_device const gridstream = {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate this for the other rates and change the name to match the rate.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added the other rates, and they appear to work, at least with my saved samples. However, I cannot determine a way to output the bitrate in the data table.

@merbanan
Copy link
Owner

merbanan commented Sep 2, 2023

Looks good, make sure to run the maintainer update script also.

@gdt
Copy link
Collaborator

gdt commented Oct 5, 2023

There are changes to README and man page that seem like they have crept in vs being intended.

@@ -254,7 +254,9 @@
DECL(fineoffset_ws90) \
DECL(thermopro_tx2c) \
DECL(tfa_303151) \

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Blank line should be left. While it doesn't matter if it's there or not in the grand scheme of things, commits should not make unintended changes.

{0x142A, "Washington", "Puget Sound Energy"},
{0x47F7, "Pennsylvania", "PPL Electric"},
{0x22c6, "Long Island, NY", "PSEG Long Island"}};

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to have a comment explaining if you have to search, or what the grand plan is. Looks like search, but there is wisdom in somebody's head not in comments right now.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decoder attempts all of the known CRC init values until it finds one that works. If they all fail, either the data is actually bad, or the CRC init is not known. In the case of the latter, using the reveng program with several fixed length bytestreams with CRC terminated values can output a previously unknown CRC init value, which is how the current values in the struct were derived. This struct would continue to be expanded (through PRs) as new ones are identified.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's irregular, but makes sense. What I was asking for was for the PR to be amended so that this wisdom would be in the code, so that people reading it, long after the pull request is closed, would see it. (My review is that when we merge, there should be no reason for anyone to look at the PR history.)

0x7F,
0xF8,
};
uint8_t *b = malloc(bitbuffer->bits_per_row[0] / 8);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this is normal, but I'm nervous about not storing b's length for bounds checking.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the maximum size of the gridstream data stream is not known (to my knowledge), and the bitbuffer length can be arbitrarily sized, there was no way to set a maximum size of b without the possibility of overflow issues.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, but needs to be explained in comments in the code to be merged.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The last offset you are reading seems b[37]? Then clamp the input length from bitbuffer->bits_per_row[0] - offset - 37 to 38*8 and use a fixed 38 byte buffer. Or any sane upper length really.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CRC location is based on an offset value that is stored in the datastream and there have been some discovered streams that were much longer than 38 bytes, but are still unknown at this time on how to interpret. Arbitrarily limiting the size would reduce the streams that would captured and/or potentially cause memory exception.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assume a sane upper bound, say 50(?) bytes, and abort with a warning if a longer message is encountered. That will additionally gain insight into the protocol. Potentially accepting multiple kB of message does not sound proper. If length is a byte then it's bounded anyway ;)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't know for certain, but it is believed length is a double byte, as the byte before has always been 0x00. But then again, if it is always 0x00, that is effectively a single byte.


decoder_output_data(decoder, data);
break;
case 0xD2:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spacing is odd here, and that makes it a tiny bit harder to read.

free(b);
return DECODE_FAIL_MIC;
}
sprintf(found_crc, "%04x", known_crc_init[crcidx].value);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get what's happening here, but given that location/provider are not precomputed it made me think there was a bug. I would prefer to see all three handled the same way so that the reader is led to the right conclusion the first time.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

found_crc is the only one that needs a transform to string from the struct in order to be displayed properly in the output data. The location/provider is already in a string.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would still be nice to regularize and/or comment, to make a reader who is competent but not expert quickly come to the right conclusion.

case 0xD5:
sprintf(destaddress_str, "%02x%02x%02x%02x", b[5], b[6], b[7], b[8]);
sprintf(srcaddress_str, "%02x%02x%02x%02x", b[9], b[10], b[11], b[12]);
if (stream_len == 0x47) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are conditional "include this item if this condition is true" constructs. That would perhaps make this easier to understand, if that fits the situation.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would conditional items be handled with the data output? I've not found any example of conditional constructs with data make.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See fineoffset_ws80.c and look for DATA_COND. There is a way to have what is basically an if statement and if true, the item is added, and if not, it isn't. At least that's my understanding.

# protocol 245 # ThermoPro TX-2C Thermometer
# protocol 245 # ThermoPro TX-2C Thermometer and Humidity sensor
protocol 246 # TFA 30.3151 Weather Station
protocol 247 # Gridstream decoder
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"decoder" has no content . Perhaps

   protocol 247 # Gridstream (power meters)

if that's fair.

@doctorkb
Copy link

doctorkb commented Feb 2, 2024

I think this might solve my need -- my water company just swapped out the previous meter for a Landis + Gyr GridLinx Interpreter which supposedly connects to the GridStream network.

Is there anything I can do to help get this out of development and into production? It looks like it maybe just failed a single test on code style...

@zuckschwerdt
Copy link
Collaborator

The CRLF problems need to be fixed, indentation fixed, some comments added, style updated (as per the review) and then it should be in good shape to merge. Maybe we need to manually merge with those fixes added?

@zuckschwerdt zuckschwerdt self-assigned this Feb 2, 2024
@krvmk
Copy link
Author

krvmk commented Feb 2, 2024

The CRLF problems need to be fixed, indentation fixed, some comments added, style updated (as per the review) and then it should be in good shape to merge. Maybe we need to manually merge with those fixes added?

The CRLF problems need to be fixed, indentation fixed, some comments added, style updated (as per the review) and then it should be in good shape to merge. Maybe we need to manually merge with those fixes added?

I had made most of the recommendations to a separate branch here, but I have not had time to move forward with finishing it up.
https://github.com/krvmk/rtl_433/blob/merge/src/devices/gridstream.c

@krvmk
Copy link
Author

krvmk commented Feb 3, 2024

Reset and repushed, but the maintainer_update changed a bunch of other stuff in the README. I'm not sure why.

@krvmk
Copy link
Author

krvmk commented Feb 7, 2024

Should be good to go now.

@gdt
Copy link
Collaborator

gdt commented Jun 4, 2024

What's the status of this? I'm looking at #2531 and it sort of seems like if this were merged we could close the issue and be down two items.

@merbanan
Copy link
Owner

merbanan commented Jun 4, 2024

LGTM, we need some samples though and a rebase.

@krvmk
Copy link
Author

krvmk commented Jun 5, 2024

LGTM, we need some samples though and a rebase.

What do you need for samples? There are some in the issue discussion #2531.

@zuckschwerdt
Copy link
Collaborator

Trouble is that this decoder uses extra headers of stdbool.h, stdlib.h, time.h. The one single bool should be an int.
The usage of strftime() and localtime() is understandable but not what we usually have in decoders.
What is stdlib used for?

@gdt
Copy link
Collaborator

gdt commented Jun 5, 2024

stdlib.h is described at https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/stdlib.h.html

It is surprising to me that use of it in rtl_433 is novel. But

./output_mqtt.c:#include <stdlib.h>
./output_influx.c:#include <stdlib.h>
./bitbuffer.c:#include <stdlib.h>
./pulse_data.c:#include <stdlib.h>
./output_file.c:#include <stdlib.h>
./baseband.c:#include <stdlib.h>
./data_tag.c:#include <stdlib.h>
./output_trigger.c:#include <stdlib.h>
./sdr.c:#include <stdlib.h>
./pulse_slicer.c:#include <stdlib.h>
./optparse.c:#include <stdlib.h>
./write_sigrok.c:#include <stdlib.h>
./pulse_detect.c:#include <stdlib.h>
./decoder_util.c:#include <stdlib.h>
./r_util.c:#include <stdlib.h>
./pulse_analyzer.c:#include <stdlib.h>
./rtl_433.c:#include <stdlib.h>
./r_api.c:#include <stdlib.h>
./term_ctl.c:#include <stdlib.h>
./samp_grab.c:#include <stdlib.h>
./mongoose.c:#include <stdlib.h>
./mongoose.c:#include <stdlib.h>
./am_analyze.c:#include <stdlib.h>
./bit_util.c:#include <stdlib.h>
./data.c:#include <stdlib.h>
./confparse.c:#include <stdlib.h>
./getopt/getopt.c:/* Don't include stdlib.h for non-GNU C libraries because some of them
./getopt/getopt.c:# include <stdlib.h>
./getopt/getopt.c:#include <stdlib.h>
./compat_paths.c:#include <stdlib.h>
./output_rtltcp.c:#include <stdlib.h>
./list.c:#include <stdlib.h>
./output_log.c:#include <stdlib.h>
./output_udp.c:#include <stdlib.h>
./pulse_detect_fsk.c:#include <stdlib.h>
./devices/flex.c:#include <stdlib.h>
./devices/blueline.c:#include <stdlib.h>
./fileformat.c:#include <stdlib.h>

@zuckschwerdt
Copy link
Collaborator

It is surprising to me that use of it in rtl_433 is novel. But

Decoders (src/devices) should not use stdlib because alloc() should not be used. It appears you don't alloc, why do you need stdlib?

@gdt
Copy link
Collaborator

gdt commented Jun 5, 2024

I guess it's easy enough to remove it and see if there is a warning.

@krvmk
Copy link
Author

krvmk commented Jun 5, 2024

It is surprising to me that use of it in rtl_433 is novel. But

Decoders (src/devices) should not use stdlib because alloc() should not be used. It appears you don't alloc, why do you need stdlib?

Good catch. It was leftover from the initial attempt using a dynamic bitbuffer size.

@dustMason
Copy link

Any chance this could be merged? I'd love to use this functionality! Thanks

@zuckschwerdt
Copy link
Collaborator

Yes, this is nearly good for merge. We need to find out if strftime() could be replaced, move the static crc_init var to a const, and attach the doc-comment with @fn.

@krvmk
Copy link
Author

krvmk commented Dec 28, 2024

Yes, this is nearly good for merge. We need to find out if strftime() could be replaced, move the static crc_init var to a const, and attach the doc-comment with @fn.

Without adding a local function to convert unix time to replace strftime(), I can only think to output the timestamp as int instead.

Added the rest and sync with current.

{0x2C22, "Santa Barbara, CA", "Southern California Edison"},
{0x142A, "Washington", "Puget Sound Energy"},
{0x47F7, "Pennsylvania", "PPL Electric"},
{0x22c6, "Long Island, NY", "PSEG Long Island"}};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one more entry:

{0x8819, "Alameda, CA", "Alameda Municipal Power"}};

@zuckschwerdt
Copy link
Collaborator

Thanks! One last question, subtype will split the output on e.g. MQTT messages. It is meant for something like compound devices where there are multiple very distinct functions, e.g. a fictional PIR motion and temperature sensor, or a meter that can be configured to electric or water. Is this the case here? Should the output be seen as multiple different devices depending on subtype? Or is it more like a msg_type where there are alternating values for the same device, e.g. a weather station that sends temp/hum and wind data in different messages.

@krvmk
Copy link
Author

krvmk commented Dec 28, 2024

Honestly, I don't really know. The protocol is not documented and there is limited knowledge on what each subtype is for. These are broadcasts and relays from utility meters in a mesh network, and the only relatively useful data that can be determined at this point is meter uptime.

If it makes sense to be merged as disabled by default, I'm good with that.

@zuckschwerdt
Copy link
Collaborator

It's okay to merge as-is. Just beware that MQTT topic might look different than expected and that we might change subtype to msg_type later to make it more useable and standard-looking.

The error is from static struct const crc_init known_crc_init. It should be static struct crc_init const known_crc_init I guess (struct crc_init is the type name).

@krvmk
Copy link
Author

krvmk commented Dec 29, 2024

That's what I get for committing before a test compile. Should be fixed now.

I'm without a suitable SDR at the moment, so I can't test with a live signal, but my old captures still work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants