Merge pull request #16538 from gemu2015/sml_update

add kamstrup, fix mbus u32
This commit is contained in:
Jason2866 2022-09-16 13:11:23 +02:00 committed by GitHub
commit 9c7e536618
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -53,6 +53,8 @@
#define TMSBSIZ 256 #define TMSBSIZ 256
#endif #endif
#define MODBUS_DEBUG
// addresses a bug in meter DWS74 // addresses a bug in meter DWS74
//#define DWS74_BUG //#define DWS74_BUG
@ -1609,16 +1611,16 @@ void sml_empty_receiver(uint32_t meters) {
void sml_shift_in(uint32_t meters,uint32_t shard) { void sml_shift_in(uint32_t meters,uint32_t shard) {
uint32_t count; uint32_t count;
#ifndef SML_OBIS_LINE #ifndef SML_OBIS_LINE
if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p' && meter_desc_p[meters].type!='R' && meter_desc_p[meters].type!='v') { if (meter_desc_p[meters].type != 'e' && meter_desc_p[meters].type != 'k' && meter_desc_p[meters].type != 'm' && meter_desc_p[meters].type != 'M' && meter_desc_p[meters].type != 'p' && meter_desc_p[meters].type != 'R' && meter_desc_p[meters].type != 'v') {
#else #else
if (meter_desc_p[meters].type!='o' && meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p' && meter_desc_p[meters].type!='R' && meter_desc_p[meters].type!='v') { if (meter_desc_p[meters].type!= 'o' && meter_desc_p[meters].type != 'e' && meter_desc_p[meters].type != 'k' && meter_desc_p[meters].type != 'm' && meter_desc_p[meters].type != 'M' && meter_desc_p[meters].type != 'p' && meter_desc_p[meters].type != 'R' && meter_desc_p[meters].type != 'v') {
#endif #endif
// shift in // shift in
for (count=0; count<SML_BSIZ-1; count++) { for (count = 0; count < SML_BSIZ - 1; count++) {
smltbuf[meters][count]=smltbuf[meters][count+1]; smltbuf[meters][count] = smltbuf[meters][count + 1];
} }
} }
uint8_t iob=(uint8_t)meter_ss[meters]->read(); uint8_t iob = (uint8_t)meter_ss[meters]->read();
if (meter_desc_p[meters].type == 'o') { if (meter_desc_p[meters].type == 'o') {
#ifndef SML_OBIS_LINE #ifndef SML_OBIS_LINE
@ -1639,37 +1641,73 @@ void sml_shift_in(uint32_t meters,uint32_t shard) {
smltbuf[meters][SML_BSIZ-1]=iob; smltbuf[meters][SML_BSIZ-1]=iob;
} else if (meter_desc_p[meters].type=='r') { } else if (meter_desc_p[meters].type=='r') {
smltbuf[meters][SML_BSIZ-1]=iob; smltbuf[meters][SML_BSIZ-1]=iob;
} else if (meter_desc_p[meters].type=='m' || meter_desc_p[meters].type=='M') { } else if (meter_desc_p[meters].type=='m' || meter_desc_p[meters].type=='M' || meter_desc_p[meters].type=='k') {
smltbuf[meters][meter_spos[meters]] = iob;
meter_spos[meters]++; if (meter_desc_p[meters].type=='k') {
if (meter_spos[meters]>=SML_BSIZ) { // Kamstrup
meter_spos[meters]=0; if (iob == 0x40) {
} meter_spos[meters] = 0;
if (meter_spos[meters]>=8) { } else if (iob == 0x0d) {
uint32_t mlen=smltbuf[meters][2]+5; uint16_t crc = KS_calculateCRC(&smltbuf[meters][0], meter_spos[meters]);
if (mlen>SML_BSIZ) mlen=SML_BSIZ; if (!crc) {
if (meter_spos[meters]>=mlen) { uint8_t *ucp = &smltbuf[meters][0];
SML_Decode(meters); for (uint16_t cnt = 0; cnt < meter_spos[meters]; cnt++) {
uint8_t iob = smltbuf[meters][cnt];
if (iob == 0x1b) {
*ucp++ = smltbuf[meters][cnt + 1] ^ 0xff;
cnt++;
} else {
*ucp++ = iob;
}
}
SML_Decode(meters);
}
sml_empty_receiver(meters); sml_empty_receiver(meters);
meter_spos[meters]=0; meter_spos[meters] = 0;
} else {
smltbuf[meters][meter_spos[meters]] = iob;
meter_spos[meters]++;
if (meter_spos[meters] >= SML_BSIZ) {
meter_spos[meters] = 0;
}
}
} else {
smltbuf[meters][meter_spos[meters]] = iob;
meter_spos[meters]++;
if (meter_spos[meters] >= SML_BSIZ) {
meter_spos[meters] = 0;
}
// modbus
if (meter_spos[meters] >= 8) {
uint32_t mlen = smltbuf[meters][2] + 5;
if (mlen > SML_BSIZ) mlen = SML_BSIZ;
if (meter_spos[meters] >= mlen) {
#ifdef MODBUS_DEBUG
AddLog(LOG_LEVEL_INFO, PSTR("receive index >> %d"),meter_desc_p[meters].index);
Hexdump(smltbuf[meters], 10);
#endif
SML_Decode(meters);
sml_empty_receiver(meters);
meter_spos[meters] = 0;
}
} }
} }
} else if (meter_desc_p[meters].type=='p') { } else if (meter_desc_p[meters].type == 'p') {
smltbuf[meters][meter_spos[meters]] = iob; smltbuf[meters][meter_spos[meters]] = iob;
meter_spos[meters]++; meter_spos[meters]++;
if (meter_spos[meters]>=7) { if (meter_spos[meters] >= 7) {
SML_Decode(meters); SML_Decode(meters);
sml_empty_receiver(meters); sml_empty_receiver(meters);
meter_spos[meters]=0; meter_spos[meters] = 0;
} }
} else if (meter_desc_p[meters].type=='R') { } else if (meter_desc_p[meters].type == 'R') {
smltbuf[meters][meter_spos[meters]] = iob; smltbuf[meters][meter_spos[meters]] = iob;
meter_spos[meters]++; meter_spos[meters]++;
if (meter_spos[meters]>=SML_BSIZ) { if (meter_spos[meters] >= SML_BSIZ) {
meter_spos[meters]=0; meter_spos[meters] = 0;
} }
} else if (meter_desc_p[meters].type=='v') { } else if (meter_desc_p[meters].type == 'v') {
if (iob==EBUS_SYNC) { if (iob == EBUS_SYNC) {
sb_counter = 0; sb_counter = 0;
SML_Decode(meters); SML_Decode(meters);
smltbuf[meters][0] = iob; smltbuf[meters][0] = iob;
@ -1682,15 +1720,15 @@ void sml_shift_in(uint32_t meters,uint32_t shard) {
} }
} }
else { else {
if (iob==EBUS_SYNC) { if (iob == EBUS_SYNC) {
// should be end of telegramm // should be end of telegramm
// QQ,ZZ,PB,SB,NN ..... CRC, ACK SYNC // QQ,ZZ,PB,SB,NN ..... CRC, ACK SYNC
if (meter_spos[meters]>4+5) { if (meter_spos[meters] > 4 + 5) {
// get telegramm lenght // get telegramm lenght
uint16_t tlen=smltbuf[meters][4]+5; uint16_t tlen = smltbuf[meters][4] + 5;
// test crc // test crc
if (smltbuf[meters][tlen]=ebus_CalculateCRC(smltbuf[meters],tlen)) { if (smltbuf[meters][tlen] = ebus_CalculateCRC(smltbuf[meters], tlen)) {
ebus_esc(smltbuf[meters],tlen); ebus_esc(smltbuf[meters], tlen);
SML_Decode(meters); SML_Decode(meters);
} else { } else {
// crc error // crc error
@ -1702,15 +1740,15 @@ void sml_shift_in(uint32_t meters,uint32_t shard) {
} }
smltbuf[meters][meter_spos[meters]] = iob; smltbuf[meters][meter_spos[meters]] = iob;
meter_spos[meters]++; meter_spos[meters]++;
if (meter_spos[meters]>=SML_BSIZ) { if (meter_spos[meters] >= SML_BSIZ) {
meter_spos[meters]=0; meter_spos[meters] = 0;
} }
} }
sb_counter++; sb_counter++;
#ifndef SML_OBIS_LINE #ifndef SML_OBIS_LINE
if (meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p' && meter_desc_p[meters].type!='R' && meter_desc_p[meters].type!='v') SML_Decode(meters); if (meter_desc_p[meters].type != 'e' && meter_desc_p[meters].type != 'm' && meter_desc_p[meters].type != 'M' && meter_desc_p[meters].type != 'k' && meter_desc_p[meters].type != 'p' && meter_desc_p[meters].type != 'R' && meter_desc_p[meters].type != 'v') SML_Decode(meters);
#else #else
if (meter_desc_p[meters].type!='o' && meter_desc_p[meters].type!='e' && meter_desc_p[meters].type!='m' && meter_desc_p[meters].type!='M' && meter_desc_p[meters].type!='p' && meter_desc_p[meters].type!='R' && meter_desc_p[meters].type!='v') SML_Decode(meters); if (meter_desc_p[meters].type != 'o' && meter_desc_p[meters].type != 'e' && meter_desc_p[meters].type != 'm' && meter_desc_p[meters].type != 'M' && meter_desc_p[meters].type != 'k' && meter_desc_p[meters].type != 'p' && meter_desc_p[meters].type != 'R' && meter_desc_p[meters].type != 'v') SML_Decode(meters);
#endif #endif
} }
@ -1723,7 +1761,7 @@ uint32_t meters;
if (meter_desc_p[meters].type != 'c') { if (meter_desc_p[meters].type != 'c') {
// poll for serial input // poll for serial input
if (!meter_ss[meters]) continue; if (!meter_ss[meters]) continue;
if (ser_act_LED_pin!=255 && (ser_act_meter_num==0 || ser_act_meter_num-1==meters)) { if (ser_act_LED_pin != 255 && (ser_act_meter_num == 0 || ser_act_meter_num - 1 == meters)) {
digitalWrite(ser_act_LED_pin, meter_ss[meters]->available() && !digitalRead(ser_act_LED_pin)); // Invert LED, if queue is continuously full digitalWrite(ser_act_LED_pin, meter_ss[meters]->available() && !digitalRead(ser_act_LED_pin)); // Invert LED, if queue is continuously full
} }
while (meter_ss[meters]->available()) { while (meter_ss[meters]->available()) {
@ -1944,7 +1982,7 @@ void SML_Decode(uint8_t index) {
// compare value // compare value
uint8_t found = 1; uint8_t found = 1;
double ebus_dval = 99; double ebus_dval = 99;
float mbus_dval = 99; double mbus_dval = 99;
while (*mp != '@') { while (*mp != '@') {
if (meter_desc_p[mindex].type == 'o' || meter_desc_p[mindex].type == 'c') { if (meter_desc_p[mindex].type == 'o' || meter_desc_p[mindex].type == 'c') {
if (*mp++ != *cp++) { if (*mp++ != *cp++) {
@ -2083,6 +2121,28 @@ void SML_Decode(uint8_t index) {
mbus_dval = (float)((cp[0]<<8) | cp[1]); mbus_dval = (float)((cp[0]<<8) | cp[1]);
mp += 4; mp += 4;
cp += 2; cp += 2;
} else if (!strncmp(mp, "kstr", 4)) {
mp += 4;
// decode the mantissa
uint32_t x = 0;
for (uint16_t i = 0; i < cp[5]; i++) {
x <<= 8;
x |= cp[i + 7];
}
// decode the exponent
int32_t i = cp[6] & 0x3f;
if (cp[6] & 0x40) {
i = -i;
};
//float ifl = pow(10, i);
float ifl = 1;
for (uint16_t x = 1; x <= i; ++x) {
ifl *= 10;
}
if (cp[6] & 0x80) {
ifl = -ifl;
}
mbus_dval = (double )(x * ifl);
} else if (!strncmp(mp, "bcd", 3)) { } else if (!strncmp(mp, "bcd", 3)) {
mp += 3; mp += 3;
uint8_t digits = strtol((char*)mp, (char**)&mp, 10); uint8_t digits = strtol((char*)mp, (char**)&mp, 10);
@ -2229,7 +2289,7 @@ void SML_Decode(uint8_t index) {
} }
} else { } else {
double dval; double dval;
if (meter_desc_p[mindex].type!='e' && meter_desc_p[mindex].type!='r' && meter_desc_p[mindex].type!='m' && meter_desc_p[mindex].type!='M' && meter_desc_p[mindex].type!='p' && meter_desc_p[mindex].type!='v') { if (meter_desc_p[mindex].type!='e' && meter_desc_p[mindex].type!='r' && meter_desc_p[mindex].type!='m' && meter_desc_p[mindex].type!='M' && meter_desc_p[mindex].type!='k' && meter_desc_p[mindex].type!='p' && meter_desc_p[mindex].type!='v') {
// get numeric values // get numeric values
if (meter_desc_p[mindex].type=='o' || meter_desc_p[mindex].type=='c') { if (meter_desc_p[mindex].type=='o' || meter_desc_p[mindex].type=='c') {
if (*mp == '(') { if (*mp == '(') {
@ -2262,47 +2322,53 @@ void SML_Decode(uint8_t index) {
} }
} else { } else {
// ebus pzem vbus or mbus or raw // ebus pzem vbus or mbus or raw
if (*mp=='b') { if (*mp == 'b') {
mp++; mp++;
uint8_t shift = *mp&7; uint8_t shift = *mp&7;
ebus_dval = (uint32_t)ebus_dval>>shift; ebus_dval = (uint32_t)ebus_dval>>shift;
ebus_dval = (uint32_t)ebus_dval&1; ebus_dval = (uint32_t)ebus_dval&1;
mp+=2; mp+=2;
} }
if (*mp=='i') { if (*mp == 'i') {
// mbus index // mbus index
mp++; mp++;
uint8_t mb_index=strtol((char*)mp,(char**)&mp,10); uint8_t mb_index = strtol((char*)mp, (char**)&mp, 10);
if (mb_index!=meter_desc_p[mindex].index) { if (mb_index != meter_desc_p[mindex].index) {
goto nextsect; goto nextsect;
} }
uint16_t pos = smltbuf[mindex][2]+3; if (meter_desc_p[mindex].type == 'k') {
if (pos>32) pos=32; // crc is already checked, get float value
uint16_t crc = MBUS_calculateCRC(&smltbuf[mindex][0],pos,0xFFFF); dval = mbus_dval;
if (lowByte(crc)!=smltbuf[mindex][pos]) goto nextsect; mp++;
if (highByte(crc)!=smltbuf[mindex][pos+1]) goto nextsect;
dval=mbus_dval;
//AddLog(LOG_LEVEL_INFO, PSTR(">> %s"),mp);
mp++;
} else {
if (meter_desc_p[mindex].type=='p') {
uint8_t crc = SML_PzemCrc(&smltbuf[mindex][0],6);
if (crc!=smltbuf[mindex][6]) goto nextsect;
dval=mbus_dval;
} else { } else {
dval=ebus_dval; uint16_t pos = smltbuf[mindex][2] + 3;
if (pos > 32) pos = 32;
uint16_t crc = MBUS_calculateCRC(&smltbuf[mindex][0], pos, 0xFFFF);
if (lowByte(crc) != smltbuf[mindex][pos]) goto nextsect;
if (highByte(crc) != smltbuf[mindex][pos + 1]) goto nextsect;
dval = mbus_dval;
//AddLog(LOG_LEVEL_INFO, PSTR(">> %s"),mp);
mp++;
}
} else {
if (meter_desc_p[mindex].type == 'p') {
uint8_t crc = SML_PzemCrc(&smltbuf[mindex][0],6);
if (crc != smltbuf[mindex][6]) goto nextsect;
dval = mbus_dval;
} else {
dval = ebus_dval;
} }
} }
} }
#ifdef USE_SML_MEDIAN_FILTER #ifdef USE_SML_MEDIAN_FILTER
if (meter_desc_p[mindex].flag&16) { if (meter_desc_p[mindex].flag & 16) {
meter_vars[vindex]=sml_median(&sml_mf[vindex],dval); meter_vars[vindex] = sml_median(&sml_mf[vindex], dval);
} else { } else {
meter_vars[vindex]=dval; meter_vars[vindex] = dval;
} }
#else #else
meter_vars[vindex]=dval; meter_vars[vindex] = dval;
#endif #endif
//AddLog(LOG_LEVEL_INFO, PSTR(">> %s"),mp); //AddLog(LOG_LEVEL_INFO, PSTR(">> %s"),mp);
@ -2356,11 +2422,11 @@ void SML_Immediate_MQTT(const char *mp,uint8_t index,uint8_t mindex) {
jname[count]=*cp++; jname[count]=*cp++;
} }
cp++; cp++;
uint8_t dp=atoi(cp); uint8_t dp = atoi(cp);
if (dp&0x10) { if (dp & 0x10) {
// immediate mqtt // immediate mqtt
dtostrfd(meter_vars[index],dp&0xf,tpowstr); dtostrfd(meter_vars[index], dp & 0xf, tpowstr);
ResponseTime_P(PSTR(",\"%s\":{\"%s\":%s}}"),meter_desc_p[mindex].prefix,jname,tpowstr); ResponseTime_P(PSTR(",\"%s\":{\"%s\":%s}}"), meter_desc_p[mindex].prefix, jname, tpowstr);
MqttPublishTeleSensor(); MqttPublishTeleSensor();
} }
} }
@ -2383,22 +2449,22 @@ void SML_Show(boolean json) {
if (!meters_used) return; if (!meters_used) return;
int8_t lastmind=((*mp)&7)-1; int8_t lastmind = ((*mp) & 7) - 1;
if (lastmind<0 || lastmind>=meters_used) lastmind=0; if (lastmind < 0 || lastmind >= meters_used) lastmind = 0;
while (mp != NULL) { while (mp != NULL) {
if (*mp==0) break; if (*mp == 0) break;
// setup sections // setup sections
mindex=((*mp)&7)-1; mindex = ((*mp) & 7) - 1;
if (mindex<0 || mindex>=meters_used) mindex=0; if (mindex < 0 || mindex >= meters_used) mindex = 0;
if (meter_desc_p[mindex].prefix[0]=='*' && meter_desc_p[mindex].prefix[1]==0) { if (meter_desc_p[mindex].prefix[0] == '*' && meter_desc_p[mindex].prefix[1] == 0) {
nojson = 1; nojson = 1;
} else { } else {
nojson = 0; nojson = 0;
} }
mp+=2; mp += 2;
if (*mp=='=' && *(mp+1)=='h') { if (*mp == '=' && *(mp+1) == 'h') {
mp+=2; mp += 2;
// html tag // html tag
if (json) { if (json) {
mp = strchr(mp, '|'); mp = strchr(mp, '|');
@ -2407,49 +2473,49 @@ void SML_Show(boolean json) {
} }
// web ui export // web ui export
uint8_t i; uint8_t i;
for (i=0;i<sizeof(tpowstr)-2;i++) { for (i = 0; i < sizeof(tpowstr) - 2; i++) {
if (*mp=='|' || *mp==0) break; if (*mp == '|' || *mp == 0) break;
tpowstr[i]=*mp++; tpowstr[i] = *mp++;
} }
tpowstr[i]=0; tpowstr[i] = 0;
// export html // export html
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s{s}%s{e}", b_mqtt_data,tpowstr); //snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s{s}%s{e}", b_mqtt_data,tpowstr);
WSContentSend_PD(PSTR("{s}%s{e}"),tpowstr); WSContentSend_PD(PSTR("{s}%s{e}"), tpowstr);
// rewind, to ensure strchr // rewind, to ensure strchr
mp--; mp--;
mp = strchr(mp, '|'); mp = strchr(mp, '|');
if (mp) mp++; if (mp) mp++;
continue; continue;
} }
if (*mp=='=' && *(mp+1)=='s') { if (*mp == '=' && *(mp + 1) == 's') {
mp = strchr(mp, '|'); mp = strchr(mp, '|');
if (mp) mp++; if (mp) mp++;
continue; continue;
} }
// skip compare section // skip compare section
cp=strchr(mp,'@'); cp=strchr(mp, '@');
if (cp) { if (cp) {
cp++; cp++;
tststr: tststr:
if (*cp=='#') { if (*cp == '#') {
// meter id // meter id
if (*(cp + 1) == 'x') { if (*(cp + 1) == 'x') {
// convert hex to asci // convert hex to asci
sml_hex_asci(mindex, tpowstr); sml_hex_asci(mindex, tpowstr);
} else { } else {
sprintf(tpowstr,"\"%s\"",&meter_id[mindex][0]); sprintf(tpowstr,"\"%s\"", &meter_id[mindex][0]);
} }
mid=1; mid = 1;
} else if (*cp=='(') { } else if (*cp == '(') {
if (meter_desc_p[mindex].type=='o') { if (meter_desc_p[mindex].type == 'o') {
cp++; cp++;
strtol((char*)cp,(char**)&cp, 10); strtol((char*)cp,(char**)&cp, 10);
cp++; cp++;
goto tststr; goto tststr;
} else { } else {
mid=0; mid = 0;
} }
} else if (*cp=='b') { } else if (*cp == 'b') {
// bit value // bit value
#ifdef SML_BIT_TEXT #ifdef SML_BIT_TEXT
sprintf_P(tpowstr, PSTR("\"%s\""), (uint8_t)meter_vars[index]?D_ON:D_OFF); sprintf_P(tpowstr, PSTR("\"%s\""), (uint8_t)meter_vars[index]?D_ON:D_OFF);
@ -2459,41 +2525,41 @@ void SML_Show(boolean json) {
mid = 0; mid = 0;
} }
// skip scaling // skip scaling
cp=strchr(cp,','); cp = strchr(cp, ',');
if (cp) { if (cp) {
// this is the name in web UI // this is the name in web UI
cp++; cp++;
for (count=0;count<sizeof(name);count++) { for (count = 0; count < sizeof(name); count++) {
if (*cp==',') { if (*cp == ',') {
name[count]=0; name[count] = 0;
break; break;
} }
name[count]=*cp++; name[count] = *cp++;
} }
cp++; cp++;
for (count=0;count<sizeof(unit);count++) { for (count = 0; count < sizeof(unit); count++) {
if (*cp==',') { if (*cp == ',') {
unit[count]=0; unit[count] = 0;
break; break;
} }
unit[count]=*cp++; unit[count] = *cp++;
} }
cp++; cp++;
for (count=0;count<sizeof(jname);count++) { for (count = 0; count < sizeof(jname); count++) {
if (*cp==',') { if (*cp == ',') {
jname[count]=0; jname[count] = 0;
break; break;
} }
jname[count]=*cp++; jname[count] = *cp++;
} }
cp++; cp++;
if (!mid) { if (!mid) {
uint8_t dp=atoi(cp)&0xf; uint8_t dp = atoi(cp) & 0xf;
dtostrfd(meter_vars[index],dp,tpowstr); dtostrfd(meter_vars[index], dp, tpowstr);
} }
if (json) { if (json) {
@ -2502,14 +2568,14 @@ void SML_Show(boolean json) {
//AddLog(LOG_LEVEL_INFO, PSTR("not yet valid line %d"), index); //AddLog(LOG_LEVEL_INFO, PSTR("not yet valid line %d"), index);
//} //}
// json export // json export
if (index==0) { if (index == 0) {
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":{\"%s\":%s", b_mqtt_data,meter_desc_p[mindex].prefix,jname,tpowstr); //snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":{\"%s\":%s", b_mqtt_data,meter_desc_p[mindex].prefix,jname,tpowstr);
if (!nojson) { if (!nojson) {
ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%s"),meter_desc_p[mindex].prefix,jname,tpowstr); ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%s"), meter_desc_p[mindex].prefix, jname, tpowstr);
} }
} }
else { else {
if (lastmind!=mindex) { if (lastmind != mindex) {
// meter changed, close mqtt // meter changed, close mqtt
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s}", b_mqtt_data); //snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s}", b_mqtt_data);
if (!nojson) { if (!nojson) {
@ -2518,13 +2584,13 @@ void SML_Show(boolean json) {
// and open new // and open new
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":{\"%s\":%s", b_mqtt_data,meter_desc_p[mindex].prefix,jname,tpowstr); //snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":{\"%s\":%s", b_mqtt_data,meter_desc_p[mindex].prefix,jname,tpowstr);
if (!nojson) { if (!nojson) {
ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%s"),meter_desc_p[mindex].prefix,jname,tpowstr); ResponseAppend_P(PSTR(",\"%s\":{\"%s\":%s"), meter_desc_p[mindex].prefix, jname, tpowstr);
} }
lastmind=mindex; lastmind = mindex;
} else { } else {
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":%s", b_mqtt_data,jname,tpowstr); //snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s,\"%s\":%s", b_mqtt_data,jname,tpowstr);
if (!nojson) { if (!nojson) {
ResponseAppend_P(PSTR(",\"%s\":%s"),jname,tpowstr); ResponseAppend_P(PSTR(",\"%s\":%s"), jname, tpowstr);
} }
} }
} }
@ -2532,11 +2598,11 @@ void SML_Show(boolean json) {
} else { } else {
// web ui export // web ui export
//snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s{s}%s %s: {m}%s %s{e}", b_mqtt_data,meter_desc[mindex].prefix,name,tpowstr,unit); //snprintf_P(b_mqtt_data, sizeof(b_mqtt_data), "%s{s}%s %s: {m}%s %s{e}", b_mqtt_data,meter_desc[mindex].prefix,name,tpowstr,unit);
if (strcmp(name,"*")) WSContentSend_PD(PSTR("{s}%s %s {m}%s %s{e}"),meter_desc_p[mindex].prefix,name,tpowstr,unit); if (strcmp(name, "*")) WSContentSend_PD(PSTR("{s}%s %s {m}%s %s{e}"), meter_desc_p[mindex].prefix, name,tpowstr, unit);
} }
} }
} }
if (index<SML_MAX_VARS-1) { if (index < SML_MAX_VARS - 1) {
index++; index++;
} }
// next section // next section
@ -2631,8 +2697,8 @@ uint32_t debounce_time;
uint32_t SML_getlinelen(char *lp) { uint32_t SML_getlinelen(char *lp) {
uint32_t cnt; uint32_t cnt;
for (cnt=0; cnt<SML_SRCBSIZE-1; cnt++) { for (cnt = 0; cnt < SML_SRCBSIZE - 1; cnt++) {
if (lp[cnt]==SCRIPT_EOL) { if (lp[cnt] == SCRIPT_EOL) {
break; break;
} }
} }
@ -2640,27 +2706,27 @@ uint32_t cnt;
} }
uint32_t SML_getscriptsize(char *lp) { uint32_t SML_getscriptsize(char *lp) {
uint32_t mlen=0; uint32_t mlen = 0;
char dstbuf[SML_SRCBSIZE*2]; char dstbuf[SML_SRCBSIZE * 2];
while (1) { while (1) {
Replace_Cmd_Vars(lp,1,dstbuf,sizeof(dstbuf)); Replace_Cmd_Vars(lp, 1, dstbuf, sizeof(dstbuf));
lp+=SML_getlinelen(lp)+1; lp += SML_getlinelen(lp) + 1;
uint32_t slen=strlen(dstbuf); uint32_t slen = strlen(dstbuf);
//AddLog(LOG_LEVEL_INFO, PSTR("%d - %s"),slen,dstbuf); //AddLog(LOG_LEVEL_INFO, PSTR("%d - %s"),slen,dstbuf);
mlen+=slen+1; mlen += slen + 1;
if (*lp=='#') break; if (*lp == '#') break;
if (*lp=='>') break; if (*lp == '>') break;
if (*lp==0) break; if (*lp == 0) break;
} }
//AddLog(LOG_LEVEL_INFO, PSTR("len=%d"),mlen); //AddLog(LOG_LEVEL_INFO, PSTR("len=%d"),mlen);
return mlen+32; return mlen + 32;
} }
#else #else
uint32_t SML_getscriptsize(char *lp) { uint32_t SML_getscriptsize(char *lp) {
uint32_t mlen=0; uint32_t mlen = 0;
for (uint32_t cnt=0;cnt<METER_DEF_SIZE-1;cnt++) { for (uint32_t cnt = 0; cnt < METER_DEF_SIZE - 1; cnt++) {
if (lp[cnt]=='\n' && lp[cnt+1]=='#') { if (lp[cnt] == '\n' && lp[cnt + 1] == '#') {
mlen=cnt+3; mlen = cnt + 3;
break; break;
} }
} }
@ -2711,19 +2777,19 @@ void SML_GetSpecOpt(char *cp, uint32_t mnum) {
#endif #endif
void SML_Init(void) { void SML_Init(void) {
meters_used=METERS_USED; meters_used = METERS_USED;
meter_desc_p=meter_desc; meter_desc_p = meter_desc;
meter_p=meter; meter_p = meter;
sml_desc_cnt=0; sml_desc_cnt = 0;
for (uint32_t cnt=0;cnt<SML_MAX_VARS;cnt++) { for (uint32_t cnt = 0; cnt < SML_MAX_VARS; cnt++) {
meter_vars[cnt]=0; meter_vars[cnt] = 0;
dvalid[cnt]=0; dvalid[cnt] = 0;
} }
for (uint32_t cnt=0;cnt<MAX_METERS;cnt++) { for (uint32_t cnt = 0; cnt < MAX_METERS; cnt++) {
meter_spos[cnt]=0; meter_spos[cnt] = 0;
} }
#ifdef USE_SML_SPECOPT #ifdef USE_SML_SPECOPT
@ -2735,24 +2801,24 @@ void SML_Init(void) {
#ifdef USE_SCRIPT #ifdef USE_SCRIPT
for (uint32_t cnt=0;cnt<MAX_METERS;cnt++) { for (uint32_t cnt = 0; cnt < MAX_METERS; cnt++) {
if (script_meter_desc[cnt].txmem) { if (script_meter_desc[cnt].txmem) {
free(script_meter_desc[cnt].txmem); free(script_meter_desc[cnt].txmem);
} }
script_meter_desc[cnt].txmem=0; script_meter_desc[cnt].txmem = 0;
script_meter_desc[cnt].trxpin=-1; script_meter_desc[cnt].trxpin = -1;
if (meter_ss[cnt]) { if (meter_ss[cnt]) {
delete meter_ss[cnt]; delete meter_ss[cnt];
meter_ss[cnt]=NULL; meter_ss[cnt] = NULL;
} }
} }
if (bitRead(Settings->rule_enabled, 0)) { if (bitRead(Settings->rule_enabled, 0)) {
uint8_t meter_script=Run_Scripter(">M",-2,0); uint8_t meter_script=Run_Scripter(">M", -2, 0);
if (meter_script==99) { if (meter_script == 99) {
// use script definition // use script definition
if (script_meter) free(script_meter); if (script_meter) free(script_meter);
script_meter = 0; script_meter = 0;
@ -2781,23 +2847,23 @@ void SML_Init(void) {
} }
else { else {
if (!*lp || *lp == '#' || *lp == '>') { if (!*lp || *lp == '#' || *lp == '>') {
if (*(tp-1) == '|') *(tp-1) = 0; if (*(tp - 1) == '|') *(tp - 1) = 0;
break; break;
} }
if (*lp == '+') { if (*lp == '+') {
// add descriptor +1,1,c,0,10,H20 // add descriptor +1,1,c,0,10,H20
//toLogEOL(">>",lp); //toLogEOL(">>",lp);
lp++; lp++;
index = *lp&7; index = *lp & 7;
lp += 2; lp += 2;
if (index < 1 || index > meters_used) { if (index < 1 || index > meters_used) {
AddLog(LOG_LEVEL_INFO, PSTR("illegal meter number!")); AddLog(LOG_LEVEL_INFO, PSTR("illegal meter number!"));
goto next_line; goto next_line;
} }
index--; index--;
srcpin = strtol(lp,&lp,10); srcpin = strtol(lp, &lp, 10);
if (Gpio_used(abs(srcpin))) { if (Gpio_used(abs(srcpin))) {
AddLog(LOG_LEVEL_INFO, PSTR("SML: Error: Duplicate GPIO %d defined. Not usable for RX in meter number %d"),abs(srcpin),index+1); AddLog(LOG_LEVEL_INFO, PSTR("SML: Error: Duplicate GPIO %d defined. Not usable for RX in meter number %d"), abs(srcpin), index + 1);
dddef_exit: dddef_exit:
if (script_meter) free(script_meter); if (script_meter) free(script_meter);
script_meter = 0; script_meter = 0;
@ -2852,7 +2918,7 @@ dddef_exit:
lp++; lp++;
script_meter_desc[index].trxpin = strtol(lp, &lp, 10); script_meter_desc[index].trxpin = strtol(lp, &lp, 10);
if (Gpio_used(script_meter_desc[index].trxpin)) { if (Gpio_used(script_meter_desc[index].trxpin)) {
AddLog(LOG_LEVEL_INFO, PSTR("SML: Error: Duplicate GPIO %d defined. Not usable for TX in meter number %d"),script_meter_desc[index].trxpin,index+1); AddLog(LOG_LEVEL_INFO, PSTR("SML: Error: Duplicate GPIO %d defined. Not usable for TX in meter number %d"), script_meter_desc[index].trxpin, index + 1);
goto dddef_exit; goto dddef_exit;
} }
if (*lp != ',') goto next_line; if (*lp != ',') goto next_line;
@ -3016,7 +3082,7 @@ dddef_exit:
} }
next_line: next_line:
if (*lp==SCRIPT_EOL) { if (*lp == SCRIPT_EOL) {
lp++; lp++;
} else { } else {
lp = strchr(lp, SCRIPT_EOL); lp = strchr(lp, SCRIPT_EOL);
@ -3024,16 +3090,16 @@ next_line:
lp++; lp++;
} }
} }
*tp=0; *tp = 0;
meter_desc_p=script_meter_desc; meter_desc_p = script_meter_desc;
meter_p=script_meter; meter_p = script_meter;
} }
} }
#endif #endif
init10: init10:
typedef void (*function)(); typedef void (*function)();
uint8_t cindex=0; uint8_t cindex = 0;
// preloud counters // preloud counters
for (byte i = 0; i < MAX_COUNTERS; i++) { for (byte i = 0; i < MAX_COUNTERS; i++) {
RtcSettings.pulse_counter[i] = Settings->pulse_counter[i]; RtcSettings.pulse_counter[i] = Settings->pulse_counter[i];
@ -3078,7 +3144,7 @@ init10:
} else { } else {
// serial input, init // serial input, init
#ifdef SPECIAL_SS #ifdef SPECIAL_SS
if (meter_desc_p[meters].type=='m' || meter_desc_p[meters].type=='M' || meter_desc_p[meters].type=='p' || meter_desc_p[meters].type=='R' || meter_desc_p[meters].type=='v') { if (meter_desc_p[meters].type=='m' || meter_desc_p[meters].type=='M' || meter_desc_p[meters].type=='k' || meter_desc_p[meters].type=='p' || meter_desc_p[meters].type=='R' || meter_desc_p[meters].type=='v') {
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,0,TMSBSIZ); meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,0,TMSBSIZ);
} else { } else {
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,1,TMSBSIZ); meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,1,TMSBSIZ);
@ -3198,7 +3264,7 @@ uint32_t SML_SetBaud(uint32_t meter, uint32_t br) {
} }
uint32_t SML_Status(uint32_t meter) { uint32_t SML_Status(uint32_t meter) {
if (meter<1 || meter>meters_used) return 0; if (meter < 1 || meter > meters_used) return 0;
meter--; meter--;
#if defined(ED300L) || defined(AS2020) || defined(DTZ541) || defined(USE_SML_SPECOPT) #if defined(ED300L) || defined(AS2020) || defined(DTZ541) || defined(USE_SML_SPECOPT)
return sml_status[meter]; return sml_status[meter];
@ -3210,20 +3276,20 @@ uint32_t SML_Status(uint32_t meter) {
uint32_t SML_Write(uint32_t meter,char *hstr) { uint32_t SML_Write(uint32_t meter,char *hstr) {
if (meter<1 || meter>meters_used) return 0; if (meter < 1 || meter > meters_used) return 0;
meter--; meter--;
if (!meter_ss[meter]) return 0; if (!meter_ss[meter]) return 0;
SML_Send_Seq(meter,hstr); SML_Send_Seq(meter, hstr);
return 1; return 1;
} }
uint32_t SML_Read(int32_t meter,char *str, uint32_t slen) { uint32_t SML_Read(int32_t meter,char *str, uint32_t slen) {
uint8_t hflg=0; uint8_t hflg = 0;
if (meter<0) { if (meter < 0) {
meter=abs(meter); meter = abs(meter);
hflg=1; hflg = 1;
} }
if (meter<1 || meter>meters_used) return 0; if (meter < 1 || meter > meters_used) return 0;
meter--; meter--;
if (!meter_ss[meter]) return 0; if (!meter_ss[meter]) return 0;
@ -3231,26 +3297,26 @@ uint8_t hflg=0;
return 0; return 0;
} }
smltbuf[meter][meter_spos[meter]]=0; smltbuf[meter][meter_spos[meter]] = 0;
if (!hflg) { if (!hflg) {
strlcpy(str,(char*)&smltbuf[meter][0],slen); strlcpy(str, (char*)&smltbuf[meter][0], slen);
} else { } else {
uint32_t index=0; uint32_t index = 0;
for (uint32_t cnt=0; cnt<meter_spos[meter]; cnt++) { for (uint32_t cnt = 0; cnt < meter_spos[meter]; cnt++) {
sprintf(str,"%02x",smltbuf[meter][cnt]); sprintf(str,"%02x", smltbuf[meter][cnt]);
str+=2; str += 2;
index+=2; index += 2;
if (index>=slen-2) break; if (index >= slen - 2) break;
} }
} }
meter_spos[meter]=0; meter_spos[meter] = 0;
return 1; return 1;
} }
float SML_GetVal(uint32_t index) { float SML_GetVal(uint32_t index) {
if (index < 1 || index > SML_MAX_VARS) { index = 1;} if (index < 1 || index > SML_MAX_VARS) { index = 1;}
return meter_vars[index-1]; return meter_vars[index - 1];
} }
char *SML_GetSVal(uint32_t index) { char *SML_GetSVal(uint32_t index) {
@ -3357,13 +3423,13 @@ uint32_t ctime = millis();
#ifdef USE_SCRIPT #ifdef USE_SCRIPT
char *SML_Get_Sequence(char *cp,uint32_t index) { char *SML_Get_Sequence(char *cp,uint32_t index) {
if (!index) return cp; if (!index) return cp;
uint32_t cindex=0; uint32_t cindex = 0;
while (cp) { while (cp) {
cp=strchr(cp,','); cp = strchr(cp, ',');
if (cp) { if (cp) {
cp++; cp++;
cindex++; cindex++;
if (cindex==index) { if (cindex == index) {
return cp; return cp;
} }
} }
@ -3385,13 +3451,13 @@ void SML_Check_Send(void) {
script_meter_desc[cnt].script_str = 0; script_meter_desc[cnt].script_str = 0;
} else { } else {
//AddLog(LOG_LEVEL_INFO, PSTR("100 ms>> 2"),cp); //AddLog(LOG_LEVEL_INFO, PSTR("100 ms>> 2"),cp);
if (script_meter_desc[cnt].max_index>1) { if (script_meter_desc[cnt].max_index > 1) {
script_meter_desc[cnt].index++; script_meter_desc[cnt].index++;
if (script_meter_desc[cnt].index >= script_meter_desc[cnt].max_index) { if (script_meter_desc[cnt].index >= script_meter_desc[cnt].max_index) {
script_meter_desc[cnt].index = 0; script_meter_desc[cnt].index = 0;
sml_desc_cnt++; sml_desc_cnt++;
} }
cp=SML_Get_Sequence(script_meter_desc[cnt].txmem,script_meter_desc[cnt].index); cp = SML_Get_Sequence(script_meter_desc[cnt].txmem, script_meter_desc[cnt].index);
//SML_Send_Seq(cnt,cp); //SML_Send_Seq(cnt,cp);
} else { } else {
cp = script_meter_desc[cnt].txmem; cp = script_meter_desc[cnt].txmem;
@ -3461,17 +3527,49 @@ void SML_Send_Seq(uint32_t meter,char *seq) {
slen++; slen++;
if (slen >= sizeof(sbuff)-6) break; // leave space for checksum if (slen >= sizeof(sbuff)-6) break; // leave space for checksum
} }
if (script_meter_desc[meter].type == 'm' || script_meter_desc[meter].type == 'M') { if (script_meter_desc[meter].type == 'm' || script_meter_desc[meter].type == 'M' || script_meter_desc[meter].type == 'k') {
if (!rflg) { if (script_meter_desc[meter].type == 'k') {
// kamstrup, append crc, cr
*ucp++ = 0; *ucp++ = 0;
*ucp++ = 2; *ucp++ = 0;
slen += 2;
uint16_t crc = KS_calculateCRC(sbuff, slen);
ucp -= 2;
*ucp++ = highByte(crc);
*ucp++ = lowByte(crc);
// now check for escapes
uint8_t ksbuff[24];
ucp = ksbuff;
*ucp++ = 0x80;
uint8_t klen = 1;
for (uint16_t cnt = 0; cnt < slen; cnt++) {
uint8_t iob = sbuff[cnt];
if ((iob == 0x80) || (iob == 0x40) || (iob == 0x0d) || (iob == 0x06) || (iob == 0x1b)) {
*ucp++ = 0x1b;
*ucp++ = iob ^= 0xff;
klen += 2;
} else {
*ucp++ = iob;
klen++;
}
}
*ucp++ = 0xd;
slen = klen + 1;
memcpy(sbuff, ksbuff, slen);
} else {
if (!rflg) {
*ucp++ = 0;
*ucp++ = 2;
slen += 2;
}
// append crc
uint16_t crc = MBUS_calculateCRC(sbuff, slen, 0xFFFF);
*ucp++ = lowByte(crc);
*ucp++ = highByte(crc);
slen += 2; slen += 2;
} }
// append crc
uint16_t crc = MBUS_calculateCRC(sbuff, slen, 0xFFFF);
*ucp++ = lowByte(crc);
*ucp++ = highByte(crc);
slen += 2;
} }
if (script_meter_desc[meter].type == 'o') { if (script_meter_desc[meter].type == 'o') {
for (uint32_t cnt = 0; cnt < slen; cnt++) { for (uint32_t cnt = 0; cnt < slen; cnt++) {
@ -3487,18 +3585,28 @@ void SML_Send_Seq(uint32_t meter,char *seq) {
*ucp++ = SML_PzemCrc(sbuff, 6); *ucp++ = SML_PzemCrc(sbuff, 6);
slen += 6; slen += 6;
} }
meter_ss[meter]->flush();
meter_ss[meter]->write(sbuff, slen); meter_ss[meter]->write(sbuff, slen);
if (dump2log) { if (dump2log) {
#ifdef SML_DUMP_OUT_ALL #ifdef SML_DUMP_OUT_ALL
Hexdump(sbuff, slen); Hexdump(sbuff, slen);
#else #else
uint8_t type = meter_desc_p[(dump2log&7) - 1].type; uint8_t type = meter_desc_p[(dump2log&7) - 1].type;
if (type == 'm' || type == 'M') { if (type == 'm' || type == 'M' || type == 'k') {
Hexdump(sbuff, slen); Hexdump(sbuff, slen);
} }
#endif #endif
} }
#ifdef MODBUS_DEBUG
uint8_t type = script_meter_desc[meter].type;
if (!dump2log && (type == 'm' || type == 'M' || type == 'k')) {
AddLog(LOG_LEVEL_INFO, PSTR("transmit index >> %d"),meter_desc_p[meter].index);
Hexdump(sbuff, slen);
}
#endif
} }
#endif // USE_SCRIPT #endif // USE_SCRIPT
@ -3520,6 +3628,27 @@ uint16_t MBUS_calculateCRC(uint8_t *frame, uint8_t num, uint16_t start) {
return crc; return crc;
} }
uint16_t KS_calculateCRC(const uint8_t *frame, uint8_t num) {
uint32_t crc = 0;
for (uint32_t i = 0; i < num; i++) {
uint8_t mask = 0x80;
uint8_t iob = frame[i];
while (mask) {
crc <<= 1;
if (iob & mask) {
crc |= 1;
}
mask >>= 1;
if (crc & 0x10000) {
crc &= 0xffff;
crc ^= 0x1021;
}
}
}
return crc;
}
uint8_t SML_PzemCrc(uint8_t *data, uint8_t len) { uint8_t SML_PzemCrc(uint8_t *data, uint8_t len) {
uint16_t crc = 0; uint16_t crc = 0;
for (uint32_t i = 0; i < len; i++) crc += *data++; for (uint32_t i = 0; i < len; i++) crc += *data++;