2017-01-28 13:41:01 +00:00
|
|
|
|
/*
|
2019-10-27 10:13:24 +00:00
|
|
|
|
support.ino - support for Tasmota
|
2017-05-13 12:02:10 +01:00
|
|
|
|
|
2021-01-01 12:44:04 +00:00
|
|
|
|
Copyright (C) 2021 Theo Arends
|
2017-05-13 12:02:10 +01:00
|
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2017-01-28 13:41:01 +00:00
|
|
|
|
*/
|
|
|
|
|
|
2019-11-18 10:02:04 +00:00
|
|
|
|
extern "C" {
|
|
|
|
|
extern struct rst_info resetInfo;
|
|
|
|
|
}
|
|
|
|
|
|
2017-02-28 15:01:48 +00:00
|
|
|
|
/*********************************************************************************************\
|
|
|
|
|
* Watchdog extension (https://github.com/esp8266/Arduino/issues/1532)
|
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
2018-11-20 14:00:24 +00:00
|
|
|
|
#include <Ticker.h>
|
|
|
|
|
|
2017-02-28 15:01:48 +00:00
|
|
|
|
Ticker tickerOSWatch;
|
|
|
|
|
|
2019-03-31 10:59:04 +01:00
|
|
|
|
const uint32_t OSWATCH_RESET_TIME = 120;
|
2017-02-28 15:01:48 +00:00
|
|
|
|
|
2017-10-18 17:22:34 +01:00
|
|
|
|
static unsigned long oswatch_last_loop_time;
|
2019-01-28 13:08:33 +00:00
|
|
|
|
uint8_t oswatch_blocked_loop = 0;
|
2017-02-28 15:01:48 +00:00
|
|
|
|
|
2017-04-03 15:38:15 +01:00
|
|
|
|
#ifndef USE_WS2812_DMA // Collides with Neopixelbus but solves exception
|
2017-11-19 17:02:03 +00:00
|
|
|
|
//void OsWatchTicker() ICACHE_RAM_ATTR;
|
2017-04-03 15:38:15 +01:00
|
|
|
|
#endif // USE_WS2812_DMA
|
|
|
|
|
|
2018-05-11 14:00:41 +01:00
|
|
|
|
#ifdef USE_KNX
|
|
|
|
|
bool knx_started = false;
|
|
|
|
|
#endif // USE_KNX
|
|
|
|
|
|
2018-11-14 13:32:09 +00:00
|
|
|
|
void OsWatchTicker(void)
|
2017-02-28 15:01:48 +00:00
|
|
|
|
{
|
2019-07-28 12:54:52 +01:00
|
|
|
|
uint32_t t = millis();
|
2020-10-22 14:23:22 +01:00
|
|
|
|
uint32_t last_run = t - oswatch_last_loop_time;
|
2017-02-28 15:01:48 +00:00
|
|
|
|
|
|
|
|
|
#ifdef DEBUG_THEO
|
2020-02-20 09:07:00 +00:00
|
|
|
|
int32_t rssi = WiFi.RSSI();
|
2021-01-23 15:26:23 +00:00
|
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_APPLICATION D_OSWATCH " FreeRam %d, rssi %d %% (%d dBm), last_run %d"), ESP_getFreeHeap(), WifiGetRssiAsQuality(rssi), rssi, last_run);
|
2017-02-28 15:01:48 +00:00
|
|
|
|
#endif // DEBUG_THEO
|
2017-04-25 17:24:42 +01:00
|
|
|
|
if (last_run >= (OSWATCH_RESET_TIME * 1000)) {
|
2021-01-23 15:26:23 +00:00
|
|
|
|
// AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_OSWATCH " " D_BLOCKED_LOOP ". " D_RESTARTING)); // Save iram space
|
2017-10-18 17:22:34 +01:00
|
|
|
|
RtcSettings.oswatch_blocked_loop = 1;
|
|
|
|
|
RtcSettingsSave();
|
2019-12-28 13:54:26 +00:00
|
|
|
|
|
2017-09-02 13:37:02 +01:00
|
|
|
|
// ESP.restart(); // normal reboot
|
2019-12-28 13:54:26 +00:00
|
|
|
|
// ESP.reset(); // hard reset
|
|
|
|
|
// Force an exception to get a stackdump
|
2021-01-11 13:50:43 +00:00
|
|
|
|
// ESP32: Guru Meditation Error: Core 0 panic'ed (LoadProhibited). Exception was unhandled.
|
2019-12-28 13:54:26 +00:00
|
|
|
|
volatile uint32_t dummy;
|
|
|
|
|
dummy = *((uint32_t*) 0x00000000);
|
2020-11-12 18:38:21 +00:00
|
|
|
|
(void)dummy; // avoid compiler warning
|
2017-02-28 15:01:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-14 13:32:09 +00:00
|
|
|
|
void OsWatchInit(void)
|
2017-02-28 15:01:48 +00:00
|
|
|
|
{
|
2017-10-18 17:22:34 +01:00
|
|
|
|
oswatch_blocked_loop = RtcSettings.oswatch_blocked_loop;
|
|
|
|
|
RtcSettings.oswatch_blocked_loop = 0;
|
|
|
|
|
oswatch_last_loop_time = millis();
|
|
|
|
|
tickerOSWatch.attach_ms(((OSWATCH_RESET_TIME / 3) * 1000), OsWatchTicker);
|
2017-02-28 15:01:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-14 13:32:09 +00:00
|
|
|
|
void OsWatchLoop(void)
|
2017-02-28 15:01:48 +00:00
|
|
|
|
{
|
2017-10-18 17:22:34 +01:00
|
|
|
|
oswatch_last_loop_time = millis();
|
2017-02-28 15:01:48 +00:00
|
|
|
|
// while(1) delay(1000); // this will trigger the os watch
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-20 20:49:57 +00:00
|
|
|
|
bool OsWatchBlockedLoop(void)
|
|
|
|
|
{
|
|
|
|
|
return oswatch_blocked_loop;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t ResetReason(void)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
user_interface.h
|
|
|
|
|
REASON_DEFAULT_RST = 0, // "Power on" normal startup by power on
|
|
|
|
|
REASON_WDT_RST = 1, // "Hardware Watchdog" hardware watch dog reset
|
|
|
|
|
REASON_EXCEPTION_RST = 2, // "Exception" exception reset, GPIO status won’t change
|
|
|
|
|
REASON_SOFT_WDT_RST = 3, // "Software Watchdog" software watch dog reset, GPIO status won’t change
|
|
|
|
|
REASON_SOFT_RESTART = 4, // "Software/System restart" software restart ,system_restart , GPIO status won’t change
|
|
|
|
|
REASON_DEEP_SLEEP_AWAKE = 5, // "Deep-Sleep Wake" wake up from deep-sleep
|
|
|
|
|
REASON_EXT_SYS_RST = 6 // "External System" external system reset
|
|
|
|
|
*/
|
2020-04-16 17:33:26 +01:00
|
|
|
|
return ESP_ResetInfoReason();
|
2019-11-20 20:49:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-11-14 13:32:09 +00:00
|
|
|
|
String GetResetReason(void)
|
2017-02-28 15:01:48 +00:00
|
|
|
|
{
|
2017-10-18 17:22:34 +01:00
|
|
|
|
if (oswatch_blocked_loop) {
|
2019-10-30 13:08:43 +00:00
|
|
|
|
char buff[32];
|
2018-01-06 16:34:42 +00:00
|
|
|
|
strncpy_P(buff, PSTR(D_JSON_BLOCKED_LOOP), sizeof(buff));
|
2017-02-28 15:01:48 +00:00
|
|
|
|
return String(buff);
|
|
|
|
|
} else {
|
2020-04-10 17:24:08 +01:00
|
|
|
|
return ESP_getResetReason();
|
2017-02-28 15:01:48 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-01 16:04:36 +00:00
|
|
|
|
#ifdef ESP32
|
2020-12-31 11:36:35 +00:00
|
|
|
|
/*********************************************************************************************\
|
|
|
|
|
* ESP32 AutoMutex
|
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////
|
|
|
|
|
// automutex.
|
|
|
|
|
// create a mute in your driver with:
|
|
|
|
|
// void *mutex = nullptr;
|
|
|
|
|
//
|
|
|
|
|
// then protect any function with
|
2021-01-02 09:02:11 +00:00
|
|
|
|
// TasAutoMutex m(&mutex, "somename");
|
|
|
|
|
// - mutex is automatically initialised if not already intialised.
|
2020-12-31 11:36:35 +00:00
|
|
|
|
// - it will be automagically released when the function is over.
|
|
|
|
|
// - the same thread can take multiple times (recursive).
|
|
|
|
|
// - advanced options m.give() and m.take() allow you fine control within a function.
|
2021-01-02 09:02:11 +00:00
|
|
|
|
// - if take=false at creat, it will not be initially taken.
|
|
|
|
|
// - name is used in serial log of mutex deadlock.
|
|
|
|
|
// - maxWait in ticks is how long it will wait before failing in a deadlock scenario (and then emitting on serial)
|
2020-12-31 11:36:35 +00:00
|
|
|
|
class TasAutoMutex {
|
|
|
|
|
SemaphoreHandle_t mutex;
|
|
|
|
|
bool taken;
|
2021-01-02 09:02:11 +00:00
|
|
|
|
int maxWait;
|
|
|
|
|
const char *name;
|
2020-12-31 11:36:35 +00:00
|
|
|
|
public:
|
2021-01-02 09:02:11 +00:00
|
|
|
|
TasAutoMutex(void ** mutex, const char *name = "", int maxWait = 40, bool take=true);
|
2020-12-31 11:36:35 +00:00
|
|
|
|
~TasAutoMutex();
|
|
|
|
|
void give();
|
|
|
|
|
void take();
|
|
|
|
|
static void init(void ** ptr);
|
|
|
|
|
};
|
|
|
|
|
//////////////////////////////////////////
|
|
|
|
|
|
2021-01-02 09:02:11 +00:00
|
|
|
|
TasAutoMutex::TasAutoMutex(void **mutex, const char *name, int maxWait, bool take) {
|
2021-01-01 16:04:36 +00:00
|
|
|
|
if (mutex) {
|
2021-01-02 09:02:11 +00:00
|
|
|
|
if (!(*mutex)){
|
2021-01-02 13:59:02 +00:00
|
|
|
|
TasAutoMutex::init(mutex);
|
2021-01-02 09:02:11 +00:00
|
|
|
|
}
|
|
|
|
|
this->mutex = (SemaphoreHandle_t)*mutex;
|
|
|
|
|
this->maxWait = maxWait;
|
|
|
|
|
this->name = name;
|
2021-01-01 16:04:36 +00:00
|
|
|
|
if (take) {
|
2021-01-02 09:02:11 +00:00
|
|
|
|
this->taken = xSemaphoreTakeRecursive(this->mutex, this->maxWait);
|
|
|
|
|
if (!this->taken){
|
|
|
|
|
Serial.printf("\r\nMutexfail %s\r\n", this->name);
|
|
|
|
|
}
|
2020-12-31 11:36:35 +00:00
|
|
|
|
}
|
|
|
|
|
} else {
|
2021-01-01 16:04:36 +00:00
|
|
|
|
this->mutex = (SemaphoreHandle_t)nullptr;
|
2020-12-31 11:36:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-01 16:04:36 +00:00
|
|
|
|
TasAutoMutex::~TasAutoMutex() {
|
|
|
|
|
if (this->mutex) {
|
|
|
|
|
if (this->taken) {
|
2020-12-31 11:36:35 +00:00
|
|
|
|
xSemaphoreGiveRecursive(this->mutex);
|
|
|
|
|
this->taken = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-01 16:04:36 +00:00
|
|
|
|
void TasAutoMutex::init(void ** ptr) {
|
2020-12-31 11:36:35 +00:00
|
|
|
|
SemaphoreHandle_t mutex = xSemaphoreCreateRecursiveMutex();
|
|
|
|
|
(*ptr) = (void *) mutex;
|
2021-01-01 16:04:36 +00:00
|
|
|
|
// needed, else for ESP8266 as we will initialis more than once in logging
|
|
|
|
|
// (*ptr) = (void *) 1;
|
2020-12-31 11:36:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-01 16:04:36 +00:00
|
|
|
|
void TasAutoMutex::give() {
|
|
|
|
|
if (this->mutex) {
|
|
|
|
|
if (this->taken) {
|
2020-12-31 11:36:35 +00:00
|
|
|
|
xSemaphoreGiveRecursive(this->mutex);
|
|
|
|
|
this->taken= false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-01-01 16:04:36 +00:00
|
|
|
|
|
|
|
|
|
void TasAutoMutex::take() {
|
|
|
|
|
if (this->mutex) {
|
|
|
|
|
if (!this->taken) {
|
2021-01-02 09:02:11 +00:00
|
|
|
|
this->taken = xSemaphoreTakeRecursive(this->mutex, this->maxWait);
|
|
|
|
|
if (!this->taken){
|
|
|
|
|
Serial.printf("\r\nMutexfail %s\r\n", this->name);
|
|
|
|
|
}
|
2020-12-31 11:36:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-01 16:04:36 +00:00
|
|
|
|
#endif // ESP32
|
2020-12-31 11:36:35 +00:00
|
|
|
|
|
2021-01-02 09:02:11 +00:00
|
|
|
|
|
2017-03-25 16:24:11 +00:00
|
|
|
|
/*********************************************************************************************\
|
2018-01-30 13:14:55 +00:00
|
|
|
|
* Miscellaneous
|
2017-03-25 16:24:11 +00:00
|
|
|
|
\*********************************************************************************************/
|
2020-09-23 16:13:10 +01:00
|
|
|
|
/*
|
2020-08-20 16:58:58 +01:00
|
|
|
|
String GetBinary(const void* ptr, size_t count) {
|
|
|
|
|
uint32_t value = *(uint32_t*)ptr;
|
|
|
|
|
value <<= (32 - count);
|
|
|
|
|
String result;
|
|
|
|
|
result.reserve(count + 1);
|
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
|
|
|
result += (value &0x80000000) ? '1' : '0';
|
|
|
|
|
value <<= 1;
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2020-09-23 16:13:10 +01:00
|
|
|
|
*/
|
|
|
|
|
String GetBinary8(uint8_t value, size_t count) {
|
|
|
|
|
if (count > 8) { count = 8; }
|
|
|
|
|
value <<= (8 - count);
|
|
|
|
|
String result;
|
|
|
|
|
result.reserve(count + 1);
|
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
|
|
|
result += (value &0x80) ? '1' : '0';
|
|
|
|
|
value <<= 1;
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
2020-08-20 15:22:40 +01:00
|
|
|
|
|
2018-02-01 15:18:00 +00:00
|
|
|
|
// Get span until single character in string
|
|
|
|
|
size_t strchrspn(const char *str1, int character)
|
2018-01-30 13:14:55 +00:00
|
|
|
|
{
|
2018-02-01 15:18:00 +00:00
|
|
|
|
size_t ret = 0;
|
|
|
|
|
char *start = (char*)str1;
|
|
|
|
|
char *end = strchr(str1, character);
|
|
|
|
|
if (end) ret = end - start;
|
|
|
|
|
return ret;
|
2018-01-30 13:14:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-08-04 15:33:05 +01:00
|
|
|
|
uint32_t ChrCount(const char *str, const char *delim) {
|
|
|
|
|
uint32_t count = 0;
|
|
|
|
|
char* read = (char*)str;
|
|
|
|
|
char ch = '.';
|
|
|
|
|
|
|
|
|
|
while (ch != '\0') {
|
|
|
|
|
ch = *read++;
|
|
|
|
|
if (ch == *delim) { count++; }
|
|
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-30 11:27:48 +00:00
|
|
|
|
uint32_t ArgC(void) {
|
2021-01-30 16:00:50 +00:00
|
|
|
|
return (XdrvMailbox.data_len > 0) ? ChrCount(XdrvMailbox.data, ",") +1 : 0;
|
2021-01-30 11:27:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-23 05:32:54 +01:00
|
|
|
|
// Function to return a substring defined by a delimiter at an index
|
2021-01-29 15:16:20 +00:00
|
|
|
|
char* subStr(char* dest, char* str, const char *delim, int index) {
|
2021-01-30 11:27:48 +00:00
|
|
|
|
char* write = dest;
|
|
|
|
|
char* read = str;
|
|
|
|
|
char ch = '.';
|
|
|
|
|
|
|
|
|
|
while (index && (ch != '\0')) {
|
|
|
|
|
ch = *read++;
|
|
|
|
|
if (strchr(delim, ch)) {
|
|
|
|
|
index--;
|
|
|
|
|
if (index) { write = dest; }
|
|
|
|
|
} else {
|
|
|
|
|
*write++ = ch;
|
|
|
|
|
}
|
2021-01-29 15:16:20 +00:00
|
|
|
|
}
|
2021-01-30 11:27:48 +00:00
|
|
|
|
*write = '\0';
|
|
|
|
|
dest = Trim(dest);
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char* ArgV(char* dest, int index) {
|
|
|
|
|
return subStr(dest, XdrvMailbox.data, ",", index);
|
2018-07-23 05:32:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-30 16:00:50 +00:00
|
|
|
|
uint32_t ArgVul(uint32_t *args, uint32_t count) {
|
|
|
|
|
uint32_t argc = ArgC();
|
|
|
|
|
if (argc > count) { argc = count; }
|
|
|
|
|
count = argc;
|
|
|
|
|
if (argc) {
|
|
|
|
|
char argument[XdrvMailbox.data_len];
|
|
|
|
|
for (uint32_t i = 0; i < argc; i++) {
|
|
|
|
|
if (strlen(ArgV(argument, i +1))) {
|
|
|
|
|
args[i] = strtoul(argument, nullptr, 0);
|
|
|
|
|
} else {
|
|
|
|
|
count--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t ParseParameters(uint32_t count, uint32_t *params) {
|
|
|
|
|
// Destroys XdrvMailbox.data
|
2021-01-30 13:52:53 +00:00
|
|
|
|
char *p;
|
|
|
|
|
uint32_t i = 0;
|
|
|
|
|
for (char *str = strtok_r(XdrvMailbox.data, ", ", &p); str && i < count; str = strtok_r(nullptr, ", ", &p), i++) {
|
|
|
|
|
params[i] = strtoul(str, nullptr, 0);
|
|
|
|
|
}
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-01 17:20:43 +01:00
|
|
|
|
float CharToFloat(const char *str)
|
2018-04-05 11:49:43 +01:00
|
|
|
|
{
|
|
|
|
|
// simple ascii to double, because atof or strtod are too large
|
|
|
|
|
char strbuf[24];
|
|
|
|
|
|
2018-09-02 10:52:24 +01:00
|
|
|
|
strlcpy(strbuf, str, sizeof(strbuf));
|
2019-03-07 17:18:30 +00:00
|
|
|
|
char *pt = strbuf;
|
2020-08-04 15:33:05 +01:00
|
|
|
|
if (*pt == '\0') { return 0.0; }
|
|
|
|
|
|
2019-03-07 17:18:30 +00:00
|
|
|
|
while ((*pt != '\0') && isblank(*pt)) { pt++; } // Trim leading spaces
|
|
|
|
|
|
|
|
|
|
signed char sign = 1;
|
|
|
|
|
if (*pt == '-') { sign = -1; }
|
2020-06-15 17:27:04 +01:00
|
|
|
|
if (*pt == '-' || *pt == '+') { pt++; } // Skip any sign
|
2019-03-07 17:18:30 +00:00
|
|
|
|
|
2019-07-01 17:20:43 +01:00
|
|
|
|
float left = 0;
|
2019-03-07 17:18:30 +00:00
|
|
|
|
if (*pt != '.') {
|
|
|
|
|
left = atoi(pt); // Get left part
|
|
|
|
|
while (isdigit(*pt)) { pt++; } // Skip number
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-01 17:20:43 +01:00
|
|
|
|
float right = 0;
|
2019-03-07 17:18:30 +00:00
|
|
|
|
if (*pt == '.') {
|
|
|
|
|
pt++;
|
2020-06-15 17:27:04 +01:00
|
|
|
|
uint32_t max_decimals = 0;
|
|
|
|
|
while ((max_decimals < 8) && isdigit(pt[max_decimals])) { max_decimals++; }
|
|
|
|
|
pt[max_decimals] = '\0'; // Limit decimals to float max of 8
|
2019-03-07 17:18:30 +00:00
|
|
|
|
right = atoi(pt); // Decimal part
|
|
|
|
|
while (isdigit(*pt)) {
|
|
|
|
|
pt++;
|
2019-07-01 17:20:43 +01:00
|
|
|
|
right /= 10.0f;
|
2018-04-05 11:49:43 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-07 17:18:30 +00:00
|
|
|
|
|
2019-07-01 17:20:43 +01:00
|
|
|
|
float result = left + right;
|
2019-03-07 17:18:30 +00:00
|
|
|
|
if (sign < 0) {
|
|
|
|
|
return -result; // Add negative sign
|
|
|
|
|
}
|
2018-04-17 14:34:18 +01:00
|
|
|
|
return result;
|
2018-04-05 11:49:43 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-08-23 15:05:51 +01:00
|
|
|
|
int TextToInt(char *str)
|
|
|
|
|
{
|
|
|
|
|
char *p;
|
|
|
|
|
uint8_t radix = 10;
|
|
|
|
|
if ('#' == str[0]) {
|
|
|
|
|
radix = 16;
|
|
|
|
|
str++;
|
|
|
|
|
}
|
|
|
|
|
return strtol(str, &p, radix);
|
|
|
|
|
}
|
|
|
|
|
|
2018-01-20 11:12:39 +00:00
|
|
|
|
char* dtostrfd(double number, unsigned char prec, char *s)
|
2017-09-02 13:37:02 +01:00
|
|
|
|
{
|
2018-10-25 13:03:34 +01:00
|
|
|
|
if ((isnan(number)) || (isinf(number))) { // Fix for JSON output (https://stackoverflow.com/questions/1423081/json-left-out-infinity-and-nan-json-status-in-ecmascript)
|
2021-01-18 20:48:04 +00:00
|
|
|
|
strcpy_P(s, PSTR("null"));
|
2018-10-25 09:44:59 +01:00
|
|
|
|
return s;
|
|
|
|
|
} else {
|
|
|
|
|
return dtostrf(number, 1, prec, s);
|
|
|
|
|
}
|
2017-09-02 13:37:02 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-27 10:13:41 +01:00
|
|
|
|
char* Unescape(char* buffer, uint32_t* size)
|
2018-03-20 13:31:11 +00:00
|
|
|
|
{
|
|
|
|
|
uint8_t* read = (uint8_t*)buffer;
|
|
|
|
|
uint8_t* write = (uint8_t*)buffer;
|
2019-07-27 10:13:41 +01:00
|
|
|
|
int32_t start_size = *size;
|
|
|
|
|
int32_t end_size = *size;
|
2018-03-20 13:31:11 +00:00
|
|
|
|
uint8_t che = 0;
|
|
|
|
|
|
2019-01-17 16:48:34 +00:00
|
|
|
|
// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)buffer, *size);
|
|
|
|
|
|
2018-03-20 13:31:11 +00:00
|
|
|
|
while (start_size > 0) {
|
|
|
|
|
uint8_t ch = *read++;
|
|
|
|
|
start_size--;
|
|
|
|
|
if (ch != '\\') {
|
|
|
|
|
*write++ = ch;
|
|
|
|
|
} else {
|
|
|
|
|
if (start_size > 0) {
|
|
|
|
|
uint8_t chi = *read++;
|
|
|
|
|
start_size--;
|
|
|
|
|
end_size--;
|
|
|
|
|
switch (chi) {
|
|
|
|
|
case '\\': che = '\\'; break; // 5C Backslash
|
|
|
|
|
case 'a': che = '\a'; break; // 07 Bell (Alert)
|
|
|
|
|
case 'b': che = '\b'; break; // 08 Backspace
|
|
|
|
|
case 'e': che = '\e'; break; // 1B Escape
|
|
|
|
|
case 'f': che = '\f'; break; // 0C Formfeed
|
|
|
|
|
case 'n': che = '\n'; break; // 0A Linefeed (Newline)
|
|
|
|
|
case 'r': che = '\r'; break; // 0D Carriage return
|
2018-03-20 15:28:18 +00:00
|
|
|
|
case 's': che = ' '; break; // 20 Space
|
2018-03-20 13:31:11 +00:00
|
|
|
|
case 't': che = '\t'; break; // 09 Horizontal tab
|
|
|
|
|
case 'v': che = '\v'; break; // 0B Vertical tab
|
2019-01-17 16:48:34 +00:00
|
|
|
|
case 'x': {
|
|
|
|
|
uint8_t* start = read;
|
|
|
|
|
che = (uint8_t)strtol((const char*)read, (char**)&read, 16);
|
|
|
|
|
start_size -= (uint16_t)(read - start);
|
|
|
|
|
end_size -= (uint16_t)(read - start);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case '"': che = '\"'; break; // 22 Quotation mark
|
2018-03-20 13:31:11 +00:00
|
|
|
|
// case '?': che = '\?'; break; // 3F Question mark
|
|
|
|
|
default : {
|
|
|
|
|
che = chi;
|
|
|
|
|
*write++ = ch;
|
|
|
|
|
end_size++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*write++ = che;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*size = end_size;
|
2019-05-09 10:26:20 +01:00
|
|
|
|
*write++ = 0; // add the end string pointer reference
|
2019-01-17 16:48:34 +00:00
|
|
|
|
// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t*)buffer, *size);
|
|
|
|
|
|
2018-03-20 13:31:11 +00:00
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-10 16:00:37 +01:00
|
|
|
|
char* RemoveSpace(char* p) {
|
|
|
|
|
// Remove white-space character (' ','\t','\n','\v','\f','\r')
|
2018-10-14 11:53:11 +01:00
|
|
|
|
char* write = p;
|
|
|
|
|
char* read = p;
|
|
|
|
|
char ch = '.';
|
|
|
|
|
|
|
|
|
|
while (ch != '\0') {
|
|
|
|
|
ch = *read++;
|
|
|
|
|
if (!isspace(ch)) {
|
|
|
|
|
*write++ = ch;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-10 16:00:37 +01:00
|
|
|
|
char* RemoveControlCharacter(char* p) {
|
|
|
|
|
// Remove control character (0x00 .. 0x1F and 0x7F)
|
2020-10-10 15:44:35 +01:00
|
|
|
|
char* write = p;
|
|
|
|
|
char* read = p;
|
|
|
|
|
char ch = '.';
|
|
|
|
|
|
|
|
|
|
while (ch != '\0') {
|
|
|
|
|
ch = *read++;
|
|
|
|
|
if (!iscntrl(ch)) {
|
|
|
|
|
*write++ = ch;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-19 11:39:27 +01:00
|
|
|
|
*write++ = '\0';
|
2020-10-10 15:44:35 +01:00
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-26 11:18:16 +00:00
|
|
|
|
char* ReplaceChar(char* p, char find, char replace) {
|
2019-12-16 14:13:57 +00:00
|
|
|
|
char* write = (char*)p;
|
|
|
|
|
char* read = (char*)p;
|
|
|
|
|
char ch = '.';
|
|
|
|
|
|
|
|
|
|
while (ch != '\0') {
|
|
|
|
|
ch = *read++;
|
2020-12-26 11:18:16 +00:00
|
|
|
|
if (ch == find) {
|
|
|
|
|
ch = replace;
|
2019-12-16 14:13:57 +00:00
|
|
|
|
}
|
|
|
|
|
*write++ = ch;
|
|
|
|
|
}
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-26 11:18:16 +00:00
|
|
|
|
char* ReplaceCommaWithDot(char* p) {
|
|
|
|
|
return ReplaceChar(p, ',', '.');
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-26 16:10:07 +00:00
|
|
|
|
char* LowerCase(char* dest, const char* source)
|
|
|
|
|
{
|
|
|
|
|
char* write = dest;
|
|
|
|
|
const char* read = source;
|
|
|
|
|
char ch = '.';
|
|
|
|
|
|
|
|
|
|
while (ch != '\0') {
|
|
|
|
|
ch = *read++;
|
|
|
|
|
*write++ = tolower(ch);
|
|
|
|
|
}
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-23 16:20:20 +00:00
|
|
|
|
char* UpperCase(char* dest, const char* source)
|
|
|
|
|
{
|
|
|
|
|
char* write = dest;
|
|
|
|
|
const char* read = source;
|
|
|
|
|
char ch = '.';
|
|
|
|
|
|
|
|
|
|
while (ch != '\0') {
|
|
|
|
|
ch = *read++;
|
|
|
|
|
*write++ = toupper(ch);
|
|
|
|
|
}
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
char* UpperCase_P(char* dest, const char* source)
|
|
|
|
|
{
|
|
|
|
|
char* write = dest;
|
|
|
|
|
const char* read = source;
|
|
|
|
|
char ch = '.';
|
|
|
|
|
|
|
|
|
|
while (ch != '\0') {
|
|
|
|
|
ch = pgm_read_byte(read++);
|
|
|
|
|
*write++ = toupper(ch);
|
|
|
|
|
}
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-29 16:24:42 +01:00
|
|
|
|
char* Trim(char* p)
|
|
|
|
|
{
|
2020-08-04 15:33:05 +01:00
|
|
|
|
if (*p != '\0') {
|
|
|
|
|
while ((*p != '\0') && isblank(*p)) { p++; } // Trim leading spaces
|
|
|
|
|
char* q = p + strlen(p) -1;
|
|
|
|
|
while ((q >= p) && isblank(*q)) { q--; } // Trim trailing spaces
|
|
|
|
|
q++;
|
|
|
|
|
*q = '\0';
|
|
|
|
|
}
|
2018-05-29 16:24:42 +01:00
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-11 13:50:43 +00:00
|
|
|
|
String UrlEncode(const String& text) {
|
|
|
|
|
const char hex[] = "0123456789ABCDEF";
|
|
|
|
|
|
|
|
|
|
String encoded = "";
|
|
|
|
|
int len = text.length();
|
|
|
|
|
int i = 0;
|
|
|
|
|
while (i < len) {
|
|
|
|
|
char decodedChar = text.charAt(i++);
|
|
|
|
|
/*
|
|
|
|
|
if (('a' <= decodedChar && decodedChar <= 'z') ||
|
|
|
|
|
('A' <= decodedChar && decodedChar <= 'Z') ||
|
|
|
|
|
('0' <= decodedChar && decodedChar <= '9') ||
|
|
|
|
|
('=' == decodedChar)) {
|
|
|
|
|
encoded += decodedChar;
|
|
|
|
|
} else {
|
|
|
|
|
encoded += '%';
|
|
|
|
|
encoded += hex[decodedChar >> 4];
|
|
|
|
|
encoded += hex[decodedChar & 0xF];
|
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
if ((' ' == decodedChar) || ('+' == decodedChar)) {
|
|
|
|
|
encoded += '%';
|
|
|
|
|
encoded += hex[decodedChar >> 4];
|
|
|
|
|
encoded += hex[decodedChar & 0xF];
|
|
|
|
|
} else {
|
|
|
|
|
encoded += decodedChar;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return encoded;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-10 16:00:37 +01:00
|
|
|
|
/*
|
2019-12-09 20:43:30 +00:00
|
|
|
|
char* RemoveAllSpaces(char* p)
|
|
|
|
|
{
|
|
|
|
|
// remove any white space from the base64
|
|
|
|
|
char *cursor = p;
|
|
|
|
|
uint32_t offset = 0;
|
|
|
|
|
while (1) {
|
|
|
|
|
*cursor = *(cursor + offset);
|
|
|
|
|
if ((' ' == *cursor) || ('\t' == *cursor) || ('\n' == *cursor)) { // if space found, remove this char until end of string
|
|
|
|
|
offset++;
|
|
|
|
|
} else {
|
|
|
|
|
if (0 == *cursor) { break; }
|
|
|
|
|
cursor++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return p;
|
|
|
|
|
}
|
2020-10-10 16:00:37 +01:00
|
|
|
|
*/
|
2019-12-09 20:43:30 +00:00
|
|
|
|
|
2018-04-20 16:43:20 +01:00
|
|
|
|
char* NoAlNumToUnderscore(char* dest, const char* source)
|
2018-04-19 20:41:59 +01:00
|
|
|
|
{
|
|
|
|
|
char* write = dest;
|
|
|
|
|
const char* read = source;
|
|
|
|
|
char ch = '.';
|
|
|
|
|
|
|
|
|
|
while (ch != '\0') {
|
|
|
|
|
ch = *read++;
|
2018-04-20 16:43:20 +01:00
|
|
|
|
*write++ = (isalnum(ch) || ('\0' == ch)) ? ch : '_';
|
2018-04-19 20:41:59 +01:00
|
|
|
|
}
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-20 19:53:12 +00:00
|
|
|
|
char IndexSeparator(void)
|
2019-05-17 13:23:21 +01:00
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
// 20 bytes more costly !?!
|
|
|
|
|
const char separators[] = { "-_" };
|
|
|
|
|
|
|
|
|
|
return separators[Settings.flag3.use_underscore];
|
|
|
|
|
*/
|
2019-11-03 12:51:22 +00:00
|
|
|
|
if (Settings.flag3.use_underscore) { // SetOption64 - Enable "_" instead of "-" as sensor index separator
|
2019-05-17 13:23:21 +01:00
|
|
|
|
return '_';
|
|
|
|
|
} else {
|
|
|
|
|
return '-';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 16:14:20 +01:00
|
|
|
|
void SetShortcutDefault(void)
|
2018-08-27 13:53:09 +01:00
|
|
|
|
{
|
2019-07-28 16:14:20 +01:00
|
|
|
|
if ('\0' != XdrvMailbox.data[0]) { // There must be at least one character in the buffer
|
|
|
|
|
XdrvMailbox.data[0] = '0' + SC_DEFAULT; // SC_CLEAR, SC_DEFAULT, SC_USER
|
|
|
|
|
XdrvMailbox.data[1] = '\0';
|
2018-08-27 13:53:09 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-20 19:53:12 +00:00
|
|
|
|
uint8_t Shortcut(void)
|
2018-08-27 12:06:22 +01:00
|
|
|
|
{
|
|
|
|
|
uint8_t result = 10;
|
|
|
|
|
|
2019-07-28 16:14:20 +01:00
|
|
|
|
if ('\0' == XdrvMailbox.data[1]) { // Only allow single character input for shortcut
|
|
|
|
|
if (('"' == XdrvMailbox.data[0]) || ('0' == XdrvMailbox.data[0])) {
|
2018-08-27 13:53:09 +01:00
|
|
|
|
result = SC_CLEAR;
|
2018-08-27 12:06:22 +01:00
|
|
|
|
} else {
|
2019-07-28 16:14:20 +01:00
|
|
|
|
result = atoi(XdrvMailbox.data); // 1 = SC_DEFAULT, 2 = SC_USER
|
2018-08-27 12:06:22 +01:00
|
|
|
|
if (0 == result) {
|
|
|
|
|
result = 10;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-31 16:57:28 +01:00
|
|
|
|
bool ValidIpAddress(const char* str)
|
|
|
|
|
{
|
2021-01-18 15:32:58 +00:00
|
|
|
|
IPAddress ip_address;
|
|
|
|
|
return ip_address.fromString(str);
|
2019-03-31 16:57:28 +01:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-19 16:39:31 +00:00
|
|
|
|
bool ParseIPv4(uint32_t* addr, const char* str_p)
|
2017-03-25 16:24:11 +00:00
|
|
|
|
{
|
|
|
|
|
uint8_t *part = (uint8_t*)addr;
|
2019-01-28 13:08:33 +00:00
|
|
|
|
uint8_t i;
|
2021-01-18 20:48:04 +00:00
|
|
|
|
char str_r[strlen_P(str_p)+1];
|
|
|
|
|
char * str = &str_r[0];
|
|
|
|
|
strcpy_P(str, str_p);
|
2017-03-25 16:24:11 +00:00
|
|
|
|
|
|
|
|
|
*addr = 0;
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
2019-03-26 17:26:50 +00:00
|
|
|
|
part[i] = strtoul(str, nullptr, 10); // Convert byte
|
2017-03-25 16:24:11 +00:00
|
|
|
|
str = strchr(str, '.');
|
2019-03-26 17:26:50 +00:00
|
|
|
|
if (str == nullptr || *str == '\0') {
|
2017-04-25 17:24:42 +01:00
|
|
|
|
break; // No more separators, exit
|
|
|
|
|
}
|
2017-03-25 16:24:11 +00:00
|
|
|
|
str++; // Point to next character after separator
|
|
|
|
|
}
|
2017-04-25 17:24:42 +01:00
|
|
|
|
return (3 == i);
|
2017-03-25 16:24:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-30 16:54:19 +01:00
|
|
|
|
// Function to parse & check if version_str is newer than our currently installed version.
|
2017-10-18 17:22:34 +01:00
|
|
|
|
bool NewerVersion(char* version_str)
|
2017-06-30 16:54:19 +01:00
|
|
|
|
{
|
|
|
|
|
uint32_t version = 0;
|
2019-07-27 17:37:56 +01:00
|
|
|
|
uint32_t i = 0;
|
2017-06-30 16:54:19 +01:00
|
|
|
|
char *str_ptr;
|
|
|
|
|
|
2020-01-09 10:35:01 +00:00
|
|
|
|
char version_dup[strlen(version_str) +1];
|
|
|
|
|
strncpy(version_dup, version_str, sizeof(version_dup)); // Duplicate the version_str as strtok_r will modify it.
|
2017-06-30 16:54:19 +01:00
|
|
|
|
// Loop through the version string, splitting on '.' seperators.
|
2019-03-26 17:26:50 +00:00
|
|
|
|
for (char *str = strtok_r(version_dup, ".", &str_ptr); str && i < sizeof(VERSION); str = strtok_r(nullptr, ".", &str_ptr), i++) {
|
2017-06-30 16:54:19 +01:00
|
|
|
|
int field = atoi(str);
|
|
|
|
|
// The fields in a version string can only range from 0-255.
|
|
|
|
|
if ((field < 0) || (field > 255)) {
|
|
|
|
|
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++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 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);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
char* GetPowerDevice(char* dest, uint32_t idx, size_t size, uint32_t option)
|
2017-09-02 13:37:02 +01:00
|
|
|
|
{
|
2018-06-28 11:25:50 +01:00
|
|
|
|
strncpy_P(dest, S_RSLT_POWER, size); // POWER
|
2020-10-30 11:29:48 +00:00
|
|
|
|
if ((TasmotaGlobal.devices_present + option) > 1) {
|
2019-10-30 13:08:43 +00:00
|
|
|
|
char sidx[8];
|
2018-06-28 11:25:50 +01:00
|
|
|
|
snprintf_P(sidx, sizeof(sidx), PSTR("%d"), idx); // x
|
2018-11-22 14:41:30 +00:00
|
|
|
|
strncat(dest, sidx, size - strlen(dest) -1); // POWERx
|
2017-09-02 13:37:02 +01:00
|
|
|
|
}
|
|
|
|
|
return dest;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
char* GetPowerDevice(char* dest, uint32_t idx, size_t size)
|
2017-09-02 13:37:02 +01:00
|
|
|
|
{
|
2017-10-18 17:22:34 +01:00
|
|
|
|
return GetPowerDevice(dest, idx, size, 0);
|
2017-09-02 13:37:02 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-01-30 13:14:55 +00:00
|
|
|
|
float ConvertTemp(float c)
|
|
|
|
|
{
|
|
|
|
|
float result = c;
|
|
|
|
|
|
2020-10-28 16:32:07 +00:00
|
|
|
|
TasmotaGlobal.global_update = TasmotaGlobal.uptime;
|
2020-10-28 18:03:39 +00:00
|
|
|
|
TasmotaGlobal.temperature_celsius = c;
|
2019-04-15 17:12:42 +01:00
|
|
|
|
|
2019-11-03 11:33:36 +00:00
|
|
|
|
if (!isnan(c) && Settings.flag.temperature_conversion) { // SetOption8 - Switch between Celsius or Fahrenheit
|
|
|
|
|
result = c * 1.8 + 32; // Fahrenheit
|
2018-01-30 13:14:55 +00:00
|
|
|
|
}
|
2019-11-18 08:23:21 +00:00
|
|
|
|
result = result + (0.1 * Settings.temp_comp);
|
2018-01-30 13:14:55 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-18 10:07:38 +01:00
|
|
|
|
float ConvertTempToCelsius(float c)
|
|
|
|
|
{
|
|
|
|
|
float result = c;
|
|
|
|
|
|
2019-11-03 11:33:36 +00:00
|
|
|
|
if (!isnan(c) && Settings.flag.temperature_conversion) { // SetOption8 - Switch between Celsius or Fahrenheit
|
|
|
|
|
result = (c - 32) / 1.8; // Celsius
|
2019-04-18 10:07:38 +01:00
|
|
|
|
}
|
2019-11-18 08:23:21 +00:00
|
|
|
|
result = result + (0.1 * Settings.temp_comp);
|
2019-04-18 10:07:38 +01:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-14 13:32:09 +00:00
|
|
|
|
char TempUnit(void)
|
2018-01-30 13:14:55 +00:00
|
|
|
|
{
|
2020-05-07 23:53:13 +01:00
|
|
|
|
// SetOption8 - Switch between Celsius or Fahrenheit
|
2020-05-08 10:17:18 +01:00
|
|
|
|
return (Settings.flag.temperature_conversion) ? D_UNIT_FAHRENHEIT[0] : D_UNIT_CELSIUS[0];
|
2018-01-30 13:14:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-04-15 17:12:42 +01:00
|
|
|
|
float ConvertHumidity(float h)
|
|
|
|
|
{
|
2020-03-16 17:29:55 +00:00
|
|
|
|
float result = h;
|
|
|
|
|
|
2020-10-28 16:32:07 +00:00
|
|
|
|
TasmotaGlobal.global_update = TasmotaGlobal.uptime;
|
2020-10-28 18:03:39 +00:00
|
|
|
|
TasmotaGlobal.humidity = h;
|
2020-03-17 15:29:59 +00:00
|
|
|
|
|
2020-03-16 17:29:55 +00:00
|
|
|
|
result = result + (0.1 * Settings.hum_comp);
|
2019-04-15 17:12:42 +01:00
|
|
|
|
|
2020-03-16 17:29:55 +00:00
|
|
|
|
return result;
|
2019-04-15 17:12:42 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-16 15:52:22 +00:00
|
|
|
|
float CalcTempHumToDew(float t, float h)
|
|
|
|
|
{
|
2020-05-11 14:38:59 +01:00
|
|
|
|
if (isnan(h) || isnan(t)) { return NAN; }
|
2020-03-16 15:52:22 +00:00
|
|
|
|
|
|
|
|
|
if (Settings.flag.temperature_conversion) { // SetOption8 - Switch between Celsius or Fahrenheit
|
|
|
|
|
t = (t - 32) / 1.8; // Celsius
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float gamma = TaylorLog(h / 100) + 17.62 * t / (243.5 + t);
|
|
|
|
|
float result = (243.5 * gamma / (17.62 - gamma));
|
|
|
|
|
|
|
|
|
|
if (Settings.flag.temperature_conversion) { // SetOption8 - Switch between Celsius or Fahrenheit
|
|
|
|
|
result = result * 1.8 + 32; // Fahrenheit
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-04 15:55:12 +00:00
|
|
|
|
float ConvertPressure(float p)
|
2018-11-01 15:36:22 +00:00
|
|
|
|
{
|
|
|
|
|
float result = p;
|
|
|
|
|
|
2020-10-28 16:32:07 +00:00
|
|
|
|
TasmotaGlobal.global_update = TasmotaGlobal.uptime;
|
2020-10-28 18:03:39 +00:00
|
|
|
|
TasmotaGlobal.pressure_hpa = p;
|
2019-04-15 17:12:42 +01:00
|
|
|
|
|
2019-11-03 11:33:36 +00:00
|
|
|
|
if (!isnan(p) && Settings.flag.pressure_conversion) { // SetOption24 - Switch between hPa or mmHg pressure unit
|
|
|
|
|
result = p * 0.75006375541921; // mmHg
|
2018-11-01 15:36:22 +00:00
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 09:34:44 +00:00
|
|
|
|
float ConvertPressureForSeaLevel(float pressure)
|
|
|
|
|
{
|
|
|
|
|
if (pressure == 0.0f)
|
|
|
|
|
return pressure;
|
|
|
|
|
|
|
|
|
|
return ConvertPressure((pressure / FastPrecisePow(1.0 - ((float)Settings.altitude / 44330.0f), 5.255f)) - 21.6f);
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-14 13:32:09 +00:00
|
|
|
|
String PressureUnit(void)
|
2018-11-04 15:55:12 +00:00
|
|
|
|
{
|
2021-01-18 20:48:04 +00:00
|
|
|
|
return (Settings.flag.pressure_conversion) ? String(F(D_UNIT_MILLIMETER_MERCURY)) : String(F(D_UNIT_PRESSURE));
|
2018-11-04 15:55:12 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-02 14:51:33 +00:00
|
|
|
|
float ConvertSpeed(float s)
|
|
|
|
|
{
|
|
|
|
|
// Entry in m/s
|
|
|
|
|
return s * kSpeedConversionFactor[Settings.flag2.speed_conversion];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String SpeedUnit(void)
|
|
|
|
|
{
|
|
|
|
|
char speed[8];
|
|
|
|
|
return String(GetTextIndexed(speed, sizeof(speed), Settings.flag2.speed_conversion, kSpeedUnit));
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-14 13:32:09 +00:00
|
|
|
|
void ResetGlobalValues(void)
|
2018-07-24 17:41:50 +01:00
|
|
|
|
{
|
2020-10-28 16:32:07 +00:00
|
|
|
|
if ((TasmotaGlobal.uptime - TasmotaGlobal.global_update) > GLOBAL_VALUES_VALID) { // Reset after 5 minutes
|
|
|
|
|
TasmotaGlobal.global_update = 0;
|
2020-10-28 18:03:39 +00:00
|
|
|
|
TasmotaGlobal.temperature_celsius = NAN;
|
|
|
|
|
TasmotaGlobal.humidity = 0.0f;
|
|
|
|
|
TasmotaGlobal.pressure_hpa = 0.0f;
|
2018-07-24 17:41:50 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 14:48:42 +01:00
|
|
|
|
uint32_t SqrtInt(uint32_t num)
|
|
|
|
|
{
|
|
|
|
|
if (num <= 1) {
|
|
|
|
|
return num;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t x = num / 2;
|
|
|
|
|
uint32_t y;
|
|
|
|
|
do {
|
|
|
|
|
y = (x + num / x) / 2;
|
|
|
|
|
if (y >= x) {
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
x = y;
|
|
|
|
|
} while (true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t RoundSqrtInt(uint32_t num)
|
|
|
|
|
{
|
|
|
|
|
uint32_t s = SqrtInt(4 * num);
|
|
|
|
|
if (s & 1) {
|
|
|
|
|
s++;
|
|
|
|
|
}
|
|
|
|
|
return s / 2;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-27 17:37:56 +01:00
|
|
|
|
char* GetTextIndexed(char* destination, size_t destination_size, uint32_t index, const char* haystack)
|
2018-01-30 13:14:55 +00:00
|
|
|
|
{
|
|
|
|
|
// Returns empty string if not found
|
|
|
|
|
// Returns text of found
|
|
|
|
|
char* write = destination;
|
|
|
|
|
const char* read = haystack;
|
|
|
|
|
|
|
|
|
|
index++;
|
|
|
|
|
while (index--) {
|
|
|
|
|
size_t size = destination_size -1;
|
|
|
|
|
write = destination;
|
|
|
|
|
char ch = '.';
|
|
|
|
|
while ((ch != '\0') && (ch != '|')) {
|
|
|
|
|
ch = pgm_read_byte(read++);
|
|
|
|
|
if (size && (ch != '|')) {
|
|
|
|
|
*write++ = ch;
|
|
|
|
|
size--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (0 == ch) {
|
|
|
|
|
if (index) {
|
|
|
|
|
write = destination;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*write = '\0';
|
|
|
|
|
return destination;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GetCommandCode(char* destination, size_t destination_size, const char* needle, const char* haystack)
|
|
|
|
|
{
|
|
|
|
|
// Returns -1 of not found
|
|
|
|
|
// Returns index and command if found
|
|
|
|
|
int result = -1;
|
|
|
|
|
const char* read = haystack;
|
|
|
|
|
char* write = destination;
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
result++;
|
|
|
|
|
size_t size = destination_size -1;
|
|
|
|
|
write = destination;
|
|
|
|
|
char ch = '.';
|
|
|
|
|
while ((ch != '\0') && (ch != '|')) {
|
|
|
|
|
ch = pgm_read_byte(read++);
|
|
|
|
|
if (size && (ch != '|')) {
|
|
|
|
|
*write++ = ch;
|
|
|
|
|
size--;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*write = '\0';
|
|
|
|
|
if (!strcasecmp(needle, destination)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (0 == ch) {
|
|
|
|
|
result = -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
Add light synonyms
Add commands ``ChannelRemap``, ``MultiPWM``, ``AlexaCTRange``, ``PowerOnFade``, ``PWMCT``, ``WhiteBlend``, ``VirtualCT`` as synonyms for ``SetOption37, 68, 82, 91, 92, 105 and 106`` respectively
2021-01-26 13:56:58 +00:00
|
|
|
|
bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void), const uint8_t *synonyms = nullptr);
|
|
|
|
|
bool DecodeCommand(const char* haystack, void (* const MyCommand[])(void), const uint8_t *synonyms) {
|
2019-08-11 17:12:18 +01:00
|
|
|
|
GetTextIndexed(XdrvMailbox.command, CMDSZ, 0, haystack); // Get prefix if available
|
|
|
|
|
int prefix_length = strlen(XdrvMailbox.command);
|
2019-12-26 11:26:06 +00:00
|
|
|
|
if (prefix_length) {
|
|
|
|
|
char prefix[prefix_length +1];
|
|
|
|
|
snprintf_P(prefix, sizeof(prefix), XdrvMailbox.topic); // Copy prefix part only
|
|
|
|
|
if (strcasecmp(prefix, XdrvMailbox.command)) {
|
|
|
|
|
return false; // Prefix not in command
|
|
|
|
|
}
|
|
|
|
|
}
|
Add light synonyms
Add commands ``ChannelRemap``, ``MultiPWM``, ``AlexaCTRange``, ``PowerOnFade``, ``PWMCT``, ``WhiteBlend``, ``VirtualCT`` as synonyms for ``SetOption37, 68, 82, 91, 92, 105 and 106`` respectively
2021-01-26 13:56:58 +00:00
|
|
|
|
size_t syn_count = synonyms ? pgm_read_byte(synonyms) : 0;
|
2019-08-11 17:12:18 +01:00
|
|
|
|
int command_code = GetCommandCode(XdrvMailbox.command + prefix_length, CMDSZ, XdrvMailbox.topic + prefix_length, haystack);
|
|
|
|
|
if (command_code > 0) { // Skip prefix
|
Add light synonyms
Add commands ``ChannelRemap``, ``MultiPWM``, ``AlexaCTRange``, ``PowerOnFade``, ``PWMCT``, ``WhiteBlend``, ``VirtualCT`` as synonyms for ``SetOption37, 68, 82, 91, 92, 105 and 106`` respectively
2021-01-26 13:56:58 +00:00
|
|
|
|
if (command_code > syn_count) {
|
|
|
|
|
// We passed the synonyms zone, it's a regular command
|
|
|
|
|
XdrvMailbox.command_code = command_code - 1 - syn_count;
|
|
|
|
|
MyCommand[XdrvMailbox.command_code]();
|
|
|
|
|
} else {
|
|
|
|
|
// We have a SetOption synonym
|
|
|
|
|
XdrvMailbox.index = pgm_read_byte(synonyms + command_code);
|
|
|
|
|
CmndSetoptionBase(0);
|
|
|
|
|
}
|
2019-08-13 15:10:47 +01:00
|
|
|
|
return true;
|
2019-08-01 14:46:12 +01:00
|
|
|
|
}
|
2019-08-13 15:10:47 +01:00
|
|
|
|
return false;
|
2019-07-28 16:57:09 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-23 14:30:40 +00:00
|
|
|
|
const char kOptions[] PROGMEM = "OFF|" D_OFF "|FALSE|" D_FALSE "|STOP|" D_STOP "|" D_CELSIUS "|" // 0
|
|
|
|
|
"ON|" D_ON "|TRUE|" D_TRUE "|START|" D_START "|" D_FAHRENHEIT "|" D_USER "|" // 1
|
|
|
|
|
"TOGGLE|" D_TOGGLE "|" D_ADMIN "|" // 2
|
|
|
|
|
"BLINK|" D_BLINK "|" // 3
|
|
|
|
|
"BLINKOFF|" D_BLINKOFF "|" // 4
|
|
|
|
|
"ALL" ; // 255
|
|
|
|
|
|
|
|
|
|
const uint8_t sNumbers[] PROGMEM = { 0,0,0,0,0,0,0,
|
|
|
|
|
1,1,1,1,1,1,1,1,
|
2019-09-12 11:32:33 +01:00
|
|
|
|
2,2,2,
|
|
|
|
|
3,3,
|
|
|
|
|
4,4,
|
|
|
|
|
255 };
|
|
|
|
|
|
2018-05-06 15:07:42 +01:00
|
|
|
|
int GetStateNumber(char *state_text)
|
|
|
|
|
{
|
|
|
|
|
char command[CMDSZ];
|
2019-09-12 11:32:33 +01:00
|
|
|
|
int state_number = GetCommandCode(command, sizeof(command), state_text, kOptions);
|
|
|
|
|
if (state_number >= 0) {
|
|
|
|
|
state_number = pgm_read_byte(sNumbers + state_number);
|
2018-05-06 15:07:42 +01:00
|
|
|
|
}
|
|
|
|
|
return state_number;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-16 14:18:16 +01:00
|
|
|
|
String GetSerialConfig(void) {
|
2019-12-06 14:02:05 +00:00
|
|
|
|
// Settings.serial_config layout
|
|
|
|
|
// b000000xx - 5, 6, 7 or 8 data bits
|
|
|
|
|
// b00000x00 - 1 or 2 stop bits
|
|
|
|
|
// b000xx000 - None, Even or Odd parity
|
|
|
|
|
|
2021-01-18 20:48:04 +00:00
|
|
|
|
const static char kParity[] PROGMEM = "NEOI";
|
2019-12-06 14:02:05 +00:00
|
|
|
|
|
|
|
|
|
char config[4];
|
|
|
|
|
config[0] = '5' + (Settings.serial_config & 0x3);
|
2021-01-18 20:48:04 +00:00
|
|
|
|
config[1] = pgm_read_byte(&kParity[(Settings.serial_config >> 3) & 0x3]);
|
2019-12-06 14:02:05 +00:00
|
|
|
|
config[2] = '1' + ((Settings.serial_config >> 2) & 0x1);
|
|
|
|
|
config[3] = '\0';
|
|
|
|
|
return String(config);
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 16:03:22 +00:00
|
|
|
|
uint32_t GetSerialBaudrate(void) {
|
|
|
|
|
return (Serial.baudRate() / 300) * 300; // Fix ESP32 strange results like 115201
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-16 14:18:16 +01:00
|
|
|
|
void SetSerialBegin(void) {
|
2020-10-28 16:32:07 +00:00
|
|
|
|
TasmotaGlobal.baudrate = Settings.baudrate * 300;
|
2021-01-23 15:26:23 +00:00
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_SERIAL "Set to %s %d bit/s"), GetSerialConfig().c_str(), TasmotaGlobal.baudrate);
|
2019-12-06 14:02:05 +00:00
|
|
|
|
Serial.flush();
|
2020-10-30 17:27:34 +00:00
|
|
|
|
#ifdef ESP8266
|
2020-10-28 16:32:07 +00:00
|
|
|
|
Serial.begin(TasmotaGlobal.baudrate, (SerialConfig)pgm_read_byte(kTasmotaSerialConfig + Settings.serial_config));
|
2020-11-28 15:39:15 +00:00
|
|
|
|
#endif // ESP8266
|
|
|
|
|
#ifdef ESP32
|
2020-10-31 16:53:12 +00:00
|
|
|
|
delay(10); // Allow time to cleanup queues - if not used hangs ESP32
|
2020-10-31 16:28:33 +00:00
|
|
|
|
Serial.end();
|
|
|
|
|
delay(10); // Allow time to cleanup queues - if not used hangs ESP32
|
2020-10-30 17:27:34 +00:00
|
|
|
|
uint32_t config = pgm_read_dword(kTasmotaSerialConfig + Settings.serial_config);
|
|
|
|
|
Serial.begin(TasmotaGlobal.baudrate, config);
|
2020-11-28 15:39:15 +00:00
|
|
|
|
#endif // ESP32
|
2019-12-06 14:02:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-16 14:18:16 +01:00
|
|
|
|
void SetSerialConfig(uint32_t serial_config) {
|
2019-12-29 12:27:48 +00:00
|
|
|
|
if (serial_config > TS_SERIAL_8O2) {
|
|
|
|
|
serial_config = TS_SERIAL_8N1;
|
|
|
|
|
}
|
|
|
|
|
if (serial_config != Settings.serial_config) {
|
|
|
|
|
Settings.serial_config = serial_config;
|
|
|
|
|
SetSerialBegin();
|
|
|
|
|
}
|
2019-12-06 14:02:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-28 16:32:07 +00:00
|
|
|
|
void SetSerialBaudrate(uint32_t baudrate) {
|
|
|
|
|
TasmotaGlobal.baudrate = baudrate;
|
|
|
|
|
Settings.baudrate = TasmotaGlobal.baudrate / 300;
|
2020-12-07 16:03:22 +00:00
|
|
|
|
if (GetSerialBaudrate() != TasmotaGlobal.baudrate) {
|
2019-12-29 12:27:48 +00:00
|
|
|
|
SetSerialBegin();
|
|
|
|
|
}
|
2018-01-30 13:14:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-28 16:32:07 +00:00
|
|
|
|
void SetSerial(uint32_t baudrate, uint32_t serial_config) {
|
2019-12-27 14:13:40 +00:00
|
|
|
|
Settings.flag.mqtt_serial = 0; // CMND_SERIALSEND and CMND_SERIALLOG
|
|
|
|
|
Settings.serial_config = serial_config;
|
2020-10-28 16:32:07 +00:00
|
|
|
|
TasmotaGlobal.baudrate = baudrate;
|
|
|
|
|
Settings.baudrate = TasmotaGlobal.baudrate / 300;
|
2019-12-27 14:13:40 +00:00
|
|
|
|
SetSeriallog(LOG_LEVEL_NONE);
|
2019-12-29 12:27:48 +00:00
|
|
|
|
SetSerialBegin();
|
2019-12-27 14:13:40 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-10-16 14:18:16 +01:00
|
|
|
|
void ClaimSerial(void) {
|
2020-10-30 11:29:48 +00:00
|
|
|
|
TasmotaGlobal.serial_local = true;
|
2021-01-23 15:26:23 +00:00
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR("SNS: Hardware Serial"));
|
2018-05-10 16:21:26 +01:00
|
|
|
|
SetSeriallog(LOG_LEVEL_NONE);
|
2020-12-07 16:03:22 +00:00
|
|
|
|
TasmotaGlobal.baudrate = GetSerialBaudrate();
|
2020-10-28 16:32:07 +00:00
|
|
|
|
Settings.baudrate = TasmotaGlobal.baudrate / 300;
|
2018-05-09 12:43:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-16 10:21:44 +01:00
|
|
|
|
void SerialSendRaw(char *codes)
|
2018-09-16 15:09:00 +01:00
|
|
|
|
{
|
|
|
|
|
char *p;
|
|
|
|
|
char stemp[3];
|
|
|
|
|
uint8_t code;
|
2018-10-16 10:21:44 +01:00
|
|
|
|
|
|
|
|
|
int size = strlen(codes);
|
2018-09-16 15:09:00 +01:00
|
|
|
|
|
2019-10-15 07:27:32 +01:00
|
|
|
|
while (size > 1) {
|
2019-03-08 18:24:02 +00:00
|
|
|
|
strlcpy(stemp, codes, sizeof(stemp));
|
2018-09-16 15:09:00 +01:00
|
|
|
|
code = strtol(stemp, &p, 16);
|
|
|
|
|
Serial.write(code);
|
|
|
|
|
size -= 2;
|
|
|
|
|
codes += 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-19 11:34:43 +01:00
|
|
|
|
// values is a comma-delimited string: e.g. "72,101,108,108,111,32,87,111,114,108,100,33,10"
|
|
|
|
|
void SerialSendDecimal(char *values)
|
|
|
|
|
{
|
2020-07-20 01:14:01 +01:00
|
|
|
|
char *p;
|
2020-07-19 11:34:43 +01:00
|
|
|
|
uint8_t code;
|
2020-07-20 01:14:01 +01:00
|
|
|
|
for (char* str = strtok_r(values, ",", &p); str; str = strtok_r(nullptr, ",", &p)) {
|
2020-07-19 11:34:43 +01:00
|
|
|
|
code = (uint8_t)atoi(str);
|
|
|
|
|
Serial.write(code);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-02-20 15:19:48 +00:00
|
|
|
|
uint32_t GetHash(const char *buffer, size_t size)
|
|
|
|
|
{
|
|
|
|
|
uint32_t hash = 0;
|
2019-06-30 15:44:36 +01:00
|
|
|
|
for (uint32_t i = 0; i <= size; i++) {
|
2018-02-20 15:19:48 +00:00
|
|
|
|
hash += (uint8_t)*buffer++ * (i +1);
|
|
|
|
|
}
|
|
|
|
|
return hash;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-27 10:13:41 +01:00
|
|
|
|
void ShowSource(uint32_t source)
|
2018-05-28 14:52:42 +01:00
|
|
|
|
{
|
|
|
|
|
if ((source > 0) && (source < SRC_MAX)) {
|
|
|
|
|
char stemp1[20];
|
2021-01-23 15:26:23 +00:00
|
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("SRC: %s"), GetTextIndexed(stemp1, sizeof(stemp1), source, kCommandSource));
|
2018-05-28 14:52:42 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
void WebHexCode(uint32_t i, const char* code)
|
2019-04-09 12:56:19 +01:00
|
|
|
|
{
|
|
|
|
|
char scolor[10];
|
|
|
|
|
|
|
|
|
|
strlcpy(scolor, code, sizeof(scolor));
|
|
|
|
|
char* p = scolor;
|
|
|
|
|
if ('#' == p[0]) { p++; } // Skip
|
|
|
|
|
|
|
|
|
|
if (3 == strlen(p)) { // Convert 3 character to 6 character color code
|
|
|
|
|
p[6] = p[3]; // \0
|
|
|
|
|
p[5] = p[2]; // 3
|
|
|
|
|
p[4] = p[2]; // 3
|
|
|
|
|
p[3] = p[1]; // 2
|
|
|
|
|
p[2] = p[1]; // 2
|
|
|
|
|
p[1] = p[0]; // 1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t color = strtol(p, nullptr, 16);
|
|
|
|
|
/*
|
|
|
|
|
if (3 == strlen(p)) { // Convert 3 character to 6 character color code
|
|
|
|
|
uint32_t w = ((color & 0xF00) << 8) | ((color & 0x0F0) << 4) | (color & 0x00F); // 00010203
|
|
|
|
|
color = w | (w << 4); // 00112233
|
|
|
|
|
}
|
|
|
|
|
*/
|
2019-11-02 12:25:23 +00:00
|
|
|
|
uint32_t j = sizeof(Settings.web_color) / 3; // First area contains j = 18 colors
|
|
|
|
|
/*
|
|
|
|
|
if (i < j) {
|
2019-11-02 04:33:40 +00:00
|
|
|
|
Settings.web_color[i][0] = (color >> 16) & 0xFF; // Red
|
|
|
|
|
Settings.web_color[i][1] = (color >> 8) & 0xFF; // Green
|
|
|
|
|
Settings.web_color[i][2] = color & 0xFF; // Blue
|
|
|
|
|
} else {
|
|
|
|
|
Settings.web_color2[i-j][0] = (color >> 16) & 0xFF; // Red
|
|
|
|
|
Settings.web_color2[i-j][1] = (color >> 8) & 0xFF; // Green
|
|
|
|
|
Settings.web_color2[i-j][2] = color & 0xFF; // Blue
|
2019-11-02 12:25:23 +00:00
|
|
|
|
}
|
|
|
|
|
*/
|
|
|
|
|
if (i >= j) {
|
|
|
|
|
// Calculate i to index in Settings.web_color2 - Dirty(!) but saves 128 bytes code
|
|
|
|
|
i += ((((uint8_t*)&Settings.web_color2 - (uint8_t*)&Settings.web_color) / 3) - j);
|
|
|
|
|
}
|
|
|
|
|
Settings.web_color[i][0] = (color >> 16) & 0xFF; // Red
|
|
|
|
|
Settings.web_color[i][1] = (color >> 8) & 0xFF; // Green
|
|
|
|
|
Settings.web_color[i][2] = color & 0xFF; // Blue
|
2019-04-09 12:56:19 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
uint32_t WebColor(uint32_t i)
|
2019-04-09 12:56:19 +01:00
|
|
|
|
{
|
2019-11-02 12:25:23 +00:00
|
|
|
|
uint32_t j = sizeof(Settings.web_color) / 3; // First area contains j = 18 colors
|
|
|
|
|
/*
|
2019-11-02 04:33:40 +00:00
|
|
|
|
uint32_t tcolor = (i<j)? (Settings.web_color[i][0] << 16) | (Settings.web_color[i][1] << 8) | Settings.web_color[i][2] :
|
|
|
|
|
(Settings.web_color2[i-j][0] << 16) | (Settings.web_color2[i-j][1] << 8) | Settings.web_color2[i-j][2];
|
2019-11-02 12:25:23 +00:00
|
|
|
|
*/
|
|
|
|
|
if (i >= j) {
|
|
|
|
|
// Calculate i to index in Settings.web_color2 - Dirty(!) but saves 128 bytes code
|
|
|
|
|
i += ((((uint8_t*)&Settings.web_color2 - (uint8_t*)&Settings.web_color) / 3) - j);
|
|
|
|
|
}
|
|
|
|
|
uint32_t tcolor = (Settings.web_color[i][0] << 16) | (Settings.web_color[i][1] << 8) | Settings.web_color[i][2];
|
|
|
|
|
|
2019-04-09 12:56:19 +01:00
|
|
|
|
return tcolor;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-23 16:00:59 +00:00
|
|
|
|
/*********************************************************************************************\
|
|
|
|
|
* Response data handling
|
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
2019-09-04 17:06:34 +01:00
|
|
|
|
const uint16_t TIMESZ = 100; // Max number of characters in time string
|
|
|
|
|
|
|
|
|
|
char* ResponseGetTime(uint32_t format, char* time_str)
|
|
|
|
|
{
|
|
|
|
|
switch (format) {
|
|
|
|
|
case 1:
|
2019-09-06 14:46:40 +01:00
|
|
|
|
snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\",\"Epoch\":%u"), GetDateAndTime(DT_LOCAL).c_str(), UtcTime());
|
2019-09-04 17:06:34 +01:00
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":%u"), UtcTime());
|
|
|
|
|
break;
|
2020-05-25 10:44:17 +01:00
|
|
|
|
case 3:
|
2020-06-11 18:13:08 +01:00
|
|
|
|
snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL_MILLIS).c_str());
|
2020-05-25 10:44:17 +01:00
|
|
|
|
break;
|
2019-09-04 17:06:34 +01:00
|
|
|
|
default:
|
2019-09-06 14:46:40 +01:00
|
|
|
|
snprintf_P(time_str, TIMESZ, PSTR("{\"" D_JSON_TIME "\":\"%s\""), GetDateAndTime(DT_LOCAL).c_str());
|
2019-09-04 17:06:34 +01:00
|
|
|
|
}
|
|
|
|
|
return time_str;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-30 11:29:48 +00:00
|
|
|
|
void ResponseClear(void) {
|
|
|
|
|
TasmotaGlobal.mqtt_data[0] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-04 17:06:34 +01:00
|
|
|
|
int Response_P(const char* format, ...) // Content send snprintf_P char data
|
2019-03-23 16:00:59 +00:00
|
|
|
|
{
|
|
|
|
|
// This uses char strings. Be aware of sending %% if % is needed
|
2019-03-24 13:23:20 +00:00
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, format);
|
2021-01-24 15:35:36 +00:00
|
|
|
|
int len = ext_vsnprintf_P(TasmotaGlobal.mqtt_data, sizeof(TasmotaGlobal.mqtt_data), format, args);
|
2019-03-24 13:23:20 +00:00
|
|
|
|
va_end(args);
|
2019-03-23 16:00:59 +00:00
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-04 17:06:34 +01:00
|
|
|
|
int ResponseTime_P(const char* format, ...) // Content send snprintf_P char data
|
|
|
|
|
{
|
|
|
|
|
// This uses char strings. Be aware of sending %% if % is needed
|
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, format);
|
|
|
|
|
|
2020-10-30 11:29:48 +00:00
|
|
|
|
ResponseGetTime(Settings.flag2.time_format, TasmotaGlobal.mqtt_data);
|
2019-09-04 17:06:34 +01:00
|
|
|
|
|
2020-10-30 11:29:48 +00:00
|
|
|
|
int mlen = strlen(TasmotaGlobal.mqtt_data);
|
2021-01-24 15:35:36 +00:00
|
|
|
|
int len = ext_vsnprintf_P(TasmotaGlobal.mqtt_data + mlen, sizeof(TasmotaGlobal.mqtt_data) - mlen, format, args);
|
2019-09-04 17:06:34 +01:00
|
|
|
|
va_end(args);
|
|
|
|
|
return len + mlen;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-24 13:23:20 +00:00
|
|
|
|
int ResponseAppend_P(const char* format, ...) // Content send snprintf_P char data
|
2019-03-23 16:00:59 +00:00
|
|
|
|
{
|
|
|
|
|
// This uses char strings. Be aware of sending %% if % is needed
|
2019-03-24 13:23:20 +00:00
|
|
|
|
va_list args;
|
|
|
|
|
va_start(args, format);
|
2020-10-30 11:29:48 +00:00
|
|
|
|
int mlen = strlen(TasmotaGlobal.mqtt_data);
|
2021-01-24 15:35:36 +00:00
|
|
|
|
int len = ext_vsnprintf_P(TasmotaGlobal.mqtt_data + mlen, sizeof(TasmotaGlobal.mqtt_data) - mlen, format, args);
|
2019-03-24 13:23:20 +00:00
|
|
|
|
va_end(args);
|
2019-03-23 16:00:59 +00:00
|
|
|
|
return len + mlen;
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-04 17:06:34 +01:00
|
|
|
|
int ResponseAppendTimeFormat(uint32_t format)
|
2019-07-11 13:09:42 +01:00
|
|
|
|
{
|
2019-09-04 17:06:34 +01:00
|
|
|
|
char time_str[TIMESZ];
|
|
|
|
|
return ResponseAppend_P(ResponseGetTime(format, time_str));
|
2019-07-11 13:09:42 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-04 17:06:34 +01:00
|
|
|
|
int ResponseAppendTime(void)
|
2019-07-11 13:09:42 +01:00
|
|
|
|
{
|
2019-09-04 17:06:34 +01:00
|
|
|
|
return ResponseAppendTimeFormat(Settings.flag2.time_format);
|
2019-07-11 13:09:42 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-17 15:29:59 +00:00
|
|
|
|
int ResponseAppendTHD(float f_temperature, float f_humidity)
|
|
|
|
|
{
|
2021-01-24 15:35:36 +00:00
|
|
|
|
float dewpoint = CalcTempHumToDew(f_temperature, f_humidity);
|
|
|
|
|
return ResponseAppend_P(PSTR("\"" D_JSON_TEMPERATURE "\":%*_f,\"" D_JSON_HUMIDITY "\":%*_f,\"" D_JSON_DEWPOINT "\":%*_f"),
|
|
|
|
|
Settings.flag2.temperature_resolution, &f_temperature,
|
|
|
|
|
Settings.flag2.humidity_resolution, &f_humidity,
|
|
|
|
|
Settings.flag2.temperature_resolution, &dewpoint);
|
2020-03-17 15:29:59 +00:00
|
|
|
|
}
|
|
|
|
|
|
Add support for Shelly 1PM Template
Add support for Shelly 1PM Template {"NAME":"Shelly 1PM","GPIO":[56,0,0,0,82,134,0,0,0,0,0,21,0],"FLAG":2,"BASE":18} (#5716)
2019-05-13 17:26:07 +01:00
|
|
|
|
int ResponseJsonEnd(void)
|
|
|
|
|
{
|
|
|
|
|
return ResponseAppend_P(PSTR("}"));
|
|
|
|
|
}
|
|
|
|
|
|
2019-09-12 13:19:44 +01:00
|
|
|
|
int ResponseJsonEndEnd(void)
|
|
|
|
|
{
|
|
|
|
|
return ResponseAppend_P(PSTR("}}"));
|
|
|
|
|
}
|
|
|
|
|
|
2019-02-12 10:55:47 +00:00
|
|
|
|
/*********************************************************************************************\
|
|
|
|
|
* GPIO Module and Template management
|
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
2020-09-25 17:34:14 +01:00
|
|
|
|
#ifdef ESP8266
|
2020-09-25 17:15:31 +01:00
|
|
|
|
uint16_t GpioConvert(uint8_t gpio) {
|
2021-02-28 11:50:02 +00:00
|
|
|
|
if (gpio >= nitems(kGpioConvert)) {
|
2020-09-25 17:15:31 +01:00
|
|
|
|
return AGPIO(GPIO_USER);
|
|
|
|
|
}
|
|
|
|
|
return pgm_read_word(kGpioConvert + gpio);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint16_t Adc0Convert(uint8_t adc0) {
|
|
|
|
|
if (adc0 > 7) {
|
|
|
|
|
return AGPIO(GPIO_USER);
|
|
|
|
|
}
|
|
|
|
|
else if (0 == adc0) {
|
|
|
|
|
return GPIO_NONE;
|
|
|
|
|
}
|
|
|
|
|
return AGPIO(GPIO_ADC_INPUT + adc0 -1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TemplateConvert(uint8_t template8[], uint16_t template16[]) {
|
|
|
|
|
for (uint32_t i = 0; i < (sizeof(mytmplt) / 2) -2; i++) {
|
|
|
|
|
template16[i] = GpioConvert(template8[i]);
|
|
|
|
|
}
|
|
|
|
|
template16[(sizeof(mytmplt) / 2) -2] = Adc0Convert(template8[sizeof(mytmplt8285) -1]);
|
2020-09-29 13:08:48 +01:00
|
|
|
|
|
2021-01-23 15:26:23 +00:00
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("FNC: TemplateConvert"));
|
2020-09-29 17:10:21 +01:00
|
|
|
|
// AddLogBuffer(LOG_LEVEL_DEBUG, template8, sizeof(mytmplt8285));
|
|
|
|
|
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t*)template16, sizeof(mytmplt) / 2, 2);
|
2020-09-29 13:08:48 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ConvertGpios(void) {
|
2020-09-29 17:10:21 +01:00
|
|
|
|
if (Settings.gpio16_converted != 0xF5A0) {
|
2020-09-29 13:08:48 +01:00
|
|
|
|
// Convert 8-bit user template
|
2020-09-29 17:10:21 +01:00
|
|
|
|
TemplateConvert((uint8_t*)&Settings.ex_user_template8, (uint16_t*)&Settings.user_template);
|
2020-09-29 13:08:48 +01:00
|
|
|
|
|
2020-09-29 17:10:21 +01:00
|
|
|
|
for (uint32_t i = 0; i < sizeof(Settings.ex_my_gp8.io); i++) {
|
|
|
|
|
Settings.my_gp.io[i] = GpioConvert(Settings.ex_my_gp8.io[i]);
|
2020-09-29 13:08:48 +01:00
|
|
|
|
}
|
2020-09-29 17:10:21 +01:00
|
|
|
|
Settings.my_gp.io[(sizeof(myio) / 2) -1] = Adc0Convert(Settings.ex_my_adc0);
|
|
|
|
|
Settings.gpio16_converted = 0xF5A0;
|
2020-09-29 13:08:48 +01:00
|
|
|
|
|
2021-01-23 15:26:23 +00:00
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("FNC: ConvertGpios"));
|
2020-09-29 17:10:21 +01:00
|
|
|
|
// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t *)&Settings.ex_my_gp8.io, sizeof(myio8));
|
|
|
|
|
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t *)&Settings.my_gp.io, sizeof(myio) / 2, 2);
|
2020-09-29 13:08:48 +01:00
|
|
|
|
}
|
2020-09-25 17:15:31 +01:00
|
|
|
|
}
|
2020-09-30 13:19:18 +01:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
void DumpConvertTable(void) {
|
|
|
|
|
bool jsflg = false;
|
|
|
|
|
uint32_t lines = 1;
|
2021-02-28 11:50:02 +00:00
|
|
|
|
for (uint32_t i = 0; i < nitems(kGpioConvert); i++) {
|
2020-09-30 13:19:18 +01:00
|
|
|
|
uint32_t data = pgm_read_word(kGpioConvert + i);
|
|
|
|
|
if (!jsflg) {
|
|
|
|
|
Response_P(PSTR("{\"GPIOConversion%d\":{"), lines);
|
|
|
|
|
} else {
|
|
|
|
|
ResponseAppend_P(PSTR(","));
|
|
|
|
|
}
|
|
|
|
|
jsflg = true;
|
2021-02-28 11:50:02 +00:00
|
|
|
|
if ((ResponseAppend_P(PSTR("\"%d\":\"%d\""), i, data) > (MAX_LOGSZ - TOPSZ)) || (i == nitems(kGpioConvert) -1)) {
|
2020-09-30 13:19:18 +01:00
|
|
|
|
ResponseJsonEndEnd();
|
|
|
|
|
MqttPublishPrefixTopic_P(RESULT_OR_STAT, XdrvMailbox.command);
|
|
|
|
|
jsflg = false;
|
|
|
|
|
lines++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-28 11:50:02 +00:00
|
|
|
|
for (uint32_t i = 0; i < nitems(kAdcNiceList); i++) {
|
2020-09-30 13:19:18 +01:00
|
|
|
|
uint32_t data = pgm_read_word(kAdcNiceList + i);
|
|
|
|
|
if (!jsflg) {
|
|
|
|
|
Response_P(PSTR("{\"ADC0Conversion%d\":{"), lines);
|
|
|
|
|
} else {
|
|
|
|
|
ResponseAppend_P(PSTR(","));
|
|
|
|
|
}
|
|
|
|
|
jsflg = true;
|
2021-02-28 11:50:02 +00:00
|
|
|
|
if ((ResponseAppend_P(PSTR("\"%d\":\"%d\""), i, data) > (MAX_LOGSZ - TOPSZ)) || (i == nitems(kAdcNiceList) -1)) {
|
2020-09-30 13:19:18 +01:00
|
|
|
|
ResponseJsonEndEnd();
|
|
|
|
|
MqttPublishPrefixTopic_P(RESULT_OR_STAT, XdrvMailbox.command);
|
|
|
|
|
jsflg = false;
|
|
|
|
|
lines++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-30 11:29:48 +00:00
|
|
|
|
ResponseClear();
|
2020-09-30 13:19:18 +01:00
|
|
|
|
}
|
|
|
|
|
*/
|
2020-09-25 17:34:14 +01:00
|
|
|
|
#endif // ESP8266
|
2020-09-25 17:15:31 +01:00
|
|
|
|
|
2021-02-14 12:06:19 +00:00
|
|
|
|
int ICACHE_RAM_ATTR Pin(uint32_t gpio, uint32_t index = 0);
|
|
|
|
|
int ICACHE_RAM_ATTR Pin(uint32_t gpio, uint32_t index) {
|
2020-10-03 14:06:52 +01:00
|
|
|
|
uint16_t real_gpio = gpio << 5;
|
|
|
|
|
uint16_t mask = 0xFFE0;
|
|
|
|
|
if (index < GPIO_ANY) {
|
|
|
|
|
real_gpio += index;
|
|
|
|
|
mask = 0xFFFF;
|
|
|
|
|
}
|
2021-02-28 11:50:02 +00:00
|
|
|
|
for (uint32_t i = 0; i < nitems(TasmotaGlobal.gpio_pin); i++) {
|
2020-10-29 12:58:50 +00:00
|
|
|
|
if ((TasmotaGlobal.gpio_pin[i] & mask) == real_gpio) {
|
2020-04-26 16:33:27 +01:00
|
|
|
|
return i; // Pin number configured for gpio
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-02-14 12:06:19 +00:00
|
|
|
|
return -1; // No pin used for gpio
|
2020-04-26 16:33:27 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-20 16:58:21 +01:00
|
|
|
|
bool PinUsed(uint32_t gpio, uint32_t index = 0);
|
|
|
|
|
bool PinUsed(uint32_t gpio, uint32_t index) {
|
2021-02-15 16:26:55 +00:00
|
|
|
|
return (Pin(gpio, index) >= 0);
|
2020-04-27 11:54:07 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-20 16:58:21 +01:00
|
|
|
|
uint32_t GetPin(uint32_t lpin) {
|
2021-02-28 11:50:02 +00:00
|
|
|
|
if (lpin < nitems(TasmotaGlobal.gpio_pin)) {
|
2020-10-29 12:58:50 +00:00
|
|
|
|
return TasmotaGlobal.gpio_pin[lpin];
|
2020-06-20 16:58:21 +01:00
|
|
|
|
} else {
|
|
|
|
|
return GPIO_NONE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-26 16:33:27 +01:00
|
|
|
|
void SetPin(uint32_t lpin, uint32_t gpio) {
|
2020-10-29 12:58:50 +00:00
|
|
|
|
TasmotaGlobal.gpio_pin[lpin] = gpio;
|
2020-04-27 16:28:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-27 09:35:38 +01:00
|
|
|
|
void DigitalWrite(uint32_t gpio_pin, uint32_t index, uint32_t state)
|
2019-12-18 17:21:10 +00:00
|
|
|
|
{
|
2020-04-27 11:54:07 +01:00
|
|
|
|
if (PinUsed(gpio_pin, index)) {
|
2020-04-27 09:35:38 +01:00
|
|
|
|
digitalWrite(Pin(gpio_pin, index), state &1);
|
2019-12-18 17:21:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-20 19:53:12 +00:00
|
|
|
|
uint8_t ModuleNr(void)
|
2019-02-22 11:11:15 +00:00
|
|
|
|
{
|
|
|
|
|
// 0 = User module (255)
|
|
|
|
|
// 1 up = Template module 0 up
|
|
|
|
|
return (USER_MODULE == Settings.module) ? 0 : Settings.module +1;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-28 17:01:02 +00:00
|
|
|
|
uint32_t ModuleTemplate(uint32_t module) {
|
|
|
|
|
uint32_t i = 0;
|
|
|
|
|
for (i = 0; i < sizeof(kModuleNiceList); i++) {
|
2020-12-29 16:42:53 +00:00
|
|
|
|
if (module == pgm_read_byte(kModuleNiceList + i)) {
|
2020-12-28 17:01:02 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (i == sizeof(kModuleNiceList)) { i = 0; }
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
bool ValidTemplateModule(uint32_t index)
|
2019-04-05 14:27:06 +01:00
|
|
|
|
{
|
2019-06-30 15:44:36 +01:00
|
|
|
|
for (uint32_t i = 0; i < sizeof(kModuleNiceList); i++) {
|
2019-04-05 14:27:06 +01:00
|
|
|
|
if (index == pgm_read_byte(kModuleNiceList + i)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
bool ValidModule(uint32_t index)
|
2019-04-08 12:26:17 +01:00
|
|
|
|
{
|
|
|
|
|
if (index == USER_MODULE) { return true; }
|
|
|
|
|
return ValidTemplateModule(index);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-18 16:02:24 +01:00
|
|
|
|
bool ValidTemplate(const char *search) {
|
|
|
|
|
char template_name[strlen(SettingsText(SET_TEMPLATE_NAME)) +1];
|
|
|
|
|
char search_name[strlen(search) +1];
|
|
|
|
|
|
|
|
|
|
LowerCase(template_name, SettingsText(SET_TEMPLATE_NAME));
|
|
|
|
|
LowerCase(search_name, search);
|
|
|
|
|
|
|
|
|
|
return (strstr(template_name, search_name) != nullptr);
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
String AnyModuleName(uint32_t index)
|
2019-02-12 10:55:47 +00:00
|
|
|
|
{
|
|
|
|
|
if (USER_MODULE == index) {
|
2020-03-29 16:41:31 +01:00
|
|
|
|
return String(SettingsText(SET_TEMPLATE_NAME));
|
2019-02-12 10:55:47 +00:00
|
|
|
|
} else {
|
2020-12-29 16:42:53 +00:00
|
|
|
|
#ifdef ESP32
|
|
|
|
|
index = ModuleTemplate(index);
|
|
|
|
|
#endif
|
2020-03-29 16:41:31 +01:00
|
|
|
|
char name[TOPSZ];
|
2020-03-28 17:08:43 +00:00
|
|
|
|
return String(GetTextIndexed(name, sizeof(name), index, kModuleNames));
|
2019-02-12 10:55:47 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-20 19:53:12 +00:00
|
|
|
|
String ModuleName(void)
|
2019-02-12 10:55:47 +00:00
|
|
|
|
{
|
|
|
|
|
return AnyModuleName(Settings.module);
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-23 08:08:47 +01:00
|
|
|
|
#ifdef ESP8266
|
2020-08-22 17:03:20 +01:00
|
|
|
|
void GetInternalTemplate(void* ptr, uint32_t module, uint32_t option) {
|
|
|
|
|
uint8_t module_template = pgm_read_byte(kModuleTemplateList + module);
|
|
|
|
|
|
2021-01-23 15:26:23 +00:00
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("DBG: Template %d, Option %d"), module_template, option);
|
2020-08-22 17:03:20 +01:00
|
|
|
|
|
2020-09-25 17:15:31 +01:00
|
|
|
|
// template8 = GPIO 0,1,2,3,4,5,9,10,12,13,14,15,16,Adc
|
|
|
|
|
uint8_t template8[sizeof(mytmplt8285)] = { GPIO_NONE };
|
2020-08-22 17:03:20 +01:00
|
|
|
|
if (module_template < TMP_WEMOS) {
|
2020-09-25 17:15:31 +01:00
|
|
|
|
memcpy_P(&template8, &kModules8266[module_template], 6);
|
|
|
|
|
memcpy_P(&template8[8], &kModules8266[module_template].gp.io[6], 6);
|
2020-08-22 17:03:20 +01:00
|
|
|
|
} else {
|
2020-09-25 17:15:31 +01:00
|
|
|
|
memcpy_P(&template8, &kModules8285[module_template - TMP_WEMOS], sizeof(template8));
|
2020-08-22 17:03:20 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-09-29 13:08:48 +01:00
|
|
|
|
// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t *)&template8, sizeof(mytmplt8285));
|
2020-09-25 17:15:31 +01:00
|
|
|
|
|
|
|
|
|
// template16 = GPIO 0,1,2,3,4,5,9,10,12,13,14,15,16,Adc,Flg
|
|
|
|
|
uint16_t template16[(sizeof(mytmplt) / 2)] = { GPIO_NONE };
|
|
|
|
|
TemplateConvert(template8, template16);
|
2020-08-22 17:03:20 +01:00
|
|
|
|
|
|
|
|
|
uint32_t index = 0;
|
2020-09-29 13:08:48 +01:00
|
|
|
|
uint32_t size = sizeof(mycfgio); // template16[module_template].gp
|
2020-08-22 17:03:20 +01:00
|
|
|
|
switch (option) {
|
|
|
|
|
case 2: {
|
2020-09-29 13:08:48 +01:00
|
|
|
|
index = (sizeof(mytmplt) / 2) -1; // template16[module_template].flag
|
2020-09-25 17:15:31 +01:00
|
|
|
|
size = 2;
|
2020-08-22 17:03:20 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 3: {
|
2020-09-29 13:08:48 +01:00
|
|
|
|
size = sizeof(mytmplt); // template16[module_template]
|
2020-08-22 17:03:20 +01:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-09-25 17:15:31 +01:00
|
|
|
|
memcpy(ptr, &template16[index], size);
|
2020-08-22 17:03:20 +01:00
|
|
|
|
|
2021-01-23 15:26:23 +00:00
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("FNC: GetInternalTemplate option %d"), option);
|
2020-09-29 17:10:21 +01:00
|
|
|
|
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t *)ptr, size / 2, 2);
|
2020-08-22 17:03:20 +01:00
|
|
|
|
}
|
2020-08-23 08:08:47 +01:00
|
|
|
|
#endif // ESP8266
|
2020-08-22 17:03:20 +01:00
|
|
|
|
|
2020-11-24 14:14:22 +00:00
|
|
|
|
void TemplateGpios(myio *gp)
|
2019-02-12 10:55:47 +00:00
|
|
|
|
{
|
2020-04-28 17:27:07 +01:00
|
|
|
|
uint16_t *dest = (uint16_t *)gp;
|
2021-02-28 11:50:02 +00:00
|
|
|
|
uint16_t src[nitems(Settings.user_template.gp.io)];
|
2019-02-12 10:55:47 +00:00
|
|
|
|
|
2020-04-24 16:39:26 +01:00
|
|
|
|
memset(dest, GPIO_NONE, sizeof(myio));
|
2019-02-12 10:55:47 +00:00
|
|
|
|
if (USER_MODULE == Settings.module) {
|
|
|
|
|
memcpy(&src, &Settings.user_template.gp, sizeof(mycfgio));
|
|
|
|
|
} else {
|
2020-04-17 15:17:01 +01:00
|
|
|
|
#ifdef ESP8266
|
2020-08-22 17:03:20 +01:00
|
|
|
|
GetInternalTemplate(&src, Settings.module, 1);
|
2020-11-28 15:39:15 +00:00
|
|
|
|
#endif // ESP8266
|
|
|
|
|
#ifdef ESP32
|
2020-12-28 17:01:02 +00:00
|
|
|
|
memcpy_P(&src, &kModules[ModuleTemplate(Settings.module)].gp, sizeof(mycfgio));
|
2020-11-28 15:39:15 +00:00
|
|
|
|
#endif // ESP32
|
2019-02-12 10:55:47 +00:00
|
|
|
|
}
|
|
|
|
|
// 11 85 00 85 85 00 00 00 15 38 85 00 00 81
|
|
|
|
|
|
|
|
|
|
// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t *)&src, sizeof(mycfgio));
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
uint32_t j = 0;
|
2021-02-28 11:50:02 +00:00
|
|
|
|
for (uint32_t i = 0; i < nitems(Settings.user_template.gp.io); i++) {
|
2019-02-12 10:55:47 +00:00
|
|
|
|
if (6 == i) { j = 9; }
|
|
|
|
|
if (8 == i) { j = 12; }
|
|
|
|
|
dest[j] = src[i];
|
|
|
|
|
j++;
|
|
|
|
|
}
|
|
|
|
|
// 11 85 00 85 85 00 00 00 00 00 00 00 15 38 85 00 00 81
|
|
|
|
|
|
|
|
|
|
// AddLogBuffer(LOG_LEVEL_DEBUG, (uint8_t *)gp, sizeof(myio));
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-20 19:53:12 +00:00
|
|
|
|
gpio_flag ModuleFlag(void)
|
2019-02-12 10:55:47 +00:00
|
|
|
|
{
|
|
|
|
|
gpio_flag flag;
|
|
|
|
|
|
|
|
|
|
if (USER_MODULE == Settings.module) {
|
|
|
|
|
flag = Settings.user_template.flag;
|
|
|
|
|
} else {
|
2020-05-01 15:47:41 +01:00
|
|
|
|
#ifdef ESP8266
|
2020-08-22 17:03:20 +01:00
|
|
|
|
GetInternalTemplate(&flag, Settings.module, 2);
|
2020-11-28 15:39:15 +00:00
|
|
|
|
#endif // ESP8266
|
|
|
|
|
#ifdef ESP32
|
2020-12-28 17:01:02 +00:00
|
|
|
|
memcpy_P(&flag, &kModules[ModuleTemplate(Settings.module)].flag, sizeof(gpio_flag));
|
2020-11-28 15:39:15 +00:00
|
|
|
|
#endif // ESP32
|
2020-05-01 15:47:41 +01:00
|
|
|
|
}
|
2019-02-12 10:55:47 +00:00
|
|
|
|
|
|
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
void ModuleDefault(uint32_t module)
|
2019-02-12 10:55:47 +00:00
|
|
|
|
{
|
|
|
|
|
if (USER_MODULE == module) { module = WEMOS; } // Generic
|
|
|
|
|
Settings.user_template_base = module;
|
2020-12-29 16:42:53 +00:00
|
|
|
|
|
|
|
|
|
#ifdef ESP32
|
|
|
|
|
module = ModuleTemplate(module);
|
|
|
|
|
#endif
|
|
|
|
|
|
2020-03-29 16:41:31 +01:00
|
|
|
|
char name[TOPSZ];
|
|
|
|
|
SettingsUpdateText(SET_TEMPLATE_NAME, GetTextIndexed(name, sizeof(name), module, kModuleNames));
|
2020-04-17 15:17:01 +01:00
|
|
|
|
#ifdef ESP8266
|
2020-08-22 17:03:20 +01:00
|
|
|
|
GetInternalTemplate(&Settings.user_template, module, 3);
|
2020-11-28 15:39:15 +00:00
|
|
|
|
#endif // ESP8266
|
|
|
|
|
#ifdef ESP32
|
2020-12-28 17:01:02 +00:00
|
|
|
|
memcpy_P(&Settings.user_template, &kModules[module], sizeof(mytmplt));
|
2020-11-28 15:39:15 +00:00
|
|
|
|
#endif // ESP32
|
2019-02-12 10:55:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-20 19:53:12 +00:00
|
|
|
|
void SetModuleType(void)
|
2019-02-12 10:55:47 +00:00
|
|
|
|
{
|
2020-10-30 11:29:48 +00:00
|
|
|
|
TasmotaGlobal.module_type = (USER_MODULE == Settings.module) ? Settings.user_template_base : Settings.module;
|
2019-02-12 10:55:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-10-10 11:26:00 +01:00
|
|
|
|
bool FlashPin(uint32_t pin)
|
|
|
|
|
{
|
|
|
|
|
return (((pin > 5) && (pin < 9)) || (11 == pin));
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-31 15:17:30 +00:00
|
|
|
|
uint32_t ValidPin(uint32_t pin, uint32_t gpio) {
|
2019-10-10 11:26:00 +01:00
|
|
|
|
if (FlashPin(pin)) {
|
2019-12-02 09:31:33 +00:00
|
|
|
|
return GPIO_NONE; // Disable flash pins GPIO6, GPIO7, GPIO8 and GPIO11
|
2019-02-12 10:55:47 +00:00
|
|
|
|
}
|
2019-12-02 09:31:33 +00:00
|
|
|
|
|
2019-12-01 18:03:53 +00:00
|
|
|
|
if ((WEMOS == Settings.module) && !Settings.flag3.user_esp8285_enable) { // SetOption51 - Enable ESP8285 user GPIO's
|
2020-09-29 13:08:48 +01:00
|
|
|
|
if ((9 == pin) || (10 == pin)) {
|
2019-12-02 09:31:33 +00:00
|
|
|
|
return GPIO_NONE; // Disable possible flash GPIO9 and GPIO10
|
2019-11-21 14:00:35 +00:00
|
|
|
|
}
|
2019-02-12 10:55:47 +00:00
|
|
|
|
}
|
2019-11-21 14:00:35 +00:00
|
|
|
|
|
2019-12-02 09:31:33 +00:00
|
|
|
|
return gpio;
|
2019-02-12 10:55:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-31 15:17:30 +00:00
|
|
|
|
bool ValidGPIO(uint32_t pin, uint32_t gpio) {
|
2020-09-29 13:08:48 +01:00
|
|
|
|
#ifdef ESP8266
|
|
|
|
|
#ifdef USE_ADC_VCC
|
2020-09-29 13:41:45 +01:00
|
|
|
|
if (ADC0_PIN == pin) { return false; } // ADC0 = GPIO17
|
2020-09-29 13:08:48 +01:00
|
|
|
|
#endif
|
|
|
|
|
#endif
|
2020-06-24 14:58:56 +01:00
|
|
|
|
return (GPIO_USER == ValidPin(pin, BGPIO(gpio))); // Only allow GPIO_USER pins
|
2019-02-12 10:55:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-01 14:05:58 +00:00
|
|
|
|
bool ValidSpiPinUsed(uint32_t gpio) {
|
2020-12-31 15:17:30 +00:00
|
|
|
|
// ESP8266: If SPI pin selected chk if it's not one of the three Hardware SPI pins (12..14)
|
2021-01-01 14:05:58 +00:00
|
|
|
|
bool result = false;
|
2020-12-31 15:17:30 +00:00
|
|
|
|
if (PinUsed(gpio)) {
|
2021-02-14 12:06:19 +00:00
|
|
|
|
int pin = Pin(gpio);
|
2020-12-31 15:17:30 +00:00
|
|
|
|
result = ((pin < 12) || (pin > 14));
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2020-09-21 20:49:32 +01:00
|
|
|
|
bool JsonTemplate(char* dataBuf)
|
2019-02-21 13:31:31 +00:00
|
|
|
|
{
|
2020-09-29 17:10:21 +01:00
|
|
|
|
// Old: {"NAME":"Shelly 2.5","GPIO":[56,0,17,0,21,83,0,0,6,82,5,22,156],"FLAG":2,"BASE":18}
|
|
|
|
|
// New: {"NAME":"Shelly 2.5","GPIO":[320,0,32,0,224,193,0,0,640,192,608,225,3456,4736],"FLAG":0,"BASE":18}
|
Add command WebColor
* Add rule Http#Initialized
* Add command WebColor to change non-persistent GUI colors on the fly
Use a rule like:
rule3 on http#initialized do webcolor {"webcolor":["#eeeeee","#181818","#4f4f4f","#000000","#dddddd","#008000","#222222","#ff0000","#008000","#ffffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#ffffff","#999999","#000000"]} endon
or
rule3 on http#initialized do webcolor {"webcolor":["#eee","#181818","#4f4f4f","#000","#ddd","#008000","#222"]} endon
to make color changes persistent)
2019-04-08 21:37:39 +01:00
|
|
|
|
|
2020-11-06 16:09:13 +00:00
|
|
|
|
// AddLog_P(LOG_LEVEL_DEBUG, PSTR("TPL: |%s|"), dataBuf);
|
2020-10-10 11:20:15 +01:00
|
|
|
|
|
Add command WebColor
* Add rule Http#Initialized
* Add command WebColor to change non-persistent GUI colors on the fly
Use a rule like:
rule3 on http#initialized do webcolor {"webcolor":["#eeeeee","#181818","#4f4f4f","#000000","#dddddd","#008000","#222222","#ff0000","#008000","#ffffff","#1fa3ec","#0e70a4","#d43535","#931f1f","#47c266","#5aaf6f","#ffffff","#999999","#000000"]} endon
or
rule3 on http#initialized do webcolor {"webcolor":["#eee","#181818","#4f4f4f","#000","#ddd","#008000","#222"]} endon
to make color changes persistent)
2019-04-08 21:37:39 +01:00
|
|
|
|
if (strlen(dataBuf) < 9) { return false; } // Workaround exception if empty JSON like {} - Needs checks
|
|
|
|
|
|
2020-09-23 18:38:24 +01:00
|
|
|
|
JsonParser parser((char*) dataBuf);
|
|
|
|
|
JsonParserObject root = parser.getRootObject();
|
2020-09-21 20:49:32 +01:00
|
|
|
|
if (!root) { return false; }
|
2019-02-21 13:31:31 +00:00
|
|
|
|
|
|
|
|
|
// All parameters are optional allowing for partial changes
|
2020-09-21 20:49:32 +01:00
|
|
|
|
JsonParserToken val = root[PSTR(D_JSON_NAME)];
|
|
|
|
|
if (val) {
|
|
|
|
|
SettingsUpdateText(SET_TEMPLATE_NAME, val.getStr());
|
2019-02-21 13:31:31 +00:00
|
|
|
|
}
|
2020-09-21 20:49:32 +01:00
|
|
|
|
JsonParserArray arr = root[PSTR(D_JSON_GPIO)];
|
|
|
|
|
if (arr) {
|
2020-06-18 11:55:10 +01:00
|
|
|
|
#ifdef ESP8266
|
2020-10-04 14:46:20 +01:00
|
|
|
|
bool old_template = false;
|
|
|
|
|
uint8_t template8[sizeof(mytmplt8285)] = { GPIO_NONE };
|
|
|
|
|
if (13 == arr.size()) { // Possible old template
|
|
|
|
|
uint32_t gpio = 0;
|
2021-02-28 11:50:02 +00:00
|
|
|
|
for (uint32_t i = 0; i < nitems(template8) -1; i++) {
|
2020-10-04 14:46:20 +01:00
|
|
|
|
gpio = arr[i].getUInt();
|
|
|
|
|
if (gpio > 255) { // New templates might have values above 255
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
template8[i] = gpio;
|
|
|
|
|
}
|
|
|
|
|
old_template = (gpio < 256);
|
|
|
|
|
}
|
|
|
|
|
if (old_template) {
|
2020-09-30 14:26:46 +01:00
|
|
|
|
|
2021-01-23 15:26:23 +00:00
|
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("TPL: Converting template ..."));
|
2020-09-30 14:26:46 +01:00
|
|
|
|
|
|
|
|
|
val = root[PSTR(D_JSON_FLAG)];
|
|
|
|
|
if (val) {
|
2021-02-28 11:50:02 +00:00
|
|
|
|
template8[nitems(template8) -1] = val.getUInt() & 0x0F;
|
2020-09-30 14:26:46 +01:00
|
|
|
|
}
|
|
|
|
|
TemplateConvert(template8, Settings.user_template.gp.io);
|
|
|
|
|
Settings.user_template.flag.data = 0;
|
|
|
|
|
} else {
|
2020-06-18 11:55:10 +01:00
|
|
|
|
#endif
|
2021-02-28 11:50:02 +00:00
|
|
|
|
for (uint32_t i = 0; i < nitems(Settings.user_template.gp.io); i++) {
|
2020-10-04 11:46:06 +01:00
|
|
|
|
JsonParserToken val = arr[i];
|
|
|
|
|
if (!val) { break; }
|
|
|
|
|
uint16_t gpio = val.getUInt();
|
2020-09-30 14:26:46 +01:00
|
|
|
|
if (gpio == (AGPIO(GPIO_NONE) +1)) {
|
|
|
|
|
gpio = AGPIO(GPIO_USER);
|
|
|
|
|
}
|
|
|
|
|
Settings.user_template.gp.io[i] = gpio;
|
|
|
|
|
}
|
|
|
|
|
val = root[PSTR(D_JSON_FLAG)];
|
|
|
|
|
if (val) {
|
|
|
|
|
Settings.user_template.flag.data = val.getUInt();
|
|
|
|
|
}
|
2019-02-21 13:31:31 +00:00
|
|
|
|
}
|
2020-09-30 14:26:46 +01:00
|
|
|
|
#ifdef ESP8266
|
2019-02-21 13:31:31 +00:00
|
|
|
|
}
|
2020-09-30 14:26:46 +01:00
|
|
|
|
#endif
|
2020-09-21 20:49:32 +01:00
|
|
|
|
val = root[PSTR(D_JSON_BASE)];
|
|
|
|
|
if (val) {
|
|
|
|
|
uint32_t base = val.getUInt();
|
2019-04-08 12:26:17 +01:00
|
|
|
|
if ((0 == base) || !ValidTemplateModule(base -1)) { base = 18; }
|
2019-04-05 14:41:01 +01:00
|
|
|
|
Settings.user_template_base = base -1; // Default WEMOS
|
2019-02-21 13:31:31 +00:00
|
|
|
|
}
|
2020-10-10 11:20:15 +01:00
|
|
|
|
|
2021-01-23 15:26:23 +00:00
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("TPL: Converted"));
|
2020-10-10 11:20:15 +01:00
|
|
|
|
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t*)&Settings.user_template, sizeof(Settings.user_template) / 2, 2);
|
|
|
|
|
|
2019-02-21 13:31:31 +00:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-20 19:53:12 +00:00
|
|
|
|
void TemplateJson(void)
|
2019-02-12 10:55:47 +00:00
|
|
|
|
{
|
2021-01-23 15:26:23 +00:00
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("TPL: Show"));
|
2020-10-10 11:20:15 +01:00
|
|
|
|
// AddLogBufferSize(LOG_LEVEL_DEBUG, (uint8_t*)&Settings.user_template, sizeof(Settings.user_template) / 2, 2);
|
|
|
|
|
|
2020-03-29 16:41:31 +01:00
|
|
|
|
Response_P(PSTR("{\"" D_JSON_NAME "\":\"%s\",\"" D_JSON_GPIO "\":["), SettingsText(SET_TEMPLATE_NAME));
|
2021-02-28 11:50:02 +00:00
|
|
|
|
for (uint32_t i = 0; i < nitems(Settings.user_template.gp.io); i++) {
|
2020-06-18 11:55:10 +01:00
|
|
|
|
uint16_t gpio = Settings.user_template.gp.io[i];
|
|
|
|
|
if (gpio == AGPIO(GPIO_USER)) {
|
|
|
|
|
gpio = AGPIO(GPIO_NONE) +1;
|
|
|
|
|
}
|
|
|
|
|
ResponseAppend_P(PSTR("%s%d"), (i>0)?",":"", gpio);
|
2019-02-12 10:55:47 +00:00
|
|
|
|
}
|
2019-03-23 16:00:59 +00:00
|
|
|
|
ResponseAppend_P(PSTR("],\"" D_JSON_FLAG "\":%d,\"" D_JSON_BASE "\":%d}"), Settings.user_template.flag, Settings.user_template_base +1);
|
2019-02-12 10:55:47 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-02-15 16:22:06 +00:00
|
|
|
|
#if ( defined(USE_SCRIPT) && defined(SUPPORT_MQTT_EVENT) ) || defined (USE_DT_VARS)
|
|
|
|
|
|
|
|
|
|
/*********************************************************************************************\
|
|
|
|
|
* Parse json paylod with path
|
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
// parser object, source keys, delimiter, float result or NULL, string result or NULL, string size
|
|
|
|
|
// return 1 if numeric 2 if string, else 0 = not found
|
|
|
|
|
uint32_t JsonParsePath(JsonParserObject *jobj, const char *spath, char delim, float *nres, char *sres, uint32_t slen) {
|
|
|
|
|
uint32_t res = 0;
|
|
|
|
|
const char *cp = spath;
|
|
|
|
|
#ifdef DEBUG_JSON_PARSE_PATH
|
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR("JSON: parsing json key: %s from json: %s"), cp, jpath);
|
|
|
|
|
#endif
|
|
|
|
|
JsonParserObject obj = *jobj;
|
|
|
|
|
JsonParserObject lastobj = obj;
|
|
|
|
|
char selem[32];
|
|
|
|
|
uint8_t aindex = 0;
|
|
|
|
|
String value = "";
|
|
|
|
|
while (1) {
|
|
|
|
|
// read next element
|
|
|
|
|
for (uint32_t sp=0; sp<sizeof(selem)-1; sp++) {
|
|
|
|
|
if (!*cp || *cp==delim) {
|
|
|
|
|
selem[sp] = 0;
|
|
|
|
|
cp++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
selem[sp] = *cp++;
|
|
|
|
|
}
|
|
|
|
|
#ifdef DEBUG_JSON_PARSE_PATH
|
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR("JSON: cmp current key: %s"), selem);
|
|
|
|
|
#endif
|
|
|
|
|
// check for array
|
|
|
|
|
char *sp = strchr(selem,'[');
|
|
|
|
|
if (sp) {
|
|
|
|
|
*sp = 0;
|
|
|
|
|
aindex = atoi(sp+1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// now check element
|
|
|
|
|
obj = obj[selem];
|
|
|
|
|
if (!obj.isValid()) {
|
|
|
|
|
#ifdef DEBUG_JSON_PARSE_PATH
|
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR("JSON: obj invalid: %s"), selem);
|
|
|
|
|
#endif
|
|
|
|
|
JsonParserToken tok = lastobj[selem];
|
|
|
|
|
if (tok.isValid()) {
|
|
|
|
|
if (tok.isArray()) {
|
|
|
|
|
JsonParserArray array = JsonParserArray(tok);
|
|
|
|
|
value = array[aindex].getStr();
|
|
|
|
|
if (array.isNum()) {
|
|
|
|
|
if (nres) *nres=tok.getFloat();
|
|
|
|
|
res = 1;
|
|
|
|
|
} else {
|
|
|
|
|
res = 2;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
value = tok.getStr();
|
|
|
|
|
if (tok.isNum()) {
|
|
|
|
|
if (nres) *nres=tok.getFloat();
|
|
|
|
|
res = 1;
|
|
|
|
|
} else {
|
|
|
|
|
res = 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
#ifdef DEBUG_JSON_PARSE_PATH
|
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR("JSON: token invalid: %s"), selem);
|
|
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (obj.isObject()) {
|
|
|
|
|
lastobj = obj;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!*cp) break;
|
|
|
|
|
}
|
|
|
|
|
if (sres) {
|
|
|
|
|
strlcpy(sres,value.c_str(), slen);
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif // USE_SCRIPT
|
|
|
|
|
|
2018-08-26 14:42:35 +01:00
|
|
|
|
/*********************************************************************************************\
|
|
|
|
|
* Sleep aware time scheduler functions borrowed from ESPEasy
|
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
2020-02-15 11:57:23 +00:00
|
|
|
|
inline int32_t TimeDifference(uint32_t prev, uint32_t next)
|
|
|
|
|
{
|
2020-01-14 15:41:47 +00:00
|
|
|
|
return ((int32_t) (next - prev));
|
2018-08-26 14:42:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-01-14 15:41:47 +00:00
|
|
|
|
int32_t TimePassedSince(uint32_t timestamp)
|
2018-08-26 14:42:35 +01:00
|
|
|
|
{
|
|
|
|
|
// Compute the number of milliSeconds passed since timestamp given.
|
|
|
|
|
// Note: value can be negative if the timestamp has not yet been reached.
|
|
|
|
|
return TimeDifference(timestamp, millis());
|
|
|
|
|
}
|
|
|
|
|
|
2020-01-14 15:41:47 +00:00
|
|
|
|
bool TimeReached(uint32_t timer)
|
2018-08-26 14:42:35 +01:00
|
|
|
|
{
|
|
|
|
|
// Check if a certain timeout has been reached.
|
|
|
|
|
const long passed = TimePassedSince(timer);
|
|
|
|
|
return (passed >= 0);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-28 10:48:57 +00:00
|
|
|
|
void SetNextTimeInterval(uint32_t& timer, const uint32_t step)
|
2018-08-26 14:42:35 +01:00
|
|
|
|
{
|
|
|
|
|
timer += step;
|
|
|
|
|
const long passed = TimePassedSince(timer);
|
|
|
|
|
if (passed < 0) { return; } // Event has not yet happened, which is fine.
|
|
|
|
|
if (static_cast<unsigned long>(passed) > step) {
|
|
|
|
|
// No need to keep running behind, start again.
|
|
|
|
|
timer = millis() + step;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Try to get in sync again.
|
|
|
|
|
timer = millis() + (step - passed);
|
2018-05-17 14:36:45 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-02-15 11:57:23 +00:00
|
|
|
|
int32_t TimePassedSinceUsec(uint32_t timestamp)
|
|
|
|
|
{
|
|
|
|
|
return TimeDifference(timestamp, micros());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TimeReachedUsec(uint32_t timer)
|
|
|
|
|
{
|
|
|
|
|
// Check if a certain timeout has been reached.
|
|
|
|
|
const long passed = TimePassedSinceUsec(timer);
|
|
|
|
|
return (passed >= 0);
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-28 13:41:01 +00:00
|
|
|
|
/*********************************************************************************************\
|
|
|
|
|
* Basic I2C routines
|
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
|
|
|
|
#ifdef USE_I2C
|
2019-03-31 10:59:04 +01:00
|
|
|
|
const uint8_t I2C_RETRY_COUNTER = 3;
|
2017-01-28 13:41:01 +00:00
|
|
|
|
|
2019-09-29 10:52:22 +01:00
|
|
|
|
uint32_t i2c_active[4] = { 0 };
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
uint32_t i2c_buffer = 0;
|
|
|
|
|
|
|
|
|
|
bool I2cValidRead(uint8_t addr, uint8_t reg, uint8_t size)
|
2017-01-28 13:41:01 +00:00
|
|
|
|
{
|
2019-05-14 17:41:54 +01:00
|
|
|
|
uint8_t retry = I2C_RETRY_COUNTER;
|
|
|
|
|
bool status = false;
|
2017-01-28 13:41:01 +00:00
|
|
|
|
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
i2c_buffer = 0;
|
2019-05-14 17:41:54 +01:00
|
|
|
|
while (!status && retry) {
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
Wire.beginTransmission(addr); // start transmission to device
|
|
|
|
|
Wire.write(reg); // sends register address to read from
|
|
|
|
|
if (0 == Wire.endTransmission(false)) { // Try to become I2C Master, send data and collect bytes, keep master status for next request...
|
|
|
|
|
Wire.requestFrom((int)addr, (int)size); // send data n-bytes read
|
2017-04-25 17:24:42 +01:00
|
|
|
|
if (Wire.available() == size) {
|
2019-06-30 15:44:36 +01:00
|
|
|
|
for (uint32_t i = 0; i < size; i++) {
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
i2c_buffer = i2c_buffer << 8 | Wire.read(); // receive DATA
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
2019-05-14 17:41:54 +01:00
|
|
|
|
status = true;
|
2017-04-25 17:24:42 +01:00
|
|
|
|
}
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
2019-05-14 17:41:54 +01:00
|
|
|
|
retry--;
|
|
|
|
|
}
|
2020-06-01 10:51:57 +01:00
|
|
|
|
if (!retry) Wire.endTransmission();
|
2019-05-14 17:41:54 +01:00
|
|
|
|
return status;
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool I2cValidRead8(uint8_t *data, uint8_t addr, uint8_t reg)
|
|
|
|
|
{
|
|
|
|
|
bool status = I2cValidRead(addr, reg, 1);
|
|
|
|
|
*data = (uint8_t)i2c_buffer;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool I2cValidRead16(uint16_t *data, uint8_t addr, uint8_t reg)
|
|
|
|
|
{
|
|
|
|
|
bool status = I2cValidRead(addr, reg, 2);
|
|
|
|
|
*data = (uint16_t)i2c_buffer;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool I2cValidReadS16(int16_t *data, uint8_t addr, uint8_t reg)
|
|
|
|
|
{
|
|
|
|
|
bool status = I2cValidRead(addr, reg, 2);
|
|
|
|
|
*data = (int16_t)i2c_buffer;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool I2cValidRead16LE(uint16_t *data, uint8_t addr, uint8_t reg)
|
|
|
|
|
{
|
|
|
|
|
uint16_t ldata;
|
|
|
|
|
bool status = I2cValidRead16(&ldata, addr, reg);
|
|
|
|
|
*data = (ldata >> 8) | (ldata << 8);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool I2cValidReadS16_LE(int16_t *data, uint8_t addr, uint8_t reg)
|
|
|
|
|
{
|
|
|
|
|
uint16_t ldata;
|
|
|
|
|
bool status = I2cValidRead16LE(&ldata, addr, reg);
|
|
|
|
|
*data = (int16_t)ldata;
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool I2cValidRead24(int32_t *data, uint8_t addr, uint8_t reg)
|
|
|
|
|
{
|
|
|
|
|
bool status = I2cValidRead(addr, reg, 3);
|
|
|
|
|
*data = i2c_buffer;
|
|
|
|
|
return status;
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-18 17:22:34 +01:00
|
|
|
|
uint8_t I2cRead8(uint8_t addr, uint8_t reg)
|
2017-01-28 13:41:01 +00:00
|
|
|
|
{
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
I2cValidRead(addr, reg, 1);
|
|
|
|
|
return (uint8_t)i2c_buffer;
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-18 17:22:34 +01:00
|
|
|
|
uint16_t I2cRead16(uint8_t addr, uint8_t reg)
|
2017-01-28 13:41:01 +00:00
|
|
|
|
{
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
I2cValidRead(addr, reg, 2);
|
|
|
|
|
return (uint16_t)i2c_buffer;
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-18 17:22:34 +01:00
|
|
|
|
int16_t I2cReadS16(uint8_t addr, uint8_t reg)
|
2017-01-28 13:41:01 +00:00
|
|
|
|
{
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
I2cValidRead(addr, reg, 2);
|
|
|
|
|
return (int16_t)i2c_buffer;
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-18 17:22:34 +01:00
|
|
|
|
uint16_t I2cRead16LE(uint8_t addr, uint8_t reg)
|
2017-01-28 13:41:01 +00:00
|
|
|
|
{
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
I2cValidRead(addr, reg, 2);
|
|
|
|
|
uint16_t temp = (uint16_t)i2c_buffer;
|
2017-01-28 13:41:01 +00:00
|
|
|
|
return (temp >> 8) | (temp << 8);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-18 17:22:34 +01:00
|
|
|
|
int16_t I2cReadS16_LE(uint8_t addr, uint8_t reg)
|
2017-01-28 13:41:01 +00:00
|
|
|
|
{
|
2017-10-18 17:22:34 +01:00
|
|
|
|
return (int16_t)I2cRead16LE(addr, reg);
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-10-18 17:22:34 +01:00
|
|
|
|
int32_t I2cRead24(uint8_t addr, uint8_t reg)
|
2017-01-28 13:41:01 +00:00
|
|
|
|
{
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
I2cValidRead(addr, reg, 3);
|
|
|
|
|
return i2c_buffer;
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
2017-11-11 11:33:30 +00:00
|
|
|
|
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
bool I2cWrite(uint8_t addr, uint8_t reg, uint32_t val, uint8_t size)
|
2017-11-07 14:57:24 +00:00
|
|
|
|
{
|
2019-01-28 13:08:33 +00:00
|
|
|
|
uint8_t x = I2C_RETRY_COUNTER;
|
2017-11-07 14:57:24 +00:00
|
|
|
|
|
|
|
|
|
do {
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
Wire.beginTransmission((uint8_t)addr); // start transmission to device
|
|
|
|
|
Wire.write(reg); // sends register address to write to
|
2017-11-28 20:57:35 +00:00
|
|
|
|
uint8_t bytes = size;
|
|
|
|
|
while (bytes--) {
|
|
|
|
|
Wire.write((val >> (8 * bytes)) & 0xFF); // write data
|
|
|
|
|
}
|
2017-11-07 14:57:24 +00:00
|
|
|
|
x--;
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
} while (Wire.endTransmission(true) != 0 && x != 0); // end transmission
|
|
|
|
|
return (x);
|
2017-11-07 14:57:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
bool I2cWrite8(uint8_t addr, uint8_t reg, uint16_t val)
|
2017-01-28 13:41:01 +00:00
|
|
|
|
{
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
return I2cWrite(addr, reg, val, 1);
|
2017-11-11 11:33:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool I2cWrite16(uint8_t addr, uint8_t reg, uint16_t val)
|
|
|
|
|
{
|
v5.9.1j - Add Dual R2, Rewrite DS18x20 and fixes
5.9.1j
* Revert changes to xsns_05_ds18x20.ino and rename to
xsns_05_ds18x20_legacy.ino still needing library OneWire and providing
legacy JSON message:
*
"DS18x20":{"DS1":{"Type":"DS18B20","Address":"284CC48E04000079","Temperature":19.5},"DS2":{"Type":"DS18B20","Address":"283AC28304000052","Temperature":19.6}}
* Add new xdrv_05_ds18x20.ino free from library OneWire and add the
following features:
* Add support for DS1822
* Add forced setting of
12-bit resolution for selected device types (#1222)
* Add read
temperature retry counter (#1215)
* Fix lost sensors by performing
sensor probe at restart only thereby removing dynamic sensor probe
(#1215)
* Fix sensor address sorting using ascending sort on sensor
type followed by sensor address
* Rewrite JSON resulting in shorter
message allowing more sensors in default firmware image:
*
"DS18B20-1":{"Id":"00000483C23A","Temperature":19.5},"DS18B20-2":{"Id":"0000048EC44C","Temperature":19.6}
* Add additional define in user_config.h to select either single sensor
(defines disabled), new multi sensor (USE_DS18X20) or legacy multi
sensor (USE_DS18X20_LEGACY)
* Add support for Sonoff Dual R2 (#1249)
*
Fix ADS1115 detection (#1258)
2017-11-27 16:46:51 +00:00
|
|
|
|
return I2cWrite(addr, reg, val, 2);
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-07-04 17:41:00 +01:00
|
|
|
|
int8_t I2cReadBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
Wire.beginTransmission((uint8_t)addr);
|
|
|
|
|
Wire.write((uint8_t)reg);
|
|
|
|
|
Wire.endTransmission();
|
2019-01-28 13:08:33 +00:00
|
|
|
|
if (len != Wire.requestFrom((uint8_t)addr, (uint8_t)len)) {
|
2018-07-04 17:41:00 +01:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
while (len--) {
|
|
|
|
|
*reg_data = (uint8_t)Wire.read();
|
|
|
|
|
reg_data++;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int8_t I2cWriteBuffer(uint8_t addr, uint8_t reg, uint8_t *reg_data, uint16_t len)
|
|
|
|
|
{
|
|
|
|
|
Wire.beginTransmission((uint8_t)addr);
|
|
|
|
|
Wire.write((uint8_t)reg);
|
|
|
|
|
while (len--) {
|
|
|
|
|
Wire.write(*reg_data);
|
|
|
|
|
reg_data++;
|
|
|
|
|
}
|
|
|
|
|
Wire.endTransmission();
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-18 17:22:34 +01:00
|
|
|
|
void I2cScan(char *devs, unsigned int devs_len)
|
2017-01-28 13:41:01 +00:00
|
|
|
|
{
|
2018-09-29 11:34:24 +01:00
|
|
|
|
// Return error codes defined in twi.h and core_esp8266_si2c.c
|
|
|
|
|
// I2C_OK 0
|
|
|
|
|
// I2C_SCL_HELD_LOW 1 = SCL held low by another device, no procedure available to recover
|
2020-06-18 15:05:55 +01:00
|
|
|
|
// I2C_SCL_HELD_LOW_AFTER_READ 2 = I2C bus error. SCL held low beyond client clock stretch time
|
|
|
|
|
// I2C_SDA_HELD_LOW 3 = I2C bus error. SDA line held low by client/another_master after n bits
|
2018-09-29 11:34:24 +01:00
|
|
|
|
// I2C_SDA_HELD_LOW_AFTER_INIT 4 = line busy. SDA again held low by another device. 2nd master?
|
|
|
|
|
|
2019-01-28 13:08:33 +00:00
|
|
|
|
uint8_t error = 0;
|
|
|
|
|
uint8_t address = 0;
|
|
|
|
|
uint8_t any = 0;
|
2017-01-28 13:41:01 +00:00
|
|
|
|
|
2018-01-06 16:34:42 +00:00
|
|
|
|
snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_DEVICES_FOUND_AT));
|
2017-01-28 13:41:01 +00:00
|
|
|
|
for (address = 1; address <= 127; address++) {
|
|
|
|
|
Wire.beginTransmission(address);
|
|
|
|
|
error = Wire.endTransmission();
|
2017-04-25 17:24:42 +01:00
|
|
|
|
if (0 == error) {
|
2017-01-28 13:41:01 +00:00
|
|
|
|
any = 1;
|
2018-09-29 11:34:24 +01:00
|
|
|
|
snprintf_P(devs, devs_len, PSTR("%s 0x%02x"), devs, address);
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
2018-09-29 11:34:24 +01:00
|
|
|
|
else if (error != 2) { // Seems to happen anyway using this scan
|
|
|
|
|
any = 2;
|
|
|
|
|
snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"Error %d at 0x%02x"), error, address);
|
|
|
|
|
break;
|
2017-04-25 17:24:42 +01:00
|
|
|
|
}
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
|
|
|
|
if (any) {
|
2018-11-22 14:41:30 +00:00
|
|
|
|
strncat(devs, "\"}", devs_len - strlen(devs) -1);
|
2018-09-29 11:34:24 +01:00
|
|
|
|
}
|
|
|
|
|
else {
|
2018-01-06 16:34:42 +00:00
|
|
|
|
snprintf_P(devs, devs_len, PSTR("{\"" D_CMND_I2CSCAN "\":\"" D_JSON_I2CSCAN_NO_DEVICES_FOUND "\"}"));
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-25 13:27:30 +01:00
|
|
|
|
|
2019-11-07 15:56:05 +00:00
|
|
|
|
void I2cResetActive(uint32_t addr, uint32_t count = 1)
|
|
|
|
|
{
|
|
|
|
|
addr &= 0x7F; // Max I2C address is 127
|
|
|
|
|
count &= 0x7F; // Max 4 x 32 bits available
|
|
|
|
|
while (count-- && (addr < 128)) {
|
|
|
|
|
i2c_active[addr / 32] &= ~(1 << (addr % 32));
|
|
|
|
|
addr++;
|
|
|
|
|
}
|
2021-01-23 15:26:23 +00:00
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("I2C: Active %08X,%08X,%08X,%08X"), i2c_active[0], i2c_active[1], i2c_active[2], i2c_active[3]);
|
2019-11-07 15:56:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-29 10:52:22 +01:00
|
|
|
|
void I2cSetActive(uint32_t addr, uint32_t count = 1)
|
|
|
|
|
{
|
2019-10-01 15:21:39 +01:00
|
|
|
|
addr &= 0x7F; // Max I2C address is 127
|
|
|
|
|
count &= 0x7F; // Max 4 x 32 bits available
|
2019-09-29 10:52:22 +01:00
|
|
|
|
while (count-- && (addr < 128)) {
|
|
|
|
|
i2c_active[addr / 32] |= (1 << (addr % 32));
|
|
|
|
|
addr++;
|
|
|
|
|
}
|
2021-01-23 15:26:23 +00:00
|
|
|
|
// AddLog(LOG_LEVEL_DEBUG, PSTR("I2C: Active %08X,%08X,%08X,%08X"), i2c_active[0], i2c_active[1], i2c_active[2], i2c_active[3]);
|
2019-09-29 10:52:22 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-11-09 17:34:22 +00:00
|
|
|
|
void I2cSetActiveFound(uint32_t addr, const char *types)
|
|
|
|
|
{
|
|
|
|
|
I2cSetActive(addr);
|
2021-01-23 15:26:23 +00:00
|
|
|
|
AddLog(LOG_LEVEL_INFO, S_LOG_I2C_FOUND_AT, types, addr);
|
2019-11-09 17:34:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-09-29 10:52:22 +01:00
|
|
|
|
bool I2cActive(uint32_t addr)
|
|
|
|
|
{
|
2019-10-01 15:21:39 +01:00
|
|
|
|
addr &= 0x7F; // Max I2C address is 127
|
2019-09-29 10:52:22 +01:00
|
|
|
|
if (i2c_active[addr / 32] & (1 << (addr % 32))) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-07 15:56:05 +00:00
|
|
|
|
bool I2cSetDevice(uint32_t addr)
|
2017-10-25 13:27:30 +01:00
|
|
|
|
{
|
2019-10-01 15:21:39 +01:00
|
|
|
|
addr &= 0x7F; // Max I2C address is 127
|
2019-09-29 10:52:22 +01:00
|
|
|
|
if (I2cActive(addr)) {
|
|
|
|
|
return false; // If already active report as not present;
|
|
|
|
|
}
|
2019-11-06 16:48:38 +00:00
|
|
|
|
Wire.beginTransmission((uint8_t)addr);
|
2020-01-19 13:15:18 +00:00
|
|
|
|
return (0 == Wire.endTransmission());
|
2019-11-06 16:48:38 +00:00
|
|
|
|
}
|
2017-01-28 13:41:01 +00:00
|
|
|
|
#endif // USE_I2C
|
|
|
|
|
|
|
|
|
|
/*********************************************************************************************\
|
|
|
|
|
* Syslog
|
2017-11-04 15:36:51 +00:00
|
|
|
|
*
|
2017-11-03 17:07:25 +00:00
|
|
|
|
* Example:
|
2021-01-23 15:26:23 +00:00
|
|
|
|
* AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_LOG "Any value %d"), value);
|
2017-11-03 17:07:25 +00:00
|
|
|
|
*
|
2017-01-28 13:41:01 +00:00
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
void SetSeriallog(uint32_t loglevel)
|
2018-03-18 12:47:30 +00:00
|
|
|
|
{
|
|
|
|
|
Settings.seriallog_level = loglevel;
|
2020-10-30 11:29:48 +00:00
|
|
|
|
TasmotaGlobal.seriallog_level = loglevel;
|
2020-10-29 12:37:09 +00:00
|
|
|
|
TasmotaGlobal.seriallog_timer = 0;
|
2018-03-18 12:47:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
void SetSyslog(uint32_t loglevel)
|
2019-06-03 16:05:09 +01:00
|
|
|
|
{
|
|
|
|
|
Settings.syslog_level = loglevel;
|
2020-10-30 11:29:48 +00:00
|
|
|
|
TasmotaGlobal.syslog_level = loglevel;
|
2020-10-29 12:37:09 +00:00
|
|
|
|
TasmotaGlobal.syslog_timer = 0;
|
2019-06-03 16:05:09 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-23 15:35:46 +00:00
|
|
|
|
void SyslogAsync(bool refresh) {
|
2020-12-19 16:29:14 +00:00
|
|
|
|
static IPAddress syslog_host_addr; // Syslog host IP address
|
|
|
|
|
static uint32_t syslog_host_hash = 0; // Syslog host name hash
|
|
|
|
|
static uint32_t index = 1;
|
2020-12-18 14:37:20 +00:00
|
|
|
|
|
2020-12-23 09:56:21 +00:00
|
|
|
|
if (!TasmotaGlobal.syslog_level) { return; }
|
2020-12-22 14:26:07 +00:00
|
|
|
|
if (refresh && !NeedLogRefresh(TasmotaGlobal.syslog_level, index)) { return; }
|
|
|
|
|
|
2020-12-19 16:29:14 +00:00
|
|
|
|
char* line;
|
|
|
|
|
size_t len;
|
|
|
|
|
while (GetLog(TasmotaGlobal.syslog_level, &index, &line, &len)) {
|
2020-12-23 15:35:46 +00:00
|
|
|
|
// 00:00:02.096 HTP: Web server active on wemos5 with IP address 192.168.2.172
|
|
|
|
|
// HTP: Web server active on wemos5 with IP address 192.168.2.172
|
2020-12-20 16:15:22 +00:00
|
|
|
|
uint32_t mxtime = strchr(line, ' ') - line +1; // Remove mxtime
|
|
|
|
|
if (mxtime > 0) {
|
2020-12-23 15:35:46 +00:00
|
|
|
|
uint32_t current_hash = GetHash(SettingsText(SET_SYSLOG_HOST), strlen(SettingsText(SET_SYSLOG_HOST)));
|
|
|
|
|
if (syslog_host_hash != current_hash) {
|
|
|
|
|
syslog_host_hash = current_hash;
|
|
|
|
|
WiFi.hostByName(SettingsText(SET_SYSLOG_HOST), syslog_host_addr); // If sleep enabled this might result in exception so try to do it once using hash
|
|
|
|
|
}
|
2020-12-24 10:42:59 +00:00
|
|
|
|
if (!PortUdp.beginPacket(syslog_host_addr, Settings.syslog_port)) {
|
2020-12-23 15:35:46 +00:00
|
|
|
|
TasmotaGlobal.syslog_level = 0;
|
|
|
|
|
TasmotaGlobal.syslog_timer = SYSLOG_TIMER;
|
2021-01-23 15:26:23 +00:00
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR(D_LOG_APPLICATION D_SYSLOG_HOST_NOT_FOUND ". " D_RETRY_IN " %d " D_UNIT_SECOND), SYSLOG_TIMER);
|
2020-12-24 10:42:59 +00:00
|
|
|
|
return;
|
2020-12-23 15:35:46 +00:00
|
|
|
|
}
|
2020-12-24 10:42:59 +00:00
|
|
|
|
char log_data[len +72]; // Hostname + Id + log data
|
|
|
|
|
snprintf_P(log_data, sizeof(log_data), PSTR("%s ESP-"), NetworkHostname());
|
|
|
|
|
uint32_t preamble_len = strlen(log_data);
|
|
|
|
|
len -= mxtime;
|
|
|
|
|
strlcpy(log_data +preamble_len, line +mxtime, len);
|
|
|
|
|
// wemos5 ESP-HTP: Web server active on wemos5 with IP address 192.168.2.172
|
|
|
|
|
PortUdp_write(log_data, preamble_len + len);
|
|
|
|
|
PortUdp.endPacket();
|
|
|
|
|
delay(1); // Add time for UDP handling (#5512)
|
2020-12-18 14:37:20 +00:00
|
|
|
|
}
|
2020-12-19 16:29:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-22 14:26:07 +00:00
|
|
|
|
bool NeedLogRefresh(uint32_t req_loglevel, uint32_t index) {
|
2021-01-01 16:04:36 +00:00
|
|
|
|
|
|
|
|
|
#ifdef ESP32
|
|
|
|
|
// this takes the mutex, and will be release when the class is destroyed -
|
2020-12-31 11:36:35 +00:00
|
|
|
|
// i.e. when the functon leaves You CAN call mutex.give() to leave early.
|
2021-01-02 09:02:11 +00:00
|
|
|
|
TasAutoMutex mutex(&TasmotaGlobal.log_buffer_mutex);
|
2021-01-01 16:04:36 +00:00
|
|
|
|
#endif // ESP32
|
2020-12-31 11:36:35 +00:00
|
|
|
|
|
2020-12-22 14:26:07 +00:00
|
|
|
|
// Skip initial buffer fill
|
2021-01-02 15:20:15 +00:00
|
|
|
|
if (strlen(TasmotaGlobal.log_buffer) < LOG_BUFFER_SIZE - MAX_LOGSZ) { return false; }
|
2020-12-22 14:26:07 +00:00
|
|
|
|
|
|
|
|
|
char* line;
|
|
|
|
|
size_t len;
|
|
|
|
|
if (!GetLog(req_loglevel, &index, &line, &len)) { return false; }
|
|
|
|
|
return ((line - TasmotaGlobal.log_buffer) < LOG_BUFFER_SIZE / 4);
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-19 16:29:14 +00:00
|
|
|
|
bool GetLog(uint32_t req_loglevel, uint32_t* index_p, char** entry_pp, size_t* len_p) {
|
|
|
|
|
uint32_t index = *index_p;
|
|
|
|
|
|
2020-12-24 10:42:59 +00:00
|
|
|
|
if (TasmotaGlobal.uptime < 3) { return false; } // Allow time to setup correct log level
|
2020-12-19 16:29:14 +00:00
|
|
|
|
if (!req_loglevel || (index == TasmotaGlobal.log_buffer_pointer)) { return false; }
|
|
|
|
|
|
2021-01-01 16:04:36 +00:00
|
|
|
|
#ifdef ESP32
|
|
|
|
|
// this takes the mutex, and will be release when the class is destroyed -
|
2020-12-31 11:36:35 +00:00
|
|
|
|
// i.e. when the functon leaves You CAN call mutex.give() to leave early.
|
2021-01-02 09:02:11 +00:00
|
|
|
|
TasAutoMutex mutex(&TasmotaGlobal.log_buffer_mutex);
|
2021-01-01 16:04:36 +00:00
|
|
|
|
#endif // ESP32
|
2020-12-31 11:36:35 +00:00
|
|
|
|
|
2020-12-19 16:29:14 +00:00
|
|
|
|
if (!index) { // Dump all
|
|
|
|
|
index = TasmotaGlobal.log_buffer_pointer +1;
|
|
|
|
|
if (index > 255) { index = 1; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
uint32_t loglevel = 0;
|
|
|
|
|
char* entry_p = TasmotaGlobal.log_buffer;
|
2020-12-18 14:37:20 +00:00
|
|
|
|
do {
|
2020-12-19 16:29:14 +00:00
|
|
|
|
uint32_t cur_idx = *entry_p;
|
|
|
|
|
entry_p++;
|
|
|
|
|
size_t tmp = strchrspn(entry_p, '\1');
|
2020-12-18 15:30:37 +00:00
|
|
|
|
tmp++; // Skip terminating '\1'
|
2020-12-19 16:29:14 +00:00
|
|
|
|
if (cur_idx == index) { // Found the requested entry
|
|
|
|
|
loglevel = *entry_p - '0';
|
|
|
|
|
entry_p++; // Skip loglevel
|
2020-12-18 14:37:20 +00:00
|
|
|
|
len = tmp -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-12-19 16:29:14 +00:00
|
|
|
|
entry_p += tmp;
|
|
|
|
|
} while (entry_p < TasmotaGlobal.log_buffer + LOG_BUFFER_SIZE && *entry_p != '\0');
|
|
|
|
|
index++;
|
|
|
|
|
if (index > 255) { index = 1; } // Skip 0 as it is not allowed
|
|
|
|
|
*index_p = index;
|
|
|
|
|
if ((len > 0) &&
|
|
|
|
|
(loglevel <= req_loglevel) &&
|
|
|
|
|
(TasmotaGlobal.masterlog_level <= req_loglevel)) {
|
|
|
|
|
*entry_pp = entry_p;
|
|
|
|
|
*len_p = len;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
delay(0);
|
|
|
|
|
} while (index != TasmotaGlobal.log_buffer_pointer);
|
|
|
|
|
return false;
|
2020-12-18 14:37:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-23 17:00:59 +00:00
|
|
|
|
void AddLogData(uint32_t loglevel, const char* log_data) {
|
2020-12-31 11:36:35 +00:00
|
|
|
|
|
2021-01-01 16:04:36 +00:00
|
|
|
|
#ifdef ESP32
|
|
|
|
|
// this takes the mutex, and will be release when the class is destroyed -
|
|
|
|
|
// i.e. when the functon leaves You CAN call mutex.give() to leave early.
|
2021-01-02 09:02:11 +00:00
|
|
|
|
TasAutoMutex mutex(&TasmotaGlobal.log_buffer_mutex);
|
2021-01-01 16:04:36 +00:00
|
|
|
|
#endif // ESP32
|
2020-12-31 11:36:35 +00:00
|
|
|
|
|
2020-12-17 13:16:53 +00:00
|
|
|
|
char mxtime[14]; // "13:45:21.999 "
|
|
|
|
|
snprintf_P(mxtime, sizeof(mxtime), PSTR("%02d" D_HOUR_MINUTE_SEPARATOR "%02d" D_MINUTE_SECOND_SEPARATOR "%02d.%03d "), RtcTime.hour, RtcTime.minute, RtcTime.second, RtcMillis());
|
2017-01-28 13:41:01 +00:00
|
|
|
|
|
2020-10-30 11:29:48 +00:00
|
|
|
|
if ((loglevel <= TasmotaGlobal.seriallog_level) &&
|
|
|
|
|
(TasmotaGlobal.masterlog_level <= TasmotaGlobal.seriallog_level)) {
|
2020-12-23 17:00:59 +00:00
|
|
|
|
Serial.printf("%s%s\r\n", mxtime, log_data);
|
2017-09-14 13:20:27 +01:00
|
|
|
|
}
|
2020-12-18 14:37:20 +00:00
|
|
|
|
|
|
|
|
|
uint32_t highest_loglevel = Settings.weblog_level;
|
|
|
|
|
if (Settings.mqttlog_level > highest_loglevel) { highest_loglevel = Settings.mqttlog_level; }
|
|
|
|
|
if (TasmotaGlobal.syslog_level > highest_loglevel) { highest_loglevel = TasmotaGlobal.syslog_level; }
|
2020-12-22 14:26:07 +00:00
|
|
|
|
if (TasmotaGlobal.templog_level > highest_loglevel) { highest_loglevel = TasmotaGlobal.templog_level; }
|
2020-12-24 10:42:59 +00:00
|
|
|
|
if (TasmotaGlobal.uptime < 3) { highest_loglevel = LOG_LEVEL_DEBUG_MORE; } // Log all before setup correct log level
|
2020-12-22 14:26:07 +00:00
|
|
|
|
|
2020-12-18 15:30:37 +00:00
|
|
|
|
if ((loglevel <= highest_loglevel) && // Log only when needed
|
2020-12-18 14:37:20 +00:00
|
|
|
|
(TasmotaGlobal.masterlog_level <= highest_loglevel)) {
|
2018-01-30 13:14:55 +00:00
|
|
|
|
// Delimited, zero-terminated buffer of log lines.
|
2020-12-18 14:37:20 +00:00
|
|
|
|
// Each entry has this format: [index][loglevel][log data]['\1']
|
2020-12-18 15:30:37 +00:00
|
|
|
|
TasmotaGlobal.log_buffer_pointer &= 0xFF;
|
|
|
|
|
if (!TasmotaGlobal.log_buffer_pointer) {
|
|
|
|
|
TasmotaGlobal.log_buffer_pointer++; // Index 0 is not allowed as it is the end of char string
|
2020-10-28 16:32:07 +00:00
|
|
|
|
}
|
2020-12-18 15:30:37 +00:00
|
|
|
|
while (TasmotaGlobal.log_buffer_pointer == TasmotaGlobal.log_buffer[0] || // If log already holds the next index, remove it
|
2020-12-23 17:00:59 +00:00
|
|
|
|
strlen(TasmotaGlobal.log_buffer) + strlen(log_data) + strlen(mxtime) + 4 > LOG_BUFFER_SIZE) // 4 = log_buffer_pointer + '\1' + '\0'
|
2018-01-30 13:14:55 +00:00
|
|
|
|
{
|
2020-12-18 15:30:37 +00:00
|
|
|
|
char* it = TasmotaGlobal.log_buffer;
|
|
|
|
|
it++; // Skip log_buffer_pointer
|
|
|
|
|
it += strchrspn(it, '\1'); // Skip log line
|
|
|
|
|
it++; // Skip delimiting "\1"
|
|
|
|
|
memmove(TasmotaGlobal.log_buffer, it, LOG_BUFFER_SIZE -(it-TasmotaGlobal.log_buffer)); // Move buffer forward to remove oldest log line
|
2017-04-25 17:24:42 +01:00
|
|
|
|
}
|
2020-12-18 15:30:37 +00:00
|
|
|
|
snprintf_P(TasmotaGlobal.log_buffer, sizeof(TasmotaGlobal.log_buffer), PSTR("%s%c%c%s%s\1"),
|
2020-12-23 17:00:59 +00:00
|
|
|
|
TasmotaGlobal.log_buffer, TasmotaGlobal.log_buffer_pointer++, '0'+loglevel, mxtime, log_data);
|
2020-12-18 15:30:37 +00:00
|
|
|
|
TasmotaGlobal.log_buffer_pointer &= 0xFF;
|
|
|
|
|
if (!TasmotaGlobal.log_buffer_pointer) {
|
|
|
|
|
TasmotaGlobal.log_buffer_pointer++; // Index 0 is not allowed as it is the end of char string
|
2020-10-28 16:32:07 +00:00
|
|
|
|
}
|
2017-01-28 13:41:01 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2020-01-14 11:47:48 +00:00
|
|
|
|
|
2021-01-23 12:21:17 +00:00
|
|
|
|
void AddLog(uint32_t loglevel, PGM_P formatP, ...) {
|
|
|
|
|
// To save stack space support logging for max text length of 128 characters
|
2021-01-02 15:20:15 +00:00
|
|
|
|
char log_data[LOGSZ +4];
|
2020-12-23 17:00:59 +00:00
|
|
|
|
|
2019-03-07 17:18:30 +00:00
|
|
|
|
va_list arg;
|
|
|
|
|
va_start(arg, formatP);
|
2021-01-24 15:35:36 +00:00
|
|
|
|
uint32_t len = ext_vsnprintf_P(log_data, LOGSZ +1, formatP, arg);
|
2019-03-07 17:18:30 +00:00
|
|
|
|
va_end(arg);
|
2021-01-02 15:20:15 +00:00
|
|
|
|
if (len > LOGSZ) { strcat(log_data, "..."); } // Actual data is more
|
2021-01-02 13:59:02 +00:00
|
|
|
|
|
|
|
|
|
#ifdef DEBUG_TASMOTA_CORE
|
|
|
|
|
// Profile max_len
|
|
|
|
|
static uint32_t max_len = 0;
|
|
|
|
|
if (len > max_len) {
|
|
|
|
|
max_len = len;
|
2021-01-23 12:21:17 +00:00
|
|
|
|
Serial.printf("PRF: AddLog %d\n", max_len);
|
2021-01-02 13:59:02 +00:00
|
|
|
|
}
|
|
|
|
|
#endif
|
2019-03-07 17:18:30 +00:00
|
|
|
|
|
2020-12-23 17:00:59 +00:00
|
|
|
|
AddLogData(loglevel, log_data);
|
2019-03-07 17:18:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-01-23 12:21:17 +00:00
|
|
|
|
void AddLog_P(uint32_t loglevel, PGM_P formatP, ...) {
|
|
|
|
|
// Use more stack space to support logging for max text length of 700 characters
|
|
|
|
|
char log_data[MAX_LOGSZ];
|
|
|
|
|
|
|
|
|
|
va_list arg;
|
|
|
|
|
va_start(arg, formatP);
|
2021-01-24 16:52:56 +00:00
|
|
|
|
uint32_t len = ext_vsnprintf_P(log_data, sizeof(log_data), formatP, arg);
|
2021-01-23 12:21:17 +00:00
|
|
|
|
va_end(arg);
|
|
|
|
|
|
|
|
|
|
AddLogData(loglevel, log_data);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-08 14:17:39 +01:00
|
|
|
|
void AddLog_Debug(PGM_P formatP, ...)
|
|
|
|
|
{
|
2021-01-02 15:20:15 +00:00
|
|
|
|
char log_data[MAX_LOGSZ];
|
2020-12-23 17:00:59 +00:00
|
|
|
|
|
2019-08-08 14:17:39 +01:00
|
|
|
|
va_list arg;
|
|
|
|
|
va_start(arg, formatP);
|
2021-01-24 15:35:36 +00:00
|
|
|
|
uint32_t len = ext_vsnprintf_P(log_data, sizeof(log_data), formatP, arg);
|
2019-08-08 14:17:39 +01:00
|
|
|
|
va_end(arg);
|
|
|
|
|
|
2020-12-23 17:00:59 +00:00
|
|
|
|
AddLogData(LOG_LEVEL_DEBUG, log_data);
|
2019-08-08 14:17:39 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
void AddLogBuffer(uint32_t loglevel, uint8_t *buffer, uint32_t count)
|
2018-02-04 17:09:09 +00:00
|
|
|
|
{
|
2019-08-22 11:36:13 +01:00
|
|
|
|
char hex_char[(count * 3) + 2];
|
2020-11-06 16:09:13 +00:00
|
|
|
|
AddLog_P(loglevel, PSTR("DMP: %s"), ToHex_P(buffer, count, hex_char, sizeof(hex_char), ' '));
|
2018-02-04 17:09:09 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-28 12:54:52 +01:00
|
|
|
|
void AddLogSerial(uint32_t loglevel)
|
2018-02-13 13:30:30 +00:00
|
|
|
|
{
|
2020-10-30 11:29:48 +00:00
|
|
|
|
AddLogBuffer(loglevel, (uint8_t*)TasmotaGlobal.serial_in_buffer, TasmotaGlobal.serial_in_byte_counter);
|
2018-02-13 13:30:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-12 10:26:33 +01:00
|
|
|
|
void AddLogMissed(const char *sensor, uint32_t misses)
|
2018-07-10 21:12:16 +01:00
|
|
|
|
{
|
2021-01-23 15:26:23 +00:00
|
|
|
|
AddLog(LOG_LEVEL_DEBUG, PSTR("SNS: %s missed %d"), sensor, SENSOR_MAX_MISS - misses);
|
2018-07-10 21:12:16 +01:00
|
|
|
|
}
|
2020-04-29 16:44:03 +01:00
|
|
|
|
|
|
|
|
|
void AddLogBufferSize(uint32_t loglevel, uint8_t *buffer, uint32_t count, uint32_t size) {
|
2021-01-02 13:59:02 +00:00
|
|
|
|
char log_data[4 + (count * size * 3)];
|
2020-12-23 17:00:59 +00:00
|
|
|
|
|
|
|
|
|
snprintf_P(log_data, sizeof(log_data), PSTR("DMP:"));
|
2020-04-29 16:44:03 +01:00
|
|
|
|
for (uint32_t i = 0; i < count; i++) {
|
2021-01-02 13:59:02 +00:00
|
|
|
|
if (1 == size) { // uint8_t
|
2020-12-23 17:00:59 +00:00
|
|
|
|
snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X"), log_data, *(buffer));
|
2021-01-02 13:59:02 +00:00
|
|
|
|
} else { // uint16_t
|
2020-12-23 17:00:59 +00:00
|
|
|
|
snprintf_P(log_data, sizeof(log_data), PSTR("%s %02X%02X"), log_data, *(buffer +1), *(buffer));
|
2020-04-29 16:44:03 +01:00
|
|
|
|
}
|
|
|
|
|
buffer += size;
|
|
|
|
|
}
|
2020-12-23 17:00:59 +00:00
|
|
|
|
AddLogData(loglevel, log_data);
|
2020-04-29 16:44:03 +01:00
|
|
|
|
}
|
2020-05-10 14:24:10 +01:00
|
|
|
|
|
2020-12-31 15:17:30 +00:00
|
|
|
|
void AddLogSpi(bool hardware, uint32_t clk, uint32_t mosi, uint32_t miso) {
|
|
|
|
|
// Needs optimization
|
|
|
|
|
uint32_t enabled = (hardware) ? TasmotaGlobal.spi_enabled : TasmotaGlobal.soft_spi_enabled;
|
|
|
|
|
switch(enabled) {
|
|
|
|
|
case SPI_MOSI:
|
2021-01-23 15:26:23 +00:00
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR("SPI: %s using GPIO%02d(CLK) and GPIO%02d(MOSI)"),
|
2020-12-31 15:17:30 +00:00
|
|
|
|
(hardware) ? PSTR("Hardware") : PSTR("Software"), clk, mosi);
|
|
|
|
|
break;
|
|
|
|
|
case SPI_MISO:
|
2021-01-23 15:26:23 +00:00
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR("SPI: %s using GPIO%02d(CLK) and GPIO%02d(MISO)"),
|
2020-12-31 15:17:30 +00:00
|
|
|
|
(hardware) ? PSTR("Hardware") : PSTR("Software"), clk, miso);
|
|
|
|
|
break;
|
|
|
|
|
case SPI_MOSI_MISO:
|
2021-01-23 15:26:23 +00:00
|
|
|
|
AddLog(LOG_LEVEL_INFO, PSTR("SPI: %s using GPIO%02d(CLK), GPIO%02d(MOSI) and GPIO%02d(MISO)"),
|
2020-12-31 15:17:30 +00:00
|
|
|
|
(hardware) ? PSTR("Hardware") : PSTR("Software"), clk, mosi, miso);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-15 16:22:06 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-05-22 21:14:17 +01:00
|
|
|
|
/*********************************************************************************************\
|
|
|
|
|
* Uncompress static PROGMEM strings
|
|
|
|
|
\*********************************************************************************************/
|
|
|
|
|
|
2020-06-06 19:04:10 +01:00
|
|
|
|
#ifdef USE_UNISHOX_COMPRESSION
|
2020-05-22 21:14:17 +01:00
|
|
|
|
|
2020-05-22 22:05:55 +01:00
|
|
|
|
#include <unishox.h>
|
2020-05-22 21:14:17 +01:00
|
|
|
|
|
|
|
|
|
Unishox compressor;
|
|
|
|
|
|
2020-12-21 08:56:04 +00:00
|
|
|
|
// New variant where you provide the String object yourself
|
|
|
|
|
int32_t DecompressNoAlloc(const char * compressed, size_t uncompressed_size, String & content) {
|
2020-05-22 21:14:17 +01:00
|
|
|
|
uncompressed_size += 2; // take a security margin
|
|
|
|
|
|
|
|
|
|
// We use a nasty trick here. To avoid allocating twice the buffer,
|
|
|
|
|
// we first extend the buffer of the String object to the target size (maybe overshooting by 7 bytes)
|
|
|
|
|
// then we decompress in this buffer,
|
|
|
|
|
// and finally assign the raw string to the String, which happens to work: String uses memmove(), so overlapping works
|
|
|
|
|
content.reserve(uncompressed_size);
|
|
|
|
|
char * buffer = content.begin();
|
|
|
|
|
|
|
|
|
|
int32_t len = compressor.unishox_decompress(compressed, strlen_P(compressed), buffer, uncompressed_size);
|
|
|
|
|
if (len > 0) {
|
|
|
|
|
buffer[len] = 0; // terminate string with NULL
|
|
|
|
|
content = buffer; // copy in place
|
|
|
|
|
}
|
2020-12-21 08:56:04 +00:00
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String Decompress(const char * compressed, size_t uncompressed_size) {
|
|
|
|
|
String content("");
|
|
|
|
|
DecompressNoAlloc(compressed, uncompressed_size, content);
|
2020-05-22 21:14:17 +01:00
|
|
|
|
return content;
|
|
|
|
|
}
|
|
|
|
|
|
2020-08-20 15:22:40 +01:00
|
|
|
|
#endif // USE_UNISHOX_COMPRESSION
|