mirror of
https://github.com/letscontrolit/ESPEasy.git
synced 2026-09-15 11:05:08 +00:00
[P180] Add let I2C Command, limit delay to max 10ms for 1ps/10ps/50ps
This commit is contained in:
@@ -136,13 +136,14 @@ The commands available can either be used with the full command, or the 1 charac
|
||||
* ``value.<valueIndex>``: ``(v)``: (1..4 or a value name) Store the last calculated result, or last read value, in the specified Value field.
|
||||
* ``calc.<calculation>``: ``(c)``: Calculate a result, like Rules, extra available vars: ``%value%``, ``%pvalue%``, ``%h%``, ``%b0%..%b<n>%``, ``%bx0%..%bx<n>%``, ``%w0%..%w<n>%``, ``%wx0%..%wx<n>%``
|
||||
* ``if.<calculation>``: ``(i)``: Similar to ``calc`` command, when the calculation-result is 0 execution is cancelled
|
||||
* ``delay.<msec>``: ``(d)``: (range: 0..500) Some devices, after starting a read, require some time to process data. This can be handled by a delay. If the delay is < 10 msec, it will be handled immediately, longer delays are processed asynchronous to avoid blocking the ESP for too long.
|
||||
* ``enable.<state>``: ``(l)``: (0 or 1) Set the configured **'Enable' GPIO** pin to the provided state. If the pin is not configured, this command is ignored, and processing continues.
|
||||
* ``delay.<msec>``: ``(d)``: (range: 0..500, 0..10 for 1/sec, 10/sec, 50/sec events) Some devices, f.e. after starting a measurement, require some time to process data. This can be handled by a delay. If the delay is <= 10 msec, it will be handled immediately, longer delays are processed asynchronous to avoid blocking the ESP for too long.
|
||||
* ``enable.<state>``: ``(a)``: (0 or 1) Set the configured **'Enable' GPIO** pin to the provided state. If the pin is not configured, this command is ignored, and processing continues.
|
||||
* ``reset.<state>.<msec>``: ``(z)``: (state = 0 or 1, msec = range: 0..500) Pulse the configured **'Reset' GPIO** pin for the provided time (msec) to the state requested, to reset the device. If the pin is not configured, this command is ignored, and processing continues.
|
||||
* ``let.<variable>.<calculation>``: ``(l)``: Assign the result of a calculation to a global Rules variable. Can use the same plugin variables as available for ``calc`` and ``if``. The resulting variable can also be used from rules, or later in a ``calc``, ``if`` or ``let`` I2C command.
|
||||
|
||||
During processing, the data has to be made available to some of the following steps to be able to calculate the outcome. The variables to insert the data are listed here:
|
||||
|
||||
* ``%pvalue%``: The value currently in the Value field for this command sequence (when run during an Interval), can be read as 'previous-value'.
|
||||
* ``%pvalue%``: The value currently in the Value field for this command sequence (when run during an Interval or from Rules when a TaskValue index is specified), can be read as 'previous-value'.
|
||||
|
||||
The next variables are only available after an ``eval`` I2C Command is executed:
|
||||
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
// #######################################################################################################
|
||||
|
||||
/** Changelog:
|
||||
* 2025-05-05 tonhuisman: Change abbreviation for 'enable' from 'l' to 'a'
|
||||
* Add 'let.<var>.<value>' I2C command, abbreviated to 'l'. Assigns <value> to a Rules global variable
|
||||
* Limit max delay value to 10msec for 1/sec, 10/sec and 50/sec processing
|
||||
* 2025-05-02 tonhuisman: Respond successful to PLUGIN_I2C_HAS_ADDRESS request, as we can (probably) handle any I2C device...
|
||||
* 2025-05-01 tonhuisman: Suppress logging during handling of 1/sec, 10/sec and 50/sec events for better performance
|
||||
* Add genI2c,log,<0|1> subcommand for disabling/enabling plugin logging
|
||||
* Add genI2C,log,<0|1> subcommand for disabling/enabling plugin logging
|
||||
* Remove support for PLUGIN_GET_CONFIG as its not needed or useable and not implemented yet
|
||||
* 2025-04-28 tonhuisman: Add support for processing during 1/sec, 10/sec and 50/sec plugin events
|
||||
* 2025-04-27 tonhuisman: Add support vor executing a command sequence from cache: getI2C,exec,<cache-name>[,<TaskVarIndex>]
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
// These commands (not case-sensitive) must have the same order as the P180_Commands_e enum class
|
||||
const char P180_commands[] PROGMEM =
|
||||
"n|g|p|r|w|s|t|e|c|v|d|l|z|i|";
|
||||
"n|g|p|r|w|s|t|e|c|v|d|a|z|i|l|";
|
||||
const char P180_commandsLong[] PROGMEM =
|
||||
"nop|get|put|read|write|read16|write16|eval|calc|value|delay|enable|reset|if|";
|
||||
"nop|get|put|read|write|read16|write16|eval|calc|value|delay|enable|reset|if|let|";
|
||||
|
||||
// Supported data formats, _ == undefined, not processed
|
||||
const char P180_dataFormats[] PROGMEM =
|
||||
@@ -36,8 +36,9 @@ P180_Command_struct::P180_Command_struct(P180_Command_e _command,
|
||||
uint16_t _reg,
|
||||
int64_t _data,
|
||||
uint32_t _len,
|
||||
String _calculation)
|
||||
:command(_command), format(_format), reg(_reg), len(_len), calculation(_calculation) {
|
||||
String _calculation,
|
||||
String _variable)
|
||||
:command(_command), format(_format), reg(_reg), len(_len), calculation(_calculation), variable(_variable) {
|
||||
switch (format) {
|
||||
case P180_DataFormat_e::undefined: d0_uint32_t = static_cast<uint32_t>(_data); break; // Special case
|
||||
case P180_DataFormat_e::uint8_t: d0_uint8_t = static_cast<uint8_t>(_data); break;
|
||||
@@ -121,13 +122,24 @@ String P180_Command_struct::toString() {
|
||||
data = getHexValue(true);
|
||||
}
|
||||
|
||||
String result = strformat(F("cmd: '%s' (%s), fmt: '%s', reg: 0x%02x, data: %d (0x%x), len: %d"),
|
||||
cmd, cmdS, fmt, reg, getIntValue(), getUIntValue(), len);
|
||||
int64_t val{};
|
||||
|
||||
if ((P180_Command_e::Delay == command) || (P180_Command_e::Value == command)) {
|
||||
val = d0_uint32_t;
|
||||
} else {
|
||||
val = getIntValue();
|
||||
}
|
||||
String result = strformat(F("cmd: '%s' (%s), fmt: '%s', reg: 0x%x, data: %d (0x%llx), len: %d"),
|
||||
cmd, cmdS, fmt, reg, val, val, len);
|
||||
|
||||
if (!data.isEmpty()) {
|
||||
result = concat(result, strformat(F(", data_b/w: %s"), data.c_str()));
|
||||
}
|
||||
|
||||
if (!variable.isEmpty()) {
|
||||
result = concat(result, strformat(F(", variable: %s"), variable.c_str()));
|
||||
}
|
||||
|
||||
if (!calculation.isEmpty()) {
|
||||
result = concat(result, strformat(F(", calculation: %s"), calculation.c_str()));
|
||||
}
|
||||
@@ -336,6 +348,7 @@ std::vector<P180_Command_struct>P180_data_struct::parseI2CCommands(const String&
|
||||
uint32_t len = 0;
|
||||
P180_DataFormat_e fmt = P180_DataFormat_e::uint8_t;
|
||||
String calculation;
|
||||
String variable;
|
||||
|
||||
if (arg2i > -1) {
|
||||
fmt = static_cast<P180_DataFormat_e>(arg2i);
|
||||
@@ -363,6 +376,14 @@ std::vector<P180_Command_struct>P180_data_struct::parseI2CCommands(const String&
|
||||
calculation = args[arg - 1];
|
||||
stripEscapeCharacters(calculation);
|
||||
break;
|
||||
case P180_Command_e::Let: // let - l.<variable>.<calculation>
|
||||
fmt = P180_DataFormat_e::undefined;
|
||||
val = 0;
|
||||
variable = args[arg - 1];
|
||||
calculation = args[arg];
|
||||
stripEscapeCharacters(variable);
|
||||
stripEscapeCharacters(calculation);
|
||||
break;
|
||||
case P180_Command_e::Eval: // eval - e
|
||||
fmt = P180_DataFormat_e::undefined;
|
||||
break;
|
||||
@@ -416,7 +437,7 @@ std::vector<P180_Command_struct>P180_data_struct::parseI2CCommands(const String&
|
||||
}
|
||||
|
||||
if (addCmd) {
|
||||
commands.push_back(P180_Command_struct(cmd, fmt, reg, val, len, calculation));
|
||||
commands.push_back(P180_Command_struct(cmd, fmt, reg, val, len, calculation, variable));
|
||||
|
||||
if (((P180_Command_e::Write == cmd) || (P180_Command_e::RegisterWrite == cmd) || (P180_Command_e::Register16Write == cmd)) &&
|
||||
((P180_DataFormat_e::bytes == fmt) || (P180_DataFormat_e::words == fmt))) {
|
||||
@@ -799,35 +820,13 @@ bool P180_data_struct::executeI2CCommands() {
|
||||
break;
|
||||
case P180_Command_e::Calculate:
|
||||
case P180_Command_e::If:
|
||||
case P180_Command_e::Let:
|
||||
|
||||
result = true;
|
||||
|
||||
if (!_it->calculation.isEmpty()) {
|
||||
String toCalc(_it->calculation);
|
||||
|
||||
toCalc.replace(F("%pvalue%"), toString(_value)); // %pvalue%
|
||||
|
||||
if (_evalIsSet) {
|
||||
toCalc.replace(F("%value%"), toString(_evalCommand->getIntValue())); // %value%
|
||||
toCalc.replace(F("%h%"), _evalCommand->getHexValue(true)); // %h%
|
||||
|
||||
if (P180_DataFormat_e::bytes == _evalCommand->format) {
|
||||
if (toCalc.indexOf(F("%b")) > -1) {
|
||||
for (uint8_t i = 0; i < _evalCommand->data_b.size(); ++i) {
|
||||
toCalc.replace(strformat(F("%%b%d%%"), i), String(_evalCommand->data_b[i])); // %b<n>%
|
||||
toCalc.replace(strformat(F("%%bx%d%%"), i), formatToHex_no_prefix(_evalCommand->data_b[i], 2)); // %bx<n>%
|
||||
}
|
||||
}
|
||||
} else if (P180_DataFormat_e::words == _evalCommand->format) {
|
||||
if (toCalc.indexOf(F("%w")) > -1) {
|
||||
for (uint8_t i = 0; i < _evalCommand->data_w.size(); ++i) {
|
||||
toCalc.replace(strformat(F("%%w%d%%"), i), String(_evalCommand->data_w[i])); // %w<n>%
|
||||
toCalc.replace(strformat(F("%%wx%d%%"), i), formatToHex_no_prefix(_evalCommand->data_w[i], 4)); // %wx<n>%
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
String newCalc = parseTemplate(toCalc); // Process like rules
|
||||
String toCalc(replacePluginValues(_it->calculation));
|
||||
const String newCalc = parseTemplate(toCalc); // Process like rules
|
||||
ESPEASY_RULES_FLOAT_TYPE tmp{};
|
||||
|
||||
if (Calculate(newCalc, tmp) == CalculateReturnCode::OK) {
|
||||
@@ -840,6 +839,15 @@ bool P180_data_struct::executeI2CCommands() {
|
||||
_commandState = P180_CommandState_e::ConditionalExit;
|
||||
result = false; // PLUGIN_READ failed
|
||||
}
|
||||
} else if (P180_Command_e::Let == _it->command) {
|
||||
String toVar(replacePluginValues(_it->variable));
|
||||
const String newVar = parseTemplate(toVar); // Process like rules
|
||||
|
||||
if (!newVar.isEmpty() && ExtraTaskSettings.checkInvalidCharInNames(newVar.c_str())) {
|
||||
setCustomFloatVar(newVar, tmp);
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
} else {
|
||||
_value = tmp;
|
||||
}
|
||||
@@ -857,7 +865,10 @@ bool P180_data_struct::executeI2CCommands() {
|
||||
break;
|
||||
case P180_Command_e::Delay:
|
||||
{
|
||||
const uint32_t ms = min(_it->d0_uint32_t, static_cast<uint32_t>(500u)); // Reasonable limit
|
||||
const uint32_t mx = ((P180_CommandSource_e::PluginOncePerSecond == _commandSource) ||
|
||||
(P180_CommandSource_e::PluginTenPerSecond == _commandSource) ||
|
||||
(P180_CommandSource_e::PluginFiftyPerSecond == _commandSource)) ? 10u : 500u;
|
||||
const uint32_t ms = min(_it->d0_uint32_t, mx); // Reasonable limit, max 10 msec for repeating events
|
||||
|
||||
if (ms <= 10) {
|
||||
delay(ms);
|
||||
@@ -947,6 +958,35 @@ bool P180_data_struct::executeI2CCommands() {
|
||||
return result;
|
||||
}
|
||||
|
||||
String P180_data_struct::replacePluginValues(const String& inVar) {
|
||||
String result(inVar);
|
||||
|
||||
result.replace(F("%pvalue%"), toString(_value)); // %pvalue%
|
||||
|
||||
if (_evalIsSet) {
|
||||
result.replace(F("%value%"), toString(_evalCommand->getIntValue())); // %value%
|
||||
result.replace(F("%h%"), _evalCommand->getHexValue(true)); // %h%
|
||||
|
||||
if (P180_DataFormat_e::bytes == _evalCommand->format) {
|
||||
if (result.indexOf(F("%b")) > -1) {
|
||||
for (uint8_t i = 0; i < _evalCommand->data_b.size(); ++i) {
|
||||
result.replace(strformat(F("%%b%d%%"), i), String(_evalCommand->data_b[i])); // %b<n>%
|
||||
result.replace(strformat(F("%%bx%d%%"), i), formatToHex_no_prefix(_evalCommand->data_b[i], 2)); // %bx<n>%
|
||||
}
|
||||
}
|
||||
} else if (P180_DataFormat_e::words == _evalCommand->format) {
|
||||
if (result.indexOf(F("%w")) > -1) {
|
||||
for (uint8_t i = 0; i < _evalCommand->data_w.size(); ++i) {
|
||||
result.replace(strformat(F("%%w%d%%"), i), String(_evalCommand->data_w[i])); // %w<n>%
|
||||
result.replace(strformat(F("%%wx%d%%"), i), formatToHex_no_prefix(_evalCommand->data_w[i], 4)); // %wx<n>%
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool P180_data_struct::plugin_once_a_second(struct EventStruct *event) {
|
||||
if (((P180_CommandSource_e::PluginIdle != _commandSource) &&
|
||||
(P180_CommandSource_e::PluginOncePerSecond != _commandSource)) ||
|
||||
|
||||
@@ -52,9 +52,10 @@ enum class P180_Command_e : uint8_t {
|
||||
Calculate, // 'c'
|
||||
Value, // 'v'
|
||||
Delay, // 'd'
|
||||
EnableGPIO, // 'l'
|
||||
EnableGPIO, // 'a'
|
||||
ResetGPIO, // 'z'
|
||||
If, // 'i'
|
||||
Let, // 'l'
|
||||
};
|
||||
|
||||
enum class P180_DataFormat_e : uint8_t {
|
||||
@@ -101,7 +102,8 @@ struct P180_Command_struct {
|
||||
uint16_t _reg,
|
||||
int64_t _data,
|
||||
uint32_t _len,
|
||||
String _calculation);
|
||||
String _calculation,
|
||||
String _variable);
|
||||
# ifndef LIMIT_BUILD_SIZE
|
||||
String toString();
|
||||
# endif // ifndef LIMIT_BUILD_SIZE
|
||||
@@ -143,6 +145,7 @@ struct P180_Command_struct {
|
||||
std::vector<uint8_t> data_b;
|
||||
std::vector<uint16_t>data_w;
|
||||
String calculation;
|
||||
String variable;
|
||||
};
|
||||
|
||||
struct P180_data_struct : public PluginTaskData_base {
|
||||
@@ -170,6 +173,7 @@ private:
|
||||
const String& line,
|
||||
const bool update);
|
||||
bool executeI2CCommands();
|
||||
String replacePluginValues(const String& inVar);
|
||||
|
||||
const __FlashStringHelper* cacheSuffix(P180_CommandSource_e source);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user