Manual
do
Maker
.
com
O ESP32 é incrível e já deve ter sido possível reparar que é o meu preferido pela quantidade de artigos que escrevo sobre ele. Usar microfone no ESP32, vejam só, é tão simples quanto usar um display. Ou talvez mais simples.
Com um módulo microfone de eletreto, um ESP32, jumpers e um pouco de código, faremos o ESP32 enviar streming de áudio para um Raspberry, que será aplicado em um próximo artigo em algo bem interessante.
Para esse projeto você precisará:
O circuito é simples. Alimenta-se com 5V e GND, coloca-se um divisor de tensão na saída e pronto. O divisor de tensão é necessário porque a saída do módulo varia entre 0 e 2.25V e o ADC do ESP32 é de apenas 1V (mas 12 bits). Para conectar o microfone no ESP32, utilize a saída do jumper verde, como mostrado no desenho abaixo.
Não sei como aproximar mais de 1V, esses resistores são valores padrão, não adianta eu colocar 5k e 4k pra bater 1V. A diferença representa 7%, talvez passar um pouquinho não seja problema. No caso, 4k3 e 5k6.
Antes de darmos sequência à configuração do microfone no ESP32, vamos ao Raspberry Pi. Faça login no sistema e como root, execute o configurador do sistema. No meu caso, o IP do Raspberry é fixo porque ele também é meu servidor DNS e broker MQTT. Seu IP é 192.168.1.2, por isso daqui em diante será utilizado no exemplo. Além disso, configurei o usuário root nele:
ssh root@192.168.1.2
#após login:
raspi-config
No menu em ncurses que se abre, vá em Advanced Options > Audio > Force 3.5mm ('headphone') jack.
Ou então, configure diretamente no boot.txt para funcionar no boot e para funcionar imediatamente, digite:
modprobe snd_bcm2835
depmod -a
amixer cset numid=1
Se (em ambos os casos) obtiver um erro como "amixer: Control default open error: No such file or directory", tente atualizar o sistema:
apt-get update
apt-get upgrade
Reinicie o sistema e tente novamente.
O programa NetCat é utilizado para estabelecer conexões e escutas UDP e TCP. A porta utilizada nesse exemplo é a 8888, mas você pode optar por qualquer porta livre no sistema, acima de 1024. Se quiser verificar previamente se a porta que escolheu está em uso, faça-o com o comando:
netstat -naut|awk '{print $4}'| egrep '8888$'
Substituindo 8888 pelo número da porta escolhida, claro. Esse comando só retornará alguma coisa se a porta estiver em uso. Por exemplo:
Nesse caso, selecione outra porta ou pare o serviço que estiver rodando nela, se for dispensável.
O Aplay é um programa de linha de comando para gravar (arecord) e tocar áudio com ALSA. Passaremos o formato e a taxa de amostragem em Hz. Esse formato mataria de infarto qualquer audiófilo, mas devemos lembrar que estamos fazendo quase uma mágica aqui. O comando a seguir abre a recepção por rede e toca o áudio recebido. Coloque uma caixa de som no jack do Raspberry ou use um headphone para ouvir.
nc -l 8888 | aplay -r 8000 -f U8
Agora que o Raspberry está pronto para escuta, vamos subir o sketch que habilitará o microfone no ESP32.
#include <Arduino.h>
#include <WiFi.h>
#include <driver/adc.h>
#define AUDIO_BUFFER_MAX 800
uint8_t audioBuffer[AUDIO_BUFFER_MAX];
uint8_t transmitBuffer[AUDIO_BUFFER_MAX];
uint32_t bufferPointer = 0;
const char* ssid = "SUA_REDE_AQUI";
const char* password = "SUA_SENHA_AQUI";
const char* host = "IP_DO_RASPBERRY";
bool transmitNow = false;
WiFiClient client;
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
void IRAM_ATTR onTimer() {
portENTER_CRITICAL_ISR(&timerMux); // para rodar um código crítico sem ser interrompido.
int adcVal = adc1_get_voltage(ADC1_CHANNEL_0); // faz a leitura do ADC
uint8_t value = map(adcVal, 0 , 4096, 0, 255); // mapeamento para 8 bits
audioBuffer[bufferPointer] = value; // armazenamento do valor
bufferPointer++;
// Ação no preenchimento do buffer
if (bufferPointer == AUDIO_BUFFER_MAX) {
bufferPointer = 0;
memcpy(transmitBuffer, audioBuffer, AUDIO_BUFFER_MAX); // transfere o buffer
transmitNow = true; // flag para envio do buffer
}
portEXIT_CRITICAL_ISR(&timerMux); // prioridade no código crítico
}
void setup() {
Serial.begin(115200);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("MY IP address: ");
Serial.println(WiFi.localIP());
adc1_config_width(ADC_WIDTH_12Bit);
adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_0db); //ADC 1 canal 0 GPIO36
const int port = 8888;
while (!client.connect(host, port)) {
Serial.println("connection failed");
delay(1000);
}
Serial.println("connected to server");
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 125, true);
timerAlarmEnable(timer);
}
void loop() {
// aguarda pela ordem de envio
if (transmitNow) {
transmitNow = false;
client.write((const uint8_t *)audioBuffer, sizeof(audioBuffer));
}
}
Invés de utilizar um flag no loop, podemos criar uma task no núcleo 0 do ESP32 a ser iniciada em cada preenchimento do buffer. Do jeito que está, o ESP32 não tem outra aplicação que não seja a de transmitir áudio. Mas isso veremos em outro artigo, onde pretendo inserir mais alguns recursos. Manterei a surpresa.
Pra reforçar, esse código é só uma prova de conceito, sem tratamento de exceções, mas já será modificado no próximo artigo relacionado, quando estaremos adicionando mais recursos ao sistema. Nesse momento, só rode o sketch do ESP32 após iniciar a escuta no Raspberry, ok?
Aproveitei a ocasião para dar uma mexida no Raspberry e acabei prejudicando o sistema. Agora estou baixando uma imagem nova e precisarei reconfigurar o servidor DNS, broker MQTT, reconhecimento facial e o servidor de streaming de áudio. Por isso, devo demorar uns dias pra postar o vídeo, mas logo mais você poderá vê-lo em nosso canal DobitAoByteBrasil no Youtube. Se inscreva, clique no sininho para receber notificações e, por favor, gaste 1 clique de mouse dando seu like para motivar mais vídeos.
Inscreva-se no nosso canal Manual do Maker no YouTube.
Também estamos no Instagram.
Autor do blog "Do bit Ao Byte / Manual do Maker".
Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.