Lendo sensores analógicos em Deep Sleep com ULP - ESP32

Leitura de sensores analógicos pelo ADC com ULP

Neste tutorial, aprenderemos a ler um sensor analógico através do ADC no ESP32 em Deep Sleep, utilizando o ULP. Esse método de leitura, permite uma economia de bateria gigantesca, já que não precisamos acordar o microcontrolador para efetuar a leitura, situação na qual o gasto é bem maior do que quando mesmo procedimento é feito pelo ULP.

Figura 1 - ULP.

[toc]

kit robotica educacional com Arduino ESP ou Microbit

Por que utilizar?

Imagine que em seu projeto portátil, é preciso ler o sensor de temperatura a cada 30 segundos e para isso, logicamente, precisamos acordar o microcontrolador para efetuar essa tarefa, entretanto, o consumo do ESP32 nesse caso seria de 40mA apenas para ler o sensor. Com o ULP, podemos fazer essa leitura em Deep Sleep e o consumo enquanto o ULP está funcionando não passaria de 150uA, com uma média de consumo dependendo do duty cycle do ULP. Com isso, economizaríamos nesse caso de 150uA (100% duty cycle), aproximadamente 270x mais bateria do que se acordássemos o microcontrolador para essa mesma tarefa.

É fácil perceber o aumento incrível na duração da bateria que poderia ser obtido apenas ao usar o ULP para ler o sensor, as aplicações desse guerreiro são muito grandes, mas o foco é para Sleep.

Se você ainda não conhece o ULP, clique aqui para ver a introdução sobre este incrível coprocessador de baixo consumo presente no ESP32.


Mãos a obra - Lendo um sensor de temperatura em Deep Sleep

Componentes necessários

Códigos do projeto

- Main code (C ou C++), responsável pela programação do ESP32 em si.

#include <C:/msys32/ESP32/ESP32/components/arduino/cores/esp32/Arduino.h>
#include <C:/msys32/ESP32/esp-idf/components/driver/include/driver/rtc_io.h>
#include <C:/msys32/ESP32/esp-idf/components/driver/include/driver/adc.h>
#include <C:/msys32/ESP32/esp-idf/components/ulp/ulp.c>
#include <C:/msys32/ESP32/ESP32/build/main/ulp_main.h>
extern "C"
{
#include <C:/msys32/ESP32/esp-idf/components/esp32/include/esp_clk.h>
}
//Pode ser preciso arrumar os diretorios das bibliotecas
//Pode ser preciso remover o "extern 'C'{}" e definir a biblioteca fora dele, alguns usuarios relatam erro sem o uso do extern

extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end");
void ulp();


extern "C" void app_main()
{
	initArduino();//inicia configuracoes do arduino, caso nao use o Arduino component, remova essa linha
	pinMode(2, OUTPUT);

	if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_ULP)//se o wakeup for por causa do ULP, tomara alguma atitude
	{
		digitalWrite(2, 1);
		delay(500);
		digitalWrite(2, 0);
	}
	else//se nao, iniciara o ULP
	{
		ulp();//configura e inicializa o ulp
	}
	

	esp_sleep_enable_ulp_wakeup();//habilita o wakeup pelo ULP
	esp_deep_sleep_start();//entra em deep sleep eterno
}




void ulp()
{
	adc1_config_channel_atten(ADC1_CHANNEL_4, ADC_ATTEN_11db);
	adc1_config_width(ADC_WIDTH_12Bit);
	adc1_ulp_enable();
	//configura o ADC1 #4 (GPIO32) para 3.3V 12bit e atribui o uso ao ULP
	
	ulp_set_wakeup_period(0, 10000000);//ativa o timer de wakeup do ULP apos cada HALT para 10seg

	ulp_load_binary(0, ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start) / sizeof(uint32_t));//carrega os arquivos
	ulp_run((&ulp_main - RTC_SLOW_MEM) / sizeof(uint32_t));//inicia o ULP
}

- ULP code (Assembly .S), responsável pela programação do ULP em si.

#include "soc/soc_ulp.h"
#include "soc/rtc_io_reg.h"
#include "soc/sens_reg.h"
#include "soc/rtc_cntl_reg.h"


.bss//secao das variaveis


.text//secao do codigo


	.global main
	main:

		move r0, 0//r0 = 0
		move r1, 0//r1 = 0
		stage_rst//stage_cnt = 0

		leitura:
			stage_inc 1//stage_cnt++
			adc r1, 0, 4+1//r1 = leitura ADC GPIO32
			add r0, r0, r1//r0 = r0+r1
			jumps leitura, 4, lt//loop responsavel pelas leituras, equivale a um FOR()

		rsh r0, r0, 2//calcula a media das 4 leituras r0 = r0/4
		jumpr wakeup, 496, ge//se a media da leitura que esta em r0 for maior ou igual que 496 (40 graus celsius), acorda o mcu
		halt//coloca o ULP em sleep e ativa o timer de wakeup (definido com 10 segundos no main code)

	wakeup:
		wake//acorda o mcu
		halt

Entendendo a fundo

Software

leitura:
			stage_inc 1
			adc r1, 0, 4+1
			add r0, r0, r1
			jumps leitura, 4, lt

		rsh r0, r0, 2
		jumpr wakeup, 496, ge
		halt

A parte interessante e diferente do código é justamente a leitura do canal ADC, nós poderíamos fazer mais simples sem utilizar uma estrutura de repetição FOR(), entretanto, é sempre interessante fazer uma média de leituras (inclusive com delay's) para evitar ruídos e coisas do tipo. Já ensinamos a fazer o laço FOR() no tutorial introdutório do ULP.

  1. É feito a leitura do canal ADC no GPIO32 4x e a cada leitura, o valor é somado no registrador R0.
  2. Após as 4 leituras precisamos tirar a média entre eles, entretanto, não há mnemônicos simples para divisão como "DIV". Usaremos os operadores de BitWise (RSH - Right Shift Bit) para dividir o valor por 4. Por causa da divisão facilitada com o RSH na base 2 (2, 4, 8, 16...), também faremos uma quantidade de leituras na base 2.

Não se esqueça de conferir o Set de Instruções do ULP nas referências em caso de dúvidas.

-Mnemônico ADC

adc r1, 0, 4+1

Efetua a leitura do ADC no GPIO32 e atribui o valor ao registrador R1.

O segundo e terceiro operador (parâmetro) deste mnemônico se refere a tabela abaixo, onde:

Segundo operador: Controlador ADC (ADC1 = 0 ou ADC2 = 1). Usamos "0" pois o GPIO32 faz parte do ADC1.

Terceiro operador: MUX+1. O GPIO32 esta descrito como "...CH4", logo foi usado seu canal+1 (4+1).

Figura 2 - Pinagem MUX do ADC.

-Mnemônico RSH

rsh r0, r0, 2

O Right Shift Bit foi usado para calcular a média das 4 leituras. Lembre-se que os valores decimais (após a vírgula) são excluídos, restando apenas os inteiros.

-Mnemônico JUMPR

jumpr wakeup, 496, ge

Esse é o nosso "IF a moda antiga", que pula para a label "wakeup" se o valor do ADC for maior ou igual que 496. Ocasionando no Wake up do ESP32 pelo ULP.

Pelo fato de operações aritméticas não serem tão simples neste Assembly do ULP, em vez da condicional (jumpr) que faz o wake up do ESP32 usar valores como 32°C, onde é preciso efetuar varias contas, usaremos o valor direto do ADC que economiza processamento e tempo. O valor 496 no ADC com o LM35 equivale a 40°C .

Mais informações sobre o LM35 podem ser vistas clicando aqui.


Considerações finais

Mesmo que as aplicações do ULP estejam voltadas a Sleep, podemos usa-lo até com o ESP32 ligado, para por exemplo ler o canal ADC enquanto o ESP32 faz outra tarefa em que não se possa "perder tempo" lendo os lentos canais de ADC. Também é possível criar funções ISR para criar interrupções entre ULP e Main Core, deixando a brincadeira em um nível muito mais sério e interessante.


Desafios

O desafio desta vez é criar a rotina de interrupção citada acima (ISR) para uma comunicação extremamente rápida e eficiente entre ULP e Main Core. Você pode procurar no datasheet sobre o registrador (bit) que o comando WAKE do ULP ativa quando o ESP32 não esta em Sleep e criar a ISR.

Referências

http://esp-idf.readthedocs.io/en/latest/api-guides/ulp_instruction_set.html

https://portal.vidadesilicio.com.br/ultra-low-power-coprocessor-ulp-esp32/


Instalando a ESP IDF no Windows - ESP32

Instalando a ESP IDF no Windows - ESP32

Neste tutorial, vamos aprender a instalar a Toolchain juntamente com a ESP IDF, que é um ambiente completo de desenvolvimento pro ESP32, onde é suportado todas features, opções e configurações sobre o microcontrolador, permitindo que o ESP32 se encaixe perfeitamente nos mais variados projetos, desde simples até industriais!

[toc]

O que é a ESP IDF e quais são os pontos fortes?

Normalmente chamada apenas de IDF, podemos dizer que esta engloba todos os conjuntos de API's e configurações do ESP32. Já a Toolchain (mingw32) consiste em uma ferramenta que compila e constrói o código que escrevemos juntamente com as configurações escolhidas no "menuconfig". Por fim, ESP IDF é o conjunto de bibliotecas feitas pro ESP32.

Os principais pontos a serem levados em consideração para você trocar a Arduino IDE ou outra, pela IDF são:

  • Suporte a todas features, incluindo Bluetooth, Flash Encryption e Secure Boot (itens importantes para quem pretende criar e vender produtos).
  • Configuração total do sistema, como por exemplo eFuses, clock, watchdog's, timer's, memória dinâmica para WiFi e até tempo de Wake-UP após Deep sleep.

Mãos a obra - Testando a ESP IDF

Instalando a Toolchain e ESP IDF

Usaremos alguns diretórios padrões para instalação, você pode optar por escolher qualquer nome e local, mas tome cuidado ao seguir o tutorial "ao pé da letra".

1-) Faça o download da Toolchain em: http://esp-idf.readthedocs.io/en/latest/get-started/windows-setup.html

Figura 1 - Download Toolchain.

2-) Abra o .ZIP e copie a pasta "msys32" para a raiz do seu HD "C:\".

Figura 2 - Copiando a pasta.

3-) Faça o download do .ZIP da ultima release que encontrar, nesse caso é a 3.0-rc1: https://github.com/espressif/esp-idf/releases

Nota: há outras maneiras de baixar a IDF, como pelo próprio Toolchain. Caso precise de outros métodos, veja as referencias.

Figura 3 - Download da IDF.

4-) Crie uma pasta chamada "ESP32" na raiz da "msys32" que você copiou no passo 2. Logo em seguida, abra o .ZIP do passo 3, copie a pasta dentro desse novo diretório "ESP32" e renomeie para "esp-idf". Ficara parecido com: "C:\msys32\ESP32\esp-idf"

Figura 4 - Pasta renomeada.

5-) Precisamos adicionar uma variável à Toolchain que guardará o diretório da pasta "esp-idf", sem isso não funcionará corretamente.

Vá no diretório "C:\msys32\etc\profile.d" copie e cole qualquer script (extensão .SH), fazendo com que o arquivo fique duplicado.

Figura 5 - Duplicando um script.

Renomeie este script duplicado para "export_idf_path" e abra-o com algum editor de texto como bloco de notas e apague tudo o que estiver escrito. Escreva essa nova e única linha que indica ao Toolchain o diretório da esp-idf que instalamos:

export IDF_PATH="C:\msys32\ESP32\esp-idf"

Figura 6 - export_idf_path editado para o caminho da esp-idf.

Finalmente instalamos todo o ambiente de programação e desenvolvimento do ESP32 no Windows, agora vamos copiar a pasta de algum exemplo da IDF para que seja nosso local de trabalho.

Vamos copiar a pasta do projeto "hello world" que se encontra em "C:\msys32\ESP32\esp-idf\examples\get-started\" no mesmo local que nossa pasta "esp-idf" e renomeá-la "esp32".

Figura 6 - Pasta hello world a ser copiada.
Figura 7 - Hello world copiado e renomeado.

Pronto! Podemos usufruir de todas features e configurações do ESP32. Se você não souber usar o painel da IDF, logo abaixo em "Entendendo a fundo", explicaremos como usar a IDF para compilar e configurar seus projetos.

Componentes necessários

Código do projeto

Não iremos programar nada! Apenas dar upload do antigo "hello world"  que copiamos logo acima, veja abaixo em "Entendendo a fundo" como dar upload do código ao ESP32 pelo painel da IDF.

Colocando para funcionar

Após o upload do código e ver o monitor, já temos algumas informações sobre o nosso microcontrolador.

Figura 8 - IDF MONITOR.

Entendendo a fundo

IDF

Para dar upload dos códigos, ver o "Serial monitor", editar configurações do Bootloader e etc, precisamos usar o painel (CMD) da IDF.

Antes de mostrarmos alguns comandos da IDF, vamos configurar o nosso projeto ESP32 (antigo hello world) para que o upload seja feito e vejamos funcionando.

1-) Abra o programa "mingw32" que se encontra em "C:\msys32\", abra a pasta do projeto com "cd /esp32/esp32" e abra o menu de configurações com "make menuconfig"

Figura 9 - Abrindo a pasta do projeto e configurando.

Aqui é o paraíso do ESP32, onde conseguimos configurar praticamente tudo desse microcontrolador, desde clock até alocação de memórias dinâmicas e Watchdog.

Precisamos escolher a porta COM em que seu ESP32 está plugado no computador, você encontra isso em "Gerenciador de dispositivos" no Windows, o nosso está na porta COM8.

2-) Vá em "Serial flasher config" e altere a "Default serial port" para sua porta COM, o nosso será COM8. Você também pode alterar a velocidade de upload caso seu conversor suporte, normalmente é o CP2102 que suporta até a opção "921600".

Aconselhamos fortemente que se você pretende usar o ESP32 para algo comercial ou projeto sério, leia e configure todas as opções do ESP32 corretamente para seu projeto, você pode ver a ajuda de cada configuração apertando "H" e nas referencias.

Agora que configuramos a porta COM correta para upload, salve e saia do menuconfig.

3-) Digite "make flash monitor" para que comece a compilação do seu código, isso pode demorar MUITO toda vez que você altera o menuconfig, pois o Toolchain recompila todos os componentes, entretanto, após a primeira compilação depois da alteração do menuconfig, não demora mais que 60 segundos.

A dica para acelerar essa primeira compilação é usar todos os núcleos do seu processador. É indicado usar o numero de núcleos + 1. Nosso computador tem 4 núcleos, então vamos usar o seguinte comando: "make flash monitor -j5", isso deixa a compilação extremamente mais rápida.

Nosso código será finalmente enviado ao ESP32 e automaticamente após o termino, será aberto o "Serial monitor" que mostrará algumas informações sobre seu microcontrolador.

A partir daqui você já pode aproveitar a IDF, também existem alguns comandos interessantes e únicos:

  • make help: mostra todos os comandos da IDF e o que fazem.
  • make: mostra alguns comandos de compilação e endereços dos binários.
  • make flash: da upload do código ao ESP32.
  • make monitor: abre o "Serial monitor".
  • make clean: apaga a pasta BUILD do projeto.
  • make erase_flash: apaga toda a FLASH do ESP32, incluindo seções de memória não volátil como NVS, FAT e EEPROM.
  • make size: mostra o tamanho dos binários como o uso da FLASH e RAM.

Considerações finais

A ESP-IDF é um prato cheio pra quem pretende se aprofundar no ESP32 desde motivos profissionais ou educacionais, uma vez que este é barato e confiável até para aplicações industriais, diferente do ESP8266.

Você ainda pode usar bibliotecas do Arduino como a WiFi, Serial e WiFiClient em vez das originais da IDF, isso é chamado de "Arduino component", onde podemos usar tanto as bibliotecas e funções do Arduino, quanto da IDF. Para isso, precisará alterar algumas coisas da pasta do seu projeto que são ensinadas aqui: https://github.com/espressif/arduino-esp32/blob/master/docs/esp-idf_component.md

Referencias

http://esp-idf.readthedocs.io/en/latest/get-started/windows-setup.html

http://esp-idf.readthedocs.io/en/latest/get-started/index.html

http://esp-idf.readthedocs.io/en/latest/get-started/add-idf_path-to-profile.html

https://github.com/espressif/arduino-esp32/blob/master/docs/esp-idf_component.md