При конфигурировании датчиков возникает необходимость ввести поправочные коэффициенты после калибровки. В идеале иметь возможность в конфигурационном файле задать простую математическую формулу для корректировки значений получаемых с датчика. К сожалению, для Arduino мне не удалось найти нормальную библиотеку для парсинга (вычисления) математических выражений заданных в виде строки.
Ресурсы микроконтроллера надо беречь, поэтому использовать код для продвинутых анализаторов строковых математических функций не годится. Поэтому я написал очень простой математический решатель, который не поддерживает приоритет математических операций и скобки, но его более чем достаточно для ввода поправочных коэффициентов.
Небольшой пример использования.
String expression = "%V%*1.1+0.5"; float value = modbusCfg.Eval(expression, 10.5); Serial.println("Value of " + expression + ": " + String(value)); expression = "1.1+2.2*%V%+0.5"; value = modbusCfg.Eval(expression, 10.5); Serial.println("Value of " + expression + ": " + String(value)); expression = "1+0.5"; value = modbusCfg.Eval(expression, 10); Serial.println("Value of " + expression + ": " + String(value));
В исходном строковом выражнии %V% заменяется на значение полученное от датчика. Например, датчики нередко возвращают int и для приведения цифр к нормальной величине нужно разделить на 10. В этом случае выражение будет %V%/10. При этом даже цифровые термодатчики врут, поскольку дешевые термодатчики, похоже, никто не калибрует. Например, если термодатчик врет на 0,7 градуса, показывая завышенные значения, то выражение будет %V%/10 + 0.7.
Поскольку, нередко, достаточно умножить полученное значение на 0.1, например. В этом случае в выражении достаточно написать 0.1 — операция умножения выполняется по-умолчанию, если не указан %V%.
Этот блок кода используется в моей библиотеке ModbusConfig для конфигурирования датчиков различного типа.
void ModbusConfig::math_operation(math_op op, float &prev_val, String &current_val, float &value) { if (prev_val != -16101975) //Check if the default value has changed { if (current_val.length() > 0) { float second = current_val.toFloat(); switch (op) { case math_op::Mul: value = prev_val * second; break; case math_op::Add: value = prev_val + second; break; case math_op::Div: value = prev_val / second; break; case math_op::Sub: value = prev_val - second; break; case math_op::None: printf("There is no operation."); } prev_val = value; current_val = ""; } } else { if (current_val.length() > 0) { prev_val = current_val.toFloat(); current_val = ""; } } } float ModbusConfig::Eval(String expr, float value) { String pattern = "%V%"; int pos = expr.indexOf(pattern); if (pos == -1) { expr = "%V%*" + expr; } expr.replace("%V%", String(value)); String val = ""; math_op last_op = math_op::None; float prev_val = -16101975;//Set the some default value to check if the value has changed value = 0; for (int i = 0; i < expr.length(); i++) { switch (expr[i]) { case '*': math_operation(last_op, prev_val, val, value); last_op = math_op::Mul; break; case '+': math_operation(last_op, prev_val, val, value); last_op = math_op::Add; break; case '/': math_operation(last_op, prev_val, val, value); last_op = math_op::Div; break; case '-': math_operation(last_op, prev_val, val, value); last_op = math_op::Sub; break; default: if (isdigit(expr[i]) || expr[i] == '.' || expr[i] == ',') { val += (expr[i] == ',') ? '.' : expr[i]; } break; } } math_operation(last_op, prev_val, val, value); return value; }