Skip to content

Commit f9e2483

Browse files
authored
Merge pull request #5190 from tonhuisman/feature/Add-map-math-function
[Functions] Add 'map' function
2 parents d259f96 + a6acdfb commit f9e2483

File tree

7 files changed

+101
-24
lines changed

7 files changed

+101
-24
lines changed

docs/source/Rules/Rules.rst

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,6 +1420,8 @@ Basic Math Functions
14201420
* ``round(x)`` Rounds to the nearest integer, but rounds halfway cases away from zero (instead of to the nearest even integer).
14211421
* ``^`` The caret is used as the exponentiation operator for calculating the value of x to the power of y (x\ :sup:`y`).
14221422

1423+
* ``map(value:fromLow:fromHigh:toLow:toHigh)`` Maps value x in the fromLow/fromHigh range to toLow/toHigh values. Similar to the Arduino map() function. See examples below. (Using a colon as an argument separator to not interfere with regular argument processing)
1424+
14231425
Rules example:
14241426

14251427
.. code-block:: none
@@ -1454,6 +1456,24 @@ Called with event ``eventname2=1.234,100``
14541456
213379 : Info : ACT : LogEntry,'pow of 1.234^100 = 1353679866.79107'
14551457
213382 : Info : pow of 1.234^100 = 1353679866.79107
14561458
1459+
Examples using the ``map()`` function. Map does not constrain the values within the given range, but uses extrapolation when the input value goes outside the ``fromLow`` / ``fromHigh`` range.
1460+
1461+
Missing values for the map function default to 0.
1462+
1463+
.. code-block:: none
1464+
1465+
on ds1#temp do
1466+
let,1,%eventvalue1|20% // use default of 20 degrees
1467+
let,2,map(%v2%:-10:40:1:60) // Convert a temperature range -10..40 to a 60 pixel LED stripe
1468+
NeoPixelLine,1,%v2%,255,255,255 // Draw a white line on the LED strip
1469+
endon
1470+
1471+
.. code-block:: none
1472+
1473+
on eventname3 do
1474+
let,1,map(%eventvalue1|10%:0:100:100:0) // Reverse mapping of a value, 0..100 will output 100..0
1475+
LogEntry,'Input value %eventvalue1|10% mapped to: %v1%'
1476+
endon
14571477
14581478
14591479
Trigonometric Functions

src/src/Helpers/ESPEasy_math.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,4 +331,18 @@ double sqrt(const double x)
331331

332332

333333

334-
#endif
334+
#endif
335+
336+
ESPEASY_RULES_FLOAT_TYPE mapADCtoFloat(ESPEASY_RULES_FLOAT_TYPE float_value,
337+
ESPEASY_RULES_FLOAT_TYPE adc1,
338+
ESPEASY_RULES_FLOAT_TYPE adc2,
339+
ESPEASY_RULES_FLOAT_TYPE out1,
340+
ESPEASY_RULES_FLOAT_TYPE out2)
341+
{
342+
if (!approximatelyEqual(adc1, adc2))
343+
{
344+
const ESPEASY_RULES_FLOAT_TYPE normalized = (float_value - adc1) / (adc2 - adc1);
345+
float_value = normalized * (out2 - out1) + out1;
346+
}
347+
return float_value;
348+
}

src/src/Helpers/ESPEasy_math.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,11 @@ bool essentiallyZero(const double& a);
5959
#endif
6060
bool essentiallyZero(const float& a);
6161

62+
ESPEASY_RULES_FLOAT_TYPE mapADCtoFloat(ESPEASY_RULES_FLOAT_TYPE float_value,
63+
ESPEASY_RULES_FLOAT_TYPE adc1,
64+
ESPEASY_RULES_FLOAT_TYPE adc2,
65+
ESPEASY_RULES_FLOAT_TYPE out1,
66+
ESPEASY_RULES_FLOAT_TYPE out2);
67+
6268

6369
#endif // HELPERS_ESPEASY_MATH_H

src/src/Helpers/Hardware.cpp

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -336,21 +336,6 @@ int espeasy_analogRead(int pin) {
336336

337337
#endif // ifdef ESP8266
338338

339-
float mapADCtoFloat(float float_value,
340-
float adc1,
341-
float adc2,
342-
float out1,
343-
float out2)
344-
{
345-
if (!approximatelyEqual(adc1, adc2))
346-
{
347-
const float normalized = (float_value - adc1) / (adc2 - adc1);
348-
float_value = normalized * (out2 - out1) + out1;
349-
}
350-
return float_value;
351-
}
352-
353-
354339
#ifdef ESP32
355340

356341
// ESP32 ADC calibration datatypes.

src/src/Helpers/Hardware.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,6 @@ extern int lastADCvalue; // Keep track of last ADC value as it cannot be read wh
3434
int espeasy_analogRead(int pin);
3535
#endif // ifdef ESP8266
3636

37-
float mapADCtoFloat(float float_value,
38-
float adc1,
39-
float adc2,
40-
float out1,
41-
float out2);
42-
4337

4438
#ifdef ESP32
4539
void initADC();

src/src/Helpers/Rules_calculate.cpp

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "../ESPEasyCore/ESPEasy_Log.h"
55
#include "../Globals/RamTracker.h"
66
#include "../Helpers/ESPEasy_math.h"
7+
#include "../Helpers/Hardware.h"
78
#include "../Helpers/Numerical.h"
89
#include "../Helpers/StringConverter.h"
910

@@ -71,6 +72,14 @@ bool RulesCalculate_t::is_unary_operator(char c)
7172
*/
7273
}
7374

75+
// quinary = 5-argument functions
76+
bool RulesCalculate_t::is_quinary_operator(char c)
77+
{
78+
const UnaryOperator op = static_cast<UnaryOperator>(c);
79+
80+
return op == UnaryOperator::Map;
81+
}
82+
7483
CalculateReturnCode RulesCalculate_t::push(ESPEASY_RULES_FLOAT_TYPE value)
7584
{
7685
if (sp != sp_max) // Full
@@ -261,6 +270,22 @@ ESPEASY_RULES_FLOAT_TYPE RulesCalculate_t::apply_unary_operator(char op, ESPEASY
261270
return ret;
262271
}
263272

273+
ESPEASY_RULES_FLOAT_TYPE RulesCalculate_t::apply_quinary_operator(char op,
274+
ESPEASY_RULES_FLOAT_TYPE first,
275+
ESPEASY_RULES_FLOAT_TYPE second,
276+
ESPEASY_RULES_FLOAT_TYPE third,
277+
ESPEASY_RULES_FLOAT_TYPE fourth,
278+
ESPEASY_RULES_FLOAT_TYPE fifth)
279+
{
280+
ESPEASY_RULES_FLOAT_TYPE ret{};
281+
const UnaryOperator qu_op = static_cast<UnaryOperator>(op);
282+
283+
if (UnaryOperator::Map == qu_op) {
284+
return mapADCtoFloat(first, second, third, fourth, fifth);
285+
}
286+
return ret;
287+
}
288+
264289
/*
265290
char * RulesCalculate_t::next_token(char *linep)
266291
{
@@ -295,6 +320,16 @@ CalculateReturnCode RulesCalculate_t::RPNCalculate(char *token)
295320

296321
// FIXME TD-er: Regardless whether it is an error, all code paths return ret;
297322
// if (isError(ret)) { return ret; }
323+
} else if (is_quinary_operator(token[0]) && (token[1] == 0))
324+
{
325+
ESPEASY_RULES_FLOAT_TYPE fifth = pop();
326+
ESPEASY_RULES_FLOAT_TYPE fourth = pop();
327+
ESPEASY_RULES_FLOAT_TYPE third = pop();
328+
ESPEASY_RULES_FLOAT_TYPE second = pop();
329+
ESPEASY_RULES_FLOAT_TYPE first = pop();
330+
331+
ret = push(apply_quinary_operator(token[0], first, second, third, fourth, fifth));
332+
298333
} else {
299334
// Fetch next if there is any
300335
ESPEASY_RULES_FLOAT_TYPE value{};
@@ -352,6 +387,8 @@ unsigned int RulesCalculate_t::op_arg_count(const char c)
352387
if (is_unary_operator(c)) { return 1; }
353388

354389
if (is_operator(c)) { return 2; }
390+
391+
if (is_quinary_operator(c)) { return 5; }
355392
return 0;
356393
}
357394

@@ -399,7 +436,7 @@ CalculateReturnCode RulesCalculate_t::doCalculate(const char *input, ESPEASY_RUL
399436
}
400437

401438
// If the token is an operator, op1, then:
402-
else if (is_operator(c) || is_unary_operator(c))
439+
else if (is_operator(c) || is_unary_operator(c) || is_quinary_operator(c))
403440
{
404441
*(TokenPos) = 0; // Mark end of token string
405442
error = RPNCalculate(token);
@@ -443,6 +480,14 @@ CalculateReturnCode RulesCalculate_t::doCalculate(const char *input, ESPEASY_RUL
443480
++sl;
444481
}
445482

483+
// Process the token at a colon (separator)
484+
else if (c == ':')
485+
{
486+
*(TokenPos) = 0; // Mark end of token string
487+
error = RPNCalculate(token);
488+
TokenPos = token;
489+
}
490+
446491
// If the token is a left parenthesis, then push it onto the stack.
447492
else if (c == '(')
448493
{
@@ -609,6 +654,8 @@ const __FlashStringHelper* toString(UnaryOperator op)
609654
return F("atan");
610655
case UnaryOperator::ArcTan_d:
611656
return F("atan_d");
657+
case UnaryOperator::Map:
658+
return F("map");
612659
}
613660
return F("");
614661
}
@@ -644,6 +691,7 @@ String RulesCalculate_t::preProces(const String& input)
644691
,UnaryOperator::Tan
645692
,UnaryOperator::Tan_d
646693
#endif // if FEATURE_TRIGONOMETRIC_FUNCTIONS_RULES
694+
,UnaryOperator::Map
647695

648696
};
649697

src/src/Helpers/Rules_calculate.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ enum class UnaryOperator : uint8_t {
5353
ArcCos, // Arc Cosine (radian)
5454
ArcCos_d, // Arc Cosine (degree)
5555
ArcTan, // Arc Tangent (radian)
56-
ArcTan_d // Arc Tangent (degree)
56+
ArcTan_d, // Arc Tangent (degree)
57+
Map, // Map (value, lowFrom, highFrom, lowTo, highTo) (not really unary...)
5758
};
5859

5960
void preProcessReplace(String & input,
@@ -78,6 +79,8 @@ class RulesCalculate_t {
7879

7980
bool is_unary_operator(char c);
8081

82+
bool is_quinary_operator(char c);
83+
8184
CalculateReturnCode push(ESPEASY_RULES_FLOAT_TYPE value);
8285

8386
ESPEASY_RULES_FLOAT_TYPE pop();
@@ -89,6 +92,13 @@ class RulesCalculate_t {
8992
ESPEASY_RULES_FLOAT_TYPE apply_unary_operator(char op,
9093
ESPEASY_RULES_FLOAT_TYPE first);
9194

95+
ESPEASY_RULES_FLOAT_TYPE apply_quinary_operator(char op,
96+
ESPEASY_RULES_FLOAT_TYPE first,
97+
ESPEASY_RULES_FLOAT_TYPE second,
98+
ESPEASY_RULES_FLOAT_TYPE third,
99+
ESPEASY_RULES_FLOAT_TYPE fourth,
100+
ESPEASY_RULES_FLOAT_TYPE fifth);
101+
92102
// char * next_token(char *linep);
93103

94104
CalculateReturnCode RPNCalculate(char *token);

0 commit comments

Comments
 (0)