mirror of https://github.com/arendst/Tasmota.git
Merge branch 'development' into pre-release
This commit is contained in:
commit
2055d06825
|
@ -75,6 +75,7 @@
|
||||||
| USE_DHT | - | - | x | x | x | x | x |
|
| USE_DHT | - | - | x | x | x | x | x |
|
||||||
| USE_MAX31855 | - | - | - | - | x | - | - |
|
| USE_MAX31855 | - | - | - | - | x | - | - |
|
||||||
| USE_MAX31865 | - | - | - | - | - | - | - |
|
| USE_MAX31865 | - | - | - | - | - | - | - |
|
||||||
|
| USE_THERMOSTAT | - | - | - | - | - | - | - |
|
||||||
| | | | | | | | |
|
| | | | | | | | |
|
||||||
| Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks
|
| Feature or Sensor | minimal | lite | tasmota | knx | sensors | ir | display | Remarks
|
||||||
| USE_I2C | - | - | x | x | x | - | x |
|
| USE_I2C | - | - | x | x | x | - | x |
|
||||||
|
|
|
@ -52,6 +52,15 @@ The following binary downloads have been compiled with ESP8266/Arduino library c
|
||||||
|
|
||||||
## Changelog
|
## Changelog
|
||||||
|
|
||||||
|
### Version 8.3.1 Fred
|
||||||
|
|
||||||
|
- Change KNX pow function to approximative pow saving 5k of code space
|
||||||
|
- Change Mutichannel Gas sensor pow function to approximative pow saving 5k of code space
|
||||||
|
- Change Quick Power Cycle detection from 4 to 7 power interrupts (#4066)
|
||||||
|
- Fix default state of ``SetOption73 0`` for button decoupling and send multi-press and hold MQTT messages
|
||||||
|
- Fix HAss discovery
|
||||||
|
- Add command ``DeviceName`` defaults to FriendlyName1 and replaces FriendlyName1 in GUI
|
||||||
|
|
||||||
### Version 8.3.0 Fred
|
### Version 8.3.0 Fred
|
||||||
|
|
||||||
- Breaking Change Device Groups multicast address and port (#8270)
|
- Breaking Change Device Groups multicast address and port (#8270)
|
||||||
|
|
|
@ -57,7 +57,7 @@ bool MutichannelGasSensor::isError()
|
||||||
|
|
||||||
unsigned char MutichannelGasSensor::getVersion()
|
unsigned char MutichannelGasSensor::getVersion()
|
||||||
{
|
{
|
||||||
if(get_addr_dta(CMD_READ_EEPROM, ADDR_IS_SET) == 1126) // get version
|
if(get_addr_dta(CMD_READ_EEPROM, ADDR_IS_SET) == 1126) // get version
|
||||||
{
|
{
|
||||||
__version = 2;
|
__version = 2;
|
||||||
return __version;
|
return __version;
|
||||||
|
@ -105,66 +105,66 @@ START:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Wire.requestFrom(i2cAddress, (uint8_t)2);
|
Wire.requestFrom(i2cAddress, (uint8_t)2);
|
||||||
|
|
||||||
unsigned int dta = 0;
|
unsigned int dta = 0;
|
||||||
|
|
||||||
unsigned char raw[10];
|
unsigned char raw[10];
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
|
|
||||||
while(Wire.available())
|
while(Wire.available())
|
||||||
{
|
{
|
||||||
raw[cnt++] = Wire.read();
|
raw[cnt++] = Wire.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cnt == 0)goto START;
|
if(cnt == 0)goto START;
|
||||||
|
|
||||||
dta = raw[0];
|
dta = raw[0];
|
||||||
dta <<= 8;
|
dta <<= 8;
|
||||||
dta += raw[1];
|
dta += raw[1];
|
||||||
|
|
||||||
switch(addr_reg)
|
switch(addr_reg)
|
||||||
{
|
{
|
||||||
case CH_VALUE_NH3:
|
case CH_VALUE_NH3:
|
||||||
|
|
||||||
if(dta > 0)
|
if(dta > 0)
|
||||||
{
|
{
|
||||||
adcValueR0_NH3_Buf = dta;
|
adcValueR0_NH3_Buf = dta;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dta = adcValueR0_NH3_Buf;
|
dta = adcValueR0_NH3_Buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CH_VALUE_CO:
|
case CH_VALUE_CO:
|
||||||
|
|
||||||
if(dta > 0)
|
if(dta > 0)
|
||||||
{
|
{
|
||||||
adcValueR0_CO_Buf = dta;
|
adcValueR0_CO_Buf = dta;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dta = adcValueR0_CO_Buf;
|
dta = adcValueR0_CO_Buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CH_VALUE_NO2:
|
case CH_VALUE_NO2:
|
||||||
|
|
||||||
if(dta > 0)
|
if(dta > 0)
|
||||||
{
|
{
|
||||||
adcValueR0_NO2_Buf = dta;
|
adcValueR0_NO2_Buf = dta;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
dta = adcValueR0_NO2_Buf;
|
dta = adcValueR0_NO2_Buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
return dta;
|
return dta;
|
||||||
|
@ -173,7 +173,7 @@ START:
|
||||||
unsigned int MutichannelGasSensor::get_addr_dta(unsigned char addr_reg, unsigned char __dta)
|
unsigned int MutichannelGasSensor::get_addr_dta(unsigned char addr_reg, unsigned char __dta)
|
||||||
{
|
{
|
||||||
int trys = 0;
|
int trys = 0;
|
||||||
START:
|
START:
|
||||||
__send_error = false;
|
__send_error = false;
|
||||||
Wire.beginTransmission(i2cAddress);
|
Wire.beginTransmission(i2cAddress);
|
||||||
Wire.write(addr_reg);
|
Wire.write(addr_reg);
|
||||||
|
@ -185,25 +185,25 @@ START:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Wire.requestFrom(i2cAddress, (uint8_t)2);
|
Wire.requestFrom(i2cAddress, (uint8_t)2);
|
||||||
|
|
||||||
unsigned int dta = 0;
|
unsigned int dta = 0;
|
||||||
|
|
||||||
unsigned char raw[10];
|
unsigned char raw[10];
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
|
|
||||||
while(Wire.available())
|
while(Wire.available())
|
||||||
{
|
{
|
||||||
raw[cnt++] = Wire.read();
|
raw[cnt++] = Wire.read();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cnt == 0)goto START;
|
if(cnt == 0)goto START;
|
||||||
|
|
||||||
dta = raw[0];
|
dta = raw[0];
|
||||||
dta <<= 8;
|
dta <<= 8;
|
||||||
dta += raw[1];
|
dta += raw[1];
|
||||||
|
|
||||||
|
|
||||||
return dta;
|
return dta;
|
||||||
}
|
}
|
||||||
|
@ -269,7 +269,7 @@ int16_t MutichannelGasSensor::readR0(void)
|
||||||
int16_t rtnData = 0;
|
int16_t rtnData = 0;
|
||||||
|
|
||||||
rtnData = readData(0x11);
|
rtnData = readData(0x11);
|
||||||
|
|
||||||
if(rtnData > 0)
|
if(rtnData > 0)
|
||||||
res0[0] = rtnData;
|
res0[0] = rtnData;
|
||||||
else
|
else
|
||||||
|
@ -327,6 +327,30 @@ int16_t MutichannelGasSensor::readR(void)
|
||||||
** Returns:
|
** Returns:
|
||||||
float value - concentration of the gas
|
float value - concentration of the gas
|
||||||
*********************************************************************************************************/
|
*********************************************************************************************************/
|
||||||
|
float MutichannelGasSensor_pow(float a, float b)
|
||||||
|
{
|
||||||
|
// https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp/
|
||||||
|
// calculate approximation with fraction of the exponent
|
||||||
|
int e = abs((int)b);
|
||||||
|
union {
|
||||||
|
double d;
|
||||||
|
int x[2];
|
||||||
|
} u = { a };
|
||||||
|
u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447);
|
||||||
|
u.x[0] = 0;
|
||||||
|
// exponentiation by squaring with the exponent's integer part
|
||||||
|
// double r = u.d makes everything much slower, not sure why
|
||||||
|
double r = 1.0;
|
||||||
|
while (e) {
|
||||||
|
if (e & 1) {
|
||||||
|
r *= a;
|
||||||
|
}
|
||||||
|
a *= a;
|
||||||
|
e >>= 1;
|
||||||
|
}
|
||||||
|
return r * u.d;
|
||||||
|
}
|
||||||
|
|
||||||
float MutichannelGasSensor::calcGas(int gas)
|
float MutichannelGasSensor::calcGas(int gas)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -338,7 +362,7 @@ float MutichannelGasSensor::calcGas(int gas)
|
||||||
if(readR0() >= 0) r0_inited = true;
|
if(readR0() >= 0) r0_inited = true;
|
||||||
else return -1.0f;
|
else return -1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(readR() < 0)
|
if(readR() < 0)
|
||||||
return -2.0f;
|
return -2.0f;
|
||||||
|
|
||||||
|
@ -353,65 +377,65 @@ float MutichannelGasSensor::calcGas(int gas)
|
||||||
int A0_0 = get_addr_dta(6, ADDR_USER_ADC_HN3);
|
int A0_0 = get_addr_dta(6, ADDR_USER_ADC_HN3);
|
||||||
int A0_1 = get_addr_dta(6, ADDR_USER_ADC_CO);
|
int A0_1 = get_addr_dta(6, ADDR_USER_ADC_CO);
|
||||||
int A0_2 = get_addr_dta(6, ADDR_USER_ADC_NO2);
|
int A0_2 = get_addr_dta(6, ADDR_USER_ADC_NO2);
|
||||||
|
|
||||||
int An_0 = get_addr_dta(CH_VALUE_NH3);
|
int An_0 = get_addr_dta(CH_VALUE_NH3);
|
||||||
int An_1 = get_addr_dta(CH_VALUE_CO);
|
int An_1 = get_addr_dta(CH_VALUE_CO);
|
||||||
int An_2 = get_addr_dta(CH_VALUE_NO2);
|
int An_2 = get_addr_dta(CH_VALUE_NO2);
|
||||||
|
|
||||||
ratio0 = (float)An_0/(float)A0_0*(1023.0-A0_0)/(1023.0-An_0);
|
ratio0 = (float)An_0/(float)A0_0*(1023.0-A0_0)/(1023.0-An_0);
|
||||||
ratio1 = (float)An_1/(float)A0_1*(1023.0-A0_1)/(1023.0-An_1);
|
ratio1 = (float)An_1/(float)A0_1*(1023.0-A0_1)/(1023.0-An_1);
|
||||||
ratio2 = (float)An_2/(float)A0_2*(1023.0-A0_2)/(1023.0-An_2);
|
ratio2 = (float)An_2/(float)A0_2*(1023.0-A0_2)/(1023.0-An_2);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float c = 0;
|
float c = 0;
|
||||||
|
|
||||||
switch(gas)
|
switch(gas)
|
||||||
{
|
{
|
||||||
case CO:
|
case CO:
|
||||||
{
|
{
|
||||||
c = pow(ratio1, -1.179)*4.385; //mod by jack
|
c = MutichannelGasSensor_pow(ratio1, -1.179)*4.385; //mod by jack
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NO2:
|
case NO2:
|
||||||
{
|
{
|
||||||
c = pow(ratio2, 1.007)/6.855; //mod by jack
|
c = MutichannelGasSensor_pow(ratio2, 1.007)/6.855; //mod by jack
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case NH3:
|
case NH3:
|
||||||
{
|
{
|
||||||
c = pow(ratio0, -1.67)/1.47; //modi by jack
|
c = MutichannelGasSensor_pow(ratio0, -1.67)/1.47; //modi by jack
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case C3H8: //add by jack
|
case C3H8: //add by jack
|
||||||
{
|
{
|
||||||
c = pow(ratio0, -2.518)*570.164;
|
c = MutichannelGasSensor_pow(ratio0, -2.518)*570.164;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case C4H10: //add by jack
|
case C4H10: //add by jack
|
||||||
{
|
{
|
||||||
c = pow(ratio0, -2.138)*398.107;
|
c = MutichannelGasSensor_pow(ratio0, -2.138)*398.107;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GAS_CH4: //add by jack
|
case GAS_CH4: //add by jack
|
||||||
{
|
{
|
||||||
c = pow(ratio1, -4.363)*630.957;
|
c = MutichannelGasSensor_pow(ratio1, -4.363)*630.957;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case H2: //add by jack
|
case H2: //add by jack
|
||||||
{
|
{
|
||||||
c = pow(ratio1, -1.8)*0.73;
|
c = MutichannelGasSensor_pow(ratio1, -1.8)*0.73;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case C2H5OH: //add by jack
|
case C2H5OH: //add by jack
|
||||||
{
|
{
|
||||||
c = pow(ratio1, -1.552)*1.622;
|
c = MutichannelGasSensor_pow(ratio1, -1.552)*1.622;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(2==__version)ledOff();
|
if(2==__version)ledOff();
|
||||||
return isnan(c)?-3:c;
|
return isnan(c)?-3:c;
|
||||||
}
|
}
|
||||||
|
@ -474,7 +498,7 @@ void MutichannelGasSensor::doCalibrate(void)
|
||||||
a0 = get_addr_dta(CH_VALUE_NH3);
|
a0 = get_addr_dta(CH_VALUE_NH3);
|
||||||
a1 = get_addr_dta(CH_VALUE_CO);
|
a1 = get_addr_dta(CH_VALUE_CO);
|
||||||
a2 = get_addr_dta(CH_VALUE_NO2);
|
a2 = get_addr_dta(CH_VALUE_NO2);
|
||||||
|
|
||||||
Serial.print(a0);
|
Serial.print(a0);
|
||||||
Serial.print('\t');
|
Serial.print('\t');
|
||||||
Serial.print(a1);
|
Serial.print(a1);
|
||||||
|
@ -482,44 +506,44 @@ void MutichannelGasSensor::doCalibrate(void)
|
||||||
Serial.print(a2);
|
Serial.print(a2);
|
||||||
Serial.println('\t');
|
Serial.println('\t');
|
||||||
ledOn();
|
ledOn();
|
||||||
|
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
for(i=0; i<20; i++)
|
for(i=0; i<20; i++)
|
||||||
{
|
{
|
||||||
if((a0 - get_addr_dta(CH_VALUE_NH3)) > 2 || (get_addr_dta(CH_VALUE_NH3) - a0) > 2)cnt++;
|
if((a0 - get_addr_dta(CH_VALUE_NH3)) > 2 || (get_addr_dta(CH_VALUE_NH3) - a0) > 2)cnt++;
|
||||||
if((a1 - get_addr_dta(CH_VALUE_CO)) > 2 || (get_addr_dta(CH_VALUE_CO) - a1) > 2)cnt++;
|
if((a1 - get_addr_dta(CH_VALUE_CO)) > 2 || (get_addr_dta(CH_VALUE_CO) - a1) > 2)cnt++;
|
||||||
if((a2 - get_addr_dta(CH_VALUE_NO2)) > 2 || (get_addr_dta(CH_VALUE_NO2) - a2) > 2)cnt++;
|
if((a2 - get_addr_dta(CH_VALUE_NO2)) > 2 || (get_addr_dta(CH_VALUE_NO2) - a2) > 2)cnt++;
|
||||||
|
|
||||||
if(cnt>5)
|
if(cnt>5)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
delay(1000);
|
delay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
ledOff();
|
ledOff();
|
||||||
if(cnt <= 5)break;
|
if(cnt <= 5)break;
|
||||||
delay(200);
|
delay(200);
|
||||||
}
|
}
|
||||||
|
|
||||||
Serial.print("write user adc value: ");
|
Serial.print("write user adc value: ");
|
||||||
Serial.print(a0);Serial.print('\t');
|
Serial.print(a0);Serial.print('\t');
|
||||||
Serial.print(a1);Serial.print('\t');
|
Serial.print(a1);Serial.print('\t');
|
||||||
Serial.print(a2);Serial.println('\t');
|
Serial.print(a2);Serial.println('\t');
|
||||||
|
|
||||||
unsigned char tmp[7];
|
unsigned char tmp[7];
|
||||||
|
|
||||||
tmp[0] = 7;
|
tmp[0] = 7;
|
||||||
|
|
||||||
tmp[1] = a0>>8;
|
tmp[1] = a0>>8;
|
||||||
tmp[2] = a0&0xff;
|
tmp[2] = a0&0xff;
|
||||||
|
|
||||||
tmp[3] = a1>>8;
|
tmp[3] = a1>>8;
|
||||||
tmp[4] = a1&0xff;
|
tmp[4] = a1&0xff;
|
||||||
|
|
||||||
tmp[5] = a2>>8;
|
tmp[5] = a2>>8;
|
||||||
tmp[6] = a2&0xff;
|
tmp[6] = a2&0xff;
|
||||||
|
|
||||||
write_i2c(i2cAddress, tmp, 7);
|
write_i2c(i2cAddress, tmp, 7);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -563,7 +587,7 @@ void MutichannelGasSensor::display_eeprom()
|
||||||
Serial.println("ERROR: display_eeprom() is NOT support by V1 firmware.");
|
Serial.println("ERROR: display_eeprom() is NOT support by V1 firmware.");
|
||||||
return ;
|
return ;
|
||||||
}
|
}
|
||||||
|
|
||||||
Serial.print("ADDR_IS_SET = "); Serial.println(get_addr_dta(CMD_READ_EEPROM, ADDR_IS_SET));
|
Serial.print("ADDR_IS_SET = "); Serial.println(get_addr_dta(CMD_READ_EEPROM, ADDR_IS_SET));
|
||||||
Serial.print("ADDR_FACTORY_ADC_NH3 = "); Serial.println(get_addr_dta(CMD_READ_EEPROM, ADDR_FACTORY_ADC_NH3));
|
Serial.print("ADDR_FACTORY_ADC_NH3 = "); Serial.println(get_addr_dta(CMD_READ_EEPROM, ADDR_FACTORY_ADC_NH3));
|
||||||
Serial.print("ADDR_FACTORY_ADC_CO = "); Serial.println(get_addr_dta(CMD_READ_EEPROM, ADDR_FACTORY_ADC_CO));
|
Serial.print("ADDR_FACTORY_ADC_CO = "); Serial.println(get_addr_dta(CMD_READ_EEPROM, ADDR_FACTORY_ADC_CO));
|
||||||
|
@ -581,7 +605,7 @@ float MutichannelGasSensor::getR0(unsigned char ch) // 0:CH3, 1:CO, 2:NO
|
||||||
Serial.println("ERROR: getR0() is NOT support by V1 firmware.");
|
Serial.println("ERROR: getR0() is NOT support by V1 firmware.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int a = 0;
|
int a = 0;
|
||||||
switch(ch)
|
switch(ch)
|
||||||
{
|
{
|
||||||
|
@ -590,19 +614,19 @@ float MutichannelGasSensor::getR0(unsigned char ch) // 0:CH3, 1:CO, 2:NO
|
||||||
Serial.print("a_ch3 = ");
|
Serial.print("a_ch3 = ");
|
||||||
Serial.println(a);
|
Serial.println(a);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: // CO
|
case 1: // CO
|
||||||
a = get_addr_dta(CMD_READ_EEPROM, ADDR_USER_ADC_CO);
|
a = get_addr_dta(CMD_READ_EEPROM, ADDR_USER_ADC_CO);
|
||||||
Serial.print("a_co = ");
|
Serial.print("a_co = ");
|
||||||
Serial.println(a);
|
Serial.println(a);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // NO2
|
case 2: // NO2
|
||||||
a = get_addr_dta(CMD_READ_EEPROM, ADDR_USER_ADC_NO2);
|
a = get_addr_dta(CMD_READ_EEPROM, ADDR_USER_ADC_NO2);
|
||||||
Serial.print("a_no2 = ");
|
Serial.print("a_no2 = ");
|
||||||
Serial.println(a);
|
Serial.println(a);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,31 +636,31 @@ float MutichannelGasSensor::getR0(unsigned char ch) // 0:CH3, 1:CO, 2:NO
|
||||||
|
|
||||||
float MutichannelGasSensor::getRs(unsigned char ch) // 0:CH3, 1:CO, 2:NO2
|
float MutichannelGasSensor::getRs(unsigned char ch) // 0:CH3, 1:CO, 2:NO2
|
||||||
{
|
{
|
||||||
|
|
||||||
if(__version == 1)
|
if(__version == 1)
|
||||||
{
|
{
|
||||||
Serial.println("ERROR: getRs() is NOT support by V1 firmware.");
|
Serial.println("ERROR: getRs() is NOT support by V1 firmware.");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int a = 0;
|
int a = 0;
|
||||||
switch(ch)
|
switch(ch)
|
||||||
{
|
{
|
||||||
case 0: // NH3
|
case 0: // NH3
|
||||||
a = get_addr_dta(1);
|
a = get_addr_dta(1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 1: // CO
|
case 1: // CO
|
||||||
a = get_addr_dta(2);
|
a = get_addr_dta(2);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 2: // NO2
|
case 2: // NO2
|
||||||
a = get_addr_dta(3);
|
a = get_addr_dta(3);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:;
|
default:;
|
||||||
}
|
}
|
||||||
|
|
||||||
float r = 56.0*(float)a/(1023.0-(float)a);
|
float r = 56.0*(float)a/(1023.0-(float)a);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -645,12 +669,12 @@ float MutichannelGasSensor::getRs(unsigned char ch) // 0:CH3, 1:CO, 2:NO
|
||||||
// 2. change adc value of R0 to default
|
// 2. change adc value of R0 to default
|
||||||
void MutichannelGasSensor::factory_setting()
|
void MutichannelGasSensor::factory_setting()
|
||||||
{
|
{
|
||||||
|
|
||||||
unsigned char tmp[7];
|
unsigned char tmp[7];
|
||||||
|
|
||||||
unsigned char error;
|
unsigned char error;
|
||||||
unsigned char address = 0;
|
unsigned char address = 0;
|
||||||
|
|
||||||
for(address = 1; address < 127; address++ )
|
for(address = 1; address < 127; address++ )
|
||||||
{
|
{
|
||||||
// The i2c_scanner uses the return value of
|
// The i2c_scanner uses the return value of
|
||||||
|
@ -661,11 +685,11 @@ void MutichannelGasSensor::factory_setting()
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
{
|
{
|
||||||
// change i2c to 0x04
|
// change i2c to 0x04
|
||||||
|
|
||||||
Serial.print("I2C address is: 0x");
|
Serial.print("I2C address is: 0x");
|
||||||
Serial.println(address, HEX);
|
Serial.println(address, HEX);
|
||||||
Serial.println("Change I2C address to 0x04");
|
Serial.println("Change I2C address to 0x04");
|
||||||
|
|
||||||
dta_test[0] = CMD_CHANGE_I2C;
|
dta_test[0] = CMD_CHANGE_I2C;
|
||||||
dta_test[1] = 0x04;
|
dta_test[1] = 0x04;
|
||||||
write_i2c(address, dta_test, 2);
|
write_i2c(address, dta_test, 2);
|
||||||
|
@ -680,15 +704,15 @@ void MutichannelGasSensor::factory_setting()
|
||||||
unsigned int a0 = get_addr_dta(CMD_READ_EEPROM, ADDR_FACTORY_ADC_NH3);
|
unsigned int a0 = get_addr_dta(CMD_READ_EEPROM, ADDR_FACTORY_ADC_NH3);
|
||||||
unsigned int a1 = get_addr_dta(CMD_READ_EEPROM, ADDR_FACTORY_ADC_CO);
|
unsigned int a1 = get_addr_dta(CMD_READ_EEPROM, ADDR_FACTORY_ADC_CO);
|
||||||
unsigned int a2 = get_addr_dta(CMD_READ_EEPROM, ADDR_FACTORY_ADC_NO2);
|
unsigned int a2 = get_addr_dta(CMD_READ_EEPROM, ADDR_FACTORY_ADC_NO2);
|
||||||
|
|
||||||
tmp[0] = 7;
|
tmp[0] = 7;
|
||||||
tmp[1] = a0>>8;
|
tmp[1] = a0>>8;
|
||||||
tmp[2] = a0&0xff;
|
tmp[2] = a0&0xff;
|
||||||
tmp[3] = a1>>8;
|
tmp[3] = a1>>8;
|
||||||
tmp[4] = a1&0xff;
|
tmp[4] = a1&0xff;
|
||||||
|
|
||||||
tmp[5] = a2>>8;
|
tmp[5] = a2>>8;
|
||||||
tmp[6] = a2&0xff;
|
tmp[6] = a2&0xff;
|
||||||
delay(100);
|
delay(100);
|
||||||
write_i2c(i2cAddress, tmp, 7);
|
write_i2c(i2cAddress, tmp, 7);
|
||||||
delay(100);
|
delay(100);
|
||||||
|
@ -699,13 +723,13 @@ void MutichannelGasSensor::change_i2c_address(unsigned char addr)
|
||||||
dta_test[0] = CMD_CHANGE_I2C;
|
dta_test[0] = CMD_CHANGE_I2C;
|
||||||
dta_test[1] = addr;
|
dta_test[1] = addr;
|
||||||
write_i2c(i2cAddress, dta_test, 2);
|
write_i2c(i2cAddress, dta_test, 2);
|
||||||
|
|
||||||
|
|
||||||
Serial.print("FUNCTION: CHANGE I2C ADDRESS: 0X");
|
Serial.print("FUNCTION: CHANGE I2C ADDRESS: 0X");
|
||||||
Serial.print(i2cAddress, HEX);
|
Serial.print(i2cAddress, HEX);
|
||||||
Serial.print(" > 0x");
|
Serial.print(" > 0x");
|
||||||
Serial.println(addr, HEX);
|
Serial.println(addr, HEX);
|
||||||
|
|
||||||
i2cAddress = addr;
|
i2cAddress = addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,513 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Python Class for compressing short strings.
|
||||||
|
|
||||||
|
This class contains a highly modified and optimized version of Unishox
|
||||||
|
for Tasmota converted in C ported to Pyhton3.
|
||||||
|
|
||||||
|
It was basically developed to individually compress and decompress small strings
|
||||||
|
(see https://github.com/siara-cc/Unishox)
|
||||||
|
In general compression utilities such as zip, gzip do not compress short strings
|
||||||
|
well and often expand them. They also use lots of memory which makes them unusable
|
||||||
|
in constrained environments like Arduino.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Unishox:
|
||||||
|
"""
|
||||||
|
This is a highly modified and optimized version of Unishox
|
||||||
|
for Tasmota, aimed at compressing `Rules` which are typically
|
||||||
|
short strings from 50 to 500 bytes.
|
||||||
|
|
||||||
|
@author Stephan Hadinger
|
||||||
|
@revised Norbert Richter
|
||||||
|
"""
|
||||||
|
|
||||||
|
# pylint: disable=bad-continuation,bad-whitespace,line-too-long
|
||||||
|
cl_95 = [0x4000 + 3, 0x3F80 + 11, 0x3D80 + 11, 0x3C80 + 10, 0x3BE0 + 12, 0x3E80 + 10, 0x3F40 + 11, 0x3EC0 + 10, 0x3BA0 + 11, 0x3BC0 + 11, 0x3D60 + 11, 0x3B60 + 11, 0x3A80 + 10, 0x3AC0 + 10, 0x3A00 + 9, 0x3B00 + 10, 0x38C0 + 10, 0x3900 + 10, 0x3940 + 11, 0x3960 + 11, 0x3980 + 11, 0x39A0 + 11, 0x39C0 + 11, 0x39E0 + 12, 0x39F0 + 12, 0x3880 + 10, 0x3CC0 + 10, 0x3C00 + 9, 0x3D00 + 10, 0x3E00 + 9, 0x3F00 + 10, 0x3B40 + 11, 0x3BF0 + 12, 0x2B00 + 8, 0x21C0 + 11, 0x20C0 + 10, 0x2100 + 10, 0x2600 + 7, 0x2300 + 11, 0x21E0 + 12, 0x2140 + 11, 0x2D00 + 8, 0x2358 + 13, 0x2340 + 12, 0x2080 + 10, 0x21A0 + 11, 0x2E00 + 8, 0x2C00 + 8, 0x2180 + 11, 0x2350 + 13, 0x2F80 + 9, 0x2F00 + 9, 0x2A00 + 8, 0x2160 + 11, 0x2330 + 12, 0x21F0 + 12, 0x2360 + 13, 0x2320 + 12, 0x2368 + 13, 0x3DE0 + 12, 0x3FA0 + 11, 0x3DF0 + 12, 0x3D40 + 11, 0x3F60 + 11, 0x3FF0 + 12, 0xB000 + 4, 0x1C00 + 7, 0x0C00 + 6, 0x1000 + 6, 0x6000 + 3, 0x3000 + 7, 0x1E00 + 8, 0x1400 + 7, 0xD000 + 4, 0x3580 + 9, 0x3400 + 8, 0x0800 + 6, 0x1A00 + 7, 0xE000 + 4, 0xC000 + 4, 0x1800 + 7, 0x3500 + 9, 0xF800 + 5, 0xF000 + 5, 0xA000 + 4, 0x1600 + 7, 0x3300 + 8, 0x1F00 + 8, 0x3600 + 9, 0x3200 + 8, 0x3680 + 9, 0x3DA0 + 11, 0x3FC0 + 11, 0x3DC0 + 11, 0x3FE0 + 12]
|
||||||
|
|
||||||
|
# enum {SHX_STATE_1 = 1, SHX_STATE_2}; // removed Unicode state
|
||||||
|
SHX_STATE_1 = 1
|
||||||
|
SHX_STATE_2 = 2
|
||||||
|
|
||||||
|
SHX_SET1 = 0
|
||||||
|
SHX_SET1A = 1
|
||||||
|
SHX_SET1B = 2
|
||||||
|
SHX_SET2 = 3
|
||||||
|
|
||||||
|
sets = [[0, ' ', 'e', 0, 't', 'a', 'o', 'i', 'n', 's', 'r'],
|
||||||
|
[0, 'l', 'c', 'd', 'h', 'u', 'p', 'm', 'b', 'g', 'w'],
|
||||||
|
['f', 'y', 'v', 'k', 'q', 'j', 'x', 'z', 0, 0, 0],
|
||||||
|
[0, '9', '0', '1', '2', '3', '4', '5', '6', '7', '8'],
|
||||||
|
['.', ',', '-', '/', '?', '+', ' ', '(', ')', '$', '@'],
|
||||||
|
[';', '#', ':', '<', '^', '*', '"', '{', '}', '[', ']'],
|
||||||
|
['=', '%', '\'', '>', '&', '_', '!', '\\', '|', '~', '`']]
|
||||||
|
|
||||||
|
us_vcode = [2 + (0 << 3), 3 + (3 << 3), 3 + (1 << 3), 4 + (6 << 3), 0,
|
||||||
|
# 5, 6, 7, 8, 9, 10
|
||||||
|
4 + (4 << 3), 3 + (2 << 3), 4 + (8 << 3), 0, 0, 0,
|
||||||
|
# 11, 12, 13, 14, 15
|
||||||
|
4 + (7 << 3), 0, 4 + (5 << 3), 0, 5 + (9 << 3),
|
||||||
|
# 16, 17, 18, 19, 20, 21, 22, 23
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
# 24, 25, 26, 27, 28, 29, 30, 31
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 5 + (10 << 3) ]
|
||||||
|
# 0, 1, 2, 3, 4, 5, 6, 7,
|
||||||
|
us_hcode = [1 + (1 << 3), 2 + (0 << 3), 0, 3 + (2 << 3), 0, 0, 0, 5 + (3 << 3),
|
||||||
|
# 8, 9, 10, 11, 12, 13, 14, 15,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 5 + (5 << 3),
|
||||||
|
# 16, 17, 18, 19, 20, 21, 22, 23
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 5 + (4 << 3),
|
||||||
|
# 24, 25, 26, 27, 28, 29, 30, 31
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 5 + (6 << 3) ]
|
||||||
|
# pylint: enable=bad-continuation,bad-whitespace
|
||||||
|
|
||||||
|
ESCAPE_MARKER = 0x2A
|
||||||
|
|
||||||
|
TERM_CODE = 0x37C0
|
||||||
|
# TERM_CODE_LEN = 10
|
||||||
|
DICT_CODE = 0x0000
|
||||||
|
DICT_CODE_LEN = 5
|
||||||
|
#DICT_OTHER_CODE = 0x0000
|
||||||
|
#DICT_OTHER_CODE_LEN = 6
|
||||||
|
RPT_CODE_TASMOTA = 0x3780
|
||||||
|
RPT_CODE_TASMOTA_LEN = 10
|
||||||
|
BACK2_STATE1_CODE = 0x2000
|
||||||
|
BACK2_STATE1_CODE_LEN = 4
|
||||||
|
#BACK_FROM_UNI_CODE = 0xFE00
|
||||||
|
#BACK_FROM_UNI_CODE_LEN = 8
|
||||||
|
LF_CODE = 0x3700
|
||||||
|
LF_CODE_LEN = 9
|
||||||
|
TAB_CODE = 0x2400
|
||||||
|
TAB_CODE_LEN = 7
|
||||||
|
ALL_UPPER_CODE = 0x2200
|
||||||
|
ALL_UPPER_CODE_LEN = 8
|
||||||
|
SW2_STATE2_CODE = 0x3800
|
||||||
|
SW2_STATE2_CODE_LEN = 7
|
||||||
|
ST2_SPC_CODE = 0x3B80
|
||||||
|
ST2_SPC_CODE_LEN = 11
|
||||||
|
BIN_CODE_TASMOTA = 0x8000
|
||||||
|
BIN_CODE_TASMOTA_LEN = 3
|
||||||
|
|
||||||
|
NICE_LEN = 5
|
||||||
|
|
||||||
|
mask = [0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF]
|
||||||
|
|
||||||
|
# pylint: disable=missing-function-docstring,invalid-name
|
||||||
|
|
||||||
|
# Input
|
||||||
|
# out = bytearray
|
||||||
|
def append_bits(self, out, ol, code, clen, state):
|
||||||
|
#print("Append bits {ol} {code} {clen} {state}".format(ol=ol, code=code, clen=clen, state=state))
|
||||||
|
if state == self.SHX_STATE_2:
|
||||||
|
# remove change state prefix
|
||||||
|
if (code >> 9) == 0x1C:
|
||||||
|
code <<= 7
|
||||||
|
clen -= 7
|
||||||
|
while clen > 0:
|
||||||
|
cur_bit = ol % 8
|
||||||
|
blen = 8 if (clen > 8) else clen
|
||||||
|
a_byte = (code >> 8) & self.mask[blen - 1]
|
||||||
|
#print("append_bits a_byte {ab} blen {blen}".format(ab=a_byte,blen=blen))
|
||||||
|
a_byte >>= cur_bit
|
||||||
|
if blen + cur_bit > 8:
|
||||||
|
blen = (8 - cur_bit)
|
||||||
|
if cur_bit == 0:
|
||||||
|
out[ol // 8] = a_byte
|
||||||
|
else:
|
||||||
|
out[ol // 8] |= a_byte
|
||||||
|
code <<= blen
|
||||||
|
ol += blen
|
||||||
|
if 0 == ol % 8: # pylint: disable=misplaced-comparison-constant
|
||||||
|
# we completed a full byte
|
||||||
|
last_c = out[(ol // 8) - 1]
|
||||||
|
if last_c in (0, self.ESCAPE_MARKER):
|
||||||
|
out[ol // 8] = 1 + last_c # increment to 0x01 or 0x2B
|
||||||
|
out[(ol // 8) -1] = self.ESCAPE_MARKER # replace old value with marker
|
||||||
|
ol += 8 # add one full byte
|
||||||
|
clen -= blen
|
||||||
|
return ol
|
||||||
|
|
||||||
|
codes = [0x82, 0xC3, 0xE5, 0xED, 0xF5] # pylint: disable=bad-whitespace
|
||||||
|
bit_len = [ 5, 7, 9, 12, 16] # pylint: disable=bad-whitespace
|
||||||
|
|
||||||
|
def encodeCount(self, out, ol, count):
|
||||||
|
#print("encodeCount ol = {ol}, count = {count}".format(ol=ol, count=count))
|
||||||
|
till = 0
|
||||||
|
base = 0
|
||||||
|
for i in range(len(self.bit_len)):
|
||||||
|
bit_len_i = self.bit_len[i]
|
||||||
|
till += (1 << bit_len_i)
|
||||||
|
if count < till:
|
||||||
|
codes_i = self.codes[i]
|
||||||
|
ol = self.append_bits(out, ol, (codes_i & 0xF8) << 8, codes_i & 0x07, 1)
|
||||||
|
#print("encodeCount append_bits ol = {ol}, code = {code}, len = {len}".format(ol=ol,code=(codes_i & 0xF8) << 8,len=codes_i & 0x07))
|
||||||
|
ol = self.append_bits(out, ol, (count - base) << (16 - bit_len_i), bit_len_i, 1)
|
||||||
|
#print("encodeCount append_bits ol = {ol}, code = {code}, len = {len}".format(ol=ol,code=(count - base) << (16 - bit_len_i),len=bit_len_i))
|
||||||
|
return ol
|
||||||
|
base = till
|
||||||
|
return ol
|
||||||
|
|
||||||
|
# Returns (int, ol, state, is_all_upper)
|
||||||
|
def matchOccurance(self, inn, len_, l_, out, ol, state, is_all_upper):
|
||||||
|
# int j, k;
|
||||||
|
longest_dist = 0
|
||||||
|
longest_len = 0
|
||||||
|
#for (j = l_ - self.NICE_LEN; j >= 0; j--) {
|
||||||
|
j = l_ - self.NICE_LEN
|
||||||
|
while j >= 0:
|
||||||
|
k = l_
|
||||||
|
#for (k = l_; k < len && j + k - l_ < l_; k++) {
|
||||||
|
while k < len_ and j + k - l_ < l_:
|
||||||
|
if inn[k] != inn[j + k - l_]:
|
||||||
|
break
|
||||||
|
k += 1
|
||||||
|
if k - l_ > self.NICE_LEN - 1:
|
||||||
|
match_len = k - l_ - self.NICE_LEN
|
||||||
|
match_dist = l_ - j - self.NICE_LEN + 1
|
||||||
|
if match_len > longest_len:
|
||||||
|
longest_len = match_len
|
||||||
|
longest_dist = match_dist
|
||||||
|
j -= 1
|
||||||
|
|
||||||
|
if longest_len:
|
||||||
|
#print("longest_len {ll}".format(ll=longest_len))
|
||||||
|
#ol_save = ol
|
||||||
|
if state == self.SHX_STATE_2 or is_all_upper:
|
||||||
|
is_all_upper = 0
|
||||||
|
state = self.SHX_STATE_1
|
||||||
|
ol = self.append_bits(out, ol, self.BACK2_STATE1_CODE, self.BACK2_STATE1_CODE_LEN, state)
|
||||||
|
|
||||||
|
ol = self.append_bits(out, ol, self.DICT_CODE, self.DICT_CODE_LEN, 1)
|
||||||
|
ol = self.encodeCount(out, ol, longest_len)
|
||||||
|
ol = self.encodeCount(out, ol, longest_dist)
|
||||||
|
#print("longest_len {ll} longest_dist {ld} ol {ols}-{ol}".format(ll=longest_len, ld=longest_dist, ol=ol, ols=ol_save))
|
||||||
|
l_ += longest_len + self.NICE_LEN
|
||||||
|
l_ -= 1
|
||||||
|
|
||||||
|
return l_, ol, state, is_all_upper
|
||||||
|
return -l_, ol, state, is_all_upper
|
||||||
|
|
||||||
|
|
||||||
|
def compress(self, inn, len_, out, len_out):
|
||||||
|
ol = 0
|
||||||
|
state = self.SHX_STATE_1
|
||||||
|
is_all_upper = 0
|
||||||
|
l = 0
|
||||||
|
while l < len_:
|
||||||
|
# for (l=0; l<len_; l++) {
|
||||||
|
|
||||||
|
c_in = inn[l]
|
||||||
|
|
||||||
|
if l and l < len_ - 4:
|
||||||
|
if c_in == inn[l - 1] and c_in == inn[l + 1] and c_in == inn[l + 2] and c_in == inn[l + 3]:
|
||||||
|
rpt_count = l + 4
|
||||||
|
while rpt_count < len_ and inn[rpt_count] == c_in:
|
||||||
|
rpt_count += 1
|
||||||
|
rpt_count -= l
|
||||||
|
|
||||||
|
if state == self.SHX_STATE_2 or is_all_upper:
|
||||||
|
is_all_upper = 0
|
||||||
|
state = self.SHX_STATE_1
|
||||||
|
ol = self.append_bits(out, ol, self.BACK2_STATE1_CODE, self.BACK2_STATE1_CODE_LEN, state) # back to lower case and Set1
|
||||||
|
|
||||||
|
ol = self.append_bits(out, ol, self.RPT_CODE_TASMOTA, self.RPT_CODE_TASMOTA_LEN, 1) # reusing CRLF for RPT
|
||||||
|
ol = self.encodeCount(out, ol, rpt_count - 4)
|
||||||
|
l += rpt_count
|
||||||
|
#l -= 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if l < (len_ - self.NICE_LEN + 1):
|
||||||
|
#l_old = l
|
||||||
|
(l, ol, state, is_all_upper) = self.matchOccurance(inn, len_, l, out, ol, state, is_all_upper)
|
||||||
|
if l > 0:
|
||||||
|
#print("matchOccurance l = {l} l_old = {lo}".format(l=l,lo=l_old))
|
||||||
|
l += 1 # for loop
|
||||||
|
continue
|
||||||
|
|
||||||
|
l = -l
|
||||||
|
|
||||||
|
if state == self.SHX_STATE_2: # if Set2
|
||||||
|
if ord(' ') <= c_in <= ord('@') or ord('[') <= c_in <= ord('`') or ord('{') <= c_in <= ord('~'):
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
state = self.SHX_STATE_1 # back to Set1 and lower case
|
||||||
|
ol = self.append_bits(out, ol, self.BACK2_STATE1_CODE, self.BACK2_STATE1_CODE_LEN, state)
|
||||||
|
|
||||||
|
is_upper = 0
|
||||||
|
if ord('A') <= c_in <= ord('Z'):
|
||||||
|
is_upper = 1
|
||||||
|
else:
|
||||||
|
if is_all_upper:
|
||||||
|
is_all_upper = 0
|
||||||
|
ol = self.append_bits(out, ol, self.BACK2_STATE1_CODE, self.BACK2_STATE1_CODE_LEN, state)
|
||||||
|
|
||||||
|
if 32 <= c_in <= 126:
|
||||||
|
if is_upper and not is_all_upper:
|
||||||
|
ll = l+5
|
||||||
|
# for (ll=l+5; ll>=l && ll<len_; ll--) {
|
||||||
|
while l <= ll < len_:
|
||||||
|
if inn[ll] < ord('A') or inn[ll] > ord('Z'):
|
||||||
|
break
|
||||||
|
|
||||||
|
ll -= 1
|
||||||
|
|
||||||
|
if ll == l-1:
|
||||||
|
ol = self.append_bits(out, ol, self.ALL_UPPER_CODE, self.ALL_UPPER_CODE_LEN, state) # CapsLock
|
||||||
|
is_all_upper = 1
|
||||||
|
|
||||||
|
if state == self.SHX_STATE_1 and ord('0') <= c_in <= ord('9'):
|
||||||
|
ol = self.append_bits(out, ol, self.SW2_STATE2_CODE, self.SW2_STATE2_CODE_LEN, state) # Switch to sticky Set2
|
||||||
|
state = self.SHX_STATE_2
|
||||||
|
|
||||||
|
c_in -= 32
|
||||||
|
if is_all_upper and is_upper:
|
||||||
|
c_in += 32
|
||||||
|
if c_in == 0 and state == self.SHX_STATE_2:
|
||||||
|
ol = self.append_bits(out, ol, self.ST2_SPC_CODE, self.ST2_SPC_CODE_LEN, state) # space from Set2 ionstead of Set1
|
||||||
|
else:
|
||||||
|
# ol = self.append_bits(out, ol, pgm_read_word(&c_95[c_in]), pgm_read_byte(&l_95[c_in]), state); // original version with c/l in split arrays
|
||||||
|
cl = self.cl_95[c_in]
|
||||||
|
ol = self.append_bits(out, ol, cl & 0xFFF0, cl & 0x000F, state)
|
||||||
|
|
||||||
|
elif c_in == 10:
|
||||||
|
ol = self.append_bits(out, ol, self.LF_CODE, self.LF_CODE_LEN, state) # LF
|
||||||
|
elif c_in == '\t':
|
||||||
|
ol = self.append_bits(out, ol, self.TAB_CODE, self.TAB_CODE_LEN, state) # TAB
|
||||||
|
else:
|
||||||
|
ol = self.append_bits(out, ol, self.BIN_CODE_TASMOTA, self.BIN_CODE_TASMOTA_LEN, state) # Binary, we reuse the Unicode marker which 3 bits instead of 9
|
||||||
|
ol = self.encodeCount(out, ol, (255 - c_in) & 0xFF)
|
||||||
|
|
||||||
|
|
||||||
|
# check that we have some headroom in the output buffer
|
||||||
|
if ol // 8 >= len_out - 4:
|
||||||
|
return -1 # we risk overflow and crash
|
||||||
|
|
||||||
|
l += 1
|
||||||
|
|
||||||
|
bits = ol % 8
|
||||||
|
if bits:
|
||||||
|
ol = self.append_bits(out, ol, self.TERM_CODE, 8 - bits, 1) # 0011 0111 1100 0000 TERM = 0011 0111 11
|
||||||
|
return (ol + 7) // 8
|
||||||
|
# return ol // 8 + 1 if (ol%8) else 0
|
||||||
|
|
||||||
|
|
||||||
|
def getBitVal(self, inn, bit_no, count):
|
||||||
|
c_in = inn[bit_no >> 3]
|
||||||
|
if bit_no >> 3 and self.ESCAPE_MARKER == inn[(bit_no >> 3) - 1]:
|
||||||
|
c_in -= 1
|
||||||
|
r = 1 << count if (c_in & (0x80 >> (bit_no % 8))) else 0
|
||||||
|
#print("getBitVal r={r}".format(r=r))
|
||||||
|
return r
|
||||||
|
|
||||||
|
# Returns:
|
||||||
|
# 0..11
|
||||||
|
# or -1 if end of stream
|
||||||
|
def getCodeIdx(self, code_type, inn, len_, bit_no_p):
|
||||||
|
code = 0
|
||||||
|
count = 0
|
||||||
|
while count < 5:
|
||||||
|
# detect marker
|
||||||
|
if self.ESCAPE_MARKER == inn[bit_no_p >> 3]:
|
||||||
|
bit_no_p += 8 # skip marker
|
||||||
|
|
||||||
|
if bit_no_p >= len_:
|
||||||
|
return -1, bit_no_p
|
||||||
|
|
||||||
|
code += self.getBitVal(inn, bit_no_p, count)
|
||||||
|
bit_no_p += 1
|
||||||
|
count += 1
|
||||||
|
code_type_code = code_type[code]
|
||||||
|
if code_type_code and (code_type_code & 0x07) == count:
|
||||||
|
#print("getCodeIdx = {r}".format(r=code_type_code >> 3))
|
||||||
|
return code_type_code >> 3, bit_no_p
|
||||||
|
|
||||||
|
#print("getCodeIdx not found = {r}".format(r=1))
|
||||||
|
return 1, bit_no_p
|
||||||
|
|
||||||
|
def getNumFromBits(self, inn, bit_no, count):
|
||||||
|
ret = 0
|
||||||
|
while count:
|
||||||
|
count -= 1
|
||||||
|
if self.ESCAPE_MARKER == inn[bit_no >> 3]:
|
||||||
|
bit_no += 8 # skip marker
|
||||||
|
ret += self.getBitVal(inn, bit_no, count)
|
||||||
|
bit_no += 1
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def readCount(self, inn, bit_no_p, len_):
|
||||||
|
(idx, bit_no_p) = self.getCodeIdx(self.us_hcode, inn, len_, bit_no_p)
|
||||||
|
if idx >= 1:
|
||||||
|
idx -= 1 # we skip v = 1 (code '0') since we no more accept 2 bits encoding
|
||||||
|
if idx >= 5 or idx < 0:
|
||||||
|
return 0, bit_no_p # unsupported or end of stream
|
||||||
|
till = 0
|
||||||
|
bit_len_idx = 0
|
||||||
|
base = 0
|
||||||
|
#for (uint32_t i = 0; i <= idx; i++) {
|
||||||
|
i = 0
|
||||||
|
while i <= idx:
|
||||||
|
# for i in range(idx):
|
||||||
|
base = till
|
||||||
|
bit_len_idx = self.bit_len[i]
|
||||||
|
till += (1 << bit_len_idx)
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
count = self.getNumFromBits(inn, bit_no_p, bit_len_idx) + base
|
||||||
|
#print("readCount getNumFromBits = {count} ({bl})".format(count=count,bl=bit_len_idx))
|
||||||
|
|
||||||
|
bit_no_p += bit_len_idx
|
||||||
|
return count, bit_no_p
|
||||||
|
|
||||||
|
def decodeRepeat(self, inn, len_, out, ol, bit_no):
|
||||||
|
#print("decodeRepeat Enter")
|
||||||
|
(dict_len, bit_no) = self.readCount(inn, bit_no, len_)
|
||||||
|
dict_len += self.NICE_LEN
|
||||||
|
(dist, bit_no) = self.readCount(inn, bit_no, len_)
|
||||||
|
dist += self.NICE_LEN - 1
|
||||||
|
#memcpy(out + ol, out + ol - dist, dict_len);
|
||||||
|
i = 0
|
||||||
|
while i < dict_len:
|
||||||
|
#for i in range(dict_len):
|
||||||
|
out[ol + i] = out[ol - dist + i]
|
||||||
|
i += 1
|
||||||
|
ol += dict_len
|
||||||
|
|
||||||
|
return ol, bit_no
|
||||||
|
|
||||||
|
def decompress(self, inn, len_, out, len_out):
|
||||||
|
ol = 0
|
||||||
|
bit_no = 0
|
||||||
|
dstate = self.SHX_SET1
|
||||||
|
is_all_upper = 0
|
||||||
|
|
||||||
|
len_ <<= 3 # *8, len_ in bits
|
||||||
|
out[ol] = 0
|
||||||
|
while bit_no < len_:
|
||||||
|
c = 0
|
||||||
|
is_upper = is_all_upper
|
||||||
|
(v, bit_no) = self.getCodeIdx(self.us_vcode, inn, len_, bit_no) # read vCode
|
||||||
|
#print("bit_no {b}. v = {v}".format(b=bit_no,v=v))
|
||||||
|
if v < 0:
|
||||||
|
break # end of stream
|
||||||
|
h = dstate # Set1 or Set2
|
||||||
|
if v == 0: # Switch which is common to Set1 and Set2, first entry
|
||||||
|
(h, bit_no) = self.getCodeIdx(self.us_hcode, inn, len_, bit_no) # read hCode
|
||||||
|
#print("bit_no {b}. h = {h}".format(b=bit_no,h=h))
|
||||||
|
if h < 0:
|
||||||
|
break # end of stream
|
||||||
|
if h == self.SHX_SET1: # target is Set1
|
||||||
|
if dstate == self.SHX_SET1: # Switch from Set1 to Set1 us UpperCase
|
||||||
|
if is_all_upper: # if CapsLock, then back to LowerCase
|
||||||
|
is_upper = 0
|
||||||
|
is_all_upper = 0
|
||||||
|
continue
|
||||||
|
|
||||||
|
(v, bit_no) = self.getCodeIdx(self.us_vcode, inn, len_, bit_no) # read again vCode
|
||||||
|
if v < 0:
|
||||||
|
break # end of stream
|
||||||
|
if v == 0:
|
||||||
|
(h, bit_no) = self.getCodeIdx(self.us_hcode, inn, len_, bit_no) # read second hCode
|
||||||
|
if h < 0:
|
||||||
|
break # end of stream
|
||||||
|
if h == self.SHX_SET1: # If double Switch Set1, the CapsLock
|
||||||
|
is_all_upper = 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
is_upper = 1 # anyways, still uppercase
|
||||||
|
else:
|
||||||
|
dstate = self.SHX_SET1 # if Set was not Set1, switch to Set1
|
||||||
|
continue
|
||||||
|
|
||||||
|
elif h == self.SHX_SET2: # If Set2, switch dstate to Set2
|
||||||
|
if dstate == self.SHX_SET1:
|
||||||
|
dstate = self.SHX_SET2
|
||||||
|
continue
|
||||||
|
|
||||||
|
if h != self.SHX_SET1: # all other Sets (why not else)
|
||||||
|
(v, bit_no) = self.getCodeIdx(self.us_vcode, inn, len_, bit_no) # we changed set, now read vCode for char
|
||||||
|
if v < 0:
|
||||||
|
break # end of stream
|
||||||
|
|
||||||
|
if v == 0 and h == self.SHX_SET1A:
|
||||||
|
#print("v = 0, h = self.SHX_SET1A")
|
||||||
|
if is_upper:
|
||||||
|
(temp, bit_no) = self.readCount(inn, bit_no, len_)
|
||||||
|
out[ol] = 255 - temp # binary
|
||||||
|
ol += 1
|
||||||
|
else:
|
||||||
|
(ol, bit_no) = self.decodeRepeat(inn, len_, out, ol, bit_no) # dist
|
||||||
|
continue
|
||||||
|
|
||||||
|
if h == self.SHX_SET1 and v == 3:
|
||||||
|
# was Unicode, will do Binary instead
|
||||||
|
(temp, bit_no) = self.readCount(inn, bit_no, len_)
|
||||||
|
out[ol] = 255 - temp # binary
|
||||||
|
ol += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if h < 7 and v < 11:
|
||||||
|
#print("h {h} v {v}".format(h=h,v=v))
|
||||||
|
c = ord(self.sets[h][v])
|
||||||
|
if ord('a') <= c <= ord('z'):
|
||||||
|
if is_upper:
|
||||||
|
c -= 32 # go to UpperCase for letters
|
||||||
|
else: # handle all other cases
|
||||||
|
if is_upper and dstate == self.SHX_SET1 and v == 1:
|
||||||
|
c = ord('\t') # If UpperCase Space, change to TAB
|
||||||
|
if h == self.SHX_SET1B:
|
||||||
|
if 8 == v: # was LF or RPT, now only LF # pylint: disable=misplaced-comparison-constant
|
||||||
|
out[ol] = ord('\n')
|
||||||
|
ol += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if 9 == v: # was CRLF, now RPT # pylint: disable=misplaced-comparison-constant
|
||||||
|
(count, bit_no) = self.readCount(inn, bit_no, len_)
|
||||||
|
count += 4
|
||||||
|
if ol + count >= len_out:
|
||||||
|
return -1 # overflow
|
||||||
|
|
||||||
|
rpt_c = out[ol - 1]
|
||||||
|
while count:
|
||||||
|
count -= 1
|
||||||
|
out[ol] = rpt_c
|
||||||
|
ol += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if 10 == v: # pylint: disable=misplaced-comparison-constant
|
||||||
|
break # TERM, stop decoding
|
||||||
|
|
||||||
|
out[ol] = c
|
||||||
|
ol += 1
|
||||||
|
|
||||||
|
if ol >= len_out:
|
||||||
|
return -1 # overflow
|
||||||
|
|
||||||
|
return ol
|
||||||
|
|
||||||
|
# pylint: enable=missing-function-docstring
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# pylint: disable=line-too-long
|
||||||
|
UNISHOX = Unishox()
|
||||||
|
BYTES_ = bytearray(2048)
|
||||||
|
INN = bytearray(b'ON Switch1#State==1 DO Add1 1 ENDON ON Var1#State==0 DO ShutterStop1 ENDON ON Var1#State==1 DO ShutterClose1 ENDON ON Var1#State>=2 DO Var1 0 ENDON ON Shutter1#Close DO Var1 0 ENDON ON Switch2#State==1 DO Add2 1 ENDON ON Var2#State==0 DO ShutterStop1 ENDON ON Var2#State==1 DO ShutterOpen1 ENDON ON Var2#State>=2 DO Var2 0 ENDON ON Shutter1#Open DO Var2 0 ENDON')
|
||||||
|
LEN_ = UNISHOX.compress(INN, len(INN), BYTES_, len(BYTES_))
|
||||||
|
print("Compressed from {fromm} to {to} ({p}%)".format(fromm=len(INN), to=LEN_, p=(100-LEN_/len(INN)*100)))
|
||||||
|
|
||||||
|
OUT = bytearray(2048)
|
||||||
|
LEN_ = UNISHOX.decompress(BYTES_, LEN_, OUT, len(OUT))
|
||||||
|
print(str(OUT, 'utf-8').split('\x00')[0])
|
|
@ -153,52 +153,47 @@ const uint16_t BIN_CODE_TASMOTA_LEN = 3;
|
||||||
// uint16_t mask[] PROGMEM = {0x8000, 0xC000, 0xE000, 0xF000, 0xF800, 0xFC00, 0xFE00, 0xFF00};
|
// uint16_t mask[] PROGMEM = {0x8000, 0xC000, 0xE000, 0xF000, 0xF800, 0xFC00, 0xFE00, 0xFF00};
|
||||||
uint8_t mask[] PROGMEM = {0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF};
|
uint8_t mask[] PROGMEM = {0x80, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE, 0xFF};
|
||||||
|
|
||||||
int append_bits(char *out, size_t ol, unsigned int code, int clen, byte state) {
|
|
||||||
|
|
||||||
byte cur_bit;
|
|
||||||
byte blen;
|
|
||||||
unsigned char a_byte;
|
|
||||||
|
|
||||||
if (state == SHX_STATE_2) {
|
void Unishox::append_bits(unsigned int code, int clen) {
|
||||||
// remove change state prefix
|
|
||||||
if ((code >> 9) == 0x1C) {
|
byte cur_bit;
|
||||||
code <<= 7;
|
byte blen;
|
||||||
clen -= 7;
|
unsigned char a_byte;
|
||||||
|
|
||||||
|
if (state == SHX_STATE_2) {
|
||||||
|
// remove change state prefix
|
||||||
|
if ((code >> 9) == 0x1C) {
|
||||||
|
code <<= 7;
|
||||||
|
clen -= 7;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (clen > 0) {
|
||||||
|
cur_bit = ol % 8;
|
||||||
|
blen = (clen > 8 ? 8 : clen);
|
||||||
|
a_byte = (code >> 8) & pgm_read_word(&mask[blen - 1]);
|
||||||
|
a_byte >>= cur_bit;
|
||||||
|
if (blen + cur_bit > 8)
|
||||||
|
blen = (8 - cur_bit);
|
||||||
|
if (out) { // if out == nullptr, then we are in dry-run mode
|
||||||
|
if (cur_bit == 0)
|
||||||
|
out[ol >> 3] = a_byte;
|
||||||
|
else
|
||||||
|
out[ol >> 3] |= a_byte;
|
||||||
|
}
|
||||||
|
code <<= blen;
|
||||||
|
ol += blen;
|
||||||
|
if ((out) && (0 == ol % 8)) { // if out == nullptr, dry-run mode. We miss the escaping of characters in the length
|
||||||
|
// we completed a full byte
|
||||||
|
char last_c = out[(ol / 8) - 1];
|
||||||
|
if ((0 == last_c) || (ESCAPE_MARKER == last_c)) {
|
||||||
|
out[ol >> 3] = 1 + last_c; // increment to 0x01 or 0x2B
|
||||||
|
out[(ol >>3) -1] = ESCAPE_MARKER; // replace old value with marker
|
||||||
|
ol += 8; // add one full byte
|
||||||
}
|
}
|
||||||
//if (code == 14272 && clen == 10) {
|
}
|
||||||
// code = 9084;
|
clen -= blen;
|
||||||
// clen = 14;
|
}
|
||||||
//}
|
|
||||||
}
|
|
||||||
while (clen > 0) {
|
|
||||||
cur_bit = ol % 8;
|
|
||||||
blen = (clen > 8 ? 8 : clen);
|
|
||||||
// a_byte = (code & pgm_read_word(&mask[blen - 1])) >> 8;
|
|
||||||
// a_byte = (code & (pgm_read_word(&mask[blen - 1]) << 8)) >> 8;
|
|
||||||
a_byte = (code >> 8) & pgm_read_word(&mask[blen - 1]);
|
|
||||||
a_byte >>= cur_bit;
|
|
||||||
if (blen + cur_bit > 8)
|
|
||||||
blen = (8 - cur_bit);
|
|
||||||
if (out) { // if out == nullptr, then we are in dry-run mode
|
|
||||||
if (cur_bit == 0)
|
|
||||||
out[ol / 8] = a_byte;
|
|
||||||
else
|
|
||||||
out[ol / 8] |= a_byte;
|
|
||||||
}
|
|
||||||
code <<= blen;
|
|
||||||
ol += blen;
|
|
||||||
if ((out) && (0 == ol % 8)) { // if out == nullptr, dry-run mode. We miss the escaping of characters in the length
|
|
||||||
// we completed a full byte
|
|
||||||
char last_c = out[(ol / 8) - 1];
|
|
||||||
if ((0 == last_c) || (ESCAPE_MARKER == last_c)) {
|
|
||||||
out[ol / 8] = 1 + last_c; // increment to 0x01 or 0x2B
|
|
||||||
out[(ol / 8) -1] = ESCAPE_MARKER; // replace old value with marker
|
|
||||||
ol += 8; // add one full byte
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clen -= blen;
|
|
||||||
}
|
|
||||||
return ol;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// First five bits are code and Last three bits of codes represent length
|
// First five bits are code and Last three bits of codes represent length
|
||||||
|
@ -210,40 +205,36 @@ byte codes[] PROGMEM = { 0x82, 0xC3, 0xE5, 0xED, 0xF5 };
|
||||||
byte bit_len[] PROGMEM = { 5, 7, 9, 12, 16 };
|
byte bit_len[] PROGMEM = { 5, 7, 9, 12, 16 };
|
||||||
// uint16_t adder[7] PROGMEM = { 0, 32, 160, 672, 4768 }; // no more used
|
// uint16_t adder[7] PROGMEM = { 0, 32, 160, 672, 4768 }; // no more used
|
||||||
|
|
||||||
int encodeCount(char *out, int ol, int count) {
|
void Unishox::encodeCount(int32_t count) {
|
||||||
int till = 0;
|
int till = 0;
|
||||||
int base = 0;
|
int base = 0;
|
||||||
for (int i = 0; i < sizeof(bit_len); i++) {
|
for (uint32_t i = 0; i < sizeof(bit_len); i++) {
|
||||||
uint32_t bit_len_i = pgm_read_byte(&bit_len[i]);
|
uint32_t bit_len_i = pgm_read_byte(&bit_len[i]);
|
||||||
till += (1 << bit_len_i);
|
till += (1 << bit_len_i);
|
||||||
if (count < till) {
|
if (count < till) {
|
||||||
byte codes_i = pgm_read_byte(&codes[i]);
|
byte codes_i = pgm_read_byte(&codes[i]);
|
||||||
ol = append_bits(out, ol, (codes_i & 0xF8) << 8, codes_i & 0x07, 1);
|
append_bits((codes_i & 0xF8) << 8, codes_i & 0x07);
|
||||||
// ol = append_bits(out, ol, (count - pgm_read_word(&adder[i])) << (16 - bit_len_i), bit_len_i, 1);
|
// ol = append_bits(out, ol, (count - pgm_read_word(&adder[i])) << (16 - bit_len_i), bit_len_i, 1);
|
||||||
ol = append_bits(out, ol, (count - base) << (16 - bit_len_i), bit_len_i, 1);
|
append_bits((count - base) << (16 - bit_len_i), bit_len_i);
|
||||||
return ol;
|
return;
|
||||||
}
|
}
|
||||||
base = till;
|
base = till;
|
||||||
}
|
}
|
||||||
return ol;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int matchOccurance(const char *in, int len, int l, char *out, int *ol, byte *state, byte *is_all_upper) {
|
bool Unishox::matchOccurance(void) {
|
||||||
int j, k;
|
int32_t j, k;
|
||||||
int longest_dist = 0;
|
uint32_t longest_dist = 0;
|
||||||
int longest_len = 0;
|
uint32_t longest_len = 0;
|
||||||
for (j = l - NICE_LEN; j >= 0; j--) {
|
for (j = l - NICE_LEN; j >= 0; j--) {
|
||||||
for (k = l; k < len && j + k - l < l; k++) {
|
for (k = l; k < len && j + k - l < l; k++) {
|
||||||
if (in[k] != in[j + k - l])
|
if (in[k] != in[j + k - l])
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// while ((((unsigned char) in[k]) >> 6) == 2)
|
|
||||||
// k--; // Skip partial UTF-8 matches
|
|
||||||
//if ((in[k - 1] >> 3) == 0x1E || (in[k - 1] >> 4) == 0x0E || (in[k - 1] >> 5) == 0x06)
|
|
||||||
// k--;
|
|
||||||
if (k - l > NICE_LEN - 1) {
|
if (k - l > NICE_LEN - 1) {
|
||||||
int match_len = k - l - NICE_LEN;
|
uint32_t match_len = k - l - NICE_LEN;
|
||||||
int match_dist = l - j - NICE_LEN + 1;
|
uint32_t match_dist = l - j - NICE_LEN + 1;
|
||||||
if (match_len > longest_len) {
|
if (match_len > longest_len) {
|
||||||
longest_len = match_len;
|
longest_len = match_len;
|
||||||
longest_dist = match_dist;
|
longest_dist = match_dist;
|
||||||
|
@ -251,19 +242,18 @@ int matchOccurance(const char *in, int len, int l, char *out, int *ol, byte *sta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (longest_len) {
|
if (longest_len) {
|
||||||
if (*state == SHX_STATE_2 || *is_all_upper) {
|
if (state == SHX_STATE_2 || is_all_upper) {
|
||||||
*is_all_upper = 0;
|
is_all_upper = 0;
|
||||||
*state = SHX_STATE_1;
|
state = SHX_STATE_1;
|
||||||
*ol = append_bits(out, *ol, BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN, *state);
|
append_bits(BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN);
|
||||||
}
|
}
|
||||||
*ol = append_bits(out, *ol, DICT_CODE, DICT_CODE_LEN, 1);
|
append_bits(DICT_CODE, DICT_CODE_LEN);
|
||||||
*ol = encodeCount(out, *ol, longest_len);
|
encodeCount(longest_len);
|
||||||
*ol = encodeCount(out, *ol, longest_dist);
|
encodeCount(longest_dist);
|
||||||
l += (longest_len + NICE_LEN);
|
l += longest_len + NICE_LEN - 1;
|
||||||
l--;
|
return true;
|
||||||
return l;
|
|
||||||
}
|
}
|
||||||
return -l;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compress a buffer.
|
// Compress a buffer.
|
||||||
|
@ -275,15 +265,18 @@ int matchOccurance(const char *in, int len, int l, char *out, int *ol, byte *sta
|
||||||
// Output:
|
// Output:
|
||||||
// - if >= 0: size of the compressed buffer. The output buffer does not contain NULL bytes, and it is not NULL terminated
|
// - if >= 0: size of the compressed buffer. The output buffer does not contain NULL bytes, and it is not NULL terminated
|
||||||
// - if < 0: an error occured, most certainly the output buffer was not large enough
|
// - if < 0: an error occured, most certainly the output buffer was not large enough
|
||||||
int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out) {
|
int32_t Unishox::unishox_compress(const char *p_in, size_t p_len, char *p_out, size_t p_len_out) {
|
||||||
|
in = p_in;
|
||||||
|
len = p_len;
|
||||||
|
out = p_out;
|
||||||
|
len_out = p_len_out;
|
||||||
|
|
||||||
char *ptr;
|
char *ptr;
|
||||||
byte bits;
|
byte bits;
|
||||||
byte state;
|
|
||||||
|
|
||||||
int l, ll, ol;
|
int ll;
|
||||||
char c_in, c_next;
|
char c_in, c_next;
|
||||||
byte is_upper, is_all_upper;
|
byte is_upper;
|
||||||
|
|
||||||
ol = 0;
|
ol = 0;
|
||||||
state = SHX_STATE_1;
|
state = SHX_STATE_1;
|
||||||
|
@ -302,23 +295,20 @@ int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out)
|
||||||
if (state == SHX_STATE_2 || is_all_upper) {
|
if (state == SHX_STATE_2 || is_all_upper) {
|
||||||
is_all_upper = 0;
|
is_all_upper = 0;
|
||||||
state = SHX_STATE_1;
|
state = SHX_STATE_1;
|
||||||
ol = append_bits(out, ol, BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN, state); // back to lower case and Set1
|
append_bits(BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN); // back to lower case and Set1
|
||||||
}
|
}
|
||||||
// ol = append_bits(out, ol, RPT_CODE, RPT_CODE_LEN, 1);
|
// ol = append_bits(out, ol, RPT_CODE, RPT_CODE_LEN, 1);
|
||||||
ol = append_bits(out, ol, RPT_CODE_TASMOTA, RPT_CODE_TASMOTA_LEN, 1); // reusing CRLF for RPT
|
append_bits(RPT_CODE_TASMOTA, RPT_CODE_TASMOTA_LEN); // reusing CRLF for RPT
|
||||||
ol = encodeCount(out, ol, rpt_count - 4);
|
encodeCount(rpt_count - 4);
|
||||||
l += rpt_count;
|
l += rpt_count - 1;
|
||||||
l--;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (l < (len - NICE_LEN + 1)) {
|
if (l < (len - NICE_LEN + 1)) {
|
||||||
l = matchOccurance(in, len, l, out, &ol, &state, &is_all_upper);
|
if (matchOccurance()) {
|
||||||
if (l > 0) {
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
l = -l;
|
|
||||||
}
|
}
|
||||||
if (state == SHX_STATE_2) { // if Set2
|
if (state == SHX_STATE_2) { // if Set2
|
||||||
if ((c_in >= ' ' && c_in <= '@') ||
|
if ((c_in >= ' ' && c_in <= '@') ||
|
||||||
|
@ -326,7 +316,7 @@ int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out)
|
||||||
(c_in >= '{' && c_in <= '~')) {
|
(c_in >= '{' && c_in <= '~')) {
|
||||||
} else {
|
} else {
|
||||||
state = SHX_STATE_1; // back to Set1 and lower case
|
state = SHX_STATE_1; // back to Set1 and lower case
|
||||||
ol = append_bits(out, ol, BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN, state);
|
append_bits(BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,7 +326,7 @@ int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out)
|
||||||
else {
|
else {
|
||||||
if (is_all_upper) {
|
if (is_all_upper) {
|
||||||
is_all_upper = 0;
|
is_all_upper = 0;
|
||||||
ol = append_bits(out, ol, BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN, state);
|
append_bits(BACK2_STATE1_CODE, BACK2_STATE1_CODE_LEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,37 +341,30 @@ int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (ll == l-1) {
|
if (ll == l-1) {
|
||||||
ol = append_bits(out, ol, ALL_UPPER_CODE, ALL_UPPER_CODE_LEN, state); // CapsLock
|
append_bits(ALL_UPPER_CODE, ALL_UPPER_CODE_LEN); // CapsLock
|
||||||
is_all_upper = 1;
|
is_all_upper = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (state == SHX_STATE_1 && c_in >= '0' && c_in <= '9') {
|
if (state == SHX_STATE_1 && c_in >= '0' && c_in <= '9') {
|
||||||
ol = append_bits(out, ol, SW2_STATE2_CODE, SW2_STATE2_CODE_LEN, state); // Switch to sticky Set2
|
append_bits(SW2_STATE2_CODE, SW2_STATE2_CODE_LEN); // Switch to sticky Set2
|
||||||
state = SHX_STATE_2;
|
state = SHX_STATE_2;
|
||||||
}
|
}
|
||||||
c_in -= 32;
|
c_in -= 32;
|
||||||
if (is_all_upper && is_upper)
|
if (is_all_upper && is_upper)
|
||||||
c_in += 32;
|
c_in += 32;
|
||||||
if (c_in == 0 && state == SHX_STATE_2)
|
if (c_in == 0 && state == SHX_STATE_2)
|
||||||
ol = append_bits(out, ol, ST2_SPC_CODE, ST2_SPC_CODE_LEN, state); // space from Set2 ionstead of Set1
|
append_bits(ST2_SPC_CODE, ST2_SPC_CODE_LEN); // space from Set2 ionstead of Set1
|
||||||
else {
|
else {
|
||||||
// ol = append_bits(out, ol, pgm_read_word(&c_95[c_in]), pgm_read_byte(&l_95[c_in]), state); // original version with c/l in split arrays
|
|
||||||
uint16_t cl = pgm_read_word(&cl_95[c_in]);
|
uint16_t cl = pgm_read_word(&cl_95[c_in]);
|
||||||
ol = append_bits(out, ol, cl & 0xFFF0, cl & 0x000F, state);
|
append_bits(cl & 0xFFF0, cl & 0x000F);
|
||||||
}
|
}
|
||||||
} else
|
} else if (c_in == 10) {
|
||||||
// if (c_in == 13 && c_next == 10) { // CRLF disabled
|
append_bits(LF_CODE, LF_CODE_LEN); // LF
|
||||||
// ol = append_bits(out, ol, CRLF_CODE, CRLF_CODE_LEN, state); // CRLF
|
} else if (c_in == '\t') {
|
||||||
// l++;
|
append_bits(TAB_CODE, TAB_CODE_LEN); // TAB
|
||||||
// } else
|
|
||||||
if (c_in == 10) {
|
|
||||||
ol = append_bits(out, ol, LF_CODE, LF_CODE_LEN, state); // LF
|
|
||||||
} else
|
|
||||||
if (c_in == '\t') {
|
|
||||||
ol = append_bits(out, ol, TAB_CODE, TAB_CODE_LEN, state); // TAB
|
|
||||||
} else {
|
} else {
|
||||||
ol = append_bits(out, ol, BIN_CODE_TASMOTA, BIN_CODE_TASMOTA_LEN, state); // Binary, we reuse the Unicode marker which 3 bits instead of 9
|
append_bits(BIN_CODE_TASMOTA, BIN_CODE_TASMOTA_LEN); // Binary, we reuse the Unicode marker which 3 bits instead of 9
|
||||||
ol = encodeCount(out, ol, (unsigned char) 255 - c_in);
|
encodeCount((unsigned char) 255 - c_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check that we have some headroom in the output buffer
|
// check that we have some headroom in the output buffer
|
||||||
|
@ -392,50 +375,46 @@ int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out)
|
||||||
|
|
||||||
bits = ol % 8;
|
bits = ol % 8;
|
||||||
if (bits) {
|
if (bits) {
|
||||||
ol = append_bits(out, ol, TERM_CODE, 8 - bits, 1); // 0011 0111 1100 0000 TERM = 0011 0111 11
|
state = SHX_STATE_1;
|
||||||
|
append_bits(TERM_CODE, 8 - bits); // 0011 0111 1100 0000 TERM = 0011 0111 11
|
||||||
}
|
}
|
||||||
return ol/8+(ol%8?1:0);
|
return ol/8+(ol%8?1:0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getBitVal(const char *in, int bit_no, int count) {
|
uint32_t Unishox::getNextBit(void) {
|
||||||
char c_in = in[bit_no >> 3];
|
if (8 == bit_no) {
|
||||||
if ((bit_no >> 3) && (ESCAPE_MARKER == in[(bit_no >> 3) - 1])) { // if previous byte is a marker, decrement
|
byte_in = in[byte_no++];
|
||||||
c_in--;
|
if (ESCAPE_MARKER == byte_in) {
|
||||||
|
byte_in = in[byte_no++] - 1;
|
||||||
|
}
|
||||||
|
bit_no = 0;
|
||||||
}
|
}
|
||||||
return (c_in & (0x80 >> (bit_no % 8)) ? 1 << count : 0);
|
return byte_in & (0x80 >> bit_no++) ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns:
|
// Returns:
|
||||||
// 0..11
|
// 0..11
|
||||||
// or -1 if end of stream
|
// or -1 if end of stream
|
||||||
int getCodeIdx(char *code_type, const char *in, int len, int *bit_no_p) {
|
int32_t Unishox::getCodeIdx(const char *code_type) {
|
||||||
int code = 0;
|
int32_t code = 0;
|
||||||
int count = 0;
|
int32_t count = 0;
|
||||||
do {
|
do {
|
||||||
// detect marker
|
if (bit_no >= len)
|
||||||
if (ESCAPE_MARKER == in[*bit_no_p >> 3]) {
|
|
||||||
*bit_no_p += 8; // skip marker
|
|
||||||
}
|
|
||||||
if (*bit_no_p >= len)
|
|
||||||
return -1; // invalid state
|
return -1; // invalid state
|
||||||
code += getBitVal(in, *bit_no_p, count);
|
code += getNextBit() << count;
|
||||||
(*bit_no_p)++;
|
|
||||||
count++;
|
count++;
|
||||||
uint8_t code_type_code = pgm_read_byte(&code_type[code]);
|
uint8_t code_type_code = pgm_read_byte(&code_type[code]);
|
||||||
if (code_type_code && (code_type_code & 0x07) == count) {
|
if (code_type_code && (code_type_code & 0x07) == count) {
|
||||||
return code_type_code >> 3;
|
return code_type_code >> 3;
|
||||||
}
|
}
|
||||||
} while (count < 5);
|
} while (count < 5);
|
||||||
return 1; // skip if code not found
|
return -1; // skip if code not found
|
||||||
}
|
}
|
||||||
|
|
||||||
int getNumFromBits(const char *in, int bit_no, int count) {
|
int32_t Unishox::getNumFromBits(uint32_t count) {
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
while (count--) {
|
while (count--) {
|
||||||
if (ESCAPE_MARKER == in[bit_no >> 3]) {
|
ret += getNextBit() << count;
|
||||||
bit_no += 8; // skip marker
|
|
||||||
}
|
|
||||||
ret += getBitVal(in, bit_no++, count);
|
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -452,8 +431,8 @@ int getNumFromBits(const char *in, int bit_no, int count) {
|
||||||
// uint16_t adder_read[] PROGMEM = {0, 32, 160, 672, 4768 };
|
// uint16_t adder_read[] PROGMEM = {0, 32, 160, 672, 4768 };
|
||||||
|
|
||||||
// Code size optimized, recalculate adder[] like in encodeCount
|
// Code size optimized, recalculate adder[] like in encodeCount
|
||||||
int readCount(const char *in, int *bit_no_p, int len) {
|
uint32_t Unishox::readCount(void) {
|
||||||
int idx = getCodeIdx(us_hcode, in, len, bit_no_p);
|
int32_t idx = getCodeIdx(us_hcode);
|
||||||
if (idx >= 1) idx--; // we skip v = 1 (code '0') since we no more accept 2 bits encoding
|
if (idx >= 1) idx--; // we skip v = 1 (code '0') since we no more accept 2 bits encoding
|
||||||
if ((idx >= sizeof(bit_len)) || (idx < 0)) return 0; // unsupported or end of stream
|
if ((idx >= sizeof(bit_len)) || (idx < 0)) return 0; // unsupported or end of stream
|
||||||
|
|
||||||
|
@ -465,44 +444,41 @@ int readCount(const char *in, int *bit_no_p, int len) {
|
||||||
bit_len_idx = pgm_read_byte(&bit_len[i]);
|
bit_len_idx = pgm_read_byte(&bit_len[i]);
|
||||||
till += (1 << bit_len_idx);
|
till += (1 << bit_len_idx);
|
||||||
}
|
}
|
||||||
int count = getNumFromBits(in, *bit_no_p, bit_len_idx) + base;
|
int count = getNumFromBits(bit_len_idx) + base;
|
||||||
|
|
||||||
(*bit_no_p) += bit_len_idx;
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
int decodeRepeat(const char *in, int len, char *out, int ol, int *bit_no) {
|
void Unishox::decodeRepeat(void) {
|
||||||
int dict_len = readCount(in, bit_no, len) + NICE_LEN;
|
uint32_t dict_len = readCount() + NICE_LEN;
|
||||||
int dist = readCount(in, bit_no, len) + NICE_LEN - 1;
|
uint32_t dist = readCount() + NICE_LEN - 1;
|
||||||
memcpy(out + ol, out + ol - dist, dict_len);
|
memcpy(out + ol, out + ol - dist, dict_len);
|
||||||
ol += dict_len;
|
ol += dict_len;
|
||||||
|
|
||||||
return ol;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t unishox_decompress(const char *in, size_t len, char *out, size_t len_out) {
|
int32_t Unishox::unishox_decompress(const char *p_in, size_t p_len, char *p_out, size_t p_len_out) {
|
||||||
|
in = p_in;
|
||||||
|
len = p_len;
|
||||||
|
out = p_out;
|
||||||
|
len_out = p_len_out;
|
||||||
|
|
||||||
int dstate;
|
ol = 0;
|
||||||
int bit_no;
|
bit_no = 8; // force load of first byte, pretending we expired the last one
|
||||||
byte is_all_upper;
|
byte_no = 0;
|
||||||
|
|
||||||
int ol = 0;
|
|
||||||
bit_no = 0;
|
|
||||||
dstate = SHX_SET1;
|
dstate = SHX_SET1;
|
||||||
is_all_upper = 0;
|
is_all_upper = 0;
|
||||||
|
|
||||||
len <<= 3; // *8, len in bits
|
len <<= 3; // *8, len in bits
|
||||||
out[ol] = 0;
|
out[ol] = 0;
|
||||||
while (bit_no < len) {
|
while (bit_no < len) {
|
||||||
int h, v;
|
int32_t h, v;
|
||||||
char c = 0;
|
char c = 0;
|
||||||
byte is_upper = is_all_upper;
|
byte is_upper = is_all_upper;
|
||||||
int orig_bit_no = bit_no;
|
v = getCodeIdx(us_vcode); // read vCode
|
||||||
v = getCodeIdx(us_vcode, in, len, &bit_no); // read vCode
|
|
||||||
if (v < 0) break; // end of stream
|
if (v < 0) break; // end of stream
|
||||||
h = dstate; // Set1 or Set2
|
h = dstate; // Set1 or Set2
|
||||||
if (v == 0) { // Switch which is common to Set1 and Set2, first entry
|
if (v == 0) { // Switch which is common to Set1 and Set2, first entry
|
||||||
h = getCodeIdx(us_hcode, in, len, &bit_no); // read hCode
|
h = getCodeIdx(us_hcode); // read hCode
|
||||||
if (h < 0) break; // end of stream
|
if (h < 0) break; // end of stream
|
||||||
if (h == SHX_SET1) { // target is Set1
|
if (h == SHX_SET1) { // target is Set1
|
||||||
if (dstate == SHX_SET1) { // Switch from Set1 to Set1 us UpperCase
|
if (dstate == SHX_SET1) { // Switch from Set1 to Set1 us UpperCase
|
||||||
|
@ -510,10 +486,10 @@ int32_t unishox_decompress(const char *in, size_t len, char *out, size_t len_out
|
||||||
is_upper = is_all_upper = 0;
|
is_upper = is_all_upper = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
v = getCodeIdx(us_vcode, in, len, &bit_no); // read again vCode
|
v = getCodeIdx(us_vcode); // read again vCode
|
||||||
if (v < 0) break; // end of stream
|
if (v < 0) break; // end of stream
|
||||||
if (v == 0) {
|
if (v == 0) {
|
||||||
h = getCodeIdx(us_hcode, in, len, &bit_no); // read second hCode
|
h = getCodeIdx(us_hcode); // read second hCode
|
||||||
if (h < 0) break; // end of stream
|
if (h < 0) break; // end of stream
|
||||||
if (h == SHX_SET1) { // If double Switch Set1, the CapsLock
|
if (h == SHX_SET1) { // If double Switch Set1, the CapsLock
|
||||||
is_all_upper = 1;
|
is_all_upper = 1;
|
||||||
|
@ -532,23 +508,23 @@ int32_t unishox_decompress(const char *in, size_t len, char *out, size_t len_out
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (h != SHX_SET1) { // all other Sets (why not else)
|
if (h != SHX_SET1) { // all other Sets (why not else)
|
||||||
v = getCodeIdx(us_vcode, in, len, &bit_no); // we changed set, now read vCode for char
|
v = getCodeIdx(us_vcode); // we changed set, now read vCode for char
|
||||||
if (v < 0) break; // end of stream
|
if (v < 0) break; // end of stream
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v == 0 && h == SHX_SET1A) {
|
if (v == 0 && h == SHX_SET1A) {
|
||||||
if (is_upper) {
|
if (is_upper) {
|
||||||
out[ol++] = 255 - readCount(in, &bit_no, len); // binary
|
out[ol++] = 255 - readCount(); // binary
|
||||||
} else {
|
} else {
|
||||||
ol = decodeRepeat(in, len, out, ol, &bit_no); // dist
|
decodeRepeat(); // dist
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (h == SHX_SET1 && v == 3) {
|
if (h == SHX_SET1 && v == 3) {
|
||||||
// was Unicode, will do Binary instead
|
// was Unicode, will do Binary instead
|
||||||
out[ol++] = 255 - readCount(in, &bit_no, len); // binary
|
out[ol++] = 255 - readCount(); // binary
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (h < 7 && v < 11) // TODO: are these the actual limits? Not 11x7 ?
|
if (h < 7 && v < 11) // TODO: are these the actual limits? Not 11x7 ?
|
||||||
|
@ -561,22 +537,11 @@ int32_t unishox_decompress(const char *in, size_t len, char *out, size_t len_out
|
||||||
c = '\t'; // If UpperCase Space, change to TAB
|
c = '\t'; // If UpperCase Space, change to TAB
|
||||||
if (h == SHX_SET1B) {
|
if (h == SHX_SET1B) {
|
||||||
if (8 == v) { // was LF or RPT, now only LF
|
if (8 == v) { // was LF or RPT, now only LF
|
||||||
// if (is_upper) { // rpt
|
|
||||||
// int count = readCount(in, &bit_no, len);
|
|
||||||
// count += 4;
|
|
||||||
// char rpt_c = out[ol - 1];
|
|
||||||
// while (count--)
|
|
||||||
// out[ol++] = rpt_c;
|
|
||||||
// } else {
|
|
||||||
out[ol++] = '\n';
|
out[ol++] = '\n';
|
||||||
// }
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (9 == v) { // was CRLF, now RPT
|
if (9 == v) { // was CRLF, now RPT
|
||||||
// out[ol++] = '\r'; // CRLF removed
|
uint32_t count = readCount() + 4;
|
||||||
// out[ol++] = '\n';
|
|
||||||
int count = readCount(in, &bit_no, len);
|
|
||||||
count += 4;
|
|
||||||
if (ol + count >= len_out) {
|
if (ol + count >= len_out) {
|
||||||
return -1; // overflow
|
return -1; // overflow
|
||||||
}
|
}
|
||||||
|
@ -598,5 +563,4 @@ int32_t unishox_decompress(const char *in, size_t len, char *out, size_t len_out
|
||||||
}
|
}
|
||||||
|
|
||||||
return ol;
|
return ol;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,45 @@
|
||||||
#define unishox
|
#define unishox
|
||||||
|
|
||||||
extern int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out);
|
extern int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out);
|
||||||
extern int32_t unishox_decompress(const char *in, size_t len, char *out, size_t len_out);
|
//extern int32_t unishox_decompress(const char *in, size_t len, char *out, size_t len_out);
|
||||||
|
|
||||||
|
class Unishox {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Unishox() {};
|
||||||
|
|
||||||
|
int32_t unishox_decompress(const char *in, size_t len, char *out, size_t len_out);
|
||||||
|
int32_t unishox_compress(const char *in, size_t len, char *out, size_t len_out);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void append_bits(unsigned int code, int clen);
|
||||||
|
void encodeCount(int32_t count);
|
||||||
|
bool matchOccurance(void);
|
||||||
|
|
||||||
|
uint32_t getNextBit(void);
|
||||||
|
int32_t getCodeIdx(const char *code_type);
|
||||||
|
uint32_t readCount(void);
|
||||||
|
void decodeRepeat(void);
|
||||||
|
int32_t getNumFromBits(uint32_t count);
|
||||||
|
|
||||||
|
inline void writeOut(char c) { out[ol++] = c; }
|
||||||
|
|
||||||
|
int32_t l;
|
||||||
|
uint32_t ol;
|
||||||
|
int32_t bit_no;
|
||||||
|
uint32_t byte_no;
|
||||||
|
const char * in;
|
||||||
|
char * out;
|
||||||
|
size_t len;
|
||||||
|
size_t len_out;
|
||||||
|
|
||||||
|
uint8_t dstate;
|
||||||
|
unsigned char byte_in;
|
||||||
|
uint8_t state;
|
||||||
|
uint8_t is_all_upper;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -35,12 +35,36 @@ uint16_t ESPKNXIP::data_to_2byte_uint(uint8_t *data)
|
||||||
return (uint16_t)((data[1] << 8) | data[2]);
|
return (uint16_t)((data[1] << 8) | data[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float esp_knx_pow(float a, float b)
|
||||||
|
{
|
||||||
|
// https://martin.ankerl.com/2012/01/25/optimized-approximative-pow-in-c-and-cpp/
|
||||||
|
// calculate approximation with fraction of the exponent
|
||||||
|
int e = abs((int)b);
|
||||||
|
union {
|
||||||
|
double d;
|
||||||
|
int x[2];
|
||||||
|
} u = { a };
|
||||||
|
u.x[1] = (int)((b - e) * (u.x[1] - 1072632447) + 1072632447);
|
||||||
|
u.x[0] = 0;
|
||||||
|
// exponentiation by squaring with the exponent's integer part
|
||||||
|
// double r = u.d makes everything much slower, not sure why
|
||||||
|
double r = 1.0;
|
||||||
|
while (e) {
|
||||||
|
if (e & 1) {
|
||||||
|
r *= a;
|
||||||
|
}
|
||||||
|
a *= a;
|
||||||
|
e >>= 1;
|
||||||
|
}
|
||||||
|
return r * u.d;
|
||||||
|
}
|
||||||
|
|
||||||
float ESPKNXIP::data_to_2byte_float(uint8_t *data)
|
float ESPKNXIP::data_to_2byte_float(uint8_t *data)
|
||||||
{
|
{
|
||||||
//uint8_t sign = (data[1] & 0b10000000) >> 7;
|
//uint8_t sign = (data[1] & 0b10000000) >> 7;
|
||||||
uint8_t expo = (data[1] & 0b01111000) >> 3;
|
uint8_t expo = (data[1] & 0b01111000) >> 3;
|
||||||
int16_t mant = ((data[1] & 0b10000111) << 8) | data[2];
|
int16_t mant = ((data[1] & 0b10000111) << 8) | data[2];
|
||||||
return 0.01f * mant * pow(2, expo);
|
return 0.01f * mant * esp_knx_pow(2, expo);
|
||||||
}
|
}
|
||||||
|
|
||||||
time_of_day_t ESPKNXIP::data_to_3byte_time(uint8_t *data)
|
time_of_day_t ESPKNXIP::data_to_3byte_time(uint8_t *data)
|
||||||
|
|
|
@ -188,7 +188,7 @@ build_flags = ${esp_defaults.build_flags}
|
||||||
-D sint16_t=int16_t
|
-D sint16_t=int16_t
|
||||||
-D memcpy_P=memcpy
|
-D memcpy_P=memcpy
|
||||||
-D memcmp_P=memcmp
|
-D memcmp_P=memcmp
|
||||||
; -D USE_CONFIG_OVERRIDE
|
-D USE_CONFIG_OVERRIDE
|
||||||
|
|
||||||
lib_extra_dirs =
|
lib_extra_dirs =
|
||||||
libesp32
|
libesp32
|
||||||
|
|
|
@ -2,6 +2,24 @@
|
||||||
|
|
||||||
## Released
|
## Released
|
||||||
|
|
||||||
|
### 8.3.1 20200518
|
||||||
|
|
||||||
|
- Release Fred
|
||||||
|
|
||||||
|
### 8.3.0.2 20200517
|
||||||
|
|
||||||
|
- Fix HAss discovery
|
||||||
|
- Add command ``DeviceName`` defaults to FriendlyName1 and replaces FriendlyName1 in GUI
|
||||||
|
|
||||||
|
### 8.3.0.1 20200514
|
||||||
|
|
||||||
|
- Change KNX pow function to approximative pow saving 5k of code space
|
||||||
|
- Change Mutichannel Gas sensor pow function to approximative pow saving 5k of code space
|
||||||
|
- Change Quick Power Cycle detection from 4 to 7 power interrupts (#4066)
|
||||||
|
- Fix default state of ``SetOption73 0`` for button decoupling and send multi-press and hold MQTT messages
|
||||||
|
|
||||||
|
## Released
|
||||||
|
|
||||||
### 8.3.0 20200514
|
### 8.3.0 20200514
|
||||||
|
|
||||||
- Release Fred
|
- Release Fred
|
||||||
|
|
|
@ -276,6 +276,7 @@
|
||||||
#define D_WCFG_5_WAIT "Wait"
|
#define D_WCFG_5_WAIT "Wait"
|
||||||
#define D_WCFG_6_SERIAL "Serial"
|
#define D_WCFG_6_SERIAL "Serial"
|
||||||
#define D_WCFG_7_WIFIMANAGER_RESET_ONLY "ManagerRst"
|
#define D_WCFG_7_WIFIMANAGER_RESET_ONLY "ManagerRst"
|
||||||
|
#define D_CMND_DEVICENAME "DeviceName"
|
||||||
#define D_CMND_FRIENDLYNAME "FriendlyName"
|
#define D_CMND_FRIENDLYNAME "FriendlyName"
|
||||||
#define D_CMND_SWITCHMODE "SwitchMode"
|
#define D_CMND_SWITCHMODE "SwitchMode"
|
||||||
#define D_CMND_INTERLOCK "Interlock"
|
#define D_CMND_INTERLOCK "Interlock"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Други параметри"
|
#define D_OTHER_PARAMETERS "Други параметри"
|
||||||
#define D_TEMPLATE "Модел"
|
#define D_TEMPLATE "Модел"
|
||||||
#define D_ACTIVATE "Активирай"
|
#define D_ACTIVATE "Активирай"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Парола на уеб администратора"
|
#define D_WEB_ADMIN_PASSWORD "Парола на уеб администратора"
|
||||||
#define D_MQTT_ENABLE "Активиране на MQTT"
|
#define D_MQTT_ENABLE "Активиране на MQTT"
|
||||||
#define D_FRIENDLY_NAME "Приятелско име"
|
#define D_FRIENDLY_NAME "Приятелско име"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Další nastavení"
|
#define D_OTHER_PARAMETERS "Další nastavení"
|
||||||
#define D_TEMPLATE "Šablona"
|
#define D_TEMPLATE "Šablona"
|
||||||
#define D_ACTIVATE "Aktivovat"
|
#define D_ACTIVATE "Aktivovat"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Heslo Web administrátora"
|
#define D_WEB_ADMIN_PASSWORD "Heslo Web administrátora"
|
||||||
#define D_MQTT_ENABLE "MQTT aktivní"
|
#define D_MQTT_ENABLE "MQTT aktivní"
|
||||||
#define D_FRIENDLY_NAME "Friendly Name"
|
#define D_FRIENDLY_NAME "Friendly Name"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Sonstige Einstellungen"
|
#define D_OTHER_PARAMETERS "Sonstige Einstellungen"
|
||||||
#define D_TEMPLATE "Vorlage"
|
#define D_TEMPLATE "Vorlage"
|
||||||
#define D_ACTIVATE "Aktivieren"
|
#define D_ACTIVATE "Aktivieren"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Passwort für Web Oberfläche"
|
#define D_WEB_ADMIN_PASSWORD "Passwort für Web Oberfläche"
|
||||||
#define D_MQTT_ENABLE "MQTT aktivieren"
|
#define D_MQTT_ENABLE "MQTT aktivieren"
|
||||||
#define D_FRIENDLY_NAME "Name [friendly name]"
|
#define D_FRIENDLY_NAME "Name [friendly name]"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Άλλες παράμετροι"
|
#define D_OTHER_PARAMETERS "Άλλες παράμετροι"
|
||||||
#define D_TEMPLATE "Πρότυπο"
|
#define D_TEMPLATE "Πρότυπο"
|
||||||
#define D_ACTIVATE "Ενεργοποίηση"
|
#define D_ACTIVATE "Ενεργοποίηση"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Κωδικός διαχειριστή"
|
#define D_WEB_ADMIN_PASSWORD "Κωδικός διαχειριστή"
|
||||||
#define D_MQTT_ENABLE "Ενεργοποίηση MQTT"
|
#define D_MQTT_ENABLE "Ενεργοποίηση MQTT"
|
||||||
#define D_FRIENDLY_NAME "Φιλική ονομασία"
|
#define D_FRIENDLY_NAME "Φιλική ονομασία"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Other parameters"
|
#define D_OTHER_PARAMETERS "Other parameters"
|
||||||
#define D_TEMPLATE "Template"
|
#define D_TEMPLATE "Template"
|
||||||
#define D_ACTIVATE "Activate"
|
#define D_ACTIVATE "Activate"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Web Admin Password"
|
#define D_WEB_ADMIN_PASSWORD "Web Admin Password"
|
||||||
#define D_MQTT_ENABLE "MQTT enable"
|
#define D_MQTT_ENABLE "MQTT enable"
|
||||||
#define D_FRIENDLY_NAME "Friendly Name"
|
#define D_FRIENDLY_NAME "Friendly Name"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Otros parámetros"
|
#define D_OTHER_PARAMETERS "Otros parámetros"
|
||||||
#define D_TEMPLATE "Plantilla"
|
#define D_TEMPLATE "Plantilla"
|
||||||
#define D_ACTIVATE "Activar"
|
#define D_ACTIVATE "Activar"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Clave Administrador Web"
|
#define D_WEB_ADMIN_PASSWORD "Clave Administrador Web"
|
||||||
#define D_MQTT_ENABLE "Habilitar MQTT"
|
#define D_MQTT_ENABLE "Habilitar MQTT"
|
||||||
#define D_FRIENDLY_NAME "Nombre Amigable"
|
#define D_FRIENDLY_NAME "Nombre Amigable"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Autres paramètres"
|
#define D_OTHER_PARAMETERS "Autres paramètres"
|
||||||
#define D_TEMPLATE "Modèle"
|
#define D_TEMPLATE "Modèle"
|
||||||
#define D_ACTIVATE "Activer"
|
#define D_ACTIVATE "Activer"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Mot de passe Web Admin"
|
#define D_WEB_ADMIN_PASSWORD "Mot de passe Web Admin"
|
||||||
#define D_MQTT_ENABLE "MQTT activé"
|
#define D_MQTT_ENABLE "MQTT activé"
|
||||||
#define D_FRIENDLY_NAME "Surnom"
|
#define D_FRIENDLY_NAME "Surnom"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "פרמטרים שונים"
|
#define D_OTHER_PARAMETERS "פרמטרים שונים"
|
||||||
#define D_TEMPLATE "תבנית"
|
#define D_TEMPLATE "תבנית"
|
||||||
#define D_ACTIVATE "הפעל"
|
#define D_ACTIVATE "הפעל"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "סיסמת מנהל"
|
#define D_WEB_ADMIN_PASSWORD "סיסמת מנהל"
|
||||||
#define D_MQTT_ENABLE "MQTT אפשר"
|
#define D_MQTT_ENABLE "MQTT אפשר"
|
||||||
#define D_FRIENDLY_NAME "שם ידידותי"
|
#define D_FRIENDLY_NAME "שם ידידותי"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Egyéb beállítások"
|
#define D_OTHER_PARAMETERS "Egyéb beállítások"
|
||||||
#define D_TEMPLATE "Template"
|
#define D_TEMPLATE "Template"
|
||||||
#define D_ACTIVATE "Activate"
|
#define D_ACTIVATE "Activate"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Web admin jelszó"
|
#define D_WEB_ADMIN_PASSWORD "Web admin jelszó"
|
||||||
#define D_MQTT_ENABLE "MQTT engedélyezése"
|
#define D_MQTT_ENABLE "MQTT engedélyezése"
|
||||||
#define D_FRIENDLY_NAME "Név"
|
#define D_FRIENDLY_NAME "Név"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Altri parametri"
|
#define D_OTHER_PARAMETERS "Altri parametri"
|
||||||
#define D_TEMPLATE "Modello"
|
#define D_TEMPLATE "Modello"
|
||||||
#define D_ACTIVATE "Attiva"
|
#define D_ACTIVATE "Attiva"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Password amministratore web"
|
#define D_WEB_ADMIN_PASSWORD "Password amministratore web"
|
||||||
#define D_MQTT_ENABLE "Abilita MQTT"
|
#define D_MQTT_ENABLE "Abilita MQTT"
|
||||||
#define D_FRIENDLY_NAME "Nome amichevole"
|
#define D_FRIENDLY_NAME "Nome amichevole"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "기타 설정"
|
#define D_OTHER_PARAMETERS "기타 설정"
|
||||||
#define D_TEMPLATE "템플릿"
|
#define D_TEMPLATE "템플릿"
|
||||||
#define D_ACTIVATE "활성화"
|
#define D_ACTIVATE "활성화"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Web Admin 비밀번호"
|
#define D_WEB_ADMIN_PASSWORD "Web Admin 비밀번호"
|
||||||
#define D_MQTT_ENABLE "MQTT 사용"
|
#define D_MQTT_ENABLE "MQTT 사용"
|
||||||
#define D_FRIENDLY_NAME "Friendly Name"
|
#define D_FRIENDLY_NAME "Friendly Name"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Overige parameters"
|
#define D_OTHER_PARAMETERS "Overige parameters"
|
||||||
#define D_TEMPLATE "Sjabloon"
|
#define D_TEMPLATE "Sjabloon"
|
||||||
#define D_ACTIVATE "Activeer"
|
#define D_ACTIVATE "Activeer"
|
||||||
|
#define D_DEVICE_NAME "Apparaatnaam"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Web Admin Wachtwoord"
|
#define D_WEB_ADMIN_PASSWORD "Web Admin Wachtwoord"
|
||||||
#define D_MQTT_ENABLE "MQTT ingeschakeld"
|
#define D_MQTT_ENABLE "MQTT ingeschakeld"
|
||||||
#define D_FRIENDLY_NAME "Beschrijvende naam"
|
#define D_FRIENDLY_NAME "Beschrijvende naam"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Inne parametry"
|
#define D_OTHER_PARAMETERS "Inne parametry"
|
||||||
#define D_TEMPLATE "Szablon"
|
#define D_TEMPLATE "Szablon"
|
||||||
#define D_ACTIVATE "Aktywuj"
|
#define D_ACTIVATE "Aktywuj"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Hasło administratora"
|
#define D_WEB_ADMIN_PASSWORD "Hasło administratora"
|
||||||
#define D_MQTT_ENABLE "Załącz MQTT"
|
#define D_MQTT_ENABLE "Załącz MQTT"
|
||||||
#define D_FRIENDLY_NAME "Nazwa"
|
#define D_FRIENDLY_NAME "Nazwa"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Outros parâmetros"
|
#define D_OTHER_PARAMETERS "Outros parâmetros"
|
||||||
#define D_TEMPLATE "Modelo"
|
#define D_TEMPLATE "Modelo"
|
||||||
#define D_ACTIVATE "Activate"
|
#define D_ACTIVATE "Activate"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Senha de WEB Admin"
|
#define D_WEB_ADMIN_PASSWORD "Senha de WEB Admin"
|
||||||
#define D_MQTT_ENABLE "MQTT habilitado"
|
#define D_MQTT_ENABLE "MQTT habilitado"
|
||||||
#define D_FRIENDLY_NAME "Nome amigável"
|
#define D_FRIENDLY_NAME "Nome amigável"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Outros parametros"
|
#define D_OTHER_PARAMETERS "Outros parametros"
|
||||||
#define D_TEMPLATE "Modelo"
|
#define D_TEMPLATE "Modelo"
|
||||||
#define D_ACTIVATE "Ativar"
|
#define D_ACTIVATE "Ativar"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Palavra Chave do Admin WEB"
|
#define D_WEB_ADMIN_PASSWORD "Palavra Chave do Admin WEB"
|
||||||
#define D_MQTT_ENABLE "MQTT habilitado"
|
#define D_MQTT_ENABLE "MQTT habilitado"
|
||||||
#define D_FRIENDLY_NAME "Nome amigável"
|
#define D_FRIENDLY_NAME "Nome amigável"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Alți paramatri"
|
#define D_OTHER_PARAMETERS "Alți paramatri"
|
||||||
#define D_TEMPLATE "Template"
|
#define D_TEMPLATE "Template"
|
||||||
#define D_ACTIVATE "Activare"
|
#define D_ACTIVATE "Activare"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Parolă Web Admin"
|
#define D_WEB_ADMIN_PASSWORD "Parolă Web Admin"
|
||||||
#define D_MQTT_ENABLE "Activare MQTT"
|
#define D_MQTT_ENABLE "Activare MQTT"
|
||||||
#define D_FRIENDLY_NAME "Friendly Name"
|
#define D_FRIENDLY_NAME "Friendly Name"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Параметры Прочие"
|
#define D_OTHER_PARAMETERS "Параметры Прочие"
|
||||||
#define D_TEMPLATE "Template"
|
#define D_TEMPLATE "Template"
|
||||||
#define D_ACTIVATE "Activate"
|
#define D_ACTIVATE "Activate"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Пароль Web администратора"
|
#define D_WEB_ADMIN_PASSWORD "Пароль Web администратора"
|
||||||
#define D_MQTT_ENABLE "MQTT активен"
|
#define D_MQTT_ENABLE "MQTT активен"
|
||||||
#define D_FRIENDLY_NAME "Дружественное Имя"
|
#define D_FRIENDLY_NAME "Дружественное Имя"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Ostatné nastavenia"
|
#define D_OTHER_PARAMETERS "Ostatné nastavenia"
|
||||||
#define D_TEMPLATE "Template"
|
#define D_TEMPLATE "Template"
|
||||||
#define D_ACTIVATE "Activate"
|
#define D_ACTIVATE "Activate"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Heslo Web administrátora"
|
#define D_WEB_ADMIN_PASSWORD "Heslo Web administrátora"
|
||||||
#define D_MQTT_ENABLE "MQTT aktívne"
|
#define D_MQTT_ENABLE "MQTT aktívne"
|
||||||
#define D_FRIENDLY_NAME "Friendly Name"
|
#define D_FRIENDLY_NAME "Friendly Name"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Andra parametrar"
|
#define D_OTHER_PARAMETERS "Andra parametrar"
|
||||||
#define D_TEMPLATE "Template"
|
#define D_TEMPLATE "Template"
|
||||||
#define D_ACTIVATE "Activate"
|
#define D_ACTIVATE "Activate"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Webbadmin-lösenord"
|
#define D_WEB_ADMIN_PASSWORD "Webbadmin-lösenord"
|
||||||
#define D_MQTT_ENABLE "MQTT aktivera"
|
#define D_MQTT_ENABLE "MQTT aktivera"
|
||||||
#define D_FRIENDLY_NAME "Läsbart namn"
|
#define D_FRIENDLY_NAME "Läsbart namn"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Diğer parametreler"
|
#define D_OTHER_PARAMETERS "Diğer parametreler"
|
||||||
#define D_TEMPLATE "Template"
|
#define D_TEMPLATE "Template"
|
||||||
#define D_ACTIVATE "Activate"
|
#define D_ACTIVATE "Activate"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Web Yönetici Şifresi"
|
#define D_WEB_ADMIN_PASSWORD "Web Yönetici Şifresi"
|
||||||
#define D_MQTT_ENABLE "MQTT aktif"
|
#define D_MQTT_ENABLE "MQTT aktif"
|
||||||
#define D_FRIENDLY_NAME "Kullanıcı Dostu İsim"
|
#define D_FRIENDLY_NAME "Kullanıcı Dostu İsim"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "Параметри Інше"
|
#define D_OTHER_PARAMETERS "Параметри Інше"
|
||||||
#define D_TEMPLATE "Шаблони"
|
#define D_TEMPLATE "Шаблони"
|
||||||
#define D_ACTIVATE "Активований"
|
#define D_ACTIVATE "Активований"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "Гасло адміністратора Web"
|
#define D_WEB_ADMIN_PASSWORD "Гасло адміністратора Web"
|
||||||
#define D_MQTT_ENABLE "MQTT активний"
|
#define D_MQTT_ENABLE "MQTT активний"
|
||||||
#define D_FRIENDLY_NAME "Дружня назва"
|
#define D_FRIENDLY_NAME "Дружня назва"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "其他设置"
|
#define D_OTHER_PARAMETERS "其他设置"
|
||||||
#define D_TEMPLATE "模板"
|
#define D_TEMPLATE "模板"
|
||||||
#define D_ACTIVATE "启用"
|
#define D_ACTIVATE "启用"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "WEB 管理密码"
|
#define D_WEB_ADMIN_PASSWORD "WEB 管理密码"
|
||||||
#define D_MQTT_ENABLE "启用MQTT"
|
#define D_MQTT_ENABLE "启用MQTT"
|
||||||
#define D_FRIENDLY_NAME "昵称"
|
#define D_FRIENDLY_NAME "昵称"
|
||||||
|
|
|
@ -302,6 +302,7 @@
|
||||||
#define D_OTHER_PARAMETERS "其他設置"
|
#define D_OTHER_PARAMETERS "其他設置"
|
||||||
#define D_TEMPLATE "Template"
|
#define D_TEMPLATE "Template"
|
||||||
#define D_ACTIVATE "Activate"
|
#define D_ACTIVATE "Activate"
|
||||||
|
#define D_DEVICE_NAME "Device Name"
|
||||||
#define D_WEB_ADMIN_PASSWORD "WEB管理密碼"
|
#define D_WEB_ADMIN_PASSWORD "WEB管理密碼"
|
||||||
#define D_MQTT_ENABLE "啟用MQTT"
|
#define D_MQTT_ENABLE "啟用MQTT"
|
||||||
#define D_FRIENDLY_NAME "昵稱"
|
#define D_FRIENDLY_NAME "昵稱"
|
||||||
|
|
|
@ -397,7 +397,7 @@
|
||||||
// -- Rules or Script ----------------------------
|
// -- Rules or Script ----------------------------
|
||||||
// Select none or only one of the below defines USE_RULES or USE_SCRIPT
|
// Select none or only one of the below defines USE_RULES or USE_SCRIPT
|
||||||
#define USE_RULES // Add support for rules (+8k code)
|
#define USE_RULES // Add support for rules (+8k code)
|
||||||
#define USE_RULES_COMPRESSION // Compresses rules in Flash at about ~50% (+3.8k code)
|
#define USE_RULES_COMPRESSION // Compresses rules in Flash at about ~50% (+3.3k code)
|
||||||
//#define USE_SCRIPT // Add support for script (+17k code)
|
//#define USE_SCRIPT // Add support for script (+17k code)
|
||||||
//#define USE_SCRIPT_FATFS 4 // Script: Add FAT FileSystem Support
|
//#define USE_SCRIPT_FATFS 4 // Script: Add FAT FileSystem Support
|
||||||
|
|
||||||
|
@ -699,6 +699,11 @@
|
||||||
#define THERMOSTAT_TIME_OUTPUT_DELAY 180 // Default output delay between state change and real actuation event (f.i. valve open/closed)
|
#define THERMOSTAT_TIME_OUTPUT_DELAY 180 // Default output delay between state change and real actuation event (f.i. valve open/closed)
|
||||||
#define THERMOSTAT_TEMP_INIT 180 // Default init target temperature for the thermostat controller
|
#define THERMOSTAT_TEMP_INIT 180 // Default init target temperature for the thermostat controller
|
||||||
#define THERMOSTAT_TIME_MAX_OUTPUT_INCONSIST 3 // Default maximum time where the input and the outpus shall differ (for diagnostic) in minutes
|
#define THERMOSTAT_TIME_MAX_OUTPUT_INCONSIST 3 // Default maximum time where the input and the outpus shall differ (for diagnostic) in minutes
|
||||||
|
#define THERMOSTAT_TIME_MAX_AUTOTUNE 21600 // Maximum time for the PI autotune function to complete in seconds
|
||||||
|
#define THERMOSTAT_DUTYCYCLE_AUTOTUNE 35 // Default duty cycle (in % over PI cycle time) for the step response of the autotune PI function
|
||||||
|
#define THERMOSTAT_PEAKNUMBER_AUTOTUNE 8 // Default number of peak temperatures (max or min) to be used for the autotune PI function
|
||||||
|
#define THERMOSTAT_TEMP_BAND_NO_PEAK_DET 1 // Default temperature band in thenths of degrees celsius within no peak will be detected
|
||||||
|
#define THERMOSTAT_TIME_STD_DEV_PEAK_DET_OK 10 // Default standard deviation in minutes of the oscillation periods within the peak detection is successful
|
||||||
|
|
||||||
// -- End of general directives -------------------
|
// -- End of general directives -------------------
|
||||||
|
|
||||||
|
|
|
@ -357,9 +357,9 @@ void UpdateQuickPowerCycle(bool update)
|
||||||
#else // ESP32
|
#else // ESP32
|
||||||
QPCRead(&pc_register, sizeof(pc_register));
|
QPCRead(&pc_register, sizeof(pc_register));
|
||||||
#endif // ESP8266 - ESP32
|
#endif // ESP8266 - ESP32
|
||||||
if (update && ((pc_register & 0xFFFFFFF0) == 0xFFA55AB0)) {
|
if (update && ((pc_register & 0xFFFFFF80) == 0xFFA55A80)) {
|
||||||
uint32_t counter = ((pc_register & 0xF) << 1) & 0xF;
|
uint32_t counter = ((pc_register & 0x7F) << 1) & 0x7F;
|
||||||
if (0 == counter) { // 4 power cycles in a row
|
if (0 == counter) { // 7 power cycles in a row
|
||||||
SettingsErase(3); // Quickly reset all settings including QuickPowerCycle flag
|
SettingsErase(3); // Quickly reset all settings including QuickPowerCycle flag
|
||||||
EspRestart(); // And restart
|
EspRestart(); // And restart
|
||||||
} else {
|
} else {
|
||||||
|
@ -372,8 +372,8 @@ void UpdateQuickPowerCycle(bool update)
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("QPC: Flag %02X"), counter);
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("QPC: Flag %02X"), counter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (pc_register != 0xFFA55ABF) {
|
else if (pc_register != 0xFFA55AFF) {
|
||||||
pc_register = 0xFFA55ABF;
|
pc_register = 0xFFA55AFF;
|
||||||
#ifdef ESP8266
|
#ifdef ESP8266
|
||||||
// Assume flash is default all ones and setting a bit to zero does not need an erase
|
// Assume flash is default all ones and setting a bit to zero does not need an erase
|
||||||
if (ESP.flashEraseSector(pc_location)) {
|
if (ESP.flashEraseSector(pc_location)) {
|
||||||
|
@ -754,6 +754,7 @@ void SettingsDefaultSet2(void)
|
||||||
SettingsUpdateText(SET_FRIENDLYNAME2, PSTR(FRIENDLY_NAME"2"));
|
SettingsUpdateText(SET_FRIENDLYNAME2, PSTR(FRIENDLY_NAME"2"));
|
||||||
SettingsUpdateText(SET_FRIENDLYNAME3, PSTR(FRIENDLY_NAME"3"));
|
SettingsUpdateText(SET_FRIENDLYNAME3, PSTR(FRIENDLY_NAME"3"));
|
||||||
SettingsUpdateText(SET_FRIENDLYNAME4, PSTR(FRIENDLY_NAME"4"));
|
SettingsUpdateText(SET_FRIENDLYNAME4, PSTR(FRIENDLY_NAME"4"));
|
||||||
|
SettingsUpdateText(SET_DEVICENAME, SettingsText(SET_FRIENDLYNAME1));
|
||||||
SettingsUpdateText(SET_OTAURL, PSTR(OTA_URL));
|
SettingsUpdateText(SET_OTAURL, PSTR(OTA_URL));
|
||||||
|
|
||||||
// Power
|
// Power
|
||||||
|
@ -1411,6 +1412,10 @@ void SettingsDelta(void)
|
||||||
if (Settings.rules[2][0] == 0) { Settings.rules[2][1] = 0; }
|
if (Settings.rules[2][0] == 0) { Settings.rules[2][1] = 0; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Settings.version < 0x08030002) {
|
||||||
|
SettingsUpdateText(SET_DEVICENAME, SettingsText(SET_FRIENDLYNAME1));
|
||||||
|
}
|
||||||
|
|
||||||
Settings.version = VERSION;
|
Settings.version = VERSION;
|
||||||
SettingsSave(1);
|
SettingsSave(1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ const char kTasmotaCommands[] PROGMEM = "|" // No prefix
|
||||||
D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|"
|
D_CMND_MODULE "|" D_CMND_MODULES "|" D_CMND_GPIO "|" D_CMND_GPIOS "|" D_CMND_TEMPLATE "|" D_CMND_PWM "|" D_CMND_PWMFREQUENCY "|" D_CMND_PWMRANGE "|"
|
||||||
D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|"
|
D_CMND_BUTTONDEBOUNCE "|" D_CMND_SWITCHDEBOUNCE "|" D_CMND_SYSLOG "|" D_CMND_LOGHOST "|" D_CMND_LOGPORT "|" D_CMND_SERIALSEND "|" D_CMND_BAUDRATE "|" D_CMND_SERIALCONFIG "|"
|
||||||
D_CMND_SERIALDELIMITER "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|"
|
D_CMND_SERIALDELIMITER "|" D_CMND_IPADDRESS "|" D_CMND_NTPSERVER "|" D_CMND_AP "|" D_CMND_SSID "|" D_CMND_PASSWORD "|" D_CMND_HOSTNAME "|" D_CMND_WIFICONFIG "|"
|
||||||
D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|"
|
D_CMND_DEVICENAME "|" D_CMND_FRIENDLYNAME "|" D_CMND_SWITCHMODE "|" D_CMND_INTERLOCK "|" D_CMND_TELEPERIOD "|" D_CMND_RESET "|" D_CMND_TIME "|" D_CMND_TIMEZONE "|" D_CMND_TIMESTD "|"
|
||||||
D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|"
|
D_CMND_TIMEDST "|" D_CMND_ALTITUDE "|" D_CMND_LEDPOWER "|" D_CMND_LEDSTATE "|" D_CMND_LEDMASK "|" D_CMND_WIFIPOWER "|" D_CMND_TEMPOFFSET "|" D_CMND_HUMOFFSET "|"
|
||||||
D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM "|"
|
D_CMND_SPEEDUNIT "|" D_CMND_GLOBAL_TEMP "|" D_CMND_GLOBAL_HUM "|"
|
||||||
#ifdef USE_I2C
|
#ifdef USE_I2C
|
||||||
|
@ -48,7 +48,7 @@ void (* const TasmotaCommand[])(void) PROGMEM = {
|
||||||
&CmndModule, &CmndModules, &CmndGpio, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange,
|
&CmndModule, &CmndModules, &CmndGpio, &CmndGpios, &CmndTemplate, &CmndPwm, &CmndPwmfrequency, &CmndPwmrange,
|
||||||
&CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig,
|
&CmndButtonDebounce, &CmndSwitchDebounce, &CmndSyslog, &CmndLoghost, &CmndLogport, &CmndSerialSend, &CmndBaudrate, &CmndSerialConfig,
|
||||||
&CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig,
|
&CmndSerialDelimiter, &CmndIpAddress, &CmndNtpServer, &CmndAp, &CmndSsid, &CmndPassword, &CmndHostname, &CmndWifiConfig,
|
||||||
&CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd,
|
&CmndDevicename, &CmndFriendlyname, &CmndSwitchMode, &CmndInterlock, &CmndTeleperiod, &CmndReset, &CmndTime, &CmndTimezone, &CmndTimeStd,
|
||||||
&CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset, &CmndHumOffset,
|
&CmndTimeDst, &CmndAltitude, &CmndLedPower, &CmndLedState, &CmndLedMask, &CmndWifiPower, &CmndTempOffset, &CmndHumOffset,
|
||||||
&CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum,
|
&CmndSpeedUnit, &CmndGlobalTemp, &CmndGlobalHum,
|
||||||
#ifdef USE_I2C
|
#ifdef USE_I2C
|
||||||
|
@ -395,11 +395,11 @@ void CmndStatus(void)
|
||||||
for (uint32_t i = 0; i < MAX_SWITCHES; i++) {
|
for (uint32_t i = 0; i < MAX_SWITCHES; i++) {
|
||||||
snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]);
|
snprintf_P(stemp2, sizeof(stemp2), PSTR("%s%s%d" ), stemp2, (i > 0 ? "," : ""), Settings.switchmode[i]);
|
||||||
}
|
}
|
||||||
Response_P(PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\""
|
Response_P(PSTR("{\"" D_CMND_STATUS "\":{\"" D_CMND_MODULE "\":%d,\"" D_CMND_DEVICENAME "\":\"%s\",\"" D_CMND_FRIENDLYNAME "\":[%s],\"" D_CMND_TOPIC "\":\"%s\",\""
|
||||||
D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\""
|
D_CMND_BUTTONTOPIC "\":\"%s\",\"" D_CMND_POWER "\":%d,\"" D_CMND_POWERONSTATE "\":%d,\"" D_CMND_LEDSTATE "\":%d,\""
|
||||||
D_CMND_LEDMASK "\":\"%04X\",\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\""
|
D_CMND_LEDMASK "\":\"%04X\",\"" D_CMND_SAVEDATA "\":%d,\"" D_JSON_SAVESTATE "\":%d,\"" D_CMND_SWITCHTOPIC "\":\"%s\",\""
|
||||||
D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"),
|
D_CMND_SWITCHMODE "\":[%s],\"" D_CMND_BUTTONRETAIN "\":%d,\"" D_CMND_SWITCHRETAIN "\":%d,\"" D_CMND_SENSORRETAIN "\":%d,\"" D_CMND_POWERRETAIN "\":%d}}"),
|
||||||
ModuleNr(), stemp, mqtt_topic,
|
ModuleNr(), SettingsText(SET_DEVICENAME), stemp, mqtt_topic,
|
||||||
SettingsText(SET_MQTT_BUTTON_TOPIC), power, Settings.poweronstate, Settings.ledstate,
|
SettingsText(SET_MQTT_BUTTON_TOPIC), power, Settings.poweronstate, Settings.ledstate,
|
||||||
Settings.ledmask, Settings.save_data,
|
Settings.ledmask, Settings.save_data,
|
||||||
Settings.flag.save_state, // SetOption0 - Save power state and use after restart
|
Settings.flag.save_state, // SetOption0 - Save power state and use after restart
|
||||||
|
@ -1488,6 +1488,14 @@ void CmndWifiConfig(void)
|
||||||
Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_config, GetTextIndexed(stemp1, sizeof(stemp1), Settings.sta_config, kWifiConfig));
|
Response_P(S_JSON_COMMAND_NVALUE_SVALUE, XdrvMailbox.command, Settings.sta_config, GetTextIndexed(stemp1, sizeof(stemp1), Settings.sta_config, kWifiConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CmndDevicename(void)
|
||||||
|
{
|
||||||
|
if (!XdrvMailbox.grpflg && (XdrvMailbox.data_len > 0)) {
|
||||||
|
SettingsUpdateText(SET_DEVICENAME, ('"' == XdrvMailbox.data[0]) ? "" : (SC_DEFAULT == Shortcut()) ? SettingsText(SET_FRIENDLYNAME1) : XdrvMailbox.data);
|
||||||
|
}
|
||||||
|
ResponseCmndChar(SettingsText(SET_DEVICENAME));
|
||||||
|
}
|
||||||
|
|
||||||
void CmndFriendlyname(void)
|
void CmndFriendlyname(void)
|
||||||
{
|
{
|
||||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_FRIENDLYNAMES)) {
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= MAX_FRIENDLYNAMES)) {
|
||||||
|
@ -1600,8 +1608,9 @@ void CmndTeleperiod(void)
|
||||||
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) {
|
if ((XdrvMailbox.payload >= 0) && (XdrvMailbox.payload < 3601)) {
|
||||||
Settings.tele_period = (1 == XdrvMailbox.payload) ? TELE_PERIOD : XdrvMailbox.payload;
|
Settings.tele_period = (1 == XdrvMailbox.payload) ? TELE_PERIOD : XdrvMailbox.payload;
|
||||||
if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) Settings.tele_period = 10; // Do not allow periods < 10 seconds
|
if ((Settings.tele_period > 0) && (Settings.tele_period < 10)) Settings.tele_period = 10; // Do not allow periods < 10 seconds
|
||||||
tele_period = Settings.tele_period;
|
// tele_period = Settings.tele_period;
|
||||||
}
|
}
|
||||||
|
tele_period = Settings.tele_period; // Show teleperiod data also on empty command
|
||||||
ResponseCmndNumber(Settings.tele_period);
|
ResponseCmndNumber(Settings.tele_period);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -292,6 +292,7 @@ enum SettingsTextIndex { SET_OTAURL,
|
||||||
SET_MQTT_GRP_TOPIC2, SET_MQTT_GRP_TOPIC3, SET_MQTT_GRP_TOPIC4,
|
SET_MQTT_GRP_TOPIC2, SET_MQTT_GRP_TOPIC3, SET_MQTT_GRP_TOPIC4,
|
||||||
SET_TEMPLATE_NAME,
|
SET_TEMPLATE_NAME,
|
||||||
SET_DEV_GROUP_NAME1, SET_DEV_GROUP_NAME2, SET_DEV_GROUP_NAME3, SET_DEV_GROUP_NAME4,
|
SET_DEV_GROUP_NAME1, SET_DEV_GROUP_NAME2, SET_DEV_GROUP_NAME3, SET_DEV_GROUP_NAME4,
|
||||||
|
SET_DEVICENAME,
|
||||||
SET_MAX };
|
SET_MAX };
|
||||||
|
|
||||||
enum DevGroupMessageType { DGR_MSGTYP_FULL_STATUS, DGR_MSGTYP_PARTIAL_UPDATE, DGR_MSGTYP_UPDATE, DGR_MSGTYP_UPDATE_MORE_TO_COME, DGR_MSGTYP_UPDATE_DIRECT, DGR_MSGTYPE_UPDATE_COMMAND };
|
enum DevGroupMessageType { DGR_MSGTYP_FULL_STATUS, DGR_MSGTYP_PARTIAL_UPDATE, DGR_MSGTYP_UPDATE, DGR_MSGTYP_UPDATE_MORE_TO_COME, DGR_MSGTYP_UPDATE_DIRECT, DGR_MSGTYPE_UPDATE_COMMAND };
|
||||||
|
|
|
@ -307,7 +307,7 @@ void setup(void) {
|
||||||
|
|
||||||
SetPowerOnState();
|
SetPowerOnState();
|
||||||
|
|
||||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_CORE_RELEASE), PROJECT, SettingsText(SET_FRIENDLYNAME1), my_version, my_image);
|
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_PROJECT " %s %s " D_VERSION " %s%s-" ARDUINO_CORE_RELEASE), PROJECT, SettingsText(SET_DEVICENAME), my_version, my_image);
|
||||||
#ifdef FIRMWARE_MINIMAL
|
#ifdef FIRMWARE_MINIMAL
|
||||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION));
|
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_WARNING_MINIMAL_VERSION));
|
||||||
#endif // FIRMWARE_MINIMAL
|
#endif // FIRMWARE_MINIMAL
|
||||||
|
|
|
@ -20,7 +20,7 @@
|
||||||
#ifndef _TASMOTA_VERSION_H_
|
#ifndef _TASMOTA_VERSION_H_
|
||||||
#define _TASMOTA_VERSION_H_
|
#define _TASMOTA_VERSION_H_
|
||||||
|
|
||||||
const uint32_t VERSION = 0x08030000;
|
const uint32_t VERSION = 0x08030100;
|
||||||
|
|
||||||
// Lowest compatible version
|
// Lowest compatible version
|
||||||
const uint32_t VERSION_COMPATIBLE = 0x07010006;
|
const uint32_t VERSION_COMPATIBLE = 0x07010006;
|
||||||
|
|
|
@ -455,6 +455,8 @@ const char HTTP_FORM_OTHER[] PROGMEM =
|
||||||
"<label><b>" D_WEB_ADMIN_PASSWORD "</b><input type='checkbox' onclick='sp(\"wp\")'></label><br><input id='wp' type='password' placeholder='" D_WEB_ADMIN_PASSWORD "' value='" D_ASTERISK_PWD "'><br>"
|
"<label><b>" D_WEB_ADMIN_PASSWORD "</b><input type='checkbox' onclick='sp(\"wp\")'></label><br><input id='wp' type='password' placeholder='" D_WEB_ADMIN_PASSWORD "' value='" D_ASTERISK_PWD "'><br>"
|
||||||
"<br>"
|
"<br>"
|
||||||
"<label><input id='b1' type='checkbox'%s><b>" D_MQTT_ENABLE "</b></label><br>"
|
"<label><input id='b1' type='checkbox'%s><b>" D_MQTT_ENABLE "</b></label><br>"
|
||||||
|
"<br>"
|
||||||
|
"<label><b>" D_DEVICE_NAME "</b> (%s)</label><br><input id='dn' placeholder='' value='%s'><br>"
|
||||||
"<br>";
|
"<br>";
|
||||||
|
|
||||||
const char HTTP_FORM_END[] PROGMEM =
|
const char HTTP_FORM_END[] PROGMEM =
|
||||||
|
@ -849,7 +851,7 @@ void WSContentStart_P(const char* title, bool auth)
|
||||||
WSContentBegin(200, CT_HTML);
|
WSContentBegin(200, CT_HTML);
|
||||||
|
|
||||||
if (title != nullptr) {
|
if (title != nullptr) {
|
||||||
WSContentSend_P(HTTP_HEADER1, SettingsText(SET_FRIENDLYNAME1), title);
|
WSContentSend_P(HTTP_HEADER1, SettingsText(SET_DEVICENAME), title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -893,7 +895,7 @@ void WSContentSendStyle_P(const char* formatP, ...)
|
||||||
WebColor(COL_TEXT_WARNING),
|
WebColor(COL_TEXT_WARNING),
|
||||||
#endif
|
#endif
|
||||||
WebColor(COL_TITLE),
|
WebColor(COL_TITLE),
|
||||||
ModuleName().c_str(), SettingsText(SET_FRIENDLYNAME1));
|
ModuleName().c_str(), SettingsText(SET_DEVICENAME));
|
||||||
if (Settings.flag3.gui_hostname_ip) { // SetOption53 - Show hostanme and IP address in GUI main menu
|
if (Settings.flag3.gui_hostname_ip) { // SetOption53 - Show hostanme and IP address in GUI main menu
|
||||||
bool lip = (static_cast<uint32_t>(WiFi.localIP()) != 0);
|
bool lip = (static_cast<uint32_t>(WiFi.localIP()) != 0);
|
||||||
bool sip = (static_cast<uint32_t>(WiFi.softAPIP()) != 0);
|
bool sip = (static_cast<uint32_t>(WiFi.softAPIP()) != 0);
|
||||||
|
@ -1992,7 +1994,9 @@ void HandleOtherConfiguration(void)
|
||||||
TemplateJson();
|
TemplateJson();
|
||||||
char stemp[strlen(mqtt_data) +1];
|
char stemp[strlen(mqtt_data) +1];
|
||||||
strlcpy(stemp, mqtt_data, sizeof(stemp)); // Get JSON template
|
strlcpy(stemp, mqtt_data, sizeof(stemp)); // Get JSON template
|
||||||
WSContentSend_P(HTTP_FORM_OTHER, stemp, (USER_MODULE == Settings.module) ? " checked disabled" : "", (Settings.flag.mqtt_enabled) ? " checked" : ""); // SetOption3 - Enable MQTT
|
WSContentSend_P(HTTP_FORM_OTHER, stemp, (USER_MODULE == Settings.module) ? " checked disabled" : "",
|
||||||
|
(Settings.flag.mqtt_enabled) ? " checked" : "", // SetOption3 - Enable MQTT
|
||||||
|
SettingsText(SET_FRIENDLYNAME1), SettingsText(SET_DEVICENAME));
|
||||||
|
|
||||||
uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present;
|
uint32_t maxfn = (devices_present > MAX_FRIENDLYNAMES) ? MAX_FRIENDLYNAMES : (!devices_present) ? 1 : devices_present;
|
||||||
#ifdef USE_SONOFF_IFAN
|
#ifdef USE_SONOFF_IFAN
|
||||||
|
@ -2042,6 +2046,8 @@ void OtherSaveSettings(void)
|
||||||
char friendlyname[TOPSZ];
|
char friendlyname[TOPSZ];
|
||||||
char message[LOGSZ];
|
char message[LOGSZ];
|
||||||
|
|
||||||
|
WebGetArg("dn", tmp, sizeof(tmp));
|
||||||
|
SettingsUpdateText(SET_DEVICENAME, (!strlen(tmp)) ? "" : (!strcmp(tmp,"1")) ? SettingsText(SET_FRIENDLYNAME1) : tmp);
|
||||||
WebGetArg("wp", tmp, sizeof(tmp));
|
WebGetArg("wp", tmp, sizeof(tmp));
|
||||||
SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp);
|
SettingsUpdateText(SET_WEBPWD, (!strlen(tmp)) ? "" : (strchr(tmp,'*')) ? SettingsText(SET_WEBPWD) : tmp);
|
||||||
Settings.flag.mqtt_enabled = Webserver->hasArg("b1"); // SetOption3 - Enable MQTT
|
Settings.flag.mqtt_enabled = Webserver->hasArg("b1"); // SetOption3 - Enable MQTT
|
||||||
|
@ -2053,7 +2059,8 @@ void OtherSaveSettings(void)
|
||||||
#endif // USE_EMULATION_WEMO || USE_EMULATION_HUE
|
#endif // USE_EMULATION_WEMO || USE_EMULATION_HUE
|
||||||
#endif // USE_EMULATION
|
#endif // USE_EMULATION
|
||||||
|
|
||||||
snprintf_P(message, sizeof(message), PSTR(D_LOG_OTHER D_MQTT_ENABLE " %s, " D_CMND_EMULATION " %d, " D_CMND_FRIENDLYNAME), GetStateText(Settings.flag.mqtt_enabled), Settings.flag2.emulation);
|
snprintf_P(message, sizeof(message), PSTR(D_LOG_OTHER D_MQTT_ENABLE " %s, " D_CMND_EMULATION " %d, " D_CMND_DEVICENAME " %s, " D_CMND_FRIENDLYNAME),
|
||||||
|
GetStateText(Settings.flag.mqtt_enabled), Settings.flag2.emulation, SettingsText(SET_DEVICENAME));
|
||||||
for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) {
|
for (uint32_t i = 0; i < MAX_FRIENDLYNAMES; i++) {
|
||||||
snprintf_P(webindex, sizeof(webindex), PSTR("a%d"), i);
|
snprintf_P(webindex, sizeof(webindex), PSTR("a%d"), i);
|
||||||
WebGetArg(webindex, tmp, sizeof(tmp));
|
WebGetArg(webindex, tmp, sizeof(tmp));
|
||||||
|
|
|
@ -836,28 +836,44 @@ void LightStateClass::HsToRgb(uint16_t hue, uint8_t sat, uint8_t *r_r, uint8_t *
|
||||||
|
|
||||||
#define POW FastPrecisePowf
|
#define POW FastPrecisePowf
|
||||||
|
|
||||||
|
//
|
||||||
|
// Matrix 3x3 multiplied to a 3 vector, result in a 3 vector
|
||||||
|
//
|
||||||
|
void mat3x3(const float *mat33, const float *vec3, float *res3) {
|
||||||
|
for (uint32_t i = 0; i < 3; i++) {
|
||||||
|
const float * v = vec3;
|
||||||
|
*res3 = 0.0f;
|
||||||
|
for (uint32_t j = 0; j < 3; j++) {
|
||||||
|
*res3 += *mat33++ * *v++;
|
||||||
|
}
|
||||||
|
res3++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LightStateClass::RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y) {
|
void LightStateClass::RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x, float *r_y) {
|
||||||
float x = 0.31271f; // default medium white
|
float x = 0.31271f; // default medium white
|
||||||
float y = 0.32902f;
|
float y = 0.32902f;
|
||||||
|
|
||||||
if (i_r + i_b + i_g > 0) {
|
if (i_r + i_b + i_g > 0) {
|
||||||
float r = (float)i_r / 255.0f;
|
float rgb[3] = { (float)i_r, (float)i_g, (float)i_b };
|
||||||
float g = (float)i_g / 255.0f;
|
|
||||||
float b = (float)i_b / 255.0f;
|
|
||||||
// https://gist.github.com/popcorn245/30afa0f98eea1c2fd34d
|
// https://gist.github.com/popcorn245/30afa0f98eea1c2fd34d
|
||||||
// Gamma correction
|
// Gamma correction
|
||||||
r = (r > 0.04045f) ? POW((r + 0.055f) / (1.0f + 0.055f), 2.4f) : (r / 12.92f);
|
for (uint32_t i = 0; i < 3; i++) {
|
||||||
g = (g > 0.04045f) ? POW((g + 0.055f) / (1.0f + 0.055f), 2.4f) : (g / 12.92f);
|
rgb[i] = rgb[i] / 255.0f;
|
||||||
b = (b > 0.04045f) ? POW((b + 0.055f) / (1.0f + 0.055f), 2.4f) : (b / 12.92f);
|
rgb[i] = (rgb[i] > 0.04045f) ? POW((rgb[i] + 0.055f) / (1.0f + 0.055f), 2.4f) : (rgb[i] / 12.92f);
|
||||||
|
}
|
||||||
|
|
||||||
// conversion to X, Y, Z
|
// conversion to X, Y, Z
|
||||||
// Y is also the Luminance
|
// Y is also the Luminance
|
||||||
float X = r * 0.649926f + g * 0.103455f + b * 0.197109f;
|
float XYZ[3];
|
||||||
float Y = r * 0.234327f + g * 0.743075f + b * 0.022598f;
|
static const float XYZ_factors[] = { 0.649926f, 0.103455f, 0.197109f,
|
||||||
float Z = r * 0.000000f + g * 0.053077f + b * 1.035763f;
|
0.234327f, 0.743075f, 0.022598f,
|
||||||
|
0.000000f, 0.053077f, 1.035763f };
|
||||||
|
mat3x3(XYZ_factors, rgb, XYZ);
|
||||||
|
|
||||||
x = X / (X + Y + Z);
|
float XYZ_sum = XYZ[0] + XYZ[1] + XYZ[2];
|
||||||
y = Y / (X + Y + Z);
|
x = XYZ[0] / XYZ_sum;
|
||||||
|
y = XYZ[1] / XYZ_sum;
|
||||||
// we keep the raw gamut, one nice thing could be to convert to a narrower gamut
|
// we keep the raw gamut, one nice thing could be to convert to a narrower gamut
|
||||||
}
|
}
|
||||||
if (r_x) *r_x = x;
|
if (r_x) *r_x = x;
|
||||||
|
@ -866,36 +882,33 @@ void LightStateClass::RgbToXy(uint8_t i_r, uint8_t i_g, uint8_t i_b, float *r_x,
|
||||||
|
|
||||||
void LightStateClass::XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb)
|
void LightStateClass::XyToRgb(float x, float y, uint8_t *rr, uint8_t *rg, uint8_t *rb)
|
||||||
{
|
{
|
||||||
|
float XYZ[3], rgb[3];
|
||||||
x = (x > 0.99f ? 0.99f : (x < 0.01f ? 0.01f : x));
|
x = (x > 0.99f ? 0.99f : (x < 0.01f ? 0.01f : x));
|
||||||
y = (y > 0.99f ? 0.99f : (y < 0.01f ? 0.01f : y));
|
y = (y > 0.99f ? 0.99f : (y < 0.01f ? 0.01f : y));
|
||||||
float z = 1.0f - x - y;
|
float z = 1.0f - x - y;
|
||||||
//float Y = 1.0f;
|
XYZ[0] = x / y;
|
||||||
float X = x / y;
|
XYZ[1] = 1.0f;
|
||||||
float Z = z / y;
|
XYZ[2] = z / y;
|
||||||
// float r = X * 1.4628067f - 0.1840623f - Z * 0.2743606f;
|
|
||||||
// float g = -X * 0.5217933f + 1.4472381f + Z * 0.0677227f;
|
|
||||||
// float b = X * 0.0349342f - 0.0968930f + Z * 1.2884099f;
|
|
||||||
float r = X * 3.2406f - 1.5372f - Z * 0.4986f;
|
|
||||||
float g = -X * 0.9689f + 1.8758f + Z * 0.0415f;
|
|
||||||
float b = X * 0.0557f - 0.2040f + Z * 1.0570f;
|
|
||||||
float max = (r > g && r > b) ? r : (g > b) ? g : b;
|
|
||||||
r = r / max; // normalize to max == 1.0
|
|
||||||
g = g / max;
|
|
||||||
b = b / max;
|
|
||||||
r = (r <= 0.0031308f) ? 12.92f * r : 1.055f * POW(r, (1.0f / 2.4f)) - 0.055f;
|
|
||||||
g = (g <= 0.0031308f) ? 12.92f * g : 1.055f * POW(g, (1.0f / 2.4f)) - 0.055f;
|
|
||||||
b = (b <= 0.0031308f) ? 12.92f * b : 1.055f * POW(b, (1.0f / 2.4f)) - 0.055f;
|
|
||||||
//
|
|
||||||
// AddLog_P2(LOG_LEVEL_DEBUG_MORE, "XyToRgb XZ (%s %s) rgb (%s %s %s)",
|
|
||||||
// String(X,5).c_str(), String(Z,5).c_str(),
|
|
||||||
// String(r,5).c_str(), String(g,5).c_str(),String(b,5).c_str());
|
|
||||||
|
|
||||||
int32_t ir = r * 255.0f + 0.5f;
|
static const float rgb_factors[] = { 3.2406f, -1.5372f, -0.4986f,
|
||||||
int32_t ig = g * 255.0f + 0.5f;
|
-0.9689f, 1.8758f, 0.0415f,
|
||||||
int32_t ib = b * 255.0f + 0.5f;
|
0.0557f, -0.2040f, 1.0570f };
|
||||||
if (rr) { *rr = (ir > 255 ? 255: (ir < 0 ? 0 : ir)); }
|
mat3x3(rgb_factors, XYZ, rgb);
|
||||||
if (rg) { *rg = (ig > 255 ? 255: (ig < 0 ? 0 : ig)); }
|
float max = (rgb[0] > rgb[1] && rgb[0] > rgb[2]) ? rgb[0] : (rgb[1] > rgb[2]) ? rgb[1] : rgb[2];
|
||||||
if (rb) { *rb = (ib > 255 ? 255: (ib < 0 ? 0 : ib)); }
|
|
||||||
|
for (uint32_t i = 0; i < 3; i++) {
|
||||||
|
rgb[i] = rgb[i] / max; // normalize to max == 1.0
|
||||||
|
rgb[i] = (rgb[i] <= 0.0031308f) ? 12.92f * rgb[i] : 1.055f * POW(rgb[i], (1.0f / 2.4f)) - 0.055f; // gamma
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t irgb[3];
|
||||||
|
for (uint32_t i = 0; i < 3; i++) {
|
||||||
|
irgb[i] = rgb[i] * 255.0f + 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rr) { *rr = (irgb[0] > 255 ? 255: (irgb[0] < 0 ? 0 : irgb[0])); }
|
||||||
|
if (rg) { *rg = (irgb[1] > 255 ? 255: (irgb[1] < 0 ? 0 : irgb[1])); }
|
||||||
|
if (rb) { *rb = (irgb[2] > 255 ? 255: (irgb[2] < 0 ? 0 : irgb[2])); }
|
||||||
}
|
}
|
||||||
|
|
||||||
class LightControllerClass {
|
class LightControllerClass {
|
||||||
|
|
|
@ -213,6 +213,7 @@ char rules_vars[MAX_RULE_VARS][33] = {{ 0 }};
|
||||||
#ifdef USE_RULES_COMPRESSION
|
#ifdef USE_RULES_COMPRESSION
|
||||||
// Statically allocate one String per rule
|
// Statically allocate one String per rule
|
||||||
String k_rules[MAX_RULE_SETS] = { String(), String(), String() }; // Strings are created empty
|
String k_rules[MAX_RULE_SETS] = { String(), String(), String() }; // Strings are created empty
|
||||||
|
Unishox compressor; // singleton
|
||||||
#endif // USE_RULES_COMPRESSION
|
#endif // USE_RULES_COMPRESSION
|
||||||
|
|
||||||
// Returns whether the rule is uncompressed, which means the first byte is not NULL
|
// Returns whether the rule is uncompressed, which means the first byte is not NULL
|
||||||
|
@ -256,6 +257,7 @@ size_t GetRuleLenStorage(uint32_t idx) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_RULES_COMPRESSION
|
||||||
// internal function, do the actual decompression
|
// internal function, do the actual decompression
|
||||||
void GetRule_decompress(String &rule, const char *rule_head) {
|
void GetRule_decompress(String &rule, const char *rule_head) {
|
||||||
size_t buf_len = 1 + *rule_head * 8; // the first byte contains size of buffer for uncompressed rule / 8, buf_len may overshoot by 7
|
size_t buf_len = 1 + *rule_head * 8; // the first byte contains size of buffer for uncompressed rule / 8, buf_len may overshoot by 7
|
||||||
|
@ -268,12 +270,13 @@ void GetRule_decompress(String &rule, const char *rule_head) {
|
||||||
rule.reserve(buf_len);
|
rule.reserve(buf_len);
|
||||||
char* buf = rule.begin();
|
char* buf = rule.begin();
|
||||||
|
|
||||||
int32_t len_decompressed = unishox_decompress(rule_head, strlen(rule_head), buf, buf_len);
|
int32_t len_decompressed = compressor.unishox_decompress(rule_head, strlen(rule_head), buf, buf_len);
|
||||||
buf[len_decompressed] = 0; // add NULL terminator
|
buf[len_decompressed] = 0; // add NULL terminator
|
||||||
|
|
||||||
// AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: Rawdecompressed: %d"), len_decompressed);
|
// AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: Rawdecompressed: %d"), len_decompressed);
|
||||||
rule = buf; // assign the raw string to the String object (in reality re-writing the same data in the same place)
|
rule = buf; // assign the raw string to the String object (in reality re-writing the same data in the same place)
|
||||||
}
|
}
|
||||||
|
#endif // USE_RULES_COMPRESSION
|
||||||
|
|
||||||
//
|
//
|
||||||
// Read rule in memory, uncompress if needed
|
// Read rule in memory, uncompress if needed
|
||||||
|
@ -308,7 +311,7 @@ String GetRule(uint32_t idx) {
|
||||||
// If out == nullptr, we are in dry-run mode, so don't keep rule in cache
|
// If out == nullptr, we are in dry-run mode, so don't keep rule in cache
|
||||||
int32_t SetRule_compress(uint32_t idx, const char *in, size_t in_len, char *out, size_t out_len) {
|
int32_t SetRule_compress(uint32_t idx, const char *in, size_t in_len, char *out, size_t out_len) {
|
||||||
int32_t len_compressed;
|
int32_t len_compressed;
|
||||||
len_compressed = unishox_compress(in, in_len, out, out_len);
|
len_compressed = compressor.unishox_compress(in, in_len, out, out_len);
|
||||||
|
|
||||||
if (len_compressed >= 0) { // negative means compression failed because of buffer too small, we leave the rule untouched
|
if (len_compressed >= 0) { // negative means compression failed because of buffer too small, we leave the rule untouched
|
||||||
// check if we need to store in cache
|
// check if we need to store in cache
|
||||||
|
@ -357,7 +360,7 @@ int32_t SetRule(uint32_t idx, const char *content, bool append = false) {
|
||||||
int32_t len_compressed, len_uncompressed;
|
int32_t len_compressed, len_uncompressed;
|
||||||
|
|
||||||
len_uncompressed = strlen(Settings.rules[idx]);
|
len_uncompressed = strlen(Settings.rules[idx]);
|
||||||
len_compressed = unishox_compress(Settings.rules[idx], len_uncompressed, nullptr /* dry-run */, MAX_RULE_SIZE + 8);
|
len_compressed = compressor.unishox_compress(Settings.rules[idx], len_uncompressed, nullptr /* dry-run */, MAX_RULE_SIZE + 8);
|
||||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: Stored uncompressed, would compress from %d to %d (-%d%%)"), len_uncompressed, len_compressed, 100 - changeUIntScale(len_compressed, 0, len_uncompressed, 0, 100));
|
AddLog_P2(LOG_LEVEL_INFO, PSTR("RUL: Stored uncompressed, would compress from %d to %d (-%d%%)"), len_uncompressed, len_compressed, 100 - changeUIntScale(len_compressed, 0, len_uncompressed, 0, 100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -641,13 +641,14 @@ char *script;
|
||||||
|
|
||||||
#ifdef USE_LIGHT
|
#ifdef USE_LIGHT
|
||||||
#ifdef USE_WS2812
|
#ifdef USE_WS2812
|
||||||
void ws2812_set_array(float *array ,uint8_t len) {
|
void ws2812_set_array(float *array ,uint32_t len, uint32_t offset) {
|
||||||
|
|
||||||
Ws2812ForceSuspend();
|
Ws2812ForceSuspend();
|
||||||
for (uint8_t cnt=0;cnt<len;cnt++) {
|
for (uint32_t cnt=0;cnt<len;cnt++) {
|
||||||
if (cnt>Settings.light_pixels) break;
|
uint32_t index=cnt+offset;
|
||||||
|
if (index>Settings.light_pixels) break;
|
||||||
uint32_t col=array[cnt];
|
uint32_t col=array[cnt];
|
||||||
Ws2812SetColor(cnt+1,col>>16,col>>8,col,0);
|
Ws2812SetColor(index+1,col>>16,col>>8,col,0);
|
||||||
}
|
}
|
||||||
Ws2812ForceUpdate();
|
Ws2812ForceUpdate();
|
||||||
}
|
}
|
||||||
|
@ -2444,7 +2445,7 @@ char *ForceStringVar(char *lp,char *dstr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// replace vars in cmd %var%
|
// replace vars in cmd %var%
|
||||||
void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize) {
|
void Replace_Cmd_Vars(char *srcbuf,uint32_t srcsize, char *dstbuf,uint32_t dstsize) {
|
||||||
char *cp;
|
char *cp;
|
||||||
uint16_t count;
|
uint16_t count;
|
||||||
uint8_t vtype;
|
uint8_t vtype;
|
||||||
|
@ -2455,6 +2456,7 @@ void Replace_Cmd_Vars(char *srcbuf,char *dstbuf,uint16_t dstsize) {
|
||||||
char string[SCRIPT_MAXSSIZE];
|
char string[SCRIPT_MAXSSIZE];
|
||||||
dstsize-=2;
|
dstsize-=2;
|
||||||
for (count=0;count<dstsize;count++) {
|
for (count=0;count<dstsize;count++) {
|
||||||
|
if (srcsize && (*cp==SCRIPT_EOL)) break;
|
||||||
if (*cp=='%') {
|
if (*cp=='%') {
|
||||||
cp++;
|
cp++;
|
||||||
if (*cp=='%') {
|
if (*cp=='%') {
|
||||||
|
@ -2869,7 +2871,11 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
|
||||||
lp=GetNumericResult(lp,OPER_EQU,&cv_inc,0);
|
lp=GetNumericResult(lp,OPER_EQU,&cv_inc,0);
|
||||||
//SCRIPT_SKIP_EOL
|
//SCRIPT_SKIP_EOL
|
||||||
cv_ptr=lp;
|
cv_ptr=lp;
|
||||||
floop=1;
|
if (*cv_count<cv_max) {
|
||||||
|
floop=1;
|
||||||
|
} else {
|
||||||
|
floop=2;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// error
|
// error
|
||||||
toLogEOL("for error",lp);
|
toLogEOL("for error",lp);
|
||||||
|
@ -2877,11 +2883,20 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
|
||||||
} else if (!strncmp(lp,"next",4) && floop>0) {
|
} else if (!strncmp(lp,"next",4) && floop>0) {
|
||||||
// for next loop
|
// for next loop
|
||||||
*cv_count+=cv_inc;
|
*cv_count+=cv_inc;
|
||||||
if (*cv_count<=cv_max) {
|
if (floop==1) {
|
||||||
lp=cv_ptr;
|
if (*cv_count<=cv_max) {
|
||||||
|
lp=cv_ptr;
|
||||||
|
} else {
|
||||||
|
lp+=4;
|
||||||
|
floop=0;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
lp+=4;
|
if (*cv_count>=cv_max) {
|
||||||
floop=0;
|
lp=cv_ptr;
|
||||||
|
} else {
|
||||||
|
lp+=4;
|
||||||
|
floop=0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3010,6 +3025,12 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
|
||||||
lp+=7;
|
lp+=7;
|
||||||
lp=isvar(lp,&vtype,&ind,0,0,0);
|
lp=isvar(lp,&vtype,&ind,0,0,0);
|
||||||
if (vtype!=VAR_NV) {
|
if (vtype!=VAR_NV) {
|
||||||
|
SCRIPT_SKIP_SPACES
|
||||||
|
if (*lp!=')') {
|
||||||
|
lp=GetNumericResult(lp,OPER_EQU,&fvar,0);
|
||||||
|
} else {
|
||||||
|
fvar=0;
|
||||||
|
}
|
||||||
// found variable as result
|
// found variable as result
|
||||||
uint8_t index=glob_script_mem.type[ind.index].index;
|
uint8_t index=glob_script_mem.type[ind.index].index;
|
||||||
if ((vtype&STYPE)==0) {
|
if ((vtype&STYPE)==0) {
|
||||||
|
@ -3018,7 +3039,7 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
|
||||||
uint8_t len=0;
|
uint8_t len=0;
|
||||||
float *fa=Get_MFAddr(index,&len);
|
float *fa=Get_MFAddr(index,&len);
|
||||||
//Serial.printf(">> 2 %d\n",(uint32_t)*fa);
|
//Serial.printf(">> 2 %d\n",(uint32_t)*fa);
|
||||||
if (fa && len) ws2812_set_array(fa,len);
|
if (fa && len) ws2812_set_array(fa,len,fvar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3069,7 +3090,7 @@ int16_t Run_Scripter(const char *type, int8_t tlen, char *js) {
|
||||||
//AddLog_P(LOG_LEVEL_INFO, tmp);
|
//AddLog_P(LOG_LEVEL_INFO, tmp);
|
||||||
// replace vars in cmd
|
// replace vars in cmd
|
||||||
char *tmp=cmdmem+SCRIPT_CMDMEM/2;
|
char *tmp=cmdmem+SCRIPT_CMDMEM/2;
|
||||||
Replace_Cmd_Vars(cmd,tmp,SCRIPT_CMDMEM/2);
|
Replace_Cmd_Vars(cmd,0,tmp,SCRIPT_CMDMEM/2);
|
||||||
//toSLog(tmp);
|
//toSLog(tmp);
|
||||||
|
|
||||||
if (!strncmp(tmp,"print",5) || pflg) {
|
if (!strncmp(tmp,"print",5) || pflg) {
|
||||||
|
@ -4198,7 +4219,7 @@ void Script_Check_Hue(String *response) {
|
||||||
}
|
}
|
||||||
cp++;
|
cp++;
|
||||||
}
|
}
|
||||||
Replace_Cmd_Vars(line,tmp,sizeof(tmp));
|
Replace_Cmd_Vars(line,0,tmp,sizeof(tmp));
|
||||||
// check for hue defintions
|
// check for hue defintions
|
||||||
// NAME, TYPE , vars
|
// NAME, TYPE , vars
|
||||||
cp=tmp;
|
cp=tmp;
|
||||||
|
@ -5043,7 +5064,7 @@ void ScriptWebShow(char mc) {
|
||||||
WSContentSend_PD(SCRIPT_MSG_NUMINP,label,minstr,maxstr,stepstr,vstr,vname);
|
WSContentSend_PD(SCRIPT_MSG_NUMINP,label,minstr,maxstr,stepstr,vstr,vname);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Replace_Cmd_Vars(lin,tmp,sizeof(tmp));
|
Replace_Cmd_Vars(lin,0,tmp,sizeof(tmp));
|
||||||
if (optflg) {
|
if (optflg) {
|
||||||
WSContentSend_PD(PSTR("<div>%s</div>"),tmp);
|
WSContentSend_PD(PSTR("<div>%s</div>"),tmp);
|
||||||
} else {
|
} else {
|
||||||
|
@ -5052,7 +5073,7 @@ void ScriptWebShow(char mc) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (*lin==mc) {
|
if (*lin==mc) {
|
||||||
Replace_Cmd_Vars(lin+1,tmp,sizeof(tmp));
|
Replace_Cmd_Vars(lin+1,0,tmp,sizeof(tmp));
|
||||||
WSContentSend_PD(PSTR("%s"),tmp);
|
WSContentSend_PD(PSTR("%s"),tmp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5097,7 +5118,7 @@ uint8_t msect=Run_Scripter(">m",-2,0);
|
||||||
}
|
}
|
||||||
cp++;
|
cp++;
|
||||||
}
|
}
|
||||||
Replace_Cmd_Vars(line,tmp,sizeof(tmp));
|
Replace_Cmd_Vars(line,0,tmp,sizeof(tmp));
|
||||||
//client->println(tmp);
|
//client->println(tmp);
|
||||||
func(tmp);
|
func(tmp);
|
||||||
}
|
}
|
||||||
|
@ -5142,7 +5163,7 @@ void ScriptJsonAppend(void) {
|
||||||
}
|
}
|
||||||
cp++;
|
cp++;
|
||||||
}
|
}
|
||||||
Replace_Cmd_Vars(line,tmp,sizeof(tmp));
|
Replace_Cmd_Vars(line,0,tmp,sizeof(tmp));
|
||||||
ResponseAppend_P(PSTR("%s"),tmp);
|
ResponseAppend_P(PSTR("%s"),tmp);
|
||||||
}
|
}
|
||||||
if (*lp==SCRIPT_EOL) {
|
if (*lp==SCRIPT_EOL) {
|
||||||
|
|
|
@ -233,7 +233,7 @@ void HAssAnnounceRelayLight(void)
|
||||||
} else {
|
} else {
|
||||||
if (Settings.flag.hass_discovery && (RelayX || (Light.device > 0) && (max_lights > 0)) && !err_flag )
|
if (Settings.flag.hass_discovery && (RelayX || (Light.device > 0) && (max_lights > 0)) && !err_flag )
|
||||||
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
||||||
char name[33 + 2]; // friendlyname(33) + " " + index
|
char name[TOPSZ]; // friendlyname(33) + " " + index
|
||||||
char value_template[33];
|
char value_template[33];
|
||||||
char prefix[TOPSZ];
|
char prefix[TOPSZ];
|
||||||
char *command_topic = stemp1;
|
char *command_topic = stemp1;
|
||||||
|
@ -241,9 +241,9 @@ void HAssAnnounceRelayLight(void)
|
||||||
char *availability_topic = stemp3;
|
char *availability_topic = stemp3;
|
||||||
|
|
||||||
if (i > MAX_FRIENDLYNAMES) {
|
if (i > MAX_FRIENDLYNAMES) {
|
||||||
snprintf_P(name, sizeof(name), PSTR("%s %d"), SettingsText(SET_FRIENDLYNAME1), i-1);
|
snprintf_P(name, sizeof(name), PSTR("%s %s %d"), SettingsText(SET_DEVICENAME), SettingsText(SET_FRIENDLYNAME1), i-1);
|
||||||
} else {
|
} else {
|
||||||
snprintf_P(name, sizeof(name), SettingsText(SET_FRIENDLYNAME1 + i-1));
|
snprintf_P(name, sizeof(name), PSTR ("%s %s"), SettingsText(SET_DEVICENAME), SettingsText(SET_FRIENDLYNAME1 + i-1));
|
||||||
}
|
}
|
||||||
|
|
||||||
GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); // SetOption26 - Switch between POWER or POWER1
|
GetPowerDevice(value_template, i, sizeof(value_template), Settings.flag.device_index_enable); // SetOption26 - Switch between POWER or POWER1
|
||||||
|
@ -339,7 +339,7 @@ void HAssAnnouncerTriggers(uint8_t device, uint8_t present, uint8_t key, uint8_t
|
||||||
snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/device_automation/%s/config"), unique_id);
|
snprintf_P(stopic, sizeof(stopic), PSTR(HOME_ASSISTANT_DISCOVERY_PREFIX "/device_automation/%s/config"), unique_id);
|
||||||
|
|
||||||
if (Settings.flag.hass_discovery && present) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
if (Settings.flag.hass_discovery && present) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
||||||
char name[33 + 6]; // friendlyname(33) + " " + "BTN" + " " + index
|
char name[TOPSZ]; // friendlyname(33) + " " + "BTN" + " " + index
|
||||||
char value_template[33];
|
char value_template[33];
|
||||||
char prefix[TOPSZ];
|
char prefix[TOPSZ];
|
||||||
char *state_topic = stemp1;
|
char *state_topic = stemp1;
|
||||||
|
@ -391,7 +391,7 @@ void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint
|
||||||
|
|
||||||
if (Settings.flag.hass_discovery && present ) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
if (Settings.flag.hass_discovery && present ) { // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
||||||
if (!toggle || dual) {
|
if (!toggle || dual) {
|
||||||
char name[33 + 6]; // friendlyname(33) + " " + "BTN" + " " + index
|
char name[TOPSZ]; // friendlyname(33) + " " + "BTN" + " " + index
|
||||||
char value_template[33];
|
char value_template[33];
|
||||||
char prefix[TOPSZ];
|
char prefix[TOPSZ];
|
||||||
char *state_topic = stemp1;
|
char *state_topic = stemp1;
|
||||||
|
@ -403,7 +403,7 @@ void HAssAnnouncerBinSensors(uint8_t device, uint8_t present, uint8_t dual, uint
|
||||||
GetTopic_P(state_topic, STAT, mqtt_topic, jsoname);
|
GetTopic_P(state_topic, STAT, mqtt_topic, jsoname);
|
||||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||||
|
|
||||||
snprintf_P(name, sizeof(name), PSTR("%s Switch%d"), ModuleName().c_str(), device + 1);
|
snprintf_P(name, sizeof(name), PSTR("%s Switch%d"), SettingsText(SET_DEVICENAME), device + 1);
|
||||||
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
||||||
if (!pir) {
|
if (!pir) {
|
||||||
TryResponseAppend_P(HASS_DISCOVER_BIN_SWITCH, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2), SettingsText(SET_STATE_TXT1));
|
TryResponseAppend_P(HASS_DISCOVER_BIN_SWITCH, PSTR(D_RSLT_STATE), SettingsText(SET_STATE_TXT2), SettingsText(SET_STATE_TXT1));
|
||||||
|
@ -553,13 +553,13 @@ void HAssAnnounceSensor(const char *sensorname, const char *subsensortype, const
|
||||||
|
|
||||||
if (Settings.flag.hass_discovery)
|
if (Settings.flag.hass_discovery)
|
||||||
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
||||||
char name[33 + 42]; // friendlyname(33) + " " + sensorname(20?) + " " + sensortype(20?)
|
char name[TOPSZ]; // friendlyname(33) + " " + sensorname(20?) + " " + sensortype(20?)
|
||||||
char prefix[TOPSZ];
|
char prefix[TOPSZ];
|
||||||
char *state_topic = stemp1;
|
char *state_topic = stemp1;
|
||||||
char *availability_topic = stemp2;
|
char *availability_topic = stemp2;
|
||||||
|
|
||||||
GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR));
|
GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_SENSOR));
|
||||||
snprintf_P(name, sizeof(name), PSTR("%s %s %s"), ModuleName().c_str(), sensorname, MultiSubName);
|
snprintf_P(name, sizeof(name), PSTR("%s %s %s"), SettingsText(SET_DEVICENAME), sensorname, MultiSubName);
|
||||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||||
|
|
||||||
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
||||||
|
@ -694,18 +694,18 @@ void HAssAnnounceDeviceInfoAndStatusSensor(void)
|
||||||
|
|
||||||
if (Settings.flag.hass_discovery)
|
if (Settings.flag.hass_discovery)
|
||||||
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
{ // SetOption19 - Control Home Assistantautomatic discovery (See SetOption59)
|
||||||
char name[33 + 7]; // friendlyname(33) + " " + "status"
|
char name[TOPSZ]; // friendlyname(33) + " " + "status"
|
||||||
char prefix[TOPSZ];
|
char prefix[TOPSZ];
|
||||||
char *state_topic = stemp1;
|
char *state_topic = stemp1;
|
||||||
char *availability_topic = stemp2;
|
char *availability_topic = stemp2;
|
||||||
|
|
||||||
snprintf_P(name, sizeof(name), PSTR("%s status"), ModuleName().c_str());
|
snprintf_P(name, sizeof(name), PSTR("%s status"), SettingsText(SET_DEVICENAME));
|
||||||
GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE));
|
GetTopic_P(state_topic, TELE, mqtt_topic, PSTR(D_RSLT_HASS_STATE));
|
||||||
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
GetTopic_P(availability_topic, TELE, mqtt_topic, S_LWT);
|
||||||
|
|
||||||
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
Response_P(HASS_DISCOVER_BASE, name, state_topic, availability_topic);
|
||||||
TryResponseAppend_P(HASS_DISCOVER_SENSOR_HASS_STATUS, state_topic);
|
TryResponseAppend_P(HASS_DISCOVER_SENSOR_HASS_STATUS, state_topic);
|
||||||
TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP_getChipId(), ModuleName().c_str(),
|
TryResponseAppend_P(HASS_DISCOVER_DEVICE_INFO, unique_id, ESP_getChipId(), SettingsText(SET_DEVICENAME),
|
||||||
ModuleName().c_str(), my_version, my_image);
|
ModuleName().c_str(), my_version, my_image);
|
||||||
TryResponseAppend_P(PSTR("}"));
|
TryResponseAppend_P(PSTR("}"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
typedef uint64_t Z_IEEEAddress;
|
typedef uint64_t Z_IEEEAddress;
|
||||||
typedef uint16_t Z_ShortAddress;
|
typedef uint16_t Z_ShortAddress;
|
||||||
|
|
||||||
|
const uint16_t BAD_SHORTADDR = 0xFFFE;
|
||||||
|
|
||||||
enum ZnpCommandType {
|
enum ZnpCommandType {
|
||||||
Z_POLL = 0x00,
|
Z_POLL = 0x00,
|
||||||
Z_SREQ = 0x20,
|
Z_SREQ = 0x20,
|
||||||
|
|
|
@ -103,7 +103,7 @@ public:
|
||||||
// Probe the existence of device keys
|
// Probe the existence of device keys
|
||||||
// Results:
|
// Results:
|
||||||
// - 0x0000 = not found
|
// - 0x0000 = not found
|
||||||
// - 0xFFFF = bad parameter
|
// - BAD_SHORTADDR = bad parameter
|
||||||
// - 0x<shortaddr> = the device's short address
|
// - 0x<shortaddr> = the device's short address
|
||||||
uint16_t isKnownShortAddr(uint16_t shortaddr) const;
|
uint16_t isKnownShortAddr(uint16_t shortaddr) const;
|
||||||
uint16_t isKnownLongAddr(uint64_t longaddr) const;
|
uint16_t isKnownLongAddr(uint64_t longaddr) const;
|
||||||
|
@ -295,18 +295,16 @@ void Z_Devices::freeDeviceEntry(Z_Device *device) {
|
||||||
// Scan all devices to find a corresponding shortaddr
|
// Scan all devices to find a corresponding shortaddr
|
||||||
// Looks info device.shortaddr entry
|
// Looks info device.shortaddr entry
|
||||||
// In:
|
// In:
|
||||||
// shortaddr (non null)
|
// shortaddr (not BAD_SHORTADDR)
|
||||||
// Out:
|
// Out:
|
||||||
// index in _devices of entry, -1 if not found
|
// index in _devices of entry, -1 if not found
|
||||||
//
|
//
|
||||||
int32_t Z_Devices::findShortAddr(uint16_t shortaddr) const {
|
int32_t Z_Devices::findShortAddr(uint16_t shortaddr) const {
|
||||||
if (!shortaddr) { return -1; } // does not make sense to look for 0x0000 shortaddr (localhost)
|
if (BAD_SHORTADDR == shortaddr) { return -1; } // does not make sense to look for BAD_SHORTADDR shortaddr (broadcast)
|
||||||
int32_t found = 0;
|
int32_t found = 0;
|
||||||
if (shortaddr) {
|
for (auto &elem : _devices) {
|
||||||
for (auto &elem : _devices) {
|
if (elem->shortaddr == shortaddr) { return found; }
|
||||||
if (elem->shortaddr == shortaddr) { return found; }
|
found++;
|
||||||
found++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -321,11 +319,9 @@ int32_t Z_Devices::findShortAddr(uint16_t shortaddr) const {
|
||||||
int32_t Z_Devices::findLongAddr(uint64_t longaddr) const {
|
int32_t Z_Devices::findLongAddr(uint64_t longaddr) const {
|
||||||
if (!longaddr) { return -1; }
|
if (!longaddr) { return -1; }
|
||||||
int32_t found = 0;
|
int32_t found = 0;
|
||||||
if (longaddr) {
|
for (auto &elem : _devices) {
|
||||||
for (auto &elem : _devices) {
|
if (elem->longaddr == longaddr) { return found; }
|
||||||
if (elem->longaddr == longaddr) { return found; }
|
found++;
|
||||||
found++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -358,7 +354,7 @@ uint16_t Z_Devices::isKnownShortAddr(uint16_t shortaddr) const {
|
||||||
if (found >= 0) {
|
if (found >= 0) {
|
||||||
return shortaddr;
|
return shortaddr;
|
||||||
} else {
|
} else {
|
||||||
return 0; // unknown
|
return BAD_SHORTADDR; // unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,7 +364,7 @@ uint16_t Z_Devices::isKnownLongAddr(uint64_t longaddr) const {
|
||||||
const Z_Device & device = devicesAt(found);
|
const Z_Device & device = devicesAt(found);
|
||||||
return device.shortaddr; // can be zero, if not yet registered
|
return device.shortaddr; // can be zero, if not yet registered
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return BAD_SHORTADDR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,18 +373,18 @@ uint16_t Z_Devices::isKnownIndex(uint32_t index) const {
|
||||||
const Z_Device & device = devicesAt(index);
|
const Z_Device & device = devicesAt(index);
|
||||||
return device.shortaddr;
|
return device.shortaddr;
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return BAD_SHORTADDR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t Z_Devices::isKnownFriendlyName(const char * name) const {
|
uint16_t Z_Devices::isKnownFriendlyName(const char * name) const {
|
||||||
if ((!name) || (0 == strlen(name))) { return 0xFFFF; } // Error
|
if ((!name) || (0 == strlen(name))) { return BAD_SHORTADDR; } // Error
|
||||||
int32_t found = findFriendlyName(name);
|
int32_t found = findFriendlyName(name);
|
||||||
if (found >= 0) {
|
if (found >= 0) {
|
||||||
const Z_Device & device = devicesAt(found);
|
const Z_Device & device = devicesAt(found);
|
||||||
return device.shortaddr; // can be zero, if not yet registered
|
return device.shortaddr; // can be zero, if not yet registered
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return BAD_SHORTADDR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,10 +394,10 @@ uint64_t Z_Devices::getDeviceLongAddr(uint16_t shortaddr) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// We have a seen a shortaddr on the network, get the corresponding
|
// We have a seen a shortaddr on the network, get the corresponding device object
|
||||||
//
|
//
|
||||||
Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) {
|
Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) {
|
||||||
if (!shortaddr) { return *(Z_Device*) nullptr; } // this is not legal
|
if (BAD_SHORTADDR == shortaddr) { return *(Z_Device*) nullptr; } // this is not legal
|
||||||
int32_t found = findShortAddr(shortaddr);
|
int32_t found = findShortAddr(shortaddr);
|
||||||
if (found >= 0) {
|
if (found >= 0) {
|
||||||
return *(_devices[found]);
|
return *(_devices[found]);
|
||||||
|
@ -411,7 +407,7 @@ Z_Device & Z_Devices::getShortAddr(uint16_t shortaddr) {
|
||||||
}
|
}
|
||||||
// Same version but Const
|
// Same version but Const
|
||||||
const Z_Device & Z_Devices::getShortAddrConst(uint16_t shortaddr) const {
|
const Z_Device & Z_Devices::getShortAddrConst(uint16_t shortaddr) const {
|
||||||
if (!shortaddr) { return *(Z_Device*) nullptr; } // this is not legal
|
if (BAD_SHORTADDR == shortaddr) { return *(Z_Device*) nullptr; } // this is not legal
|
||||||
int32_t found = findShortAddr(shortaddr);
|
int32_t found = findShortAddr(shortaddr);
|
||||||
if (found >= 0) {
|
if (found >= 0) {
|
||||||
return *(_devices[found]);
|
return *(_devices[found]);
|
||||||
|
@ -471,7 +467,7 @@ void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
|
||||||
dirty();
|
dirty();
|
||||||
} else {
|
} else {
|
||||||
// neither short/lonf addr are found.
|
// neither short/lonf addr are found.
|
||||||
if (shortaddr || longaddr) {
|
if ((BAD_SHORTADDR != shortaddr) || longaddr) {
|
||||||
createDeviceEntry(shortaddr, longaddr);
|
createDeviceEntry(shortaddr, longaddr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -481,7 +477,6 @@ void Z_Devices::updateDevice(uint16_t shortaddr, uint64_t longaddr) {
|
||||||
// Clear all endpoints
|
// Clear all endpoints
|
||||||
//
|
//
|
||||||
void Z_Devices::clearEndpoints(uint16_t shortaddr) {
|
void Z_Devices::clearEndpoints(uint16_t shortaddr) {
|
||||||
if (!shortaddr) { return; }
|
|
||||||
Z_Device &device = getShortAddr(shortaddr);
|
Z_Device &device = getShortAddr(shortaddr);
|
||||||
if (&device == nullptr) { return; } // don't crash if not found
|
if (&device == nullptr) { return; } // don't crash if not found
|
||||||
|
|
||||||
|
@ -495,7 +490,6 @@ void Z_Devices::clearEndpoints(uint16_t shortaddr) {
|
||||||
// Add an endpoint to a shortaddr
|
// Add an endpoint to a shortaddr
|
||||||
//
|
//
|
||||||
void Z_Devices::addEndpoint(uint16_t shortaddr, uint8_t endpoint) {
|
void Z_Devices::addEndpoint(uint16_t shortaddr, uint8_t endpoint) {
|
||||||
if (!shortaddr) { return; }
|
|
||||||
if (0x00 == endpoint) { return; }
|
if (0x00 == endpoint) { return; }
|
||||||
Z_Device &device = getShortAddr(shortaddr);
|
Z_Device &device = getShortAddr(shortaddr);
|
||||||
if (&device == nullptr) { return; } // don't crash if not found
|
if (&device == nullptr) { return; } // don't crash if not found
|
||||||
|
@ -922,7 +916,7 @@ uint16_t Z_Devices::parseDeviceParam(const char * param, bool short_must_be_know
|
||||||
char dataBuf[param_len + 1];
|
char dataBuf[param_len + 1];
|
||||||
strcpy(dataBuf, param);
|
strcpy(dataBuf, param);
|
||||||
RemoveSpace(dataBuf);
|
RemoveSpace(dataBuf);
|
||||||
uint16_t shortaddr = 0;
|
uint16_t shortaddr = BAD_SHORTADDR; // start with unknown
|
||||||
|
|
||||||
if (strlen(dataBuf) < 4) {
|
if (strlen(dataBuf) < 4) {
|
||||||
// simple number 0..99
|
// simple number 0..99
|
||||||
|
@ -1018,8 +1012,8 @@ String Z_Devices::dump(uint32_t dump_mode, uint16_t status_shortaddr) const {
|
||||||
uint16_t shortaddr = device.shortaddr;
|
uint16_t shortaddr = device.shortaddr;
|
||||||
char hex[22];
|
char hex[22];
|
||||||
|
|
||||||
// ignore non-current device, if specified device is non-zero
|
// ignore non-current device, if device specified
|
||||||
if ((status_shortaddr) && (status_shortaddr != shortaddr)) { continue; }
|
if ((BAD_SHORTADDR != status_shortaddr) && (status_shortaddr != shortaddr)) { continue; }
|
||||||
|
|
||||||
JsonObject& dev = devices.createNestedObject();
|
JsonObject& dev = devices.createNestedObject();
|
||||||
|
|
||||||
|
|
|
@ -66,41 +66,6 @@ public:
|
||||||
const static uint32_t ZIGB_NAME = 0x3167697A; // 'zig1' little endian
|
const static uint32_t ZIGB_NAME = 0x3167697A; // 'zig1' little endian
|
||||||
const static size_t Z_MAX_FLASH = z_block_len - sizeof(z_flashdata_t); // 2040
|
const static size_t Z_MAX_FLASH = z_block_len - sizeof(z_flashdata_t); // 2040
|
||||||
|
|
||||||
// encoding for the most commonly 32 clusters, used for binary encoding
|
|
||||||
const uint16_t Z_ClusterNumber[] PROGMEM = {
|
|
||||||
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
|
|
||||||
0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
|
|
||||||
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
|
|
||||||
0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
|
|
||||||
0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
|
|
||||||
0x0100, 0x0101, 0x0102,
|
|
||||||
0x0201, 0x0202, 0x0203, 0x0204,
|
|
||||||
0x0300, 0x0301,
|
|
||||||
0x0400, 0x0401, 0x0402, 0x0403, 0x0404, 0x0405, 0x0406,
|
|
||||||
0x0500, 0x0501, 0x0502,
|
|
||||||
0x0700, 0x0701, 0x0702,
|
|
||||||
0x0B00, 0x0B01, 0x0B02, 0x0B03, 0x0B04, 0x0B05,
|
|
||||||
0x1000,
|
|
||||||
0xFC0F,
|
|
||||||
};
|
|
||||||
|
|
||||||
// convert a 1 byte cluster code to the actual cluster number
|
|
||||||
uint16_t fromClusterCode(uint8_t c) {
|
|
||||||
if (c >= ARRAY_SIZE(Z_ClusterNumber)) {
|
|
||||||
return 0xFFFF; // invalid
|
|
||||||
}
|
|
||||||
return pgm_read_word(&Z_ClusterNumber[c]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert a cluster number to 1 byte, or 0xFF if not in table
|
|
||||||
uint8_t toClusterCode(uint16_t c) {
|
|
||||||
for (uint32_t i = 0; i < ARRAY_SIZE(Z_ClusterNumber); i++) {
|
|
||||||
if (c == pgm_read_word(&Z_ClusterNumber[i])) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0xFF; // not found
|
|
||||||
}
|
|
||||||
|
|
||||||
class SBuffer hibernateDevice(const struct Z_Device &device) {
|
class SBuffer hibernateDevice(const struct Z_Device &device) {
|
||||||
SBuffer buf(128);
|
SBuffer buf(128);
|
||||||
|
@ -202,18 +167,8 @@ void hydrateDevices(const SBuffer &buf) {
|
||||||
for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) {
|
for (uint32_t i = 0; (i < num_devices) && (k < buf_len); i++) {
|
||||||
uint32_t dev_record_len = buf.get8(k);
|
uint32_t dev_record_len = buf.get8(k);
|
||||||
|
|
||||||
// AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Device %d Before Memory = %d // DIFF %d // record_len %d"), i, ESP_getFreeHeap(), before - ESP_getFreeHeap(), dev_record_len);
|
|
||||||
// before = ESP_getFreeHeap();
|
|
||||||
|
|
||||||
SBuffer buf_d = buf.subBuffer(k, dev_record_len);
|
SBuffer buf_d = buf.subBuffer(k, dev_record_len);
|
||||||
|
|
||||||
// char *hex_char = (char*) malloc((dev_record_len * 2) + 2);
|
|
||||||
// if (hex_char) {
|
|
||||||
// AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "/// SUB %s"),
|
|
||||||
// ToHex_P(buf_d.getBuffer(), dev_record_len, hex_char, (dev_record_len * 2) + 2));
|
|
||||||
// free(hex_char);
|
|
||||||
// }
|
|
||||||
|
|
||||||
uint32_t d = 1; // index in device buffer
|
uint32_t d = 1; // index in device buffer
|
||||||
uint16_t shortaddr = buf_d.get16(d); d += 2;
|
uint16_t shortaddr = buf_d.get16(d); d += 2;
|
||||||
uint64_t longaddr = buf_d.get64(d); d += 8;
|
uint64_t longaddr = buf_d.get64(d); d += 8;
|
||||||
|
|
|
@ -79,16 +79,6 @@ uint8_t Z_getDatatypeLen(uint8_t t) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// typedef struct Z_DataTypeMapping {
|
|
||||||
// uint8_t datatype;
|
|
||||||
// uint8_t len; // len in bytes and add 0x80 if DISCRETE
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const Z_DataTypeMapping Z_types[] PROGMEM = {
|
|
||||||
// { Znodata, 0 },
|
|
||||||
// { Zdata8, 0 },
|
|
||||||
// };
|
|
||||||
|
|
||||||
typedef union ZCLHeaderFrameControl_t {
|
typedef union ZCLHeaderFrameControl_t {
|
||||||
struct {
|
struct {
|
||||||
uint8_t frame_type : 2; // 00 = across entire profile, 01 = cluster specific
|
uint8_t frame_type : 2; // 00 = across entire profile, 01 = cluster specific
|
||||||
|
@ -572,6 +562,9 @@ typedef struct Z_AttributeConverter {
|
||||||
Z_AttrConverter func;
|
Z_AttrConverter func;
|
||||||
} Z_AttributeConverter;
|
} Z_AttributeConverter;
|
||||||
|
|
||||||
|
// Cluster numbers are store in 8 bits format to save space,
|
||||||
|
// the following tables allows the conversion from 8 bits index Cx...
|
||||||
|
// to the 16 bits actual cluster number
|
||||||
enum Cx_cluster_short {
|
enum Cx_cluster_short {
|
||||||
Cx0000, Cx0001, Cx0002, Cx0003, Cx0004, Cx0005, Cx0006, Cx0007,
|
Cx0000, Cx0001, Cx0002, Cx0003, Cx0004, Cx0005, Cx0006, Cx0007,
|
||||||
Cx0008, Cx0009, Cx000A, Cx000B, Cx000C, Cx000D, Cx000E, Cx000F,
|
Cx0008, Cx0009, Cx000A, Cx000B, Cx000C, Cx000D, Cx000E, Cx000F,
|
||||||
|
|
|
@ -177,6 +177,9 @@ int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t clus
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (attrs) {
|
if (attrs) {
|
||||||
|
if (groupaddr) {
|
||||||
|
shortaddr = BAD_SHORTADDR; // if group address, don't send to device
|
||||||
|
}
|
||||||
ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, 0, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(shortaddr));
|
ZigbeeZCLSend_Raw(shortaddr, groupaddr, cluster, endpoint, ZCL_READ_ATTRIBUTES, false, 0, attrs, attrs_len, true /* we do want a response */, zigbee_devices.getNextSeqNumber(shortaddr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,7 +187,7 @@ int32_t Z_ReadAttrCallback(uint16_t shortaddr, uint16_t groupaddr, uint16_t clus
|
||||||
|
|
||||||
// This callback is registered after a an attribute read command was made to a light, and fires if we don't get any response after 1000 ms
|
// This callback is registered after a an attribute read command was made to a light, and fires if we don't get any response after 1000 ms
|
||||||
int32_t Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
|
int32_t Z_Unreachable(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uint8_t endpoint, uint32_t value) {
|
||||||
if (shortaddr) {
|
if (BAD_SHORTADDR != shortaddr) {
|
||||||
zigbee_devices.setReachable(shortaddr, false); // mark device as reachable
|
zigbee_devices.setReachable(shortaddr, false); // mark device as reachable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,7 +211,7 @@ void zigbeeSetCommandTimer(uint16_t shortaddr, uint16_t groupaddr, uint16_t clus
|
||||||
}
|
}
|
||||||
if (wait_ms) {
|
if (wait_ms) {
|
||||||
zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback);
|
zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, Z_CAT_NONE, 0 /* value */, &Z_ReadAttrCallback);
|
||||||
if (shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group
|
if (BAD_SHORTADDR != shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group
|
||||||
zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);
|
zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -314,12 +317,12 @@ void sendHueUpdate(uint16_t shortaddr, uint16_t groupaddr, uint16_t cluster, uin
|
||||||
}
|
}
|
||||||
if (z_cat >= 0) {
|
if (z_cat >= 0) {
|
||||||
uint8_t endpoint = 0;
|
uint8_t endpoint = 0;
|
||||||
if (shortaddr) {
|
if (BAD_SHORTADDR != shortaddr) {
|
||||||
endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
|
endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
|
||||||
}
|
}
|
||||||
if ((!shortaddr) || (endpoint)) { // send if group address or endpoint is known
|
if ((BAD_SHORTADDR == shortaddr) || (endpoint)) { // send if group address or endpoint is known
|
||||||
zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback);
|
zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms, cluster, endpoint, z_cat, 0 /* value */, &Z_ReadAttrCallback);
|
||||||
if (shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group
|
if (BAD_SHORTADDR != shortaddr) { // reachability test is not possible for group addresses, since we don't know the list of devices in the group
|
||||||
zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);
|
zigbee_devices.setTimer(shortaddr, groupaddr, wait_ms + Z_CAT_REACHABILITY_TIMEOUT, cluster, endpoint, Z_CAT_REACHABILITY, 0 /* value */, &Z_Unreachable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -584,18 +584,6 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
||||||
ZI_WAIT_RECV(1000, ZBS_LOGTYPE_DEVICE) // it should be coordinator
|
ZI_WAIT_RECV(1000, ZBS_LOGTYPE_DEVICE) // it should be coordinator
|
||||||
|
|
||||||
ZI_GOTO(ZIGBEE_LABEL_START_ROUTER)
|
ZI_GOTO(ZIGBEE_LABEL_START_ROUTER)
|
||||||
// Device and Router code is common from now
|
|
||||||
// ZI_LABEL(ZIGBEE_LABEL_START_DEVICE) // Init as a router
|
|
||||||
// ZI_MQTT_STATE(ZIGBEE_STATUS_STARTING, kConfiguredDevice)
|
|
||||||
// ZI_ON_ERROR_GOTO(ZIGBEE_LABEL_ABORT)
|
|
||||||
// ZI_SEND(ZBS_AF_REGISTER_ALL) // Z_AF register for endpoint 01, profile 0x0104 Home Automation
|
|
||||||
// ZI_WAIT_RECV(1000, ZBR_AF_REGISTER)
|
|
||||||
// ZI_SEND(ZBS_STARTUPFROMAPP) // start router
|
|
||||||
// ZI_WAIT_RECV(2000, ZBR_STARTUPFROMAPP) // wait for sync ack of command
|
|
||||||
// ZI_WAIT_UNTIL_FUNC(0xFFFF, AREQ_STARTUPFROMAPP, &Z_ReceiveStateChange) // wait forever for async message that coordinator started
|
|
||||||
// ZI_SEND(ZBS_GETDEVICEINFO) // GetDeviceInfo
|
|
||||||
// ZI_WAIT_RECV_FUNC(2000, ZBR_GETDEVICEINFO, &Z_ReceiveDeviceInfo)
|
|
||||||
// ZI_GOTO(ZIGBEE_LABEL_READY)
|
|
||||||
|
|
||||||
ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_DEVICE) // Factory reset for router
|
ZI_LABEL(ZIGBEE_LABEL_FACT_RESET_DEVICE) // Factory reset for router
|
||||||
ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting)
|
ZI_MQTT_STATE(ZIGBEE_STATUS_RESET_CONF, kResetting)
|
||||||
|
@ -608,25 +596,12 @@ static const Zigbee_Instruction zb_prog[] PROGMEM = {
|
||||||
ZI_WAIT_RECV(1000, ZBR_W_OK)
|
ZI_WAIT_RECV(1000, ZBR_W_OK)
|
||||||
ZI_GOTO(ZIGBEE_LABEL_FACT_RESET_ROUTER_DEVICE_POST)
|
ZI_GOTO(ZIGBEE_LABEL_FACT_RESET_ROUTER_DEVICE_POST)
|
||||||
|
|
||||||
// ZI_SEND(ZBS_W_ALL_PAN) // write universal PAN ID = 0xFFFF
|
// Error: version of Z-Stack is not supported
|
||||||
// ZI_WAIT_RECV(1000, ZBR_W_OK)
|
|
||||||
// ZI_SEND(ZBS_W_ALL_CHANN) // write Allows all CHANNELS = 0x07FFF800, 11-26
|
|
||||||
// ZI_WAIT_RECV(1000, ZBR_W_OK)
|
|
||||||
|
|
||||||
// // Now mark the device as ready, writing 0x55 in memory slot 0x0F00
|
|
||||||
// ZI_SEND(ZBS_WNV_INITZNPHC) // Init NV ZNP Has Configured
|
|
||||||
// ZI_WAIT_RECV_FUNC(1000, ZBR_WNV_INIT_OK, &Z_CheckNVWrite)
|
|
||||||
// ZI_SEND(ZBS_WNV_ZNPHC) // Write NV ZNP Has Configured
|
|
||||||
// ZI_WAIT_RECV(1000, ZBR_WNV_OK)
|
|
||||||
|
|
||||||
// ZI_GOTO(ZIGBEE_LABEL_START_ROUTER)
|
|
||||||
// ZI_GOTO(ZIGBEE_LABEL_START_DEVICE)
|
|
||||||
|
|
||||||
|
|
||||||
ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION)
|
ZI_LABEL(ZIGBEE_LABEL_UNSUPPORTED_VERSION)
|
||||||
ZI_MQTT_STATE(ZIGBEE_STATUS_UNSUPPORTED_VERSION, kZNP12)
|
ZI_MQTT_STATE(ZIGBEE_STATUS_UNSUPPORTED_VERSION, kZNP12)
|
||||||
ZI_GOTO(ZIGBEE_LABEL_ABORT)
|
ZI_GOTO(ZIGBEE_LABEL_ABORT)
|
||||||
|
|
||||||
|
// Abort state machine, general error
|
||||||
ZI_LABEL(ZIGBEE_LABEL_ABORT) // Label 99: abort
|
ZI_LABEL(ZIGBEE_LABEL_ABORT) // Label 99: abort
|
||||||
ZI_MQTT_STATE(ZIGBEE_STATUS_ABORT, kAbort)
|
ZI_MQTT_STATE(ZIGBEE_STATUS_ABORT, kAbort)
|
||||||
ZI_LOG(LOG_LEVEL_ERROR, kZigbeeAbort)
|
ZI_LOG(LOG_LEVEL_ERROR, kZigbeeAbort)
|
||||||
|
|
|
@ -52,7 +52,7 @@ int32_t Z_ReceiveDeviceInfo(int32_t res, class SBuffer &buf) {
|
||||||
ZIGBEE_STATUS_CC_INFO, hex, short_adr, device_type, device_state,
|
ZIGBEE_STATUS_CC_INFO, hex, short_adr, device_type, device_state,
|
||||||
device_associated);
|
device_associated);
|
||||||
|
|
||||||
if (device_associated > 0) {
|
if (device_associated > 0) { // If there are devices registered in CC2530, print the list
|
||||||
uint idx = 16;
|
uint idx = 16;
|
||||||
ResponseAppend_P(PSTR(",\"AssocDevicesList\":["));
|
ResponseAppend_P(PSTR(",\"AssocDevicesList\":["));
|
||||||
for (uint32_t i = 0; i < device_associated; i++) {
|
for (uint32_t i = 0; i < device_associated; i++) {
|
||||||
|
@ -87,18 +87,20 @@ int32_t Z_Reboot(int32_t res, class SBuffer &buf) {
|
||||||
// print information about the reboot of device
|
// print information about the reboot of device
|
||||||
// 4180.02.02.00.02.06.03
|
// 4180.02.02.00.02.06.03
|
||||||
//
|
//
|
||||||
static const char Z_RebootReason[] PROGMEM = "Power-up|External|Watchdog";
|
|
||||||
|
|
||||||
uint8_t reason = buf.get8(2);
|
uint8_t reason = buf.get8(2);
|
||||||
uint8_t transport_rev = buf.get8(3);
|
uint8_t transport_rev = buf.get8(3);
|
||||||
uint8_t product_id = buf.get8(4);
|
uint8_t product_id = buf.get8(4);
|
||||||
uint8_t major_rel = buf.get8(5);
|
uint8_t major_rel = buf.get8(5);
|
||||||
uint8_t minor_rel = buf.get8(6);
|
uint8_t minor_rel = buf.get8(6);
|
||||||
uint8_t hw_rev = buf.get8(7);
|
uint8_t hw_rev = buf.get8(7);
|
||||||
char reason_str[12];
|
const char *reason_str;
|
||||||
|
|
||||||
if (reason > 3) { reason = 3; }
|
switch (reason) {
|
||||||
GetTextIndexed(reason_str, sizeof(reason_str), reason, Z_RebootReason);
|
case 0: reason_str = PSTR("Power-up"); break;
|
||||||
|
case 1: reason_str = PSTR("External"); break;
|
||||||
|
case 2: reason_str = PSTR("Watchdog"); break;
|
||||||
|
default: reason_str = PSTR("Unknown"); break;
|
||||||
|
}
|
||||||
|
|
||||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
|
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
|
||||||
"\"Status\":%d,\"Message\":\"CC2530 booted\",\"RestartReason\":\"%s\""
|
"\"Status\":%d,\"Message\":\"CC2530 booted\",\"RestartReason\":\"%s\""
|
||||||
|
@ -201,7 +203,6 @@ int32_t Z_ReceivePermitJoinStatus(int32_t res, const class SBuffer &buf) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* Z_DeviceType[] = { "Coordinator", "Router", "End Device", "Unknown" };
|
|
||||||
int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) {
|
int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) {
|
||||||
// Received ZDO_NODE_DESC_RSP
|
// Received ZDO_NODE_DESC_RSP
|
||||||
Z_ShortAddress srcAddr = buf.get16(2);
|
Z_ShortAddress srcAddr = buf.get16(2);
|
||||||
|
@ -217,15 +218,22 @@ int32_t Z_ReceiveNodeDesc(int32_t res, const class SBuffer &buf) {
|
||||||
uint16_t maxOutTransferSize = buf.get16(17);
|
uint16_t maxOutTransferSize = buf.get16(17);
|
||||||
uint8_t descriptorCapabilities = buf.get8(19);
|
uint8_t descriptorCapabilities = buf.get8(19);
|
||||||
|
|
||||||
|
|
||||||
if (0 == status) {
|
if (0 == status) {
|
||||||
uint8_t deviceType = logicalType & 0x7; // 0=coordinator, 1=router, 2=end device
|
uint8_t deviceType = logicalType & 0x7; // 0=coordinator, 1=router, 2=end device
|
||||||
if (deviceType > 3) { deviceType = 3; }
|
const char * deviceTypeStr;
|
||||||
|
switch (deviceType) {
|
||||||
|
case 0: deviceTypeStr = PSTR("Coordinator"); break;
|
||||||
|
case 1: deviceTypeStr = PSTR("Router"); break;
|
||||||
|
case 2: deviceTypeStr = PSTR("Device"); break;
|
||||||
|
default: deviceTypeStr = PSTR("Unknown"); break;
|
||||||
|
}
|
||||||
bool complexDescriptorAvailable = (logicalType & 0x08) ? 1 : 0;
|
bool complexDescriptorAvailable = (logicalType & 0x08) ? 1 : 0;
|
||||||
|
|
||||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
|
Response_P(PSTR("{\"" D_JSON_ZIGBEE_STATE "\":{"
|
||||||
"\"Status\":%d,\"NodeType\":\"%s\",\"ComplexDesc\":%s}}"),
|
"\"Status\":%d,\"NodeType\":\"%s\",\"ComplexDesc\":%s}}"),
|
||||||
ZIGBEE_STATUS_NODE_DESC, Z_DeviceType[deviceType],
|
ZIGBEE_STATUS_NODE_DESC, deviceTypeStr,
|
||||||
complexDescriptorAvailable ? "true" : "false"
|
complexDescriptorAvailable ? PSTR("true") : PSTR("false")
|
||||||
);
|
);
|
||||||
|
|
||||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
||||||
|
@ -282,15 +290,13 @@ int32_t Z_ReceiveIEEEAddr(int32_t res, const class SBuffer &buf) {
|
||||||
Uint64toHex(ieeeAddr, hex, 64);
|
Uint64toHex(ieeeAddr, hex, 64);
|
||||||
// Ping response
|
// Ping response
|
||||||
const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr);
|
const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr);
|
||||||
|
|
||||||
|
Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
|
||||||
|
",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\""), nwkAddr, hex);
|
||||||
if (friendlyName) {
|
if (friendlyName) {
|
||||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
|
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName);
|
||||||
",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\""
|
|
||||||
",\"" D_JSON_ZIGBEE_NAME "\":\"%s\"}}"), nwkAddr, hex, friendlyName);
|
|
||||||
} else {
|
|
||||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_PING "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
|
|
||||||
",\"" D_JSON_ZIGBEE_IEEE "\":\"0x%s\""
|
|
||||||
"}}"), nwkAddr, hex);
|
|
||||||
}
|
}
|
||||||
|
ResponseAppend_P(PSTR("\"}}"));
|
||||||
|
|
||||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
||||||
XdrvRulesProcess();
|
XdrvRulesProcess();
|
||||||
|
@ -398,9 +404,9 @@ int32_t Z_ReceiveEndDeviceAnnonce(int32_t res, const class SBuffer &buf) {
|
||||||
"\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\""
|
"\"Status\":%d,\"IEEEAddr\":\"0x%s\",\"ShortAddr\":\"0x%04X\""
|
||||||
",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"),
|
",\"PowerSource\":%s,\"ReceiveWhenIdle\":%s,\"Security\":%s}}"),
|
||||||
ZIGBEE_STATUS_DEVICE_ANNOUNCE, hex, nwkAddr,
|
ZIGBEE_STATUS_DEVICE_ANNOUNCE, hex, nwkAddr,
|
||||||
(capabilities & 0x04) ? "true" : "false",
|
(capabilities & 0x04) ? PSTR("true") : PSTR("false"),
|
||||||
(capabilities & 0x08) ? "true" : "false",
|
(capabilities & 0x08) ? PSTR("true") : PSTR("false"),
|
||||||
(capabilities & 0x40) ? "true" : "false"
|
(capabilities & 0x40) ? PSTR("true") : PSTR("false")
|
||||||
);
|
);
|
||||||
// query the state of the bulb (for Alexa)
|
// query the state of the bulb (for Alexa)
|
||||||
uint32_t wait_ms = 2000; // wait for 2s
|
uint32_t wait_ms = 2000; // wait for 2s
|
||||||
|
@ -444,18 +450,15 @@ int32_t Z_BindRsp(int32_t res, const class SBuffer &buf) {
|
||||||
uint8_t status = buf.get8(4);
|
uint8_t status = buf.get8(4);
|
||||||
|
|
||||||
const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr);
|
const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr);
|
||||||
|
|
||||||
|
Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), nwkAddr);
|
||||||
if (friendlyName) {
|
if (friendlyName) {
|
||||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
|
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName);
|
||||||
",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""
|
|
||||||
",\"" D_JSON_ZIGBEE_STATUS "\":%d"
|
|
||||||
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
|
|
||||||
"}}"), nwkAddr, friendlyName, status, getZigbeeStatusMessage(status).c_str());
|
|
||||||
} else {
|
|
||||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_BIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
|
|
||||||
",\"" D_JSON_ZIGBEE_STATUS "\":%d"
|
|
||||||
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
|
|
||||||
"}}"), nwkAddr, status, getZigbeeStatusMessage(status).c_str());
|
|
||||||
}
|
}
|
||||||
|
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d"
|
||||||
|
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
|
||||||
|
"}}"), status, getZigbeeStatusMessage(status).c_str());
|
||||||
|
|
||||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
||||||
XdrvRulesProcess();
|
XdrvRulesProcess();
|
||||||
|
|
||||||
|
@ -470,18 +473,14 @@ int32_t Z_UnbindRsp(int32_t res, const class SBuffer &buf) {
|
||||||
uint8_t status = buf.get8(4);
|
uint8_t status = buf.get8(4);
|
||||||
|
|
||||||
const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr);
|
const char * friendlyName = zigbee_devices.getFriendlyName(nwkAddr);
|
||||||
|
|
||||||
|
Response_P(PSTR("{\"" D_JSON_ZIGBEE_UNBIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""), nwkAddr);
|
||||||
if (friendlyName) {
|
if (friendlyName) {
|
||||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_UNBIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
|
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""), friendlyName);
|
||||||
",\"" D_JSON_ZIGBEE_NAME "\":\"%s\""
|
|
||||||
",\"" D_JSON_ZIGBEE_STATUS "\":%d"
|
|
||||||
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
|
|
||||||
"}}"), nwkAddr, friendlyName, status, getZigbeeStatusMessage(status).c_str());
|
|
||||||
} else {
|
|
||||||
Response_P(PSTR("{\"" D_JSON_ZIGBEE_UNBIND "\":{\"" D_JSON_ZIGBEE_DEVICE "\":\"0x%04X\""
|
|
||||||
",\"" D_JSON_ZIGBEE_STATUS "\":%d"
|
|
||||||
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
|
|
||||||
"}}"), nwkAddr, status, getZigbeeStatusMessage(status).c_str());
|
|
||||||
}
|
}
|
||||||
|
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
|
||||||
|
"}}"), status, getZigbeeStatusMessage(status).c_str());
|
||||||
|
|
||||||
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
MqttPublishPrefixTopic_P(RESULT_OR_TELE, PSTR(D_JSON_ZIGBEEZCL_RECEIVED));
|
||||||
XdrvRulesProcess();
|
XdrvRulesProcess();
|
||||||
|
|
||||||
|
@ -506,7 +505,6 @@ int32_t Z_MgmtBindRsp(int32_t res, const class SBuffer &buf) {
|
||||||
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d"
|
ResponseAppend_P(PSTR(",\"" D_JSON_ZIGBEE_STATUS "\":%d"
|
||||||
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
|
",\"" D_JSON_ZIGBEE_STATUS_MSG "\":\"%s\""
|
||||||
",\"BindingsTotal\":%d"
|
",\"BindingsTotal\":%d"
|
||||||
//",\"BindingsStart\":%d"
|
|
||||||
",\"Bindings\":["
|
",\"Bindings\":["
|
||||||
), status, getZigbeeStatusMessage(status).c_str(), bind_total);
|
), status, getZigbeeStatusMessage(status).c_str(), bind_total);
|
||||||
|
|
||||||
|
@ -631,7 +629,7 @@ int32_t Z_PublishAttributes(uint16_t shortaddr, uint16_t groupaddr, uint16_t clu
|
||||||
int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
int32_t Z_ReceiveAfIncomingMessage(int32_t res, const class SBuffer &buf) {
|
||||||
uint16_t groupid = buf.get16(2);
|
uint16_t groupid = buf.get16(2);
|
||||||
uint16_t clusterid = buf.get16(4);
|
uint16_t clusterid = buf.get16(4);
|
||||||
Z_ShortAddress srcaddr = buf.get16(6);
|
uint16_t srcaddr = buf.get16(6);
|
||||||
uint8_t srcendpoint = buf.get8(8);
|
uint8_t srcendpoint = buf.get8(8);
|
||||||
uint8_t dstendpoint = buf.get8(9);
|
uint8_t dstendpoint = buf.get8(9);
|
||||||
uint8_t wasbroadcast = buf.get8(10);
|
uint8_t wasbroadcast = buf.get8(10);
|
||||||
|
|
|
@ -147,7 +147,7 @@ void ZigbeeInputLoop(void)
|
||||||
// Initialize internal structures
|
// Initialize internal structures
|
||||||
void ZigbeeInit(void)
|
void ZigbeeInit(void)
|
||||||
{
|
{
|
||||||
// Check if settings if Flash are set
|
// Check if settings in Flash are set
|
||||||
if (0 == Settings.zb_channel) {
|
if (0 == Settings.zb_channel) {
|
||||||
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Initializing Zigbee parameters from defaults"));
|
AddLog_P2(LOG_LEVEL_INFO, PSTR(D_LOG_ZIGBEE "Initializing Zigbee parameters from defaults"));
|
||||||
Settings.zb_ext_panid = USE_ZIGBEE_EXTPANID;
|
Settings.zb_ext_panid = USE_ZIGBEE_EXTPANID;
|
||||||
|
@ -314,7 +314,7 @@ void ZigbeeZCLSend_Raw(uint16_t shortaddr, uint16_t groupaddr, uint16_t clusterI
|
||||||
SBuffer buf(32+len);
|
SBuffer buf(32+len);
|
||||||
buf.add8(Z_SREQ | Z_AF); // 24
|
buf.add8(Z_SREQ | Z_AF); // 24
|
||||||
buf.add8(AF_DATA_REQUEST_EXT); // 02
|
buf.add8(AF_DATA_REQUEST_EXT); // 02
|
||||||
if (0x0000 == shortaddr) { // if no shortaddr we assume group address
|
if (BAD_SHORTADDR == shortaddr) { // if no shortaddr we assume group address
|
||||||
buf.add8(Z_Addr_Group); // 01
|
buf.add8(Z_Addr_Group); // 01
|
||||||
buf.add64(groupaddr); // group address, only 2 LSB, upper 6 MSB are discarded
|
buf.add64(groupaddr); // group address, only 2 LSB, upper 6 MSB are discarded
|
||||||
buf.add8(0xFF); // dest endpoint is not used for group addresses
|
buf.add8(0xFF); // dest endpoint is not used for group addresses
|
||||||
|
@ -372,7 +372,7 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((0 == endpoint) && (shortaddr)) {
|
if ((0 == endpoint) && (BAD_SHORTADDR != shortaddr)) {
|
||||||
// endpoint is not specified, let's try to find it from shortAddr, unless it's a group address
|
// endpoint is not specified, let's try to find it from shortAddr, unless it's a group address
|
||||||
endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
|
endpoint = zigbee_devices.findFirstEndpoint(shortaddr);
|
||||||
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
|
//AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
|
||||||
|
@ -380,7 +380,7 @@ void zigbeeZCLSendStr(uint16_t shortaddr, uint16_t groupaddr, uint8_t endpoint,
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"),
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: shortaddr 0x%04X, groupaddr 0x%04X, cluster 0x%04X, endpoint 0x%02X, cmd 0x%02X, data %s"),
|
||||||
shortaddr, groupaddr, cluster, endpoint, cmd, param);
|
shortaddr, groupaddr, cluster, endpoint, cmd, param);
|
||||||
|
|
||||||
if ((0 == endpoint) && (shortaddr)) { // endpoint null is ok for group address
|
if ((0 == endpoint) && (BAD_SHORTADDR != shortaddr)) { // endpoint null is ok for group address
|
||||||
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint"));
|
AddLog_P2(LOG_LEVEL_INFO, PSTR("ZbSend: unspecified endpoint"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -415,7 +415,7 @@ void CmndZbSend(void) {
|
||||||
|
|
||||||
// params
|
// params
|
||||||
static char delim[] = ", "; // delimiters for parameters
|
static char delim[] = ", "; // delimiters for parameters
|
||||||
uint16_t device = 0x0000; // 0x0000 is local, so considered invalid
|
uint16_t device = BAD_SHORTADDR; // 0x0000 is local, so considered invalid
|
||||||
uint16_t groupaddr = 0x0000; // group address
|
uint16_t groupaddr = 0x0000; // group address
|
||||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||||
uint16_t manuf = 0x0000; // Manuf Id in ZCL frame
|
uint16_t manuf = 0x0000; // Manuf Id in ZCL frame
|
||||||
|
@ -430,9 +430,9 @@ void CmndZbSend(void) {
|
||||||
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
||||||
if (nullptr != &val_device) {
|
if (nullptr != &val_device) {
|
||||||
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||||
if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
||||||
}
|
}
|
||||||
if (0x0000 == device) { // if not found, check if we have a group
|
if (BAD_SHORTADDR == device) { // if not found, check if we have a group
|
||||||
const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group"));
|
const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group"));
|
||||||
if (nullptr != &val_group) {
|
if (nullptr != &val_group) {
|
||||||
groupaddr = strToUInt(val_group);
|
groupaddr = strToUInt(val_group);
|
||||||
|
@ -571,8 +571,8 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
||||||
|
|
||||||
// params
|
// params
|
||||||
// static char delim[] = ", "; // delimiters for parameters
|
// static char delim[] = ", "; // delimiters for parameters
|
||||||
uint16_t srcDevice = 0xFFFF; // 0xFFFF is broadcast, so considered invalid
|
uint16_t srcDevice = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
||||||
uint16_t dstDevice = 0xFFFF; // 0xFFFF is broadcast, so considered invalid
|
uint16_t dstDevice = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
||||||
uint64_t dstLongAddr = 0;
|
uint64_t dstLongAddr = 0;
|
||||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the src endpoint
|
uint8_t endpoint = 0x00; // 0x00 is invalid for the src endpoint
|
||||||
uint8_t toendpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
uint8_t toendpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||||
|
@ -585,9 +585,8 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
||||||
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
||||||
if (nullptr != &val_device) {
|
if (nullptr != &val_device) {
|
||||||
srcDevice = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
srcDevice = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||||
if (0xFFFF == srcDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
|
||||||
}
|
}
|
||||||
if ((nullptr == &val_device) || (0x0000 == srcDevice)) { ResponseCmndChar_P(PSTR("Unknown source device")); return; }
|
if ((nullptr == &val_device) || (BAD_SHORTADDR == srcDevice)) { ResponseCmndChar_P(PSTR("Unknown source device")); return; }
|
||||||
// check if IEEE address is known
|
// check if IEEE address is known
|
||||||
uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice);
|
uint64_t srcLongAddr = zigbee_devices.getDeviceLongAddr(srcDevice);
|
||||||
if (0 == srcLongAddr) { ResponseCmndChar_P(PSTR("Unknown source IEEE address")); return; }
|
if (0 == srcLongAddr) { ResponseCmndChar_P(PSTR("Unknown source IEEE address")); return; }
|
||||||
|
@ -605,7 +604,7 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
||||||
const JsonVariant &dst_device = getCaseInsensitive(json, PSTR("ToDevice"));
|
const JsonVariant &dst_device = getCaseInsensitive(json, PSTR("ToDevice"));
|
||||||
if (nullptr != &dst_device) {
|
if (nullptr != &dst_device) {
|
||||||
dstDevice = zigbee_devices.parseDeviceParam(dst_device.as<char*>());
|
dstDevice = zigbee_devices.parseDeviceParam(dst_device.as<char*>());
|
||||||
if (0xFFFF == dstDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
if (BAD_SHORTADDR == dstDevice) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
||||||
if (0x0000 == dstDevice) {
|
if (0x0000 == dstDevice) {
|
||||||
dstLongAddr = localIEEEAddr;
|
dstLongAddr = localIEEEAddr;
|
||||||
} else {
|
} else {
|
||||||
|
@ -622,8 +621,8 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind
|
||||||
if (nullptr != &to_group) { toGroup = strToUInt(to_group); }
|
if (nullptr != &to_group) { toGroup = strToUInt(to_group); }
|
||||||
|
|
||||||
// make sure we don't have conflicting parameters
|
// make sure we don't have conflicting parameters
|
||||||
if (toGroup && dstLongAddr) { ResponseCmndChar_P(PSTR("Cannot have both \"ToDevice\" and \"ToGroup\"")); return; }
|
if (&to_group && dstLongAddr) { ResponseCmndChar_P(PSTR("Cannot have both \"ToDevice\" and \"ToGroup\"")); return; }
|
||||||
if (!toGroup && !dstLongAddr) { ResponseCmndChar_P(PSTR("Missing \"ToDevice\" or \"ToGroup\"")); return; }
|
if (!&to_group && !dstLongAddr) { ResponseCmndChar_P(PSTR("Missing \"ToDevice\" or \"ToGroup\"")); return; }
|
||||||
|
|
||||||
SBuffer buf(34);
|
SBuffer buf(34);
|
||||||
buf.add8(Z_SREQ | Z_ZDO);
|
buf.add8(Z_SREQ | Z_ZDO);
|
||||||
|
@ -670,8 +669,7 @@ void CmndZbUnbind(void) {
|
||||||
void CmndZbBindState(void) {
|
void CmndZbBindState(void) {
|
||||||
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
||||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||||
if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
||||||
if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
|
||||||
|
|
||||||
SBuffer buf(10);
|
SBuffer buf(10);
|
||||||
buf.add8(Z_SREQ | Z_ZDO); // 25
|
buf.add8(Z_SREQ | Z_ZDO); // 25
|
||||||
|
@ -695,8 +693,7 @@ void CmndZbProbe(void) {
|
||||||
void CmndZbProbeOrPing(boolean probe) {
|
void CmndZbProbeOrPing(boolean probe) {
|
||||||
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
||||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||||
if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
||||||
if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
|
||||||
|
|
||||||
// everything is good, we can send the command
|
// everything is good, we can send the command
|
||||||
Z_SendIEEEAddrReq(shortaddr);
|
Z_SendIEEEAddrReq(shortaddr);
|
||||||
|
@ -731,8 +728,7 @@ void CmndZbName(void) {
|
||||||
|
|
||||||
// parse first part, <device_id>
|
// parse first part, <device_id>
|
||||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
|
||||||
if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
||||||
if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
|
||||||
|
|
||||||
if (p == nullptr) {
|
if (p == nullptr) {
|
||||||
const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr);
|
const char * friendlyName = zigbee_devices.getFriendlyName(shortaddr);
|
||||||
|
@ -763,8 +759,7 @@ void CmndZbModelId(void) {
|
||||||
|
|
||||||
// parse first part, <device_id>
|
// parse first part, <device_id>
|
||||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
|
||||||
if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
||||||
if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
|
||||||
|
|
||||||
if (p == nullptr) {
|
if (p == nullptr) {
|
||||||
const char * modelId = zigbee_devices.getModelId(shortaddr);
|
const char * modelId = zigbee_devices.getModelId(shortaddr);
|
||||||
|
@ -793,8 +788,7 @@ void CmndZbLight(void) {
|
||||||
|
|
||||||
// parse first part, <device_id>
|
// parse first part, <device_id>
|
||||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data, true); // in case of short_addr, it must be already registered
|
||||||
if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
||||||
if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
|
||||||
|
|
||||||
if (p) {
|
if (p) {
|
||||||
int8_t bulbtype = strtol(p, nullptr, 10);
|
int8_t bulbtype = strtol(p, nullptr, 10);
|
||||||
|
@ -817,8 +811,7 @@ void CmndZbLight(void) {
|
||||||
void CmndZbForget(void) {
|
void CmndZbForget(void) {
|
||||||
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
||||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||||
if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
||||||
if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
|
||||||
|
|
||||||
// everything is good, we can send the command
|
// everything is good, we can send the command
|
||||||
if (zigbee_devices.removeDevice(shortaddr)) {
|
if (zigbee_devices.removeDevice(shortaddr)) {
|
||||||
|
@ -906,7 +899,7 @@ void CmndZbRead(void) {
|
||||||
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
if (!json.success()) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; }
|
||||||
|
|
||||||
// params
|
// params
|
||||||
uint16_t device = 0xFFFF; // 0xFFFF is braodcast, so considered valid
|
uint16_t device = BAD_SHORTADDR; // BAD_SHORTADDR is broadcast, so considered invalid
|
||||||
uint16_t groupaddr = 0x0000; // if 0x0000 ignore group adress
|
uint16_t groupaddr = 0x0000; // if 0x0000 ignore group adress
|
||||||
uint16_t cluster = 0x0000; // default to general cluster
|
uint16_t cluster = 0x0000; // default to general cluster
|
||||||
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
uint8_t endpoint = 0x00; // 0x00 is invalid for the dst endpoint
|
||||||
|
@ -917,9 +910,9 @@ void CmndZbRead(void) {
|
||||||
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
const JsonVariant &val_device = getCaseInsensitive(json, PSTR("Device"));
|
||||||
if (nullptr != &val_device) {
|
if (nullptr != &val_device) {
|
||||||
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
device = zigbee_devices.parseDeviceParam(val_device.as<char*>());
|
||||||
if (0xFFFF == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
if (BAD_SHORTADDR == device) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
||||||
}
|
}
|
||||||
if (0x0000 == device) { // if not found, check if we have a group
|
if (BAD_SHORTADDR == device) { // if not found, check if we have a group
|
||||||
const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group"));
|
const JsonVariant &val_group = getCaseInsensitive(json, PSTR("Group"));
|
||||||
if (nullptr != &val_group) {
|
if (nullptr != &val_group) {
|
||||||
groupaddr = strToUInt(val_group);
|
groupaddr = strToUInt(val_group);
|
||||||
|
@ -960,9 +953,9 @@ void CmndZbRead(void) {
|
||||||
|
|
||||||
if ((0 == endpoint) && (device)) { // try to compute the endpoint
|
if ((0 == endpoint) && (device)) { // try to compute the endpoint
|
||||||
endpoint = zigbee_devices.findFirstEndpoint(device);
|
endpoint = zigbee_devices.findFirstEndpoint(device);
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbSend: guessing endpoint 0x%02X"), endpoint);
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("ZbRead: guessing endpoint 0x%02X"), endpoint);
|
||||||
}
|
}
|
||||||
if (0x0000 == device) {
|
if (BAD_SHORTADDR == device) {
|
||||||
endpoint = 0xFF; // endpoint not used for group addresses
|
endpoint = 0xFF; // endpoint not used for group addresses
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1012,9 +1005,8 @@ void CmndZbStatus(void) {
|
||||||
if (ZigbeeSerial) {
|
if (ZigbeeSerial) {
|
||||||
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; }
|
||||||
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
uint16_t shortaddr = zigbee_devices.parseDeviceParam(XdrvMailbox.data);
|
||||||
if (0xFFFF == shortaddr) { ResponseCmndChar_P(PSTR("Invalid parameter")); return; }
|
|
||||||
if (XdrvMailbox.payload > 0) {
|
if (XdrvMailbox.payload > 0) {
|
||||||
if (0x0000 == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
if (BAD_SHORTADDR == shortaddr) { ResponseCmndChar_P(PSTR("Unknown device")); return; }
|
||||||
}
|
}
|
||||||
|
|
||||||
String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr);
|
String dump = zigbee_devices.dump(XdrvMailbox.index, shortaddr);
|
||||||
|
|
|
@ -24,6 +24,10 @@
|
||||||
// Enable/disable debugging
|
// Enable/disable debugging
|
||||||
//#define DEBUG_THERMOSTAT
|
//#define DEBUG_THERMOSTAT
|
||||||
|
|
||||||
|
// Enable/disable experimental PI auto-tuning inspired by the Arduino Autotune Library by
|
||||||
|
// Brett Beauregard <br3ttb@gmail.com> brettbeauregard.com
|
||||||
|
//#define USE_PI_AUTOTUNING // (Ziegler-Nichols closed loop method)
|
||||||
|
|
||||||
#ifdef DEBUG_THERMOSTAT
|
#ifdef DEBUG_THERMOSTAT
|
||||||
#define DOMOTICZ_MAX_IDX 4
|
#define DOMOTICZ_MAX_IDX 4
|
||||||
#define DOMOTICZ_IDX1 791
|
#define DOMOTICZ_IDX1 791
|
||||||
|
@ -56,6 +60,9 @@
|
||||||
#define D_CMND_TIMEPICYCLESET "TimePiCycleSet"
|
#define D_CMND_TIMEPICYCLESET "TimePiCycleSet"
|
||||||
#define D_CMND_TEMPANTIWINDUPRESETSET "TempAntiWindupResetSet"
|
#define D_CMND_TEMPANTIWINDUPRESETSET "TempAntiWindupResetSet"
|
||||||
#define D_CMND_TEMPHYSTSET "TempHystSet"
|
#define D_CMND_TEMPHYSTSET "TempHystSet"
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
#define D_CMND_PERFLEVELAUTOTUNE "PerfLevelAutotune"
|
||||||
|
#endif // USE_PI_AUTOTUNING
|
||||||
#define D_CMND_TIMEMAXACTIONSET "TimeMaxActionSet"
|
#define D_CMND_TIMEMAXACTIONSET "TimeMaxActionSet"
|
||||||
#define D_CMND_TIMEMINACTIONSET "TimeMinActionSet"
|
#define D_CMND_TIMEMINACTIONSET "TimeMinActionSet"
|
||||||
#define D_CMND_TIMEMINTURNOFFACTIONSET "TimeMinTurnoffActionSet"
|
#define D_CMND_TIMEMINTURNOFFACTIONSET "TimeMinTurnoffActionSet"
|
||||||
|
@ -70,8 +77,15 @@
|
||||||
#define D_CMND_DIAGNOSTICMODESET "DiagnosticModeSet"
|
#define D_CMND_DIAGNOSTICMODESET "DiagnosticModeSet"
|
||||||
|
|
||||||
enum ThermostatModes { THERMOSTAT_OFF, THERMOSTAT_AUTOMATIC_OP, THERMOSTAT_MANUAL_OP, THERMOSTAT_MODES_MAX };
|
enum ThermostatModes { THERMOSTAT_OFF, THERMOSTAT_AUTOMATIC_OP, THERMOSTAT_MANUAL_OP, THERMOSTAT_MODES_MAX };
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
enum ControllerModes { CTR_HYBRID, CTR_PI, CTR_RAMP_UP, CTR_PI_AUTOTUNE, CTR_MODES_MAX };
|
||||||
|
enum ControllerHybridPhases { CTR_HYBRID_RAMP_UP, CTR_HYBRID_PI, CTR_HYBRID_PI_AUTOTUNE };
|
||||||
|
enum AutotuneStates { AUTOTUNE_OFF, AUTOTUNE_ON, AUTOTUNE_MAX };
|
||||||
|
enum AutotunePerformanceParam { AUTOTUNE_PERF_FAST, AUTOTUNE_PERF_NORMAL, AUTOTUNE_PERF_SLOW, AUTOTUNE_PERF_MAX };
|
||||||
|
#else
|
||||||
enum ControllerModes { CTR_HYBRID, CTR_PI, CTR_RAMP_UP, CTR_MODES_MAX };
|
enum ControllerModes { CTR_HYBRID, CTR_PI, CTR_RAMP_UP, CTR_MODES_MAX };
|
||||||
enum ControllerHybridPhases { CTR_HYBRID_RAMP_UP, CTR_HYBRID_PI };
|
enum ControllerHybridPhases { CTR_HYBRID_RAMP_UP, CTR_HYBRID_PI };
|
||||||
|
#endif // USE_PI_AUTOTUNING
|
||||||
enum ClimateModes { CLIMATE_HEATING, CLIMATE_COOLING, CLIMATE_MODES_MAX };
|
enum ClimateModes { CLIMATE_HEATING, CLIMATE_COOLING, CLIMATE_MODES_MAX };
|
||||||
enum InterfaceStates { IFACE_OFF, IFACE_ON };
|
enum InterfaceStates { IFACE_OFF, IFACE_ON };
|
||||||
enum InputUsage { INPUT_NOT_USED, INPUT_USED };
|
enum InputUsage { INPUT_NOT_USED, INPUT_USED };
|
||||||
|
@ -113,17 +127,29 @@ typedef union {
|
||||||
uint32_t status_output : 1; // Flag stating state of the output (0 = inactive, 1 = active)
|
uint32_t status_output : 1; // Flag stating state of the output (0 = inactive, 1 = active)
|
||||||
uint32_t status_input : 1; // Flag stating state of the input (0 = inactive, 1 = active)
|
uint32_t status_input : 1; // Flag stating state of the input (0 = inactive, 1 = active)
|
||||||
uint32_t use_input : 1; // Flag stating if the input switch shall be used to switch to manual mode
|
uint32_t use_input : 1; // Flag stating if the input switch shall be used to switch to manual mode
|
||||||
uint32_t phase_hybrid_ctr : 1; // Phase of the hybrid controller (Ramp-up or PI)
|
uint32_t phase_hybrid_ctr : 2; // Phase of the hybrid controller (Ramp-up, PI or Autotune)
|
||||||
uint32_t status_cycle_active : 1; // Status showing if cycle is active (Output ON) or not (Output OFF)
|
uint32_t status_cycle_active : 1; // Status showing if cycle is active (Output ON) or not (Output OFF)
|
||||||
uint32_t state_emergency : 1; // State for thermostat emergency
|
|
||||||
uint32_t counter_seconds : 6; // Second counter used to track minutes
|
uint32_t counter_seconds : 6; // Second counter used to track minutes
|
||||||
uint32_t output_relay_number : 4; // Output relay number
|
uint32_t output_relay_number : 4; // Output relay number
|
||||||
uint32_t input_switch_number : 3; // Input switch number
|
uint32_t input_switch_number : 3; // Input switch number
|
||||||
uint32_t output_inconsist_ctr : 2; // Counter of the minutes where the output state is inconsistent with the command
|
#ifdef USE_PI_AUTOTUNING
|
||||||
uint32_t diagnostic_mode : 1; // Diagnostic mode selected
|
uint32_t autotune_flag : 1; // Enable/disable autotune
|
||||||
uint32_t free : 1; // Free bits in Bitfield
|
uint32_t autotune_perf_mode : 2; // Autotune performance mode
|
||||||
|
uint32_t free : 1; // Free bits
|
||||||
|
#else
|
||||||
|
uint32_t free : 4; // Free bits
|
||||||
|
#endif // USE_PI_AUTOTUNING
|
||||||
};
|
};
|
||||||
} ThermostatBitfield;
|
} ThermostatStateBitfield;
|
||||||
|
|
||||||
|
typedef union {
|
||||||
|
uint8_t data;
|
||||||
|
struct {
|
||||||
|
uint8_t state_emergency : 1; // State for thermostat emergency
|
||||||
|
uint8_t diagnostic_mode : 1; // Diagnostic mode selected
|
||||||
|
uint8_t output_inconsist_ctr : 2; // Counter of the minutes where the output state is inconsistent with the command
|
||||||
|
};
|
||||||
|
} ThermostatDiagBitfield;
|
||||||
|
|
||||||
#ifdef DEBUG_THERMOSTAT
|
#ifdef DEBUG_THERMOSTAT
|
||||||
const char DOMOTICZ_MES[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\"}";
|
const char DOMOTICZ_MES[] PROGMEM = "{\"idx\":%d,\"nvalue\":%d,\"svalue\":\"%s\"}";
|
||||||
|
@ -135,28 +161,36 @@ const char kThermostatCommands[] PROGMEM = "|" D_CMND_THERMOSTATMODESET "|" D_CM
|
||||||
D_CMND_OUTPUTRELAYSET "|" D_CMND_TIMEALLOWRAMPUPSET "|" D_CMND_TEMPFORMATSET "|" D_CMND_TEMPMEASUREDSET "|"
|
D_CMND_OUTPUTRELAYSET "|" D_CMND_TIMEALLOWRAMPUPSET "|" D_CMND_TEMPFORMATSET "|" D_CMND_TEMPMEASUREDSET "|"
|
||||||
D_CMND_TEMPTARGETSET "|" D_CMND_TEMPMEASUREDGRDREAD "|" D_CMND_SENSORINPUTSET "|" D_CMND_STATEEMERGENCYSET "|"
|
D_CMND_TEMPTARGETSET "|" D_CMND_TEMPMEASUREDGRDREAD "|" D_CMND_SENSORINPUTSET "|" D_CMND_STATEEMERGENCYSET "|"
|
||||||
D_CMND_TIMEMANUALTOAUTOSET "|" D_CMND_PROPBANDSET "|" D_CMND_TIMERESETSET "|" D_CMND_TIMEPICYCLESET "|"
|
D_CMND_TIMEMANUALTOAUTOSET "|" D_CMND_PROPBANDSET "|" D_CMND_TIMERESETSET "|" D_CMND_TIMEPICYCLESET "|"
|
||||||
D_CMND_TEMPANTIWINDUPRESETSET "|" D_CMND_TEMPHYSTSET "|" D_CMND_TIMEMAXACTIONSET "|" D_CMND_TIMEMINACTIONSET "|"
|
#ifdef USE_PI_AUTOTUNING
|
||||||
D_CMND_TIMEMINTURNOFFACTIONSET "|" D_CMND_TEMPRUPDELTINSET "|" D_CMND_TEMPRUPDELTOUTSET "|" D_CMND_TIMERAMPUPMAXSET "|"
|
D_CMND_TEMPANTIWINDUPRESETSET "|" D_CMND_TEMPHYSTSET "|" D_CMND_PERFLEVELAUTOTUNE "|" D_CMND_TIMEMAXACTIONSET "|"
|
||||||
D_CMND_TIMERAMPUPCYCLESET "|" D_CMND_TEMPRAMPUPPIACCERRSET "|" D_CMND_TIMEPIPROPORTREAD "|" D_CMND_TIMEPIINTEGRREAD "|"
|
#else
|
||||||
D_CMND_TIMESENSLOSTSET "|" D_CMND_DIAGNOSTICMODESET;
|
D_CMND_TEMPANTIWINDUPRESETSET "|" D_CMND_TEMPHYSTSET "|" D_CMND_TIMEMAXACTIONSET "|"
|
||||||
|
#endif // USE_PI_AUTOTUNING
|
||||||
|
D_CMND_TIMEMINACTIONSET "|" D_CMND_TIMEMINTURNOFFACTIONSET "|" D_CMND_TEMPRUPDELTINSET "|" D_CMND_TEMPRUPDELTOUTSET "|"
|
||||||
|
D_CMND_TIMERAMPUPMAXSET "|" D_CMND_TIMERAMPUPCYCLESET "|" D_CMND_TEMPRAMPUPPIACCERRSET "|" D_CMND_TIMEPIPROPORTREAD "|"
|
||||||
|
D_CMND_TIMEPIINTEGRREAD "|" D_CMND_TIMESENSLOSTSET "|" D_CMND_DIAGNOSTICMODESET;
|
||||||
|
|
||||||
void (* const ThermostatCommand[])(void) PROGMEM = {
|
void (* const ThermostatCommand[])(void) PROGMEM = {
|
||||||
&CmndThermostatModeSet, &CmndClimateModeSet, &CmndTempFrostProtectSet, &CmndControllerModeSet, &CmndInputSwitchSet,
|
&CmndThermostatModeSet, &CmndClimateModeSet, &CmndTempFrostProtectSet, &CmndControllerModeSet, &CmndInputSwitchSet,
|
||||||
&CmndInputSwitchUse, &CmndOutputRelaySet, &CmndTimeAllowRampupSet, &CmndTempFormatSet, &CmndTempMeasuredSet,
|
&CmndInputSwitchUse, &CmndOutputRelaySet, &CmndTimeAllowRampupSet, &CmndTempFormatSet, &CmndTempMeasuredSet,
|
||||||
&CmndTempTargetSet, &CmndTempMeasuredGrdRead, &CmndSensorInputSet, &CmndStateEmergencySet, &CmndTimeManualToAutoSet,
|
&CmndTempTargetSet, &CmndTempMeasuredGrdRead, &CmndSensorInputSet, &CmndStateEmergencySet, &CmndTimeManualToAutoSet,
|
||||||
&CmndPropBandSet, &CmndTimeResetSet, &CmndTimePiCycleSet, &CmndTempAntiWindupResetSet, &CmndTempHystSet,
|
&CmndPropBandSet, &CmndTimeResetSet, &CmndTimePiCycleSet, &CmndTempAntiWindupResetSet, &CmndTempHystSet,
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
&CmndPerfLevelAutotune, &CmndTimeMaxActionSet, &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet,
|
||||||
|
#else
|
||||||
&CmndTimeMaxActionSet, &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet,
|
&CmndTimeMaxActionSet, &CmndTimeMinActionSet, &CmndTimeMinTurnoffActionSet, &CmndTempRupDeltInSet,
|
||||||
|
#endif // USE_PI_AUTOTUNING
|
||||||
&CmndTempRupDeltOutSet, &CmndTimeRampupMaxSet, &CmndTimeRampupCycleSet, &CmndTempRampupPiAccErrSet,
|
&CmndTempRupDeltOutSet, &CmndTimeRampupMaxSet, &CmndTimeRampupCycleSet, &CmndTempRampupPiAccErrSet,
|
||||||
&CmndTimePiProportRead, &CmndTimePiIntegrRead, &CmndTimeSensLostSet, &CmndDiagnosticModeSet };
|
&CmndTimePiProportRead, &CmndTimePiIntegrRead, &CmndTimeSensLostSet, &CmndDiagnosticModeSet };
|
||||||
|
|
||||||
struct THERMOSTAT {
|
struct THERMOSTAT {
|
||||||
ThermostatBitfield status; // Bittfield including states as well as several flags
|
ThermostatStateBitfield status; // Bittfield including states as well as several flags
|
||||||
uint32_t timestamp_temp_measured_update = 0; // Timestamp of latest measurement update
|
uint32_t timestamp_temp_measured_update = 0; // Timestamp of latest measurement update
|
||||||
uint32_t timestamp_temp_meas_change_update = 0; // Timestamp of latest measurement value change (> or < to previous)
|
uint32_t timestamp_temp_meas_change_update = 0; // Timestamp of latest measurement value change (> or < to previous)
|
||||||
uint32_t timestamp_output_off = 0; // Timestamp of latest thermostat output Off state
|
uint32_t timestamp_output_off = 0; // Timestamp of latest thermostat output Off state
|
||||||
uint32_t timestamp_input_on = 0; // Timestamp of latest input On state
|
uint32_t timestamp_input_on = 0; // Timestamp of latest input On state
|
||||||
uint32_t time_thermostat_total = 0; // Time thermostat on within a specific timeframe
|
uint32_t time_thermostat_total = 0; // Time thermostat on within a specific timeframe
|
||||||
uint32_t time_ctr_checkpoint = 0; // Time to finalize the control cycle within the PI strategy or to switch to PI from Rampup
|
uint32_t time_ctr_checkpoint = 0; // Time to finalize the control cycle within the PI strategy or to switch to PI from Rampup in seconds
|
||||||
uint32_t time_ctr_changepoint = 0; // Time until switching off output within the controller in seconds
|
uint32_t time_ctr_changepoint = 0; // Time until switching off output within the controller in seconds
|
||||||
int32_t temp_measured_gradient = 0; // Temperature measured gradient from sensor in thousandths of degrees per hour
|
int32_t temp_measured_gradient = 0; // Temperature measured gradient from sensor in thousandths of degrees per hour
|
||||||
int16_t temp_target_level = THERMOSTAT_TEMP_INIT; // Target level of the thermostat in tenths of degrees
|
int16_t temp_target_level = THERMOSTAT_TEMP_INIT; // Target level of the thermostat in tenths of degrees
|
||||||
|
@ -167,7 +201,7 @@ struct THERMOSTAT {
|
||||||
int32_t time_integral_pi; // Time integral part of the PI controller
|
int32_t time_integral_pi; // Time integral part of the PI controller
|
||||||
int32_t time_total_pi; // Time total (proportional + integral) of the PI controller
|
int32_t time_total_pi; // Time total (proportional + integral) of the PI controller
|
||||||
uint16_t kP_pi = 0; // kP value for the PI controller multiplied by 100 (to avoid floating point operations)
|
uint16_t kP_pi = 0; // kP value for the PI controller multiplied by 100 (to avoid floating point operations)
|
||||||
uint16_t kI_pi = 0; // kP value for the PI controller multiplied by 100 (to avoid floating point operations)
|
uint16_t kI_pi = 0; // kI value for the PI controller multiplied by 100 (to avoid floating point operations)
|
||||||
int32_t temp_rampup_meas_gradient = 0; // Temperature measured gradient from sensor in thousandths of degrees celsius per hour calculated during ramp-up
|
int32_t temp_rampup_meas_gradient = 0; // Temperature measured gradient from sensor in thousandths of degrees celsius per hour calculated during ramp-up
|
||||||
uint32_t timestamp_rampup_start = 0; // Timestamp where the ramp-up controller mode has been started
|
uint32_t timestamp_rampup_start = 0; // Timestamp where the ramp-up controller mode has been started
|
||||||
uint32_t time_rampup_deadtime = 0; // Time constant of the thermostat system (step response time)
|
uint32_t time_rampup_deadtime = 0; // Time constant of the thermostat system (step response time)
|
||||||
|
@ -195,6 +229,23 @@ struct THERMOSTAT {
|
||||||
uint8_t temp_reset_anti_windup = THERMOSTAT_TEMP_RESET_ANTI_WINDUP; // Range where reset antiwindup is disabled, in tenths of degrees celsius
|
uint8_t temp_reset_anti_windup = THERMOSTAT_TEMP_RESET_ANTI_WINDUP; // Range where reset antiwindup is disabled, in tenths of degrees celsius
|
||||||
int8_t temp_hysteresis = THERMOSTAT_TEMP_HYSTERESIS; // Range hysteresis for temperature PI controller, in tenths of degrees celsius
|
int8_t temp_hysteresis = THERMOSTAT_TEMP_HYSTERESIS; // Range hysteresis for temperature PI controller, in tenths of degrees celsius
|
||||||
uint8_t temp_frost_protect = THERMOSTAT_TEMP_FROST_PROTECT; // Minimum temperature for frost protection, in tenths of degrees celsius
|
uint8_t temp_frost_protect = THERMOSTAT_TEMP_FROST_PROTECT; // Minimum temperature for frost protection, in tenths of degrees celsius
|
||||||
|
ThermostatDiagBitfield diag; // Bittfield including diagnostic flags
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
uint8_t dutycycle_step_autotune = THERMOSTAT_DUTYCYCLE_AUTOTUNE; // Duty cycle for the step response of the autotune PI function in %
|
||||||
|
uint8_t peak_ctr = 0; // Peak counter for the autotuning function
|
||||||
|
uint8_t temp_band_no_peak_det = THERMOSTAT_TEMP_BAND_NO_PEAK_DET; // Temperature band in thenths of degrees celsius within no peak will be detected
|
||||||
|
uint8_t val_prop_band_atune = 0; // Proportional band calculated from the the PI autotune function in degrees celsius
|
||||||
|
uint32_t time_reset_atune = 0; // Reset time calculated from the PI autotune function in seconds
|
||||||
|
uint16_t pU_pi_atune = 0; // pU value ("Ultimate" period) period of self-sustaining oscillations determined when the controller gain was set to Ku in minutes (for PI autotune)
|
||||||
|
uint16_t kU_pi_atune = 0; // kU value ("Ultimate" gain) determined by increasing controller gain until self-sustaining oscillations are achieved (for PI autotune)
|
||||||
|
uint16_t kP_pi_atune = 0; // kP value calculated by the autotune PI function multiplied by 100 (to avoid floating point operations)
|
||||||
|
uint16_t kI_pi_atune = 0; // kI value calulated by the autotune PI function multiplied by 100 (to avoid floating point operations)
|
||||||
|
int16_t temp_peaks_atune[THERMOSTAT_PEAKNUMBER_AUTOTUNE]; // Array to store temperature peaks to be used by the autotune PI function
|
||||||
|
int16_t temp_abs_max_atune; // Max temperature reached within autotune
|
||||||
|
int16_t temp_abs_min_atune; // Min temperature reached within autotune
|
||||||
|
uint16_t time_peak_timestamps_atune[THERMOSTAT_PEAKNUMBER_AUTOTUNE]; // Array to store timestamps in minutes of the temperature peaks to be used by the autotune PI function
|
||||||
|
uint16_t time_std_dev_peak_det_ok = THERMOSTAT_TIME_STD_DEV_PEAK_DET_OK; // Standard deviation in minutes of the oscillation periods within the peak detection is successful
|
||||||
|
#endif // USE_PI_AUTOTUNING
|
||||||
} Thermostat[THERMOSTAT_CONTROLLER_OUTPUTS];
|
} Thermostat[THERMOSTAT_CONTROLLER_OUTPUTS];
|
||||||
|
|
||||||
/*********************************************************************************************/
|
/*********************************************************************************************/
|
||||||
|
@ -212,13 +263,17 @@ void ThermostatInit(uint8_t ctr_output)
|
||||||
Thermostat[ctr_output].status.status_output = IFACE_OFF;
|
Thermostat[ctr_output].status.status_output = IFACE_OFF;
|
||||||
Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_PI;
|
Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_PI;
|
||||||
Thermostat[ctr_output].status.status_cycle_active = CYCLE_OFF;
|
Thermostat[ctr_output].status.status_cycle_active = CYCLE_OFF;
|
||||||
Thermostat[ctr_output].status.state_emergency = EMERGENCY_OFF;
|
Thermostat[ctr_output].diag.state_emergency = EMERGENCY_OFF;
|
||||||
Thermostat[ctr_output].status.counter_seconds = 0;
|
Thermostat[ctr_output].status.counter_seconds = 0;
|
||||||
Thermostat[ctr_output].status.output_relay_number = (THERMOSTAT_RELAY_NUMBER + ctr_output);
|
Thermostat[ctr_output].status.output_relay_number = (THERMOSTAT_RELAY_NUMBER + ctr_output);
|
||||||
Thermostat[ctr_output].status.input_switch_number = (THERMOSTAT_SWITCH_NUMBER + ctr_output);
|
Thermostat[ctr_output].status.input_switch_number = (THERMOSTAT_SWITCH_NUMBER + ctr_output);
|
||||||
Thermostat[ctr_output].status.use_input = INPUT_NOT_USED;
|
Thermostat[ctr_output].status.use_input = INPUT_NOT_USED;
|
||||||
Thermostat[ctr_output].status.output_inconsist_ctr = 0;
|
Thermostat[ctr_output].diag.output_inconsist_ctr = 0;
|
||||||
Thermostat[ctr_output].status.diagnostic_mode = DIAGNOSTIC_ON;
|
Thermostat[ctr_output].diag.diagnostic_mode = DIAGNOSTIC_ON;
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
Thermostat[ctr_output].status.autotune_flag = AUTOTUNE_OFF;
|
||||||
|
Thermostat[ctr_output].status.autotune_perf_mode = AUTOTUNE_PERF_FAST;
|
||||||
|
#endif // USE_PI_AUTOTUNING
|
||||||
// Make sure the Output is OFF
|
// Make sure the Output is OFF
|
||||||
ExecuteCommandPower(Thermostat[ctr_output].status.output_relay_number, POWER_OFF, SRC_THERMOSTAT);
|
ExecuteCommandPower(Thermostat[ctr_output].status.output_relay_number, POWER_OFF, SRC_THERMOSTAT);
|
||||||
}
|
}
|
||||||
|
@ -312,10 +367,10 @@ void ThermostatSignalPostProcessingSlow(uint8_t ctr_output)
|
||||||
{
|
{
|
||||||
// Increate counter when inconsistent output state exists
|
// Increate counter when inconsistent output state exists
|
||||||
if (Thermostat[ctr_output].status.status_output != Thermostat[ctr_output].status.command_output) {
|
if (Thermostat[ctr_output].status.status_output != Thermostat[ctr_output].status.command_output) {
|
||||||
Thermostat[ctr_output].status.output_inconsist_ctr++;
|
Thermostat[ctr_output].diag.output_inconsist_ctr++;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Thermostat[ctr_output].status.output_inconsist_ctr = 0;
|
Thermostat[ctr_output].diag.output_inconsist_ctr = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,6 +388,10 @@ void ThermostatSignalProcessingFast(uint8_t ctr_output)
|
||||||
|
|
||||||
void ThermostatCtrState(uint8_t ctr_output)
|
void ThermostatCtrState(uint8_t ctr_output)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING);
|
||||||
|
#endif //USE_PI_AUTOTUNING
|
||||||
|
|
||||||
switch (Thermostat[ctr_output].status.controller_mode) {
|
switch (Thermostat[ctr_output].status.controller_mode) {
|
||||||
// Hybrid controller (Ramp-up + PI)
|
// Hybrid controller (Ramp-up + PI)
|
||||||
case CTR_HYBRID:
|
case CTR_HYBRID:
|
||||||
|
@ -340,10 +399,35 @@ void ThermostatCtrState(uint8_t ctr_output)
|
||||||
break;
|
break;
|
||||||
// PI controller
|
// PI controller
|
||||||
case CTR_PI:
|
case CTR_PI:
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
// If Autotune has been enabled (via flag)
|
||||||
|
// AND we have just reached the setpoint temperature
|
||||||
|
// AND the temperature gradient is negative for heating and positive for cooling
|
||||||
|
// then switch state to PI autotuning
|
||||||
|
if ((Thermostat[ctr_output].status.autotune_flag == AUTOTUNE_ON)
|
||||||
|
&&(Thermostat[ctr_output].temp_measured == Thermostat[ctr_output].temp_target_level)
|
||||||
|
&& ((flag_heating && (Thermostat[ctr_output].temp_measured_gradient < 0))
|
||||||
|
||(!flag_heating && (Thermostat[ctr_output].temp_measured_gradient > 0))))
|
||||||
|
{
|
||||||
|
Thermostat[ctr_output].status.controller_mode = CTR_PI_AUTOTUNE;
|
||||||
|
ThermostatPeakDetectorInit(ctr_output);
|
||||||
|
}
|
||||||
|
#endif // USE_PI_AUTOTUNING
|
||||||
break;
|
break;
|
||||||
// Ramp-up controller (predictive)
|
// Ramp-up controller (predictive)
|
||||||
case CTR_RAMP_UP:
|
case CTR_RAMP_UP:
|
||||||
break;
|
break;
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
// PI autotune
|
||||||
|
case CTR_PI_AUTOTUNE:
|
||||||
|
// If autotune finalized (flag Off)
|
||||||
|
// then go back to the PI controller
|
||||||
|
if (Thermostat[ctr_output].status.autotune_flag == AUTOTUNE_OFF)
|
||||||
|
{
|
||||||
|
Thermostat[ctr_output].status.controller_mode = CTR_PI;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif //USE_PI_AUTOTUNING
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -387,7 +471,32 @@ void ThermostatHybridCtrPhase(uint8_t ctr_output)
|
||||||
Thermostat[ctr_output].time_ctr_checkpoint = 0;
|
Thermostat[ctr_output].time_ctr_checkpoint = 0;
|
||||||
Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_RAMP_UP;
|
Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_RAMP_UP;
|
||||||
}
|
}
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
// If Autotune has been enabled (via flag)
|
||||||
|
// AND we have just reached the setpoint temperature
|
||||||
|
// AND the temperature gradient is negative for heating and positive for cooling
|
||||||
|
// then switch state to PI autotuning
|
||||||
|
if ((Thermostat[ctr_output].status.autotune_flag == AUTOTUNE_ON)
|
||||||
|
&&(Thermostat[ctr_output].temp_measured == Thermostat[ctr_output].temp_target_level)
|
||||||
|
&& ((flag_heating && (Thermostat[ctr_output].temp_measured_gradient < 0))
|
||||||
|
||(!flag_heating && (Thermostat[ctr_output].temp_measured_gradient > 0))))
|
||||||
|
{
|
||||||
|
Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_PI_AUTOTUNE;
|
||||||
|
ThermostatPeakDetectorInit(ctr_output);
|
||||||
|
}
|
||||||
|
#endif // USE_PI_AUTOTUNING
|
||||||
break;
|
break;
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
// PI autotune controller phase
|
||||||
|
case CTR_HYBRID_PI_AUTOTUNE:
|
||||||
|
// If autotune finalized (flag Off)
|
||||||
|
// then go back to the PI controller
|
||||||
|
if (Thermostat[ctr_output].status.autotune_flag == AUTOTUNE_OFF)
|
||||||
|
{
|
||||||
|
Thermostat[ctr_output].status.phase_hybrid_ctr = CTR_HYBRID_PI;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif // USE_PI_AUTOTUNING
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_THERMOSTAT
|
#ifdef DEBUG_THERMOSTAT
|
||||||
|
@ -830,6 +939,198 @@ void ThermostatWorkAutomaticRampUp(uint8_t ctr_output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
|
||||||
|
void ThermostatPeakDetectorInit(uint8_t ctr_output)
|
||||||
|
{
|
||||||
|
for (uint8_t i = 0; i < THERMOSTAT_PEAKNUMBER_AUTOTUNE; i++) {
|
||||||
|
Thermostat[ctr_output].temp_peaks_atune[i] = 0;
|
||||||
|
}
|
||||||
|
Thermostat[ctr_output].pU_pi_atune = 0;
|
||||||
|
Thermostat[ctr_output].kP_pi_atune = 0;
|
||||||
|
Thermostat[ctr_output].kI_pi_atune = 0;
|
||||||
|
Thermostat[ctr_output].kU_pi_atune = 0;
|
||||||
|
Thermostat[ctr_output].peak_ctr = 0;
|
||||||
|
Thermostat[ctr_output].temp_abs_max_atune = 0;
|
||||||
|
Thermostat[ctr_output].temp_abs_min_atune = 100;
|
||||||
|
Thermostat[ctr_output].time_ctr_checkpoint = uptime + THERMOSTAT_TIME_MAX_AUTOTUNE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThermostatPeakDetector(uint8_t ctr_output)
|
||||||
|
{
|
||||||
|
uint8_t peak_num = Thermostat[ctr_output].peak_ctr;
|
||||||
|
int16_t peak_avg = 0;
|
||||||
|
bool peak_transition = false;
|
||||||
|
// Update Max/Min Thermostat[ctr_output].temp_abs_max_atune
|
||||||
|
if (Thermostat[ctr_output].temp_measured > Thermostat[ctr_output].temp_abs_max_atune) {
|
||||||
|
Thermostat[ctr_output].temp_abs_max_atune = Thermostat[ctr_output].temp_measured;
|
||||||
|
}
|
||||||
|
if (Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_abs_min_atune) {
|
||||||
|
Thermostat[ctr_output].temp_abs_min_atune = Thermostat[ctr_output].temp_measured;
|
||||||
|
}
|
||||||
|
// For heating, even peak numbers look for maxes, odd for minds, the contrary for cooling
|
||||||
|
// If we did not found all peaks yet
|
||||||
|
if (peak_num < THERMOSTAT_PEAKNUMBER_AUTOTUNE) {
|
||||||
|
bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING);
|
||||||
|
bool cond_peak_1 = ( (Thermostat[ctr_output].temp_measured > Thermostat[ctr_output].temp_peaks_atune[peak_num])
|
||||||
|
&& (flag_heating)
|
||||||
|
|| (Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_peaks_atune[peak_num])
|
||||||
|
&& (!flag_heating));
|
||||||
|
bool cond_peak_2 = ( (Thermostat[ctr_output].temp_measured < Thermostat[ctr_output].temp_peaks_atune[peak_num])
|
||||||
|
&& (flag_heating)
|
||||||
|
|| (Thermostat[ctr_output].temp_measured > Thermostat[ctr_output].temp_peaks_atune[peak_num])
|
||||||
|
&& (!flag_heating));
|
||||||
|
bool cond_gradient_1 = ( (Thermostat[ctr_output].temp_measured_gradient > 0)
|
||||||
|
&& (flag_heating)
|
||||||
|
|| (Thermostat[ctr_output].temp_measured_gradient < 0)
|
||||||
|
&& (!flag_heating));
|
||||||
|
bool cond_gradient_2 = ( (Thermostat[ctr_output].temp_measured_gradient < 0)
|
||||||
|
&& (flag_heating)
|
||||||
|
|| (Thermostat[ctr_output].temp_measured_gradient > 0)
|
||||||
|
&& (!flag_heating));
|
||||||
|
// If peak number is even (look for max if heating and min if cooling)
|
||||||
|
if ((peak_num % 2) == 0) {
|
||||||
|
// If current temperature higher (heating) or lower (cooling) than registered value for peak
|
||||||
|
// AND temperature gradient > 0 for heating or < 0 for cooling
|
||||||
|
// then, update value
|
||||||
|
if (cond_peak_1 && cond_gradient_1) {
|
||||||
|
Thermostat[ctr_output].temp_peaks_atune[peak_num] = Thermostat[ctr_output].temp_measured;
|
||||||
|
}
|
||||||
|
// Else if current temperature lower (heating) or higher (cooling) then registered value for peak
|
||||||
|
// AND difference to peak is outside of the peak no detection band
|
||||||
|
// then the current peak value is the peak (max for heating, min for cooling), switch detection
|
||||||
|
if ( (cond_peak_2)
|
||||||
|
&& (abs(Thermostat[ctr_output].temp_measured - Thermostat[ctr_output].temp_peaks_atune[peak_num]) > Thermostat[ctr_output].temp_band_no_peak_det)) {
|
||||||
|
// Register peak timestamp;
|
||||||
|
Thermostat[ctr_output].time_peak_timestamps_atune[peak_num] = (uptime / 60);
|
||||||
|
Thermostat[ctr_output].peak_ctr++;
|
||||||
|
peak_transition = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Peak number is odd (look for min if heating and max if cooling)
|
||||||
|
else {
|
||||||
|
// If current temperature lower (heating) or higher (cooling) than registered value for peak
|
||||||
|
// AND temperature gradient < 0 for heating or > 0 for cooling
|
||||||
|
// then, update value
|
||||||
|
if (cond_peak_2 && cond_gradient_2) {
|
||||||
|
Thermostat[ctr_output].temp_peaks_atune[peak_num] = Thermostat[ctr_output].temp_measured;
|
||||||
|
}
|
||||||
|
// Else if current temperature higher (heating) or lower (cooling) then registered value for peak
|
||||||
|
// AND difference to peak is outside of the peak no detection band
|
||||||
|
// then the current peak value is the peak (min for heating, max for cooling), switch detection
|
||||||
|
if ( (cond_peak_1)
|
||||||
|
&& (abs(Thermostat[ctr_output].temp_measured - Thermostat[ctr_output].temp_peaks_atune[peak_num]) > Thermostat[ctr_output].temp_band_no_peak_det)) {
|
||||||
|
// Calculate period
|
||||||
|
// Register peak timestamp;
|
||||||
|
Thermostat[ctr_output].time_peak_timestamps_atune[peak_num] = (uptime / 60);
|
||||||
|
Thermostat[ctr_output].peak_ctr++;
|
||||||
|
peak_transition = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Peak detection done, proceed to evaluate results
|
||||||
|
ThermostatAutotuneParamCalc(ctr_output);
|
||||||
|
Thermostat[ctr_output].status.autotune_flag = AUTOTUNE_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If peak detection not finalized but bigger than 3 and we have just found a peak, check if results can be extracted
|
||||||
|
if ((Thermostat[ctr_output].peak_ctr > 2) && (peak_transition)) {
|
||||||
|
//Update peak_num
|
||||||
|
peak_num = Thermostat[ctr_output].peak_ctr;
|
||||||
|
// Calculate average value among the last 3 peaks
|
||||||
|
peak_avg = (abs(Thermostat[ctr_output].temp_peaks_atune[peak_num - 1]
|
||||||
|
- Thermostat[ctr_output].temp_peaks_atune[peak_num - 2])
|
||||||
|
+ abs(Thermostat[ctr_output].temp_peaks_atune[peak_num - 2]
|
||||||
|
- Thermostat[ctr_output].temp_peaks_atune[peak_num - 3])) / 2;
|
||||||
|
|
||||||
|
if ((20 * (int32_t)peak_avg) < (int32_t)(Thermostat[ctr_output].temp_abs_max_atune - Thermostat[ctr_output].temp_abs_min_atune)) {
|
||||||
|
// Calculate average temperature among all peaks
|
||||||
|
for (uint8_t i = 0; i < peak_num; i++) {
|
||||||
|
peak_avg += Thermostat[ctr_output].temp_peaks_atune[i];
|
||||||
|
}
|
||||||
|
peak_avg /= peak_num;
|
||||||
|
// If last period crosses the average value, result valid
|
||||||
|
if (10 * abs(Thermostat[ctr_output].temp_peaks_atune[peak_num - 1] - Thermostat[ctr_output].temp_peaks_atune[peak_num - 2]) < (Thermostat[ctr_output].temp_abs_max_atune - peak_avg)) {
|
||||||
|
// Peak detection done, proceed to evaluate results
|
||||||
|
ThermostatAutotuneParamCalc(ctr_output);
|
||||||
|
Thermostat[ctr_output].status.autotune_flag = AUTOTUNE_OFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
peak_transition = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThermostatAutotuneParamCalc(uint8_t ctr_output)
|
||||||
|
{
|
||||||
|
uint8_t peak_num = Thermostat[ctr_output].peak_ctr;
|
||||||
|
|
||||||
|
// Calculate the tunning parameters
|
||||||
|
// Resolution increased to avoid float operations
|
||||||
|
Thermostat[ctr_output].kU_pi_atune = (uint16_t)(100 * ((uint32_t)400000 * (uint32_t)(Thermostat[ctr_output].dutycycle_step_autotune)) / ((uint32_t)(Thermostat[ctr_output].temp_abs_max_atune - Thermostat[ctr_output].temp_abs_min_atune) * (uint32_t)314159));
|
||||||
|
Thermostat[ctr_output].pU_pi_atune = (Thermostat[ctr_output].time_peak_timestamps_atune[peak_num - 1] - Thermostat[ctr_output].time_peak_timestamps_atune[peak_num - 2]);
|
||||||
|
|
||||||
|
switch (Thermostat[ctr_output].status.autotune_perf_mode) {
|
||||||
|
case AUTOTUNE_PERF_FAST:
|
||||||
|
// Calculate kP/Ki autotune
|
||||||
|
Thermostat[ctr_output].kP_pi_atune = (4 * Thermostat[ctr_output].kU_pi_atune) / 10;
|
||||||
|
break;
|
||||||
|
case AUTOTUNE_PERF_NORMAL:
|
||||||
|
// Calculate kP/Ki autotune
|
||||||
|
Thermostat[ctr_output].kP_pi_atune = (18 * Thermostat[ctr_output].kU_pi_atune) / 100;
|
||||||
|
break;
|
||||||
|
case AUTOTUNE_PERF_SLOW:
|
||||||
|
// Calculate kP/Ki autotune
|
||||||
|
Thermostat[ctr_output].kP_pi_atune = (13 * Thermostat[ctr_output].kU_pi_atune) / 100;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolution increased to avoid float operations
|
||||||
|
Thermostat[ctr_output].kI_pi_atune = (12 * (6000 * Thermostat[ctr_output].kU_pi_atune / Thermostat[ctr_output].pU_pi_atune)) / 10;
|
||||||
|
|
||||||
|
// Calculate PropBand Autotune
|
||||||
|
Thermostat[ctr_output].val_prop_band_atune = 100 / Thermostat[ctr_output].kP_pi_atune;
|
||||||
|
// Calculate Reset Time Autotune
|
||||||
|
Thermostat[ctr_output].time_reset_atune = (uint32_t)((((uint32_t)Thermostat[ctr_output].kP_pi_atune * (uint32_t)Thermostat[ctr_output].time_pi_cycle * 6000)) / (uint32_t)Thermostat[ctr_output].kI_pi_atune);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ThermostatWorkAutomaticPIAutotune(uint8_t ctr_output)
|
||||||
|
{
|
||||||
|
bool flag_heating = (Thermostat[ctr_output].status.climate_mode == CLIMATE_HEATING);
|
||||||
|
// If no timeout of the PI Autotune function
|
||||||
|
// AND no change in setpoint
|
||||||
|
if ((uptime < Thermostat[ctr_output].time_ctr_checkpoint)
|
||||||
|
&&(Thermostat[ctr_output].temp_target_level_ctr == Thermostat[ctr_output].temp_target_level)) {
|
||||||
|
if (uptime >= Thermostat[ctr_output].time_ctr_checkpoint) {
|
||||||
|
Thermostat[ctr_output].temp_target_level_ctr = Thermostat[ctr_output].temp_target_level;
|
||||||
|
// Calculate time_ctr_changepoint
|
||||||
|
Thermostat[ctr_output].time_ctr_changepoint = uptime + (((uint32_t)Thermostat[ctr_output].time_pi_cycle * (uint32_t)Thermostat[ctr_output].dutycycle_step_autotune) / (uint32_t)100);
|
||||||
|
// Reset cycle active
|
||||||
|
Thermostat[ctr_output].status.status_cycle_active = CYCLE_OFF;
|
||||||
|
}
|
||||||
|
// Set Output On/Off depending on the changepoint
|
||||||
|
if (uptime < Thermostat[ctr_output].time_ctr_changepoint) {
|
||||||
|
Thermostat[ctr_output].status.status_cycle_active = CYCLE_ON;
|
||||||
|
Thermostat[ctr_output].status.command_output = IFACE_ON;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Thermostat[ctr_output].status.command_output = IFACE_OFF;
|
||||||
|
}
|
||||||
|
// Update peak values
|
||||||
|
ThermostatPeakDetector(ctr_output);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Disable Autotune flag
|
||||||
|
Thermostat[ctr_output].status.autotune_flag = AUTOTUNE_OFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Thermostat[ctr_output].status.autotune_flag == AUTOTUNE_OFF) {
|
||||||
|
// Set output Off
|
||||||
|
Thermostat[ctr_output].status.command_output = IFACE_OFF;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif //USE_PI_AUTOTUNING
|
||||||
|
|
||||||
void ThermostatCtrWork(uint8_t ctr_output)
|
void ThermostatCtrWork(uint8_t ctr_output)
|
||||||
{
|
{
|
||||||
switch (Thermostat[ctr_output].status.controller_mode) {
|
switch (Thermostat[ctr_output].status.controller_mode) {
|
||||||
|
@ -842,6 +1143,12 @@ void ThermostatCtrWork(uint8_t ctr_output)
|
||||||
case CTR_HYBRID_PI:
|
case CTR_HYBRID_PI:
|
||||||
ThermostatWorkAutomaticPI(ctr_output);
|
ThermostatWorkAutomaticPI(ctr_output);
|
||||||
break;
|
break;
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
// PI autotune
|
||||||
|
case CTR_HYBRID_PI_AUTOTUNE:
|
||||||
|
ThermostatWorkAutomaticPIAutotune(ctr_output);
|
||||||
|
break;
|
||||||
|
#endif //USE_PI_AUTOTUNING
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// PI controller
|
// PI controller
|
||||||
|
@ -852,6 +1159,12 @@ void ThermostatCtrWork(uint8_t ctr_output)
|
||||||
case CTR_RAMP_UP:
|
case CTR_RAMP_UP:
|
||||||
ThermostatWorkAutomaticRampUp(ctr_output);
|
ThermostatWorkAutomaticRampUp(ctr_output);
|
||||||
break;
|
break;
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
// PI autotune
|
||||||
|
case CTR_PI_AUTOTUNE:
|
||||||
|
ThermostatWorkAutomaticPIAutotune(ctr_output);
|
||||||
|
break;
|
||||||
|
#endif //USE_PI_AUTOTUNING
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -879,17 +1192,17 @@ void ThermostatWork(uint8_t ctr_output)
|
||||||
void ThermostatDiagnostics(uint8_t ctr_output)
|
void ThermostatDiagnostics(uint8_t ctr_output)
|
||||||
{
|
{
|
||||||
// Diagnostic related to the plausibility of the output state
|
// Diagnostic related to the plausibility of the output state
|
||||||
if ((Thermostat[ctr_output].status.diagnostic_mode == DIAGNOSTIC_ON)
|
if ((Thermostat[ctr_output].diag.diagnostic_mode == DIAGNOSTIC_ON)
|
||||||
&&(Thermostat[ctr_output].status.output_inconsist_ctr >= THERMOSTAT_TIME_MAX_OUTPUT_INCONSIST)) {
|
&&(Thermostat[ctr_output].diag.output_inconsist_ctr >= THERMOSTAT_TIME_MAX_OUTPUT_INCONSIST)) {
|
||||||
Thermostat[ctr_output].status.thermostat_mode = THERMOSTAT_OFF;
|
Thermostat[ctr_output].status.thermostat_mode = THERMOSTAT_OFF;
|
||||||
Thermostat[ctr_output].status.state_emergency = EMERGENCY_ON;
|
Thermostat[ctr_output].diag.state_emergency = EMERGENCY_ON;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diagnostic related to the plausibility of the output power implemented
|
// Diagnostic related to the plausibility of the output power implemented
|
||||||
// already into the energy driver
|
// already into the energy driver
|
||||||
|
|
||||||
// If diagnostics fail, emergency enabled and thermostat shutdown triggered
|
// If diagnostics fail, emergency enabled and thermostat shutdown triggered
|
||||||
if (Thermostat[ctr_output].status.state_emergency == EMERGENCY_ON) {
|
if (Thermostat[ctr_output].diag.state_emergency == EMERGENCY_ON) {
|
||||||
ThermostatEmergencyShutdown(ctr_output);
|
ThermostatEmergencyShutdown(ctr_output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -947,10 +1260,10 @@ void ThermostatDebug(uint8_t ctr_output)
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.counter_seconds: %s"), result_chr);
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.counter_seconds: %s"), result_chr);
|
||||||
dtostrfd(Thermostat[ctr_output].status.thermostat_mode, 0, result_chr);
|
dtostrfd(Thermostat[ctr_output].status.thermostat_mode, 0, result_chr);
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.thermostat_mode: %s"), result_chr);
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.thermostat_mode: %s"), result_chr);
|
||||||
dtostrfd(Thermostat[ctr_output].status.state_emergency, 0, result_chr);
|
dtostrfd(Thermostat[ctr_output].diag.state_emergency, 0, result_chr);
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.state_emergency: %s"), result_chr);
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].diag.state_emergency: %s"), result_chr);
|
||||||
dtostrfd(Thermostat[ctr_output].status.output_inconsist_ctr, 0, result_chr);
|
dtostrfd(Thermostat[ctr_output].diag.output_inconsist_ctr, 0, result_chr);
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.output_inconsist_ctr: %s"), result_chr);
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].diag.output_inconsist_ctr: %s"), result_chr);
|
||||||
dtostrfd(Thermostat[ctr_output].status.controller_mode, 0, result_chr);
|
dtostrfd(Thermostat[ctr_output].status.controller_mode, 0, result_chr);
|
||||||
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.controller_mode: %s"), result_chr);
|
AddLog_P2(LOG_LEVEL_DEBUG, PSTR("Thermostat[ctr_output].status.controller_mode: %s"), result_chr);
|
||||||
dtostrfd(Thermostat[ctr_output].status.command_output, 0, result_chr);
|
dtostrfd(Thermostat[ctr_output].status.command_output, 0, result_chr);
|
||||||
|
@ -1049,6 +1362,8 @@ void CmndClimateModeSet(void)
|
||||||
uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data));
|
uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data));
|
||||||
if ((value >= CLIMATE_HEATING) && (value < CLIMATE_MODES_MAX)) {
|
if ((value >= CLIMATE_HEATING) && (value < CLIMATE_MODES_MAX)) {
|
||||||
Thermostat[ctr_output].status.climate_mode = value;
|
Thermostat[ctr_output].status.climate_mode = value;
|
||||||
|
// Trigger a restart of the controller
|
||||||
|
Thermostat[ctr_output].time_ctr_checkpoint = uptime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ResponseCmndNumber((int)Thermostat[ctr_output].status.climate_mode);
|
ResponseCmndNumber((int)Thermostat[ctr_output].status.climate_mode);
|
||||||
|
@ -1090,6 +1405,14 @@ void CmndControllerModeSet(void)
|
||||||
uint8_t value = (uint8_t)(XdrvMailbox.payload);
|
uint8_t value = (uint8_t)(XdrvMailbox.payload);
|
||||||
if ((value >= CTR_HYBRID) && (value < CTR_MODES_MAX)) {
|
if ((value >= CTR_HYBRID) && (value < CTR_MODES_MAX)) {
|
||||||
Thermostat[ctr_output].status.controller_mode = value;
|
Thermostat[ctr_output].status.controller_mode = value;
|
||||||
|
// Reset controller variables
|
||||||
|
Thermostat[ctr_output].timestamp_rampup_start = uptime;
|
||||||
|
Thermostat[ctr_output].temp_rampup_start = Thermostat[ctr_output].temp_measured;
|
||||||
|
Thermostat[ctr_output].temp_rampup_meas_gradient = 0;
|
||||||
|
Thermostat[ctr_output].time_rampup_deadtime = 0;
|
||||||
|
Thermostat[ctr_output].counter_rampup_cycles = 1;
|
||||||
|
Thermostat[ctr_output].time_ctr_changepoint = 0;
|
||||||
|
Thermostat[ctr_output].time_ctr_checkpoint = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ResponseCmndNumber((int)Thermostat[ctr_output].status.controller_mode);
|
ResponseCmndNumber((int)Thermostat[ctr_output].status.controller_mode);
|
||||||
|
@ -1156,11 +1479,11 @@ void CmndTimeAllowRampupSet(void)
|
||||||
uint8_t ctr_output = XdrvMailbox.index - 1;
|
uint8_t ctr_output = XdrvMailbox.index - 1;
|
||||||
if (XdrvMailbox.data_len > 0) {
|
if (XdrvMailbox.data_len > 0) {
|
||||||
uint32_t value = (uint32_t)(XdrvMailbox.payload);
|
uint32_t value = (uint32_t)(XdrvMailbox.payload);
|
||||||
if ((value >= 0) && (value < 86400)) {
|
if ((value >= 0) && (value < 1440)) {
|
||||||
Thermostat[ctr_output].time_allow_rampup = (uint16_t)(value / 60);
|
Thermostat[ctr_output].time_allow_rampup = (uint16_t)value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_allow_rampup * 60));
|
ResponseCmndNumber((int)((uint32_t)Thermostat[ctr_output].time_allow_rampup));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1266,10 +1589,10 @@ void CmndStateEmergencySet(void)
|
||||||
if (XdrvMailbox.data_len > 0) {
|
if (XdrvMailbox.data_len > 0) {
|
||||||
uint8_t value = (uint8_t)(XdrvMailbox.payload);
|
uint8_t value = (uint8_t)(XdrvMailbox.payload);
|
||||||
if ((value >= 0) && (value <= 1)) {
|
if ((value >= 0) && (value <= 1)) {
|
||||||
Thermostat[ctr_output].status.state_emergency = (uint16_t)value;
|
Thermostat[ctr_output].diag.state_emergency = (uint16_t)value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ResponseCmndNumber((int)Thermostat[ctr_output].status.state_emergency);
|
ResponseCmndNumber((int)Thermostat[ctr_output].diag.state_emergency);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1399,6 +1722,22 @@ void CmndTempHystSet(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_PI_AUTOTUNING
|
||||||
|
void CmndPerfLevelAutotune(void)
|
||||||
|
{
|
||||||
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) {
|
||||||
|
uint8_t ctr_output = XdrvMailbox.index - 1;
|
||||||
|
if (XdrvMailbox.data_len > 0) {
|
||||||
|
uint8_t value = (uint8_t)(XdrvMailbox.payload);
|
||||||
|
if ((value >= 0) && (value <= AUTOTUNE_PERF_MAX)) {
|
||||||
|
Thermostat[ctr_output].status.autotune_perf_mode = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ResponseCmndNumber((int)Thermostat[ctr_output].status.autotune_perf_mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // USE_PI_AUTOTUNING
|
||||||
|
|
||||||
void CmndTimeMaxActionSet(void)
|
void CmndTimeMaxActionSet(void)
|
||||||
{
|
{
|
||||||
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) {
|
if ((XdrvMailbox.index > 0) && (XdrvMailbox.index <= THERMOSTAT_CONTROLLER_OUTPUTS)) {
|
||||||
|
@ -1571,10 +1910,10 @@ void CmndDiagnosticModeSet(void)
|
||||||
if (XdrvMailbox.data_len > 0) {
|
if (XdrvMailbox.data_len > 0) {
|
||||||
uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data));
|
uint8_t value = (uint8_t)(CharToFloat(XdrvMailbox.data));
|
||||||
if ((value >= DIAGNOSTIC_OFF) && (value <= DIAGNOSTIC_ON)) {
|
if ((value >= DIAGNOSTIC_OFF) && (value <= DIAGNOSTIC_ON)) {
|
||||||
Thermostat[ctr_output].status.diagnostic_mode = value;
|
Thermostat[ctr_output].diag.diagnostic_mode = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ResponseCmndNumber((int)Thermostat[ctr_output].status.diagnostic_mode);
|
ResponseCmndNumber((int)Thermostat[ctr_output].diag.diagnostic_mode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -243,10 +243,10 @@ void CseSnsInit(void)
|
||||||
|
|
||||||
void CseDrvInit(void)
|
void CseDrvInit(void)
|
||||||
{
|
{
|
||||||
Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE));
|
// if (PinUsed(GPIO_CSE7766_RX) && PinUsed(GPIO_CSE7766_TX)) {
|
||||||
if (Cse.rx_buffer != nullptr) {
|
if (PinUsed(GPIO_CSE7766_RX)) {
|
||||||
// if (PinUsed(GPIO_CSE7766_RX) && PinUsed(GPIO_CSE7766_TX)) {
|
Cse.rx_buffer = (uint8_t*)(malloc(CSE_BUFFER_SIZE));
|
||||||
if (PinUsed(GPIO_CSE7766_RX)) {
|
if (Cse.rx_buffer != nullptr) {
|
||||||
energy_flg = XNRG_02;
|
energy_flg = XNRG_02;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -306,6 +306,9 @@ void MCP230xx_CheckForInterrupt(void) {
|
||||||
ResponseTime_P(PSTR(",\"MCP230XX_INT\":{\"D%i\":%i,\"MS\":%lu}}"),
|
ResponseTime_P(PSTR(",\"MCP230XX_INT\":{\"D%i\":%i,\"MS\":%lu}}"),
|
||||||
intp+(mcp230xx_port*8), ((mcp230xx_intcap >> intp) & 0x01),millis_since_last_int);
|
intp+(mcp230xx_port*8), ((mcp230xx_intcap >> intp) & 0x01),millis_since_last_int);
|
||||||
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("MCP230XX_INT"));
|
MqttPublishPrefixTopic_P(RESULT_OR_STAT, PSTR("MCP230XX_INT"));
|
||||||
|
if (Settings.flag3.hass_tele_on_power) { // SetOption59 - Send tele/%topic%/SENSOR in addition to stat/%topic%/RESULT
|
||||||
|
MqttPublishSensor();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (int_event) {
|
if (int_event) {
|
||||||
char command[19]; // Theoretical max = 'event MCPINT_D16=1' so 18 + 1 (for the \n)
|
char command[19]; // Theoretical max = 'event MCPINT_D16=1' so 18 + 1 (for the \n)
|
||||||
|
|
|
@ -45,7 +45,9 @@
|
||||||
#include <TasmotaSerial.h>
|
#include <TasmotaSerial.h>
|
||||||
|
|
||||||
// use special no wait serial driver, should be always on
|
// use special no wait serial driver, should be always on
|
||||||
|
#ifndef ESP32
|
||||||
#define SPECIAL_SS
|
#define SPECIAL_SS
|
||||||
|
#endif
|
||||||
|
|
||||||
// addresses a bug in meter DWS74
|
// addresses a bug in meter DWS74
|
||||||
//#define DWS74_BUG
|
//#define DWS74_BUG
|
||||||
|
@ -440,7 +442,9 @@ const uint8_t meter[]=
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// max number of meters , may be adjusted
|
// max number of meters , may be adjusted
|
||||||
|
#ifndef MAX_METERS
|
||||||
#define MAX_METERS 5
|
#define MAX_METERS 5
|
||||||
|
#endif
|
||||||
double meter_vars[SML_MAX_VARS];
|
double meter_vars[SML_MAX_VARS];
|
||||||
// calulate deltas
|
// calulate deltas
|
||||||
#define MAX_DVARS MAX_METERS*2
|
#define MAX_DVARS MAX_METERS*2
|
||||||
|
@ -453,7 +457,11 @@ const uint8_t *meter_p;
|
||||||
uint8_t meter_spos[MAX_METERS];
|
uint8_t meter_spos[MAX_METERS];
|
||||||
|
|
||||||
// software serial pointers
|
// software serial pointers
|
||||||
|
#ifdef ESP32
|
||||||
|
HardwareSerial *meter_ss[MAX_METERS];
|
||||||
|
#else
|
||||||
TasmotaSerial *meter_ss[MAX_METERS];
|
TasmotaSerial *meter_ss[MAX_METERS];
|
||||||
|
#endif
|
||||||
|
|
||||||
// serial buffers, may be made larger depending on telegram lenght
|
// serial buffers, may be made larger depending on telegram lenght
|
||||||
#define SML_BSIZ 48
|
#define SML_BSIZ 48
|
||||||
|
@ -774,18 +782,21 @@ uint8_t dump2log=0;
|
||||||
bool Serial_available() {
|
bool Serial_available() {
|
||||||
uint8_t num=dump2log&7;
|
uint8_t num=dump2log&7;
|
||||||
if (num<1 || num>meters_used) num=1;
|
if (num<1 || num>meters_used) num=1;
|
||||||
|
if (!meter_ss[num-1]) return 0;
|
||||||
return meter_ss[num-1]->available();
|
return meter_ss[num-1]->available();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t Serial_read() {
|
uint8_t Serial_read() {
|
||||||
uint8_t num=dump2log&7;
|
uint8_t num=dump2log&7;
|
||||||
if (num<1 || num>meters_used) num=1;
|
if (num<1 || num>meters_used) num=1;
|
||||||
|
if (!meter_ss[num-1]) return 0;
|
||||||
return meter_ss[num-1]->read();
|
return meter_ss[num-1]->read();
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t Serial_peek() {
|
uint8_t Serial_peek() {
|
||||||
uint8_t num=dump2log&7;
|
uint8_t num=dump2log&7;
|
||||||
if (num<1 || num>meters_used) num=1;
|
if (num<1 || num>meters_used) num=1;
|
||||||
|
if (!meter_ss[num-1]) return 0;
|
||||||
return meter_ss[num-1]->peek();
|
return meter_ss[num-1]->peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1186,7 +1197,8 @@ void sml_shift_in(uint32_t meters,uint32_t shard) {
|
||||||
} 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') {
|
||||||
smltbuf[meters][meter_spos[meters]] = iob;
|
smltbuf[meters][meter_spos[meters]] = iob;
|
||||||
meter_spos[meters]++;
|
meter_spos[meters]++;
|
||||||
if (meter_spos[meters]>=9) {
|
uint32_t mlen=smltbuf[meters][2]+5;
|
||||||
|
if (meter_spos[meters]>=mlen) {
|
||||||
SML_Decode(meters);
|
SML_Decode(meters);
|
||||||
sml_empty_receiver(meters);
|
sml_empty_receiver(meters);
|
||||||
meter_spos[meters]=0;
|
meter_spos[meters]=0;
|
||||||
|
@ -1236,6 +1248,7 @@ uint32_t meters;
|
||||||
for (meters=0; meters<meters_used; meters++) {
|
for (meters=0; meters<meters_used; 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;
|
||||||
while (meter_ss[meters]->available()) {
|
while (meter_ss[meters]->available()) {
|
||||||
sml_shift_in(meters,0);
|
sml_shift_in(meters,0);
|
||||||
}
|
}
|
||||||
|
@ -1530,9 +1543,10 @@ void SML_Decode(uint8_t index) {
|
||||||
if (mb_index!=meter_desc_p[mindex].index) {
|
if (mb_index!=meter_desc_p[mindex].index) {
|
||||||
goto nextsect;
|
goto nextsect;
|
||||||
}
|
}
|
||||||
uint16_t crc = MBUS_calculateCRC(&smltbuf[mindex][0],7);
|
uint16_t pos = smltbuf[mindex][2]+3;
|
||||||
if (lowByte(crc)!=smltbuf[mindex][7]) goto nextsect;
|
uint16_t crc = MBUS_calculateCRC(&smltbuf[mindex][0],pos);
|
||||||
if (highByte(crc)!=smltbuf[mindex][8]) goto nextsect;
|
if (lowByte(crc)!=smltbuf[mindex][pos]) goto nextsect;
|
||||||
|
if (highByte(crc)!=smltbuf[mindex][pos+1]) goto nextsect;
|
||||||
dval=mbus_dval;
|
dval=mbus_dval;
|
||||||
//AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),mp);
|
//AddLog_P2(LOG_LEVEL_INFO, PSTR(">> %s"),mp);
|
||||||
mp++;
|
mp++;
|
||||||
|
@ -1728,7 +1742,7 @@ 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);
|
||||||
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1830,6 +1844,52 @@ uint8_t *script_meter;
|
||||||
#define METER_DEF_SIZE 3000
|
#define METER_DEF_SIZE 3000
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef SML_REPLACE_VARS
|
||||||
|
|
||||||
|
#define SML_SRCBSIZE 256
|
||||||
|
|
||||||
|
uint32_t SML_getlinelen(char *lp) {
|
||||||
|
uint32_t cnt;
|
||||||
|
for (cnt=0; cnt<SML_SRCBSIZE-1; cnt++) {
|
||||||
|
if (lp[cnt]==SCRIPT_EOL) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t SML_getscriptsize(char *lp) {
|
||||||
|
uint32_t mlen=0;
|
||||||
|
char dstbuf[SML_SRCBSIZE*2];
|
||||||
|
while (1) {
|
||||||
|
Replace_Cmd_Vars(lp,1,dstbuf,sizeof(dstbuf));
|
||||||
|
lp+=SML_getlinelen(lp)+1;
|
||||||
|
uint32_t slen=strlen(dstbuf);
|
||||||
|
//AddLog_P2(LOG_LEVEL_INFO, PSTR("%d - %s"),slen,dstbuf);
|
||||||
|
mlen+=slen+1;
|
||||||
|
if (*lp=='#') break;
|
||||||
|
if (*lp=='>') break;
|
||||||
|
if (*lp==0) break;
|
||||||
|
}
|
||||||
|
//AddLog_P2(LOG_LEVEL_INFO, PSTR("len=%d"),mlen);
|
||||||
|
return mlen+32;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
uint32_t SML_getscriptsize(char *lp) {
|
||||||
|
uint32_t mlen=0;
|
||||||
|
for (uint32_t cnt=0;cnt<METER_DEF_SIZE-1;cnt++) {
|
||||||
|
if (lp[cnt]=='\n' && lp[cnt+1]=='#') {
|
||||||
|
mlen=cnt+3;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//AddLog_P2(LOG_LEVEL_INFO, PSTR("len=%d"),mlen);
|
||||||
|
return mlen;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool Gpio_used(uint8_t gpiopin) {
|
bool Gpio_used(uint8_t gpiopin) {
|
||||||
/*
|
/*
|
||||||
for (uint16_t i=0;i<GPIO_SENSOR_END;i++) {
|
for (uint16_t i=0;i<GPIO_SENSOR_END;i++) {
|
||||||
|
@ -1865,10 +1925,19 @@ void SML_Init(void) {
|
||||||
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;
|
||||||
|
|
||||||
|
if (meter_ss[cnt]) {
|
||||||
|
delete meter_ss[cnt];
|
||||||
|
meter_ss[cnt]=NULL;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
@ -1886,13 +1955,7 @@ void SML_Init(void) {
|
||||||
lp+=2;
|
lp+=2;
|
||||||
meters_used=strtol(lp,0,10);
|
meters_used=strtol(lp,0,10);
|
||||||
section=1;
|
section=1;
|
||||||
uint32_t mlen=0;
|
uint32_t mlen=SML_getscriptsize(lp);
|
||||||
for (uint32_t cnt=0;cnt<METER_DEF_SIZE-1;cnt++) {
|
|
||||||
if (lp[cnt]=='\n' && lp[cnt+1]=='#') {
|
|
||||||
mlen=cnt+3;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mlen==0) return; // missing end #
|
if (mlen==0) return; // missing end #
|
||||||
script_meter=(uint8_t*)calloc(mlen,1);
|
script_meter=(uint8_t*)calloc(mlen,1);
|
||||||
if (!script_meter) {
|
if (!script_meter) {
|
||||||
|
@ -1981,6 +2044,30 @@ dddef_exit:
|
||||||
goto next_line;
|
goto next_line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef SML_REPLACE_VARS
|
||||||
|
char dstbuf[SML_SRCBSIZE*2];
|
||||||
|
Replace_Cmd_Vars(lp,1,dstbuf,sizeof(dstbuf));
|
||||||
|
lp+=SML_getlinelen(lp);
|
||||||
|
//AddLog_P2(LOG_LEVEL_INFO, PSTR("%s"),dstbuf);
|
||||||
|
char *lp1=dstbuf;
|
||||||
|
if (*lp1=='-' || isdigit(*lp1)) {
|
||||||
|
//toLogEOL(">>",lp);
|
||||||
|
// add meters line -1,1-0:1.8.0*255(@10000,H2OIN,cbm,COUNTER,4|
|
||||||
|
if (*lp1=='-') lp1++;
|
||||||
|
uint8_t mnum=strtol(lp1,0,10);
|
||||||
|
if (mnum<1 || mnum>meters_used) goto next_line;
|
||||||
|
while (1) {
|
||||||
|
if (*lp1==0) {
|
||||||
|
*tp++='|';
|
||||||
|
goto next_line;
|
||||||
|
}
|
||||||
|
*tp++=*lp1++;
|
||||||
|
index++;
|
||||||
|
if (index>=METER_DEF_SIZE) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
|
||||||
if (*lp=='-' || isdigit(*lp)) {
|
if (*lp=='-' || isdigit(*lp)) {
|
||||||
//toLogEOL(">>",lp);
|
//toLogEOL(">>",lp);
|
||||||
// add meters line -1,1-0:1.8.0*255(@10000,H2OIN,cbm,COUNTER,4|
|
// add meters line -1,1-0:1.8.0*255(@10000,H2OIN,cbm,COUNTER,4|
|
||||||
|
@ -1997,6 +2084,7 @@ dddef_exit:
|
||||||
if (index>=METER_DEF_SIZE) break;
|
if (index>=METER_DEF_SIZE) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2013,6 +2101,7 @@ next_line:
|
||||||
meter_desc_p=script_meter_desc;
|
meter_desc_p=script_meter_desc;
|
||||||
meter_p=script_meter;
|
meter_p=script_meter;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
init10:
|
init10:
|
||||||
|
@ -2024,6 +2113,7 @@ init10:
|
||||||
RtcSettings.pulse_counter[i]=Settings.pulse_counter[i];
|
RtcSettings.pulse_counter[i]=Settings.pulse_counter[i];
|
||||||
sml_counters[i].sml_cnt_last_ts=millis();
|
sml_counters[i].sml_cnt_last_ts=millis();
|
||||||
}
|
}
|
||||||
|
uint32_t uart_index=2;
|
||||||
for (uint8_t meters=0; meters<meters_used; meters++) {
|
for (uint8_t meters=0; meters<meters_used; meters++) {
|
||||||
if (meter_desc_p[meters].type=='c') {
|
if (meter_desc_p[meters].type=='c') {
|
||||||
if (meter_desc_p[meters].flag&2) {
|
if (meter_desc_p[meters].flag&2) {
|
||||||
|
@ -2058,9 +2148,23 @@ init10:
|
||||||
} else {
|
} else {
|
||||||
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,1);
|
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1,1);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
#ifdef ESP32
|
||||||
|
meter_ss[meters] = new HardwareSerial(uart_index);
|
||||||
|
uart_index--;
|
||||||
|
if (uart_index<0) uart_index=0;
|
||||||
#else
|
#else
|
||||||
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1);
|
meter_ss[meters] = new TasmotaSerial(meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin,1);
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
|
if (meter_desc_p[meters].type=='M') {
|
||||||
|
meter_ss[meters]->begin(meter_desc_p[meters].params, SERIAL_8E1,meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin);
|
||||||
|
} else {
|
||||||
|
meter_ss[meters]->begin(meter_desc_p[meters].params,SERIAL_8N1,meter_desc_p[meters].srcpin,meter_desc_p[meters].trxpin);
|
||||||
|
}
|
||||||
|
#else
|
||||||
if (meter_ss[meters]->begin(meter_desc_p[meters].params)) {
|
if (meter_ss[meters]->begin(meter_desc_p[meters].params)) {
|
||||||
meter_ss[meters]->flush();
|
meter_ss[meters]->flush();
|
||||||
}
|
}
|
||||||
|
@ -2069,8 +2173,9 @@ init10:
|
||||||
Serial.begin(meter_desc_p[meters].params, SERIAL_8E1);
|
Serial.begin(meter_desc_p[meters].params, SERIAL_8E1);
|
||||||
}
|
}
|
||||||
ClaimSerial();
|
ClaimSerial();
|
||||||
|
//Serial.setRxBufferSize(512);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2262,6 +2367,11 @@ void SML_Send_Seq(uint32_t meter,char *seq) {
|
||||||
uint8_t sbuff[32];
|
uint8_t sbuff[32];
|
||||||
uint8_t *ucp=sbuff,slen=0;
|
uint8_t *ucp=sbuff,slen=0;
|
||||||
char *cp=seq;
|
char *cp=seq;
|
||||||
|
uint8_t rflg = 0;
|
||||||
|
if (*cp=='r') {
|
||||||
|
rflg = 1;
|
||||||
|
cp++;
|
||||||
|
}
|
||||||
while (*cp) {
|
while (*cp) {
|
||||||
if (!*cp || !*(cp+1)) break;
|
if (!*cp || !*(cp+1)) break;
|
||||||
if (*cp==',') break;
|
if (*cp==',') break;
|
||||||
|
@ -2272,8 +2382,10 @@ void SML_Send_Seq(uint32_t meter,char *seq) {
|
||||||
if (slen>=sizeof(sbuff)) break;
|
if (slen>=sizeof(sbuff)) break;
|
||||||
}
|
}
|
||||||
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') {
|
||||||
*ucp++=0;
|
if (!rflg) {
|
||||||
*ucp++=2;
|
*ucp++=0;
|
||||||
|
*ucp++=2;
|
||||||
|
}
|
||||||
// append crc
|
// append crc
|
||||||
uint16_t crc = MBUS_calculateCRC(sbuff,6);
|
uint16_t crc = MBUS_calculateCRC(sbuff,6);
|
||||||
*ucp++=lowByte(crc);
|
*ucp++=lowByte(crc);
|
||||||
|
@ -2412,14 +2524,18 @@ bool Xsns53(byte function) {
|
||||||
break;
|
break;
|
||||||
case FUNC_LOOP:
|
case FUNC_LOOP:
|
||||||
SML_Counter_Poll();
|
SML_Counter_Poll();
|
||||||
break;
|
|
||||||
case FUNC_EVERY_50_MSECOND:
|
|
||||||
if (dump2log) Dump2log();
|
if (dump2log) Dump2log();
|
||||||
else SML_Poll();
|
else SML_Poll();
|
||||||
break;
|
break;
|
||||||
|
// case FUNC_EVERY_50_MSECOND:
|
||||||
|
// if (dump2log) Dump2log();
|
||||||
|
// else SML_Poll();
|
||||||
|
// break;
|
||||||
#ifdef USE_SCRIPT
|
#ifdef USE_SCRIPT
|
||||||
case FUNC_EVERY_100_MSECOND:
|
case FUNC_EVERY_100_MSECOND:
|
||||||
SML_Check_Send();
|
if (bitRead(Settings.rule_enabled, 0)) {
|
||||||
|
SML_Check_Send();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
#endif // USE_SCRIPT
|
#endif // USE_SCRIPT
|
||||||
case FUNC_JSON_APPEND:
|
case FUNC_JSON_APPEND:
|
||||||
|
|
Loading…
Reference in New Issue