5.2.3 20170630
* Change Sonoff Led color conversion code
* Fix SetOption12 handling
* Simplify auto configuration upgrade
* Add option Upgrade <version_number> to only upgrade to any higher
version (Old PR #213)
* Change FallbackTopic to cmnd/<MQTTClient>/<command> <parameter>
bypassing FullTopic and Prefix (#538)
This commit is contained in:
arendst 2017-06-30 17:54:19 +02:00
parent e914053041
commit ee883bdcb8
6 changed files with 122 additions and 32 deletions

View File

@ -1,7 +1,7 @@
## Sonoff-Tasmota ## Sonoff-Tasmota
Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE. Provide ESP8266 based Sonoff by [iTead Studio](https://www.itead.cc/) and ElectroDragon IoT Relay with Serial, Web and MQTT control allowing 'Over the Air' or OTA firmware updates using Arduino IDE.
Current version is **5.2.2** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information. Current version is **5.2.3** - See [sonoff/_releasenotes.ino](https://github.com/arendst/Sonoff-Tasmota/blob/master/sonoff/_releasenotes.ino) for change information.
### **** ATTENTION Version 5.x.x specific information **** ### **** ATTENTION Version 5.x.x specific information ****

View File

@ -1,4 +1,11 @@
/* 5.2.2 20170625 /* 5.2.3 20170630
* Change Sonoff Led color conversion code
* Fix SetOption12 handling
* Simplify auto configuration upgrade
* Add option Upgrade <version_number> to only upgrade to any higher version (Old PR #213)
* Change FallbackTopic to cmnd/<MQTTClient>/<command> <parameter> bypassing FullTopic and Prefix (#538)
*
* 5.2.2 20170625
* Add configuration SaveAddress to Status 1 and Information Page * Add configuration SaveAddress to Status 1 and Information Page
* Change Sonoff Led Color conversion from AtoH to strtol * Change Sonoff Led Color conversion from AtoH to strtol
* Fix possible wrong uploads due to configuration overwrites (#542) * Fix possible wrong uploads due to configuration overwrites (#542)

View File

@ -200,15 +200,26 @@ uint32_t CFG_Address()
return _cfgLocation * SPI_FLASH_SEC_SIZE; return _cfgLocation * SPI_FLASH_SEC_SIZE;
} }
void CFG_Save(byte no_rotate) void CFG_Save(byte rotate)
{ {
/* Save configuration in eeprom or one of 7 slots below
*
* rotate 0 = Save in next flash slot
* rotate 1 = Save only in eeprom flash slot until SetOption12 0 or restart
* rotate 2 = Save in eeprom flash slot and continue depending on stop_flash_rotate
* stop_flash_rotate 0 = Allow flash slot rotation (SetOption12 0)
* stop_flash_rotate 1 = Allow only eeprom flash slot use (SetOption12 1)
*/
char log[LOGSZ]; char log[LOGSZ];
#ifndef BE_MINIMAL #ifndef BE_MINIMAL
if ((getHash() != _cfgHash) || no_rotate) { if ((getHash() != _cfgHash) || rotate) {
if (no_rotate) { if (1 == rotate) {
stop_flash_rotate = 1; // Disable flash rotate from now on stop_flash_rotate = 1; // Disable flash rotate from now on
} }
if (2 == rotate) {
_cfgLocation = CFG_LOCATION +1;
}
if (stop_flash_rotate) { if (stop_flash_rotate) {
_cfgLocation = CFG_LOCATION; _cfgLocation = CFG_LOCATION;
} else { } else {
@ -222,7 +233,7 @@ void CFG_Save(byte no_rotate)
spi_flash_erase_sector(_cfgLocation); spi_flash_erase_sector(_cfgLocation);
spi_flash_write(_cfgLocation * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG)); spi_flash_write(_cfgLocation * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG));
interrupts(); interrupts();
if (!stop_flash_rotate && no_rotate) { if (!stop_flash_rotate && rotate) {
for (byte i = 1; i < CFG_ROTATES; i++) { for (byte i = 1; i < CFG_ROTATES; i++) {
noInterrupts(); noInterrupts();
spi_flash_erase_sector(_cfgLocation -i); // Delete previous configurations by resetting to 0xFF spi_flash_erase_sector(_cfgLocation -i); // Delete previous configurations by resetting to 0xFF
@ -240,6 +251,8 @@ void CFG_Save(byte no_rotate)
void CFG_Load() void CFG_Load()
{ {
/* Load configuration from eeprom or one of 7 slots below if first load does not stop_flash_rotate
*/
char log[LOGSZ]; char log[LOGSZ];
struct SYSCFGH { struct SYSCFGH {
@ -258,7 +271,7 @@ void CFG_Load()
// snprintf_P(log, sizeof(log), PSTR("Cnfg: Check at %X with count %d and holder %X"), _cfgLocation -1, _sysCfgH.saveFlag, _sysCfgH.cfg_holder); // snprintf_P(log, sizeof(log), PSTR("Cnfg: Check at %X with count %d and holder %X"), _cfgLocation -1, _sysCfgH.saveFlag, _sysCfgH.cfg_holder);
// addLog(LOG_LEVEL_DEBUG, log); // addLog(LOG_LEVEL_DEBUG, log);
if (sysCfg.flag.stop_flash_rotate || (sysCfg.cfg_holder != _sysCfgH.cfg_holder) || (sysCfg.saveFlag > _sysCfgH.saveFlag)) { if (((sysCfg.version > 0x05000200) && sysCfg.flag.stop_flash_rotate) || (sysCfg.cfg_holder != _sysCfgH.cfg_holder) || (sysCfg.saveFlag > _sysCfgH.saveFlag)) {
break; break;
} }
delay(1); delay(1);
@ -271,8 +284,9 @@ void CFG_Load()
} }
*/ */
if (sysCfg.cfg_holder != CFG_HOLDER) { if (sysCfg.cfg_holder != CFG_HOLDER) {
/*
// Auto upgrade // Auto upgrade
if ((sysCfg.version < 0x04020000) || (sysCfg.version > 0x06000000)) { if ((sysCfg.version < 0x04020000) || (sysCfg.version > VERSION)) {
noInterrupts(); noInterrupts();
spi_flash_read((CFG_LOCATION_3) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG)); spi_flash_read((CFG_LOCATION_3) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG));
spi_flash_read((CFG_LOCATION_3 + 1) * SPI_FLASH_SEC_SIZE, (uint32*)&_sysCfgH, sizeof(SYSCFGH)); spi_flash_read((CFG_LOCATION_3 + 1) * SPI_FLASH_SEC_SIZE, (uint32*)&_sysCfgH, sizeof(SYSCFGH));
@ -287,6 +301,17 @@ void CFG_Load()
} else { } else {
CFG_Default(); CFG_Default();
} }
*/
// Auto upgrade
noInterrupts();
spi_flash_read((CFG_LOCATION_3) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG));
spi_flash_read((CFG_LOCATION_3 + 1) * SPI_FLASH_SEC_SIZE, (uint32*)&_sysCfgH, sizeof(SYSCFGH));
if (sysCfg.saveFlag < _sysCfgH.saveFlag)
spi_flash_read((CFG_LOCATION_3 + 1) * SPI_FLASH_SEC_SIZE, (uint32*)&sysCfg, sizeof(SYSCFG));
interrupts();
if ((sysCfg.cfg_holder != CFG_HOLDER) || (sysCfg.version >= 0x04020000)) {
CFG_Default();
}
} }
_cfgHash = getHash(); _cfgHash = getHash();

View File

@ -24,7 +24,7 @@
- Select IDE Tools - Flash size: "1M (no SPIFFS)" - Select IDE Tools - Flash size: "1M (no SPIFFS)"
====================================================*/ ====================================================*/
#define VERSION 0x05020200 // 5.2.2 #define VERSION 0x05020300 // 5.2.3
enum log_t {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL}; enum log_t {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE, LOG_LEVEL_ALL};
enum week_t {Last, First, Second, Third, Fourth}; enum week_t {Last, First, Second, Third, Fourth};
@ -230,6 +230,7 @@ char Version[16]; // Version string from VERSION define
char Hostname[33]; // Composed Wifi hostname char Hostname[33]; // Composed Wifi hostname
char MQTTClient[33]; // Composed MQTT Clientname char MQTTClient[33]; // Composed MQTT Clientname
uint8_t mqttcounter = 0; // MQTT connection retry counter uint8_t mqttcounter = 0; // MQTT connection retry counter
uint8_t fallbacktopic = 0; // Use Topic or FallbackTopic
unsigned long timerxs = 0; // State loop timer unsigned long timerxs = 0; // State loop timer
int state = 0; // State per second flag int state = 0; // State per second flag
int mqttflag = 2; // MQTT connection messages flag int mqttflag = 2; // MQTT connection messages flag
@ -328,19 +329,26 @@ void getClient(char* output, const char* input, byte size)
void getTopic_P(char *stopic, byte prefix, char *topic, const char* subtopic) void getTopic_P(char *stopic, byte prefix, char *topic, const char* subtopic)
{ {
char romram[CMDSZ]; char romram[CMDSZ];
String fulltopic;
snprintf_P(romram, sizeof(romram), subtopic); snprintf_P(romram, sizeof(romram), subtopic);
String fulltopic = sysCfg.mqtt_fulltopic; if (fallbacktopic) {
if ((0 == prefix) && (-1 == fulltopic.indexOf(F(MQTT_TOKEN_PREFIX)))) { fulltopic = FPSTR(PREFIXES[prefix]);
fulltopic += F("/" MQTT_TOKEN_PREFIX); // Need prefix for commands to handle mqtt topic loops fulltopic += F("/");
} fulltopic += MQTTClient;
for (byte i = 0; i < 3; i++) { } else {
if ('\0' == sysCfg.mqtt_prefix[i][0]) { fulltopic = sysCfg.mqtt_fulltopic;
snprintf_P(sysCfg.mqtt_prefix[i], sizeof(sysCfg.mqtt_prefix[i]), PREFIXES[i]); if ((0 == prefix) && (-1 == fulltopic.indexOf(F(MQTT_TOKEN_PREFIX)))) {
fulltopic += F("/" MQTT_TOKEN_PREFIX); // Need prefix for commands to handle mqtt topic loops
} }
for (byte i = 0; i < 3; i++) {
if ('\0' == sysCfg.mqtt_prefix[i][0]) {
snprintf_P(sysCfg.mqtt_prefix[i], sizeof(sysCfg.mqtt_prefix[i]), PREFIXES[i]);
}
}
fulltopic.replace(F(MQTT_TOKEN_PREFIX), sysCfg.mqtt_prefix[prefix]);
fulltopic.replace(F(MQTT_TOKEN_TOPIC), topic);
} }
fulltopic.replace(F(MQTT_TOKEN_PREFIX), sysCfg.mqtt_prefix[prefix]);
fulltopic.replace(F(MQTT_TOKEN_TOPIC), topic);
fulltopic.replace(F("#"), ""); fulltopic.replace(F("#"), "");
fulltopic.replace(F("//"), "/"); fulltopic.replace(F("//"), "/");
if (!fulltopic.endsWith("/")) { if (!fulltopic.endsWith("/")) {
@ -536,7 +544,9 @@ void mqtt_connected()
getTopic_P(stopic, 0, sysCfg.mqtt_grptopic, PSTR("#")); getTopic_P(stopic, 0, sysCfg.mqtt_grptopic, PSTR("#"));
mqttClient.subscribe(stopic); mqttClient.subscribe(stopic);
mqttClient.loop(); // Solve LmacRxBlk:1 messages mqttClient.loop(); // Solve LmacRxBlk:1 messages
fallbacktopic = 1;
getTopic_P(stopic, 0, MQTTClient, PSTR("#")); getTopic_P(stopic, 0, MQTTClient, PSTR("#"));
fallbacktopic = 0;
mqttClient.subscribe(stopic); mqttClient.subscribe(stopic);
mqttClient.loop(); // Solve LmacRxBlk:1 messages mqttClient.loop(); // Solve LmacRxBlk:1 messages
} }
@ -883,6 +893,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
#endif // USE_DOMOTICZ #endif // USE_DOMOTICZ
grpflg = (strstr(topicBuf, sysCfg.mqtt_grptopic) != NULL); grpflg = (strstr(topicBuf, sysCfg.mqtt_grptopic) != NULL);
fallbacktopic = (strstr(topicBuf, MQTTClient) != NULL);
type = strrchr(topicBuf, '/') +1; // Last part of received topic is always the command (type) type = strrchr(topicBuf, '/') +1; // Last part of received topic is always the command (type)
index = 1; index = 1;
@ -948,6 +959,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
payload = 9; payload = 9;
} }
do_cmnd_power(index, payload); do_cmnd_power(index, payload);
fallbacktopic = 0;
return; return;
} }
else if (!strcmp_P(type,PSTR("STATUS"))) { else if (!strcmp_P(type,PSTR("STATUS"))) {
@ -955,6 +967,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
payload = 99; payload = 99;
} }
publish_status(payload); publish_status(payload);
fallbacktopic = 0;
return; return;
} }
else if ((sysCfg.module != MOTOR) && !strcmp_P(type,PSTR("POWERONSTATE"))) { else if ((sysCfg.module != MOTOR) && !strcmp_P(type,PSTR("POWERONSTATE"))) {
@ -1035,7 +1048,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
} }
if (12 == index) { // stop_flash_rotate if (12 == index) { // stop_flash_rotate
stop_flash_rotate = payload; stop_flash_rotate = payload;
CFG_Save(stop_flash_rotate); CFG_Save(2);
} }
} }
} }
@ -1275,11 +1288,15 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
snprintf_P(svalue, sizeof(svalue), PSTR("{\"FlashMode\":%d}"), ESP.getFlashChipMode()); snprintf_P(svalue, sizeof(svalue), PSTR("{\"FlashMode\":%d}"), ESP.getFlashChipMode());
} }
else if (!strcmp_P(type,PSTR("UPGRADE")) || !strcmp_P(type,PSTR("UPLOAD"))) { else if (!strcmp_P(type,PSTR("UPGRADE")) || !strcmp_P(type,PSTR("UPLOAD"))) {
if (1 == payload) { // Check if the payload is numerically 1, and had no trailing chars.
// e.g. "1foo" or "1.2.3" could fool us.
// Check if the version we have been asked to upgrade to is higher than our current version.
// We also need at least 3 chars to make a valid version number string.
if (((1 == data_len) && (1 == payload)) || ((data_len >= 3) && newerVersion(dataBuf))) {
otaflag = 3; otaflag = 3;
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Upgrade\":\"Version %s from %s\"}"), Version, sysCfg.otaUrl); snprintf_P(svalue, sizeof(svalue), PSTR("{\"Upgrade\":\"Version %s from %s\"}"), Version, sysCfg.otaUrl);
} else { } else {
snprintf_P(svalue, sizeof(svalue), PSTR("{\"Upgrade\":\"Option 1 to upgrade\"}")); snprintf_P(svalue, sizeof(svalue), PSTR("{\"Upgrade\":\"Option 1 or >%s to upgrade\"}"), Version);
} }
} }
else if (!strcmp_P(type,PSTR("OTAURL"))) { else if (!strcmp_P(type,PSTR("OTAURL"))) {
@ -1573,6 +1590,7 @@ void mqttDataCb(char* topic, byte* data, unsigned int data_len)
if (svalue[0] != '\0') { if (svalue[0] != '\0') {
mqtt_publish_topic_P(5, type, svalue); mqtt_publish_topic_P(5, type, svalue);
} }
fallbacktopic = 0;
} }
/********************************************************************************************/ /********************************************************************************************/

View File

@ -189,6 +189,50 @@ void mqttfy(byte option, char* str)
} }
} }
// Function to parse & check if version_str is newer than our currently installed version.
bool newerVersion(char* version_str)
{
uint32_t version = 0;
uint8_t i = 0;
char *str_ptr;
char* version_dup = strdup(version_str); // Duplicate the version_str as strtok_r will modify it.
if (!version_dup) {
return false; // Bail if we can't duplicate. Assume bad.
}
// Loop through the version string, splitting on '.' seperators.
for (char *str = strtok_r(version_dup, ".", &str_ptr); str && i < sizeof(VERSION); str = strtok_r(NULL, ".", &str_ptr), i++) {
int field = atoi(str);
// The fields in a version string can only range from 0-255.
if ((field < 0) || (field > 255)) {
free(version_dup);
return false;
}
// Shuffle the accumulated bytes across, and add the new byte.
version = (version << 8) + field;
// Check alpha delimiter after 1.2.3 only
if ((2 == i) && isalpha(str[strlen(str)-1])) {
field = str[strlen(str)-1] & 0x1f;
version = (version << 8) + field;
i++;
}
}
free(version_dup); // We no longer need this.
// A version string should have 2-4 fields. e.g. 1.2, 1.2.3, or 1.2.3a (= 1.2.3.1).
// If not, then don't consider it a valid version string.
if ((i < 2) || (i > sizeof(VERSION))) {
return false;
}
// Keep shifting the parsed version until we hit the maximum number of tokens.
// VERSION stores the major number of the version in the most significant byte of the uint32_t.
while (i < sizeof(VERSION)) {
version <<= 8;
i++;
}
// Now we should have a fully constructed version number in uint32_t form.
return (version > VERSION);
}
/*********************************************************************************************\ /*********************************************************************************************\
* Wifi * Wifi
\*********************************************************************************************/ \*********************************************************************************************/

View File

@ -152,19 +152,15 @@ boolean sl_command(char *type, uint16_t index, char *dataBufUc, uint16_t data_le
{ {
boolean serviced = true; boolean serviced = true;
boolean coldim = false; boolean coldim = false;
char *p;
if (!strcmp_P(type,PSTR("COLOR"))) { if (!strcmp_P(type,PSTR("COLOR"))) {
uint8_t my_color[5]; uint8_t my_color[2];
char *p;
if (4 == data_len) { if (4 == data_len) {
char ccold[3], cwarm[3]; uint16_t temp = strtol(dataBufUc, &p, 16);
memcpy(ccold, dataBufUc, 2); my_color[1] = temp & 0xFF; // Warm
ccold[2] = '\0'; temp >>= 8;
memcpy(cwarm, dataBufUc + 2, 2); my_color[0] = temp & 0xFF; // Cold
cwarm[2] = '\0';
my_color[0] = strtol(ccold, &p, 16);
my_color[1] = strtol(cwarm, &p, 16);
uint16_t temp = my_color[0];
if (temp < my_color[1]) { if (temp < my_color[1]) {
temp = my_color[1]; temp = my_color[1];
} }