FAVORITAR
FecharPlease login

ESP32: Primeiros Passos com C++ no ESP-IDF

Introdução

Ao contrário do que muitos pensam, o ESP-IDF possui suporte a C++ e a programação orientada a objeto, mas essa ferramenta ainda está um pouco “escondida” na plataforma da Espressif, o que faz com que muitos desenvolvedores utilizem o ambiente sem se dar conta desse recurso, o que pode ajudar a escrever códigos mais modulares e reutilizáveis, tornando o processo de desenvolvimento mais ágil e facilitado. 

Neste artigo, será demonstrado esse recurso e seu uso em um código simples de “Hello World” usando conceitos de C++ na ESP-IDF. Este artigo foi escrito usando como base a versão 5.1.2 do ESP-IDF, sendo usado como extensão na IDE VSCode da Microsoft em um computador com sistema operacional Ubuntu 22.04.

Caso precise se familiarizar com o ambiente ESP-IDF, recomendo essa videoaula gravada pelo Pedro Minatel.

C++ e Programação Orientada a Objeto

A linguagem de programação padrão do ESP-IDF é a tradicional C, linguagem compilada e versátil que, em teoria, é capaz de executar em qualquer dispositivo, sendo leve e poderosa, capaz de unir a facilidade das linguagens de alto nível com o controle fino das linguagens de baixo nível, permitindo ao projetista desenvolver sistemas robustos e confiáveis de maneira simplificada. Com a evolução tecnológica e das linguagens de programação, a linguagem C++ surgiu durante a década de 1980, adicionando o conceito de orientação a objetos à linguagem C, tornando a mesma mais modular e com grande possibilidade de reaproveitamento de partes comuns entre diferentes códigos e tornando o processo de desenvolvimento mais ágil. Porém, o desenvolvimento de software embarcado continuou por vários anos utilizando muito pouco esse recurso, seja por conta das limitações das plataformas, as quais muitas ficaram muito tempo sem oferecer um compilador em C++, ou porque até hoje grande parte das instituições de ensino de engenharia elétrica ou eletrônica e correlatos ainda não empregaram disciplinas de programação orientada a objeto em suas grades, de maneira que o desenvolvimento de software embarcado ainda não usa esse conceito em muitos locais.

Conforme mencionado acima, o ESP-IDF possui suporte a linguagem C++, permitindo o desenvolvimento com orientação a objeto no mesmo, usando o padrão C++23 da GNU. Porém, a plataforma ainda possui as seguintes limitações em relação à linguagem:

  • O gerador de script do linker não oferece suporte para posicionamentos em nível de função para funções com ligação C++;
  • Diversos atributos de seção (como IRAM_ATTR) são ignorados quando usados com funções modelo;
  • As Vtables são colocadas na memória flash e não são acessíveis quando o cache da mesma está desativado. Portanto, chamadas de função virtual devem ser evitadas nos handlers de interrupção IRAM-Safe. Ainda, a colocação das Vtables não pode ser ajustada usando o gerador de script do linker;
  • As funções de sistema de arquivos C++ (std:filesystem) não são suportadas.

Além disso, a Espressif também orienta a evitar utilizar os comandos setjmp/longjmp em C++. O longjmp salta cegamente pelo stack sem chamar quaisquer destrutores, introduzindo facilmente comportamento indefinido e vazamentos de memória. Em vez disso, deve-se utilizar exceções em C++, pois estes garantem a chamada correta dos destrutores. Se não for possível usar exceções em C++, deve-se recorrer a alternativas como códigos de retorno simples.

Preparando o Ambiente

Para prosseguir, o ESP-IDF já deve ter sido instalado no sistema junto com o VSCode, então agora é a hora de fazê-lo caso ainda não o tenha feito.

Com o ESP-IDF devidamente instalado no sistema e adicionado como extensão no VSCode, deve-se começar a preparar o ambiente para começar a trabalhar com C++. Primeiro, deve-se iniciar um novo projeto na ESP-IDF a partir do exemplo Sample Project, que já vem com todas as configurações necessárias para começar um novo projeto.

Para isso, deve-se seguir os seguintes passos:

  1. Abra o VSCode, aperte F1 e digite ESP-IDF: New Project;
ESP-IDF C++
  1. Agora, nomeie o projeto e salve-o como quiser. Aqui, o nomeamos como “hello-cpp”;
ESP-IDF C++
  1. Na caixa de seleção acima dos templates disponíveis, selecione ESP-IDF e depois sample_project. Esse exemplo já vem com todas as configurações necessárias para criar um novo projeto. Com isso, clique em Create project using sample_project.
  1. Finalmente, essa caixa de diálogo irá aparecer no canto inferior direito da janela do VSCode. Aperte Yes para abrir o projeto recém-criado em uma nova janela.

Com isso, podemos seguir com a instalação do esp-idf-cxx, que é um componente da Espressif para o ESP-IDF que permite o uso de várias bibliotecas e APIs em C++, sendo bem útil para ser usado em projetos C++. Para tal, vá até o botão ESP-IDF: Open ESP-IDF Terminal, localizado na parte inferior do ambiente do VSCode.

No terminal aberto, digite o seguinte comando e aperte Enter para instalar o componente:

Com o componente instalado corretamente, agora deve-se renomear o arquivo main.c, localizado na pasta main, para main.cpp, para que assim a linguagem C++ possa ser usada no projeto.

Finalmente, deve-se criar uma nova pasta chamada components na raiz do projeto. É nessa pasta que serão inseridos os objetos utilizados como componentes personalizados do ESP-IDF, deixando o projeto mais organizado e tornando mais fácil realizar a portabilidade dos objetos para serem reutilizados em outros projetos. Com isso, deve-se abrir o arquivo CMakeLists.txt localizado na raiz do projeto (cuidado para não confundir com o arquivo CMakeLists.txt na pasta main) e acrescente a seguinte linha no arquivo:

Essa linha serve para sinalizar ao CMake onde está a pasta de componentes recém-criada, para que estes possam ser incluídos corretamente ao projeto. O arquivo após a adição deve ficar nestes moldes:

Com o ambiente preparado, pode-se seguir com a criação dos componentes que serão utilizados no projeto.

Criando os Componentes

Primeiro, serão criados os objetos a serem usados como componentes do ESP-IDF.

Para isso, deve-se ir até a pasta components que foi criada anteriormente e criar uma nova pasta, que aqui será chamada hello, onde será criado o objeto para o Hello World. Dentro dessa pasta, deve ser criado um novo arquivo CMakeLists.txt e ser inserido o seguinte código no mesmo:

A finalidade desse arquivo é indicar em quais arquivos-fonte estarão os códigos para o nosso objeto e em qual pasta estarão os headers desse objeto. Nesse caso, os mesmos estarão localizados em um arquivo-fonte hello.cpp e em uma pasta include, respectivamente, os quais serão criados posteriormente.

Agora, deve-se criar uma pasta chamada include dentro da pasta hello, e dentro desta um arquivo chamado hello.h. Nesse arquivo, serão criadas as classes que serão utilizadas nos objetos criados. Primeiro, deve-se adicionar recursividade para evitar que o header seja inserido no código mais de uma vez:

Com isso, será utilizado outra recursividade chamada namespace, que limita a execução de um trecho de código a apenas o escopo definido dentro do namespace. Isso é útil para evitar conflitos de nomes entre funções e classes, o que pode ocorrer em projetos que utilizam um grande número de objetos.

Então, será criada a classe HelloCpp, onde será declarada a função run onde será executada a função principal do objeto.

Segue abaixo o código finalizado do header hello.h:

Com o header criado, agora deve-se voltar para a pasta components/hello e criar um novo arquivo-fonte que aqui foi chamado hello.cpp, onde serão programadas as funções do objeto. Com o arquivo criado, deve-se primeiro incluir as bibliotecas que serão utilizadas por esse código, que nesse caso, serão o hello.h criado anteriormente e o esp_log.h, um componente do ESP-IDF que adiciona vários recursos para deixar as tarefas de logging e debug mais intuitivas.

Com as bibliotecas incluídas, aqui também será criado um namespace para adicionar recursividade ao código. IMPORTANTE: O NAMESPACE CRIADO AQUI DEVE TER O MESMO NOME DO NAMESPACE CRIADO NO HEADER ‘hello.h’.

Com o namespace criado, agora deve ser programada a função run a ser executada pelo objeto HelloCpp definido em hello.h, que deve exibir no terminal a mensagem “Hello C++” junto com um parâmetro ‘i’ que será definido posteriormente.

Segue abaixo o código completo do arquivo-fonte hello.cpp:

Até aqui, a estrutura de pastas do projeto deve estar nestes moldes:

Programando a função ‘app_main()’

Com os objetos devidamente criados, agora resta programar a função principal do programa, a app_main(), que irá ditar como o software deve se comportar. Neste projeto, essa função deverá usar o objeto criado nos passos anteriores para facilmente exibir uma mensagem de “Hello C++” juntamente com um contador de quantas vezes a função foi executada.

Primeiramente, deve-se incluir as bibliotecas que serão usadas nesse escopo, que aqui serão a biblioteca de tasks do FreeRTOS para criar o delay entre as execuções da função, e a biblioteca hello.h criada anteriormente. Além disso, o namespace HELLO criado nos passos anteriores também deve ser declarado para que possa ser usado nessa etapa.

Agora, deve-se partir para a programação da função app_main() propriamente dita. É muito importante notar que essa função foi criada em C e que isso não se altera mesmo em um projeto C++ e, por conta disso, é necessário declará-la como extern “C”.

Finalmente, agora deve-se criar um objeto app do tipo HelloCpp, junto com uma variável para contar quantas vezes a função foi executada, e deixar essa rotina em um loop infinito.

Segue abaixo o código completo da função app_main():

Com a programação terminada, agora deve-se compilar, gravar e monitorar o software após selecionar a porta serial correta:

ESP-IDF C++

Se todos os passos foram executados corretamente, o monitor serial exibirá a mensagem “Hello C++” juntamente com o contador.

ESP-IDF C++

Considerações Finais

A programação orientada a objeto e a linguagem C++ tornam o processo de desenvolvimento mais ágil e facilitado, pois permite inúmeras linhas de código serem reutilizadas nos mais diferentes projetos, o que além de diminuir consideravelmente o tempo necessário de desenvolvimento de um projeto também permite que o projetista foque mais em desenvolver a lógica e a aplicação em si ao invés de gastar seu tempo se preocupando com detalhes e configurações, o que também entrega um código final mais limpo e fácil de se manter.

O projeto completo desenvolvido neste artigo pode ser encontrado no GitHub.

Referências

Documentação ESP-IDF

Documentação esp-idf-cxx

Documentação language linkage para C++

Saiba Mais

Webinar Gravado: Programação Orientada a Eventos e C++ Moderno em Microcontroladores

Iniciando C++ em sistemas embarcados

Introdução ao C++11

Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.
Home » Software » ESP32: Primeiros Passos com C++ no ESP-IDF

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS