mirror of
https://github.com/esphome/esphome.git
synced 2024-11-23 07:28:10 +01:00
Fix handling of timestamps in Teleinfo component. (#2392)
* teleinfo: avoid a buffer overflow.
When reading tag or values, data is written to the buffer even if the
size if bigger than the buffer. Add a new 'max_len' argument to
get_field() to avoid this error.
Signed-off-by: 0hax <0hax@protonmail.com>
* teleinfo: read extra timestamp field for some tags.
Some tags has an extra timestamp field that need to be read before the
actual data.
The code is inspired by Jpsy work:
29339c14f9
Signed-off-by: 0hax <0hax@protonmail.com>
* teleinfo: increase MAX_BUF_SIZE to suffice for 3-phase Linky in Standard mode.
* teleinfo: handle DATE tag correctly.
The DATE tag is special due its format and need to be handled
separately.
Fix from DrCoolzic.
Signed-off-by: 0hax <0hax@protonmail.com>
Co-authored-by: Jörg Wagner <jwagner@digilog.de>
This commit is contained in:
parent
2eb5f89d82
commit
5624fafb3a
2 changed files with 36 additions and 5 deletions
|
@ -7,7 +7,7 @@ namespace teleinfo {
|
||||||
static const char *const TAG = "teleinfo";
|
static const char *const TAG = "teleinfo";
|
||||||
|
|
||||||
/* Helpers */
|
/* Helpers */
|
||||||
static int get_field(char *dest, char *buf_start, char *buf_end, int sep) {
|
static int get_field(char *dest, char *buf_start, char *buf_end, int sep, int max_len) {
|
||||||
char *field_end;
|
char *field_end;
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@ static int get_field(char *dest, char *buf_start, char *buf_end, int sep) {
|
||||||
if (!field_end)
|
if (!field_end)
|
||||||
return 0;
|
return 0;
|
||||||
len = field_end - buf_start;
|
len = field_end - buf_start;
|
||||||
|
if (len >= max_len)
|
||||||
|
return len;
|
||||||
strncpy(dest, buf_start, len);
|
strncpy(dest, buf_start, len);
|
||||||
dest[len] = '\0';
|
dest[len] = '\0';
|
||||||
|
|
||||||
|
@ -106,9 +108,22 @@ void TeleInfo::loop() {
|
||||||
* 0xa | Tag | 0x9 | Data | 0x9 | CRC | 0xd
|
* 0xa | Tag | 0x9 | Data | 0x9 | CRC | 0xd
|
||||||
* ^^^^^^^^^^^^^^^^^^^^^^^^^
|
* ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
* Checksum is computed on the above in standard mode.
|
* Checksum is computed on the above in standard mode.
|
||||||
|
*
|
||||||
|
* Note that some Tags may have a timestamp in Standard mode. In this case
|
||||||
|
* the group would looks like this:
|
||||||
|
* 0xa | Tag | 0x9 | Timestamp | 0x9 | Data | 0x9 | CRC | 0xd
|
||||||
|
*
|
||||||
|
* The DATE tag is a special case. The group looks like this
|
||||||
|
* 0xa | Tag | 0x9 | Timestamp | 0x9 | 0x9 | CRC | 0xd
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
while ((buf_finger = static_cast<char *>(memchr(buf_finger, (int) 0xa, buf_index_ - 1))) &&
|
while ((buf_finger = static_cast<char *>(memchr(buf_finger, (int) 0xa, buf_index_ - 1))) &&
|
||||||
((buf_finger - buf_) < buf_index_)) {
|
((buf_finger - buf_) < buf_index_)) {
|
||||||
|
/*
|
||||||
|
* Make sure timesamp is nullified between each tag as some tags don't
|
||||||
|
* have a timestamp
|
||||||
|
*/
|
||||||
|
timestamp_[0] = '\0';
|
||||||
/* Point to the first char of the group after 0xa */
|
/* Point to the first char of the group after 0xa */
|
||||||
buf_finger += 1;
|
buf_finger += 1;
|
||||||
|
|
||||||
|
@ -123,7 +138,7 @@ void TeleInfo::loop() {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* Get tag */
|
/* Get tag */
|
||||||
field_len = get_field(tag_, buf_finger, grp_end, separator_);
|
field_len = get_field(tag_, buf_finger, grp_end, separator_, MAX_TAG_SIZE);
|
||||||
if (!field_len || field_len >= MAX_TAG_SIZE) {
|
if (!field_len || field_len >= MAX_TAG_SIZE) {
|
||||||
ESP_LOGE(TAG, "Invalid tag.");
|
ESP_LOGE(TAG, "Invalid tag.");
|
||||||
break;
|
break;
|
||||||
|
@ -132,8 +147,22 @@ void TeleInfo::loop() {
|
||||||
/* Advance buf_finger to after the tag and the separator. */
|
/* Advance buf_finger to after the tag and the separator. */
|
||||||
buf_finger += field_len + 1;
|
buf_finger += field_len + 1;
|
||||||
|
|
||||||
/* Get value (after next separator) */
|
/*
|
||||||
field_len = get_field(val_, buf_finger, grp_end, separator_);
|
* If there is two separators and the tag is not equal to "DATE",
|
||||||
|
* it means there is a timestamp to read first.
|
||||||
|
*/
|
||||||
|
if (std::count(buf_finger, grp_end, separator_) == 2 && strcmp(tag_, "DATE") != 0) {
|
||||||
|
field_len = get_field(timestamp_, buf_finger, grp_end, separator_, MAX_TIMESTAMP_SIZE);
|
||||||
|
if (!field_len || field_len >= MAX_TIMESTAMP_SIZE) {
|
||||||
|
ESP_LOGE(TAG, "Invalid Timestamp");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Advance buf_finger to after the first data and the separator. */
|
||||||
|
buf_finger += field_len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
field_len = get_field(val_, buf_finger, grp_end, separator_, MAX_VAL_SIZE);
|
||||||
if (!field_len || field_len >= MAX_VAL_SIZE) {
|
if (!field_len || field_len >= MAX_VAL_SIZE) {
|
||||||
ESP_LOGE(TAG, "Invalid Value");
|
ESP_LOGE(TAG, "Invalid Value");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -11,7 +11,8 @@ namespace teleinfo {
|
||||||
*/
|
*/
|
||||||
static const uint8_t MAX_TAG_SIZE = 64;
|
static const uint8_t MAX_TAG_SIZE = 64;
|
||||||
static const uint16_t MAX_VAL_SIZE = 256;
|
static const uint16_t MAX_VAL_SIZE = 256;
|
||||||
static const uint16_t MAX_BUF_SIZE = 1024;
|
static const uint16_t MAX_BUF_SIZE = 2048;
|
||||||
|
static const uint16_t MAX_TIMESTAMP_SIZE = 14;
|
||||||
|
|
||||||
class TeleInfoListener {
|
class TeleInfoListener {
|
||||||
public:
|
public:
|
||||||
|
@ -36,6 +37,7 @@ class TeleInfo : public PollingComponent, public uart::UARTDevice {
|
||||||
uint32_t buf_index_{0};
|
uint32_t buf_index_{0};
|
||||||
char tag_[MAX_TAG_SIZE];
|
char tag_[MAX_TAG_SIZE];
|
||||||
char val_[MAX_VAL_SIZE];
|
char val_[MAX_VAL_SIZE];
|
||||||
|
char timestamp_[MAX_TIMESTAMP_SIZE];
|
||||||
enum State {
|
enum State {
|
||||||
OFF,
|
OFF,
|
||||||
ON,
|
ON,
|
||||||
|
|
Loading…
Reference in a new issue