Nosso mundo conectado; explicando máquinas de estados finitos
Diz-se que o homem/mulher foi criado à imagem de Deus. De forma semelhante, os homens/mulheres criam agora máquinas à nossa própria imagem. Um exemplo disso é a programação , ou FSM, para abreviar. Engenheiros e desenvolvedores agora usam computadores para realizar tarefas que antes eram realizadas manualmente. Por exemplo – tem alguma roupa suja por aí? – Eu sei que sim. Anteriormente, tínhamos que enxaguar as roupas em uma banheira ou pia, adicionar sabão, esfregar, enxaguar novamente e outras coisas para conseguir uma camiseta limpa para ir trabalhar ou sair à noite. Agora temos máquinas de lavar para fazer esse trabalho para nós. Chegamos a este ponto com engenheiros que projetaram milhares de produtos e dispositivos que executam programas baseados no pensamento ou ação humana. Isso não é exceção ao aprendizado de máquina atual ou a outros jargões de IA. Milhões de dispositivos e aplicações estão sendo desenvolvidos para aumentar a eficiência e a facilidade de homens e mulheres e muitos desses processos de auxílio existem graças aos FSMs.
Máquinas de Estados Finitos são simplesmente um cálculo matemático de uma série de causas com eventos. Em relação ao nosso exemplo de máquina de lavar – o FSM determina quando iniciar o ciclo de enxágue, quando centrifugar e quando parar completamente. Para entender melhor uma Máquina de Estados Finitos (FSM), primeiro precisamos definir o conceito de 'estado'. Um estado é uma informação única dentro de um programa computacional maior. O cálculo do FSM muda ou transita de um estado para outro em resposta a entradas externas. Um FSM é definido por uma listagem ou ordem lógica de seus estados; seu estado inicial e as condições de cada transição, concluindo com um estado final ou final. O FSM é uma série de pensamentos programados pelo computador para executar operações baseadas em entradas – da mesma forma que o homem pensa e age, o mesmo acontece com as nossas máquinas e os computadores que as controlam.
Os estados são o DNA do FSM, ditando o comportamento interno ou as interações com um ambiente, como aceitar entradas ou produzir resultados que podem ou não fazer com que o sistema mude ou faça a transição de seu estado. O estado deve ser executado especificamente dependendo das diferentes condicionais definidas no seu FSM. Este conceito é muito importante para engenheiros elétricos e de hardware, pois muitos problemas práticos como a programação de máquinas de lavar (quando adicionar água ou sabão, quando centrifugar ou descansar) são resolvidos facilmente usando FSM em vez dos clássicos paradigmas de programação sequencial. Em outras palavras, um FSM é uma solução mais “elétrica e eletrônica” para resolver um problema de hardware versus programação sequencial.
Abaixo estão dois exemplos de FSMs que produzem tomadas de decisão lógicas com menos tempo e energia necessários para implantar um programa lógico testado. O FSM é o primeiro passo para a computação Edge no nível de dispositivo único em aplicações industriais IoT .
Máquina Mealy: Na computação da máquina Mealy, as saídas de cada estado dependem do estado real e de seus valores de entrada atuais. Normalmente, com cada cálculo de Mealy, a entrada de um estado resulta em uma única saída para uma transição ou para um estado final. A máquina de lavar está enchendo de água – quando o nível X for atingido – interrompa a água.
Máquina Moore: Na máquina Moore, as saídas de cada estado dependem do estado real e normalmente são baseadas em sistemas sequenciais cronometrados. A máquina de lavar está girando após 4 minutos de parada da máquina.
DIAGRAMA DE ESTADO
Qualquer FSM deve ser descrito antes de ser codificado por um diagrama de estado – a forma como podemos diagramar os pensamentos da máquina. O exemplo abaixo mostra o comportamento do FSM e suas transições que são (normalmente) desenhadas usando bolhas para descrever estados e setas para transições. Além disso, uma observação comum ao executar um FSM corretamente é ter um estado presente exclusivo onde o próximo estado (futuro) que será executado possa ser facilmente identificado pelas credenciais de programação do estado.
No diagrama acima, ilustramos um processo completo da Máquina de Estados Finitos Mealy. Suponhamos que a operação comece no Estado 1 e depois faça a transição para o Estado 2 assim que as credenciais de programação forem atendidas. Após a transição para o Estágio 2, o FSM calcula o estado atual até que um gatilho seja atendido para prosseguir para o Estado 3 ou Estado 4. Observe que neste diagrama, o Estado 3 e o Estágio 4 são estados finais ou finais que resultam em dados computados para o seu resultado final do projeto.
Ubidots FSM
Agora, vamos começar a codificar um FSM para enviar dados ao Ubidots e proporcionar a você uma experiência real trabalhando com este método de programação. Para o nosso FSM – procuramos identificar e reagir ao requisito inicial. Construiremos uma máquina Moore rápida: enviaremos dados do sensor do nosso microcontrolador (Espressif ESP8266) a cada minuto para Ubidots
Com base neste requisito inicial, optamos por implementar dois estados usando um modelo de computação Moore Machine FSM:
- ESPERE: Não faça nada até que um minuto tenha passado (fique em estado inativo por aproximadamente 59 segundos).
- READ_SEND: Leia a entrada analógica do microcontrolador onde o sensor está conectado e envie o resultado para Ubidots usando MQTT na marca de 60 segundos.
O diagrama de estado abaixo ilustra a lógica de programação do nosso FSM:
A partir deste diagrama fica claro que a transição de WAIT para READ_SEND depende exclusivamente se o valor do tempo independente é maior ou menor que 60 segundos. Começando no próximo estado WAIT, o programa será executado continuamente em WAIT até que o tempo independente atinja ou exceda 60 segundos. Assim que a marca de 60 segundos for atingida, o FSM fará a transição de WAIT para READ_SEND. Depois que o valor for enviado, o FSM fará a transição de volta para WAIT para uma contagem adicional de aproximadamente 59 segundos antes de computar a transição novamente.
Codificação
Para tornar este exemplo um pouco mais simples de entender, vejamos um código FSM prático que foi dividido em quatro partes separadas para detalhar cada um dos estados e condicionais de transição. O código completo pode ser encontrado na íntegra aqui.
Parte 1 – Definir restrições
// Incluir bibliotecas #include " Ubidots ESPMQTT.h" // Definir constantes #define TOKEN "...." // Seu Ubidots TOKEN #define WIFINAME "...." //Seu SSID #define WIFIPASS "... ." // Seu passe Wifi #define WAIT 0 #define READ_SEND 1 uint8_t fsm_state = WAIT; // Estado inicial int msCount = 0; // valor flutuante do contador de tempo; // espaço de memória para o valor a ser lido Ubidots client(TOKEN);
Esta primeira parte do código não é muito interessante pois simplesmente importamos a biblioteca MQTT para envio de dados ao Ubidots e completamos algumas definições necessárias. É importante notar que aqui definimos os dois estados, WAIT e READ_SEND como constantes dentro de todo o código e definimos o estado atual usando a variável fsm_state. A próxima parte do código reserva espaço de memória para o temporizador independente e o valor a ser lido e o cliente MQTT a ser inicializado.
É importante que você não se esqueça de definir os valores adequados para o seu TOKEN e o nome e senha da sua rede WiFi. Se você não sabe onde encontrar seu token, consulte a Central de Ajuda Ubidots para obter mais dicas e truques.
Parte 2 – Retorno de chamada
// Funções Auxiliares void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Mensagem chegou ["); Serial.print(tópico); Serial.print("] "); for (int i=0;i < comprimento;i++) { Serial.print((char)payload[i]); }Serial.println(); }
Nesta parte do código, provisionamos um retorno de chamada que trata os dados do servidor quando/se necessário. Para nosso FSM, esta etapa é necessária para compilar seu código corretamente. Conforme descrito em nosso artigo MQTT , a função callback trata das alterações de suas variáveis no Ubidots e é necessário compilar o código e tê-lo definido.
Parte 3 – Principais Funções – Setup()
// Funções principais void setup() { // inicializa o pino digital LED_BUILTIN como saída. pinMode(A0, ENTRADA); cliente.wifiConnection(WIFINAME, WIFIPASS); cliente.begin(retorno de chamada); }
Agora vamos começar com as funções principais. Em nosso setup() definiremos o pino analógico zero como entrada (você deve editar o número do PIN dependendo da conexão física do seu sensor) para poder utilizar o ADC que permite ao sensor ler os dados do ambiente e representar um número flutuante como um valor. Além disso, inicializamos o cliente WiFi e passamos a função de retorno de chamada com base nos parâmetros de programação previamente definidos.
Parte 4 – Funções Principais – Loop()
void loop() { switch(fsm_state) { case ESPERE: if (msCount >= 60000){ msCount = 0; fsm_state = READ_SEND; } quebrar; caso READ_SEND: valor = analogRead(A0); if(!client.connected()){ client.reconnect(); } /* Rotina para envio de dados */ client.add("stuff", value); cliente. ubidots Publicar("fonte1"); cliente.loop(); fsm_state = ESPERE; quebrar; padrão: pausa; } // Incrementa o contador msCount += 1; atraso(1); }
Uma maneira popular de implementar FSM em microcontroladores é usar a switch-case . Para nosso exemplo, os casos serão nossos estados e as opções serão a fsm_state
. Vamos ver com mais detalhes como cada estado é projetado:
Uma maneira popular de implementar FSM em microcontroladores é usar a switch-case . Para nosso exemplo, os switch-cases serão nossos Estados e a programação que causa uma transição representada pela variável fsm_state. Aqui determinaremos READ_SEND vs WAIT onde valores de 1 ou 0 serão enviados respeitosamente para identificar cada estágio do FSM e transição entre operações com base no temporizador independente de 60 segundos.
Vamos ver com mais detalhes como cada estado é projetado:
- ESPERE: A partir do código deste estado, podemos inferir que ele não fará nada se o resultado do temporizador independente armazenado em
msCount
for inferior a 60.000 milissegundos; uma vez que esta condição é atingida, o valor defsm_state
muda e fazemos a transição para o próximo estado, o estado READ_SEND. - READ_SEND: Aqui lemos o valor do nosso sensor e simplesmente adicionamos ele a uma variável chamada “stuff” e publicamos seus dados em um dispositivo chamado “source1”. Ao executar este programa, sempre faremos a transição de volta para o estado WAIT antes de emitir uma segunda saída.
Finalmente, fora da nossa estrutura switch-case, incrementamos o valor do nosso temporizador e temos um atraso muito pequeno de 1 milissegundo para tornar o tempo consistente com o nosso contador.
Neste ponto você deve estar se perguntando por que programar tudo isso se podemos usar a programação sequencial usual? Imagine que você tem três tarefas adicionais para realizar dentro da sua rotina:
- Controle um servo motor usando PWM
- Mostrar valores em uma tela LCD
- Para fechar ou abrir um portão
Ao operar múltiplas tarefas, o FSM permite o armazenamento mínimo de dados na memória local de um dispositivo, além das funções de estado poderem executar tarefas imediatas com base nos valores de entrada e não apenas em uma única saída. Usando o FSM, você pode tomar decisões mais lógicas com menos tempo e energia necessários para implantar um programa lógico testado. O valor do FSM é sua capacidade de computar processos no nível de dispositivo único – o primeiro passo na computação Edge.
Teste
Nossos scripts funcionam conforme o esperado, um novo dispositivo chamado “source1” é criado com uma variável chamada “stuff” que recebe e salva o valor do sensor a cada 60 segundos no Ubidots .
Melhorias e Ideias
Um FSM pode ser implementado de várias maneiras e, às vezes, a instrução switch-case pode ser entediante de manter se você precisar gerenciar um número muito grande de estados. Uma melhoria adicional ao código explicado aqui na Parte 1 seria evitar a espera de 1 milissegundo entre cada caso analisado. Isso pode ser realizado usando milis().
Conclusão
Homem/mulher projetaram nossos computadores para operar à nossa própria imagem e as Máquinas de Estados Finitos são a programação que permite que as máquinas operem tarefas e forneçam aos humanos assistência e segurança valiosas em nossas vidas diárias. À medida que a tecnologia e o conhecimento para implementar FSMs se tornam continuamente mais baratos e acessíveis, continuaremos a ver computadores de placa única (SBCs) permearem fábricas industriais e lojas. O controle de processos simples com programação FSM em SBCs continua a impulsionar soluções conectadas que complementam gateway e PLCs básicos como Dell, Siemens, etc., ao mesmo tempo em que fornece soluções locais que economizam $ 10.000 em hardware e custos operacionais.