



Este projeto tem como objetivo implementar uma biblioteca para interpretar expressões aritméticas no ESP32/ESP8266 visando o uso na tomada de decisão em cenários de automação em geral. O projeto também demonstra a utilização da biblioteca.
À primeira vista, o desenvolvimento de um interpretador de expressões aritméticas para microcontroladores pode parecer um exercício acadêmico ou de pouca utilidade. No entanto, à medida que aplicações embarcadas se tornam mais complexas e conectadas, cresce a necessidade por configurações dinâmicas, ajustes em tempo de execução e flexibilidade na lógica de controle, especialmente em soluções de automação residencial, industrial e agrícola. Ao permitir a avaliação de expressões matemáticas diretamente no dispositivo, abre-se a possibilidade de criar sistemas altamente parametrizáveis. Por exemplo, um usuário pode ajustar condições de ativação de relés, alarmes ou outros atuadores a partir de expressões envolvendo múltiplos sensores — tudo isso sem necessidade de regravação do firmware. Mais ainda, ao integrar esse interpretador a uma interface web, é possível fornecer ao usuário final um painel para editar regras ou fórmulas diretamente do navegador. Essas regras podem ser armazenadas no próprio dispositivo (via SPIFFS ou LittleFS ou Banco de Dados) e verificadas periodicamente para acionar lógicas de decisão, criando uma base de regras configurável, similar a um mini motor de inferência, mas leve e prático. A motivação principal, portanto, é oferecer uma ferramenta flexível e em forma de biblioteca, compatível com plataforma ESP32/ESP8266 uma vez que existem restrições críticas de memória no Arduino Uno/Nano. O interpretador proporciona um grau de liberdade na reconfiguração de comportamentos em sistemas embarcados, tornando-os mais flexíveis, modulares e adaptáveis a longo prazo. Aplicações Práticas A utilidade de um interpretador de expressões em microcontroladores vai muito além de calcular operações matemáticas simplesmente. Ele pode ser o núcleo de sistemas embarcados que tomam decisões com base em expressões configuráveis, permitindo que o comportamento do sistema seja ajustado facilmente, sem necessidade de recompilar o firmware. Veja alguns cenários práticos:
A partir de uma dada expressão aritméticas em notação infixada (padrão standard da matemática usado em calculadoras científicas), a biblioteca faz a análise sintática da expressão dividindo em tokens (partes que compõem a expressão) e, em seguida, transformando na notação pós fixada (padrão utilizado nas calculadoras HP) para avaliar o resultado. Se na expressão existirem variáveis ou funções, a biblioteca procura numa lista interna, existindo, o valor é recuperado para entrar na avaliação do resultado. As variáveis podem ser registradas (via função pública da classe) ou serem definidas através de atribuição na própria expressão (ex: A=10;A/2), sendo que múltiplas expressões podem ser definidas na expressão usando-se o separador ‘;’. Quando uma variável é alterada para um novo valor, um call-back poder ser definido para tratar qual variável sofreu alteração e tomar alguma decisão. Caso a função não seja encontrada e, se um call-back estiver definido, o nome da função e parâmetros são passados para o call-back retornar o valor a ser considerado no cálculo da expressão. Desta forma, a aplicação que utiliza a biblioteca participará do cálculo da expressão recebendo do call-back os valores a serem considerados para compor o resultado da expressão (como se fosse macro expansão), para isso consultando sensores, bases de dados ou fazendo outros cálculos. Uma planilha eletrônica, como EXCEL por exemplo, nada mais é do que um interpretador de expressões ou fórmulas que são organizadas em linhas e colunas. Exemplo de expressão Infixada: (2*3 + 5*4) Exemplo da conversão pós fixada: 2 3 * 5 4 * +
A implementação foi para a família ESP32/ESP8266 pois a análise sintática consome um pouco mais de memória devido ao uso de estruturas mais complexas e, na família AVR (NANO/UNO), tais estruturas não existem, restando o uso de estruturas de alocação estática que consome mais recursos e só há 2K de SRAM. Com isso inviabilizou-se a portabilidade para esta plataforma. Visão geral da arquitetura A biblioteca é composta por três etapas principais:
| Símbolo | Operação | Exemplo |
| + | Soma | 2+3 |
| - | Subtração | 5-1 |
| * | Multiplicação | 3*4 |
| / | Divisão | 6/2 |
| % | Módulo | 7%4 |
| # | Potência | 2#3 = 8 |
| Símbolo | Operação | Exemplo |
| == | Igualdade | a == b |
| != | Diferente | a != b |
| > | Maior | a > b |
| < | Menor | a < b |
| >= | Maior ou igual | a >= b |
| <= | Menor ou igual | a <= b |
| Símbolo | Função Lógica | Exemplo |
| & | E lógico (AND) | a > 0 & b < 10 |
| | | | | OU lógico (OR) |
| ! | NÃO lógico (NOT) | !a == 1 |
| ^ | OU Exclusivo (XOR) | a ^ b |
| Símbolo | Função | Exemplo |
| = | Atribuição de valor | a = 2 + 3 |
| ( | Abre parêntese | (a + b) * c |
| ) | Fecha parêntese | (a + b) * c |
| ; | Separador de expressões | a=2; b=3; a+b |
| Função (assinatura) | Descrição | Exemplo | Domínio / Notas |
|---|---|---|---|
SIN(x) |
Seno de um ângulo | SIN(PI/6) = 0.5 |
– |
COS(x) |
Cosseno de um ângulo | COS(PI/3) = 0.5 |
– |
TAN(x) |
Tangente de um ângulo | TAN(PI/4) = 1 |
x ≠ PI/2 + k·PI |
ASN(x) |
Arco-seno (inverso do seno) | ASN(0.5) = PI/6 |
x ∈ [-1, 1] |
ACS(x) |
Arco-cosseno (inverso do cosseno) | ACS(0.5) = PI/3 |
x ∈ [-1, 1] |
ATN(x) |
Arco-tangente (inverso da tangente) | ATN(1) = PI/4 |
x ∈ ℝ |
INT(x) |
Parte inteira (trunca em direção a 0) | INT(-2.9) = -2 |
– |
ABS(x) |
Valor absoluto | ABS(-3) = 3 |
– |
FRAC(x) |
Parte fracionária | FRAC(3.75) = 0.75 |
Para negativos, definir política (ex.: x - floor(x)) |
LOG(x) |
Logaritmo na base 10 | LOG(100) = 2 |
x > 0 |
LN(x) |
Logaritmo natural (neperiano) | LN(E) = 1 |
x > 0 |
EXP(x) |
Exponencial e^x |
EXP(1) = E |
– |
SQR(x) |
Raiz quadrada | SQR(16) = 4 |
x ≥ 0 (sem complexos) |
CUR(x) |
Raiz cúbica | CUR(27) = 3 |
x ∈ ℝ |
SGN(x) |
Sinal do número | SGN(-5) = -1 |
Retorna 1 (positivo), 0 (zero) ou -1 (negativo) |
FACT(n) |
Fatorial | FACT(5) = 120 |
n inteiro, n ≥ 0 |
HYPSIN(x) |
Seno hiperbólico (sinh) |
HYPSIN(0) = 0 |
– |
HYPCOS(x) |
Cosseno hiperbólico (cosh) |
HYPCOS(0) = 1 |
– |
HYPTAN(x) |
Tangente hiperbólica (tanh) |
HYPTAN(0) = 0 |
– |
HYPASN(x) |
Arco-seno hiperbólico (asinh) |
HYPASN(1) ≈ 0.8814 |
x ∈ ℝ |
HYPACS(x) |
Arco-cosseno hiperbólico (acosh) |
HYPACS(1) = 0 |
x ≥ 1 |
HYPATN(x) |
Arco-tangente hiperbólica (atanh) |
HYPATN(0.5) ≈ 0.5493 |
` |
PRIME(n) |
Verifica se n é primo (1=verdadeiro, 0=falso) |
PRIME(23) = 1, PRIME(22) = 0 |
n inteiro |
| Variável | Descrição |
|---|---|
PI |
Número PI |
E |
Número neperiano |
Last |
Último valor calculado |
| Expressão | Resultado | Comentário |
|---|---|---|
A=cos(PI/3);B=sin(PI/3);A#2+B#2 |
A=0.50 B=0.87 1 | Define A e B; soma dos quadrados → identidade trigonométrica cos²+sin²=1. |
2*3+2*2 |
10 | Precedência: multiplicações antes da soma (6 + 4). |
CUR(27) |
3 | Raiz cúbica. |
LOG(10) |
1 | Log base 10. |
ln(E) |
1 | Log natural; funções são case-insensitive. |
PRIME(23) |
1 | 23 é primo → verdadeiro (1). |
PRIME(22) |
0 | 22 é composto → falso (0). |
FACT(3) |
6 | Fatorial de 3. |
SQR(16) |
4 | Raiz quadrada. |
(-1)+(2-3) |
-2 | Parênteses e números negativos. |
-10-20 |
-30 | Menos unário seguido de subtração. |
//#define DEBUG // Comente esta linha para desativar DEBUG
A biblioteca foi projetada para ser uma ferramenta para o desenvolvedor produzir aplicações mais ricas e foi pensada para “regras-como-dados”. Em vez de codificar if/else fixos no firmware, você expõe indicadores do negócio objeto da aplicação a ser desenvolvida (sensores, estados, configurações) como VARIÁVEIS e FUNÇÕES resolvidas/implementadas por call-backs. Assim, as variáveis/funções possibilitam a criação de regras que viram expressões editáveis (em arquivo, NVS/Preferences, OTA, web UI) — sem recompilar e flexibilizando a usabilidade da aplicação pelo usuário final.
Identifique medidores: tudo que o usuário final usa para decidir algo. Ex.: TEMP, HUM, LUX, PRESSAO, FLOW, VAZAO, EFICIENCIA, PRODUTIVIDADE, etc.
Mapeie estados e modos: LED, PUMP, ALARM, MODE, SETPOINT, HYST.
Extraia utilidades de negócio como funções: MAP(x,inMin,inMax,outMin,outMax), HYST(x,low,high), MEDIANA(a,b,c), IN_RANGE(x,a,b), DEBOUNCE(x,ms), etc.
Persistência e ajustes: exponha parâmetros que o usuário salva por expressão, ex.: THRESH=30, PERIODO=900, MODO=1.
Regra prática: variáveis retornam um valor atual; funções calculam/transformam a partir de argumentos. Use nomes case-insensitive e retorne
NANquando algo não se aplicar (a LIB trata como erro/ausência).
| Call-back | Assinatura | Papel na avaliação | Retorno/Efeito | Uso típico |
|---|---|---|---|---|
VariableCallback |
float(const String& nameUpper) |
Resolver variáveis lidas na expressão. | Retorne o valor; NAN se desconhecida/indisponível. |
Sensores (TEMP, HUM), estados (LED), relógio (EPOCH, HOUR), constantes (PI). |
FunctionCallback |
float(const String& nameUpper, const std::vector<String>& args) |
Implementar funções de domínio chamadas na expressão. | Retorne resultado; NAN para erro/assinatura inválida. |
MAP(...), HYST(...), MEDIANA(...), utilidades de processo. |
SettingVarCallback |
void(const String& nameUpper, const float value) |
Tomar decisões quando atribuições são feitas na expressão. | Efeito colateral (p.ex. salvar em NVS, alterar setpoint). | THRESH=30, MODO=1, KP=2.1. |
ErrorCallback |
void(const String& message) |
Receber mensagens de erro da avaliação. | Log/telemetria/alerta. | Print em Serial, buffer circular, envio via WS. |
CallbackType |
float(const String&, const std::vector<String>&) |
Alias geral compatível com funções. | Igual ao FunctionCallback. |
Compatibilidade/legado. |
Notas:
Converta
nameUpperpara maiúsculas (ou já trate como maiúsculo) para facilitar case-insensitive.Em
FunctionCallback, os argumentos vêm comoString; converta parafloat(ex.:strtof(args[i].c_str(), nullptr)ouargs[i].toFloat()).
Allowlist: reconheça apenas nomes esperados. Tudo o que não corresponder → NAN (falha segura).
Sem efeitos nas funções: mantenha FunctionCallback puro (sem side-effects). Use SettingVarCallback para mudar estado/salvar.
Perfomance: se a mesma expressão roda com alta frequência, considere usar CACHE ou temporização (ex.: suponha DOLAR como variável. Só recupere a cotação do DOLAR externamente uma vez no dia ou estabeleça intervalos menores para a atualização).
Persistência: para parâmetros de usuário, escreva em Preferences com debounce (p.ex. 3–10 s) para reduzir desgaste de flash.
Observabilidade: logue via ErrorCallback para acompanhamento na interface.
Com esses call-backs, você organiza o firmware como plataforma de execução, e deixa a “regra” virar dados (expressões). Resultado: parametrização real, mais reuso entre projetos e ajustes de produção sem recompilar.
Foram desenvolvidas duas aplicações para a demonstração do uso da biblioteca: uma BasicDemo que implementa a interação via Console Serial e uma mais avançada, WebDemo, utilizando uma interface HTML demonstrando a aplicabilidade em Automação. Materiais Necessários (WebDemo) 1x Módulo WiFi ESP32 Bluetooth 30 pinos 1x Protoboard 400 Pontos 1x Sensor de Umidade e Temperatura DHT11 1x Sensor Fotoresistor LDR de 5mm 1x Led Difuso 5mm Vermelho 1x Resistor 10K 1/4W (10 Unidades) (LDR) 1x Resistor 4K7 1/4W (10 Unidades) (DHT) 1x Resistor 220R 1/4W (10 Unidades)(LED) Opcional (BasicDemo) 1x Módulo WiFi ESP8266 NodeMcu v3 - Lolin 1x Protoboard 400 Pontos WebDemo A aplicação WebDemo utiliza um HTML com 3 abas:
Figura 1 - Aba Expressões do HTML
Figura 2 - Aba Automação do HTML
Figura 3 - Aba Informações do HTML
Figura 4 - Diagrama do Circuito WebDemo
BasiDemo A aplicação BasicDemo foi projetada para interagir com a Console Serial recebendo expressões digitadas na Console e esperando um NEWLINE. Adicionalmente, nenhum componente é utilizado, mas apenas o microcontrolador. Características:
Figura 5- Tela da Console do IDE
Figura 6- Diagrama do Circuito BasicDemo
A biblioteca é distribuída seguindo o padrão do Arduino IDE no formato ZIP. A instalação é feita através do IDE conforme figura a seguir. Dentro do ZIP estão também as duas aplicações de demonstração BasicDemo e WebDemo.
ExpressionParser
Figura 7 - Instalação da Biblioteca no IDE do Arduino
A ExpressionParser transforma lógica de controle em dados configuráveis. Em vez de “recompilar” regras no firmware, você descreve expressões que o dispositivo interpreta em tempo real — com variáveis (sensores, relógio, estado), funções (pré-definidas e/ou estendidas) e atribuições. Esse modelo eleva substancialmente a parametrização de aplicações de automação, com ganhos claros:
|
|
Este projeto tem como objetivo implementar uma biblioteca para interpretar expressões aritméticas no ESP32/ESP8266 visando o uso na tomada de decisão em cenários de automação em geral. O projeto também demonstra a utilização da biblioteca.
Encontre tudo na Loja Eletrogate com frete grátis para compras acima de R$ 200