Erlang para IoT

A onda de interesse em dispositivos microeletrônicos e sua interação entre si para as necessidades industriais e domésticas levou ao desenvolvimento de um grande número de projetistas para desenvolvimento com base em SoC (sistemas em um chip) suficientemente poderosos, bastante miniatura em relação às soluções de microcontrolador, mas que já contém um sistema operacional completo. O desenvolvimento de aplicativos para esses designers praticamente não difere do desenvolvimento usual do servidor, exceto que o limite de recursos ainda deve ser lembrado.



À medida que a produtividade e os recursos aumentam, a prática de usar linguagens interpretadas de alto nível, como Lua, Python, JS para desenvolvimento de aplicativos, ganha cada vez mais força. Algumas línguas penetram gradualmente nos "irmãos mais novos", microcontroladores, no entanto, de uma forma muito limitada.

Existem várias razões para isso:

  • prototipagem rápida - com todo o respeito à linguagem C, que desenvolve principalmente microcontroladores, é muito difícil chamá-la de concisa. Linguagens de alto nível permitem que você escreva menos código e simplifique as alterações no que já está escrito, o que é muito importante no estágio de protótipo;
  • gerenciamento automático de memória e abstração de mecanismos complexos de computação - acho que não precisa de comentários, os dois processos manuais com um volume suficiente de projeto se tornam uma fonte de muita dor de cabeça;
  • simplificação da depuração e teste - o código interpretado é mais fácil de verificar na estação de trabalho antes da hora do teste em campo;
  • luta com a complexidade - muitas vezes, uma grande produtividade gera um desejo pragmático natural de empurrar mais pré-processamento e análise para o dispositivo, o que não adiciona simplicidade ao desenvolvimento.

Infelizmente, você tem que pagar por todas as comodidades. Nesse caso, o preço da conveniência é dos recursos, desempenho e tamanho do código mais valiosos (em muitos casos, você precisa ter um ambiente de tempo de execução bastante volumoso). Portanto, a aplicação em campo de linguagens de alto nível no SoC e SoM é uma coisa ambígua e, em alguns lugares, um compromisso.

Usamos a linguagem Erlang para o desenvolvimento, aplicando-a tanto para a finalidade pretendida (criando aplicativos de servidor e um plano de controle) quanto para aplicativos da Web muito incomuns. Portanto, a idéia de usar a infraestrutura dessa linguagem para criar soluções de IoT surgiu muito antes do aparecimento de placas nas quais o tempo de execução Erlang poderia funcionar sem problemas.

Os motivos para usar Erlang foram muitos, os mais significativos:

  • Erlang é muito conveniente para analisar e criar seqüências binárias. A correspondência de padrões combinada com o processamento de dados de bits permite que protocolos binários sejam implementados muito rapidamente e sem palavras;
  • falta de variáveis ​​globais e imutabilidade para a maioria dos dados - permite escrever e, não menos importante, manter aplicativos confiáveis ​​nos quais é difícil alterar acidentalmente algo errado;
  • Processos leves de mensagens muito parecidos com o que os desenvolvedores incorporados precisam lidar. Em essência, eles são um procedimento de inicialização e um procedimento que processa as mensagens recebidas em um loop infinito, alterando o estado interno. É muito parecido com o Arduino, apenas podem haver muitos processos e eles funcionam em paralelo; além disso, no intervalo entre as mensagens, o procedimento de processamento pode ser alterado em tempo real (recarga de código quente), o que é muito conveniente quando você precisa corrigir pequenos erros ou ajustar o comportamento;
  • ambiente isolado e alocação automática de memória, eu acho, não precisam de explicação;
  • multiplataforma - o código de bytes para o tempo de execução do ERTS pode ser compilado na máquina do desenvolvedor e depois transferido para o dispositivo de destino sem problemas;
  • excelentes ferramentas de introspecção - a capacidade de conectar-se a um aplicativo em execução na rede e ver se ele fica mais lento com frequência costuma ser muito útil.

A primeira placa de trabalho em que testamos Erlang foi Carambola 2 dos desenvolvedores lituanos de 8 dispositivos , montados no popular chip AR9331. A primeira versão desta placa, infelizmente, não tinha memória flash suficiente para caber no tempo de execução. Mas a segunda versão já se permitiu acomodar o ERTS e um aplicativo pequeno.



A instalação foi realizada por um método clássico para esse tipo de dispositivo - montando uma imagem OpenWRT contendo Erlang, seguida pela atualização na memória flash do dispositivo. O primeiro lançamento do meio ambiente, infelizmente, levou à decepção - tudo travou. Eu já expliquei os motivos para isso na conferência InoThings 2018 , mas, infelizmente, como se viu mais tarde, enganou meus colegas ao nomear erroneamente a fonte desse comportamento.

Vou recontar brevemente. Ao trabalhar com arquivos, a máquina virtual ERTS usa o tipo off_t , cujo tamanho na distribuição é calculado quando o assembly é configurado automaticamente (se executado na plataforma de destino) ou é substituído no ambiente de compilação cruzada, como aconteceu no caso do OpenWRT. Não está claro por que, mas nas configurações dos processadores MIPS e derivados no arquivo de configuração do assembly é duas vezes maior do que é de fato. O problema não surgiria se o código da máquina virtual não usasse diretivas de pré-processador como

#if SIZEOF_OFF_T == 4 

e uma verificação banal no código (suspeito que o resultado final da compilação seria o mesmo, mas não havia fusível suficiente para verificar):

 if (sizeof(off_t) == 4) { 

Como resultado, o ERTS coletado na primeira iteração ao tentar ler o arquivo ~ / .erlang.cookie (um tipo de senha para identificação durante a interação da rede) no início recebeu com êxito o lixo em alta ordem e caiu com um estrondo.

O patch e o pacote com a penúltima versão do ERTS para OpenWRT podem ser baixados no GitHub . Além disso, ainda não foram observados problemas, tudo funcionou conforme o esperado.

A segunda plataforma de hardware na qual testamos o Erlang foi o designer LinkIt Smart 7688 da Mediatek e SeeedStudio , especialmente projetado para prototipagem rápida e aprendizado do básico. Esta placa é apenas a apoteose da deboche em termos de recursos - a frequência do núcleo do MIPS cresceu 1,5 vezes, mais RAM (para ERTS é importante, o GC não dorme) e mais memória flash, além da presença de um cartão microSD e a possibilidade de usar o co-processador Atmel Atmega 32U4 na versão Duo para trabalhar com periféricos.

Em geral, a plataforma era muito adequada para continuar o banquete, e a presença de dispositivos de plug-in adicionais que se conectam sem solda permite montar rápida e condicionalmente uma bancada de testes em seu joelho.

A plataforma vem com software com sua própria interface da web, além de bibliotecas Python e NodeJS para desenvolvimento. O ecossistema para a montagem não mudou - ainda é o OpenWRT. Se, por algum motivo, você encontrar toda essa diversidade supérflua, no repositório acima há pacotes contendo um conjunto mínimo de componentes necessários . Após a montagem, a imagem do flash é gravada no dispositivo e após a reinicialização, você pode usar o REPL com segurança.

Para criar aplicativos no Erlang para IoT, um problema de arquitetura precisa ser resolvido.

A linguagem foi projetada e desenvolvida de olho no que servirá como plano de controle, ou seja, camada de controle, enquanto trabalhava com ferro deveria ser através da FFI. Três tipos de interação são fornecidos para isso:

  1. ports (ports) - processos de trabalho separados, escritos em qualquer idioma, cuja interação ocorre por meio de fluxos de entrada / saída. Eles podem ser reiniciados em caso de queda, mas devido ao método de interação, sua produtividade em termos de comunicação é pequena (no entanto, será suficiente para nós);
  2. Funções NIF - parecem funções padrão da linguagem, mas sua chamada gera a execução do código compilado no espaço do tempo de execução. Em caso de erro, eles podem arrastar toda a máquina virtual para trás.
  3. Nó C - quando todo o trabalho é realizado em um processo separado e a interação é realizada como em um ambiente de tempo de execução em execução separadamente na rede. Não consideraremos essa opção devido aos custos indiretos suficientemente altos na estrutura de um dispositivo fraco.

O dilema é este: só podemos transportar coisas para o FFI (ou seja, suporte para GPIO, I2C, SPI, PWM, UART etc.) e podemos interagir diretamente com sensores e outros dispositivos em Erlang, ou pelo contrário, transferir drivers de dispositivo inteiramente para o código de módulos externos, deixando o aplicativo receber dados brutos e processá-los; nesse caso, pode fazer sentido usar o código já escrito.

Decidimos usar a primeira opção. Existem várias razões para isso:

  • porque nós podemos;
  • como já mencionei, o Erlang vem com ferramentas poderosas o suficiente para montar e desmontar seqüências binárias, enquanto é uma matemática bastante simples, que permite que você não se preocupe com a proteção contra transbordamentos e outras magias usadas para processar os resultados. Não que esse espantalho seja mágico, mas afeta os neófitos chocantemente;
  • intuitivamente, parecia que os drivers em um idioma de alto nível seriam mais simples e mais confiáveis ​​(um processo travado reiniciará o supervisor, o que levará à reinicialização do dispositivo controlado).

Portanto, a biblioteca ErlangALE foi rapidamente encontrada, que continha suporte já implementado para GPIO, I2C e SPI por meio das interfaces do kernel, e os desenvolvedores da plataforma de hardware, por sua vez, já cuidavam deles. A biblioteca para trabalhar com o UART já foi testada, além de termos adicionado o erlexec , um aplicativo que permite criar e gerenciar processos de SO.

Todos esses aplicativos usavam portas (processos binários lançados separadamente) para trabalhar com o equipamento e o sistema operacional, o que exigia suporte de compilação cruzada para linguagens C e C ++, para a qual foi escrito um script shell bastante elaborado que configurava o ambiente de construção para usar os compiladores necessários.

Para testar as decisões que tomamos, montamos um dispositivo simples do LinkIt Smart 7866, dois dispositivos I2C (sensor de temperatura e pressão BMP280 e display OLED de 128 pixels de 64 pixels) e um módulo GPS USB que envia dados via UART. O GPIO foi testado no LED da placa, funciona e a conexão do monitor SPI parecia desnecessária nesse estágio de complicação.



Acabou sendo uma aplicação bastante compacta e simples, o código fonte pode ser visualizado no Github.

Não vou me aprofundar nos fragmentos de código, mas tentarei descrever em uma visão geral como o aplicativo funciona.

Os drivers de todos os dispositivos são feitos na forma de processos gen_server, isso é conveniente porque permite adicionar parâmetros adicionais e, às vezes, o estado do dispositivo no estado. O último pode ser visto no exemplo de uart_gps - os dados do UART chegam de forma assíncrona, são analisados ​​pelo analisador NMEA0183 e os resultados são gravados no estado do processo, de onde são obtidos por solicitação.

O ciclo principal do aplicativo é descrito no módulo gps_temp_display - a cada segundo o processo lê os dados do GPS e solicita o estado de temperatura e pressão do BMP280 e os exibe no visor OLED. Por uma questão de interesse, você pode olhar para os drivers do monitor e do sensor BMP280 - tudo acabou sucintamente, 150-170 linhas por módulo.

Em geral, a tarefa acima (escrever o código do driver, combinar tudo em um aplicativo, montagem e teste) levou cerca de quatro noites, duas horas em média, ou seja, a rigor, alguns dias úteis. Parece-me pessoalmente que este é um bom indicador para tentar usar o Erlang em aplicativos incorporados mais complexos e sérios que não exigem restrições estritas em tempo real.

Obviamente, nossas tentativas de usar Erlang para sistemas embarcados não são de forma alguma as únicas. Existem vários projetos interessantes nesse sentido:

Source: https://habr.com/ru/post/pt417245/


All Articles