Você quer ganhar dinheiro com Arduino?
Quer ganhar dinheiro com Internet das Coisas? Então, você precisa investir no
seu conhecimento e também se equipar com coisas boas para aprender na prática.
Hoje vou te mostrar como expandir as portas do ESP32 com o Arduino Mega. Vamos
utilizar comunicação SPI entre ESP32 e Arduino Mega, exibindo uma explicação do
envio do estado dos pinos Digitais e Analógicos (AD) do Arduino Mega para o
ESP32.
A principal vantagem do
Arduino Mega, em minha opinião, é que ele tem muitos IOs e, portanto, a melhor
opção para projetos que necessitam de diversas portas.
RECURSOS USADOS
- ESP-WROOM32 (38 pinos)
- Arduino Mega 2560
- Protoboard
- Conversor de nível lógico 3V3-5V
- Jumpers
- Push button
- Resistor 10K ohm
MONTAGEM
*Conexão utilizando conversor
de nível:
D50 > H1 L1 < GPIO19
D51 > H2
L2 < GPIO23
D52 > H3
L3 < GPIO18
D53 > H4
L4 < GPIO5
VISUALIZAÇÃO NO MONITOR SERIAL
Monitor serial – Arduino Mega
(Após apertar o botão)
Monitor serial – ESP32
(Ao receber os dados)
Caso uma task esteja exibindo
os valores, outra task irá aguardar até que essa primeira termine (verificando o
estado da flag serialIsBusy).
A ordem de exibição das tasks
não importa. A task que conseguir setar a flag serialIsBusy para true primeiro é
a próxima que será executada.
CÓDIGO ARDUINO
Fluxograma
CÓDIGO ESP32
Fluxograma
Configurações
ESP32 - Bibliotecas
necessárias:
·
Biblioteca Slave SPI ESP32
·
Biblioteca Simple Array
CÓDIGO ARDUINO
Declarações e variáveis
// Biblioteca SPI
#include <SPI.h>
// Pinos SPI default utilizados
#define GPIO_MISO 50
#define GPIO_MOSI 51
#define GPIO_SCK 52
#define GPIO_SS 53
// Setamos as configuraçõs SPI para 16 MHz, bit mais significativo primeiro e SPI_MODE0
SPISettings settings(16000000, MSBFIRST, SPI_MODE0);
// Botão que servirá como disparo de envio SPI
int eventPin = 13;
// Estruturas que guardarão os valores dos pinos Digitais e AD
// As estruturas devem possuir no máximo 32 bytes (tamanho máximo de transações SPI). Um valor digital ocupa 1 byte de espaço, enquanto um valor AD ocupa 2 bytes.
// A variável type indica qual o tipo de estrutura for enviado, assim, do lado do slave, podemos identificar qual é a estrutura que foi recebida
// A struct "digitalPinsUntil33" guarda os valores dos pinos D2 a D33, exceto o D13 (pino usado para o botão). Possui um tamanho de 32 bytes
struct digitalPinsUntil33
{
uint8_t type = 1;
uint8_t digitalPins[31];
};
// A struct "digitalAndADPins" guarda os valores dos pinos D32 a D49 e também os A0 a A6. Possui um tamanho de 31 bytes
struct digitalAndADPins
{
uint8_t type = 2;
uint8_t digitalPins[16];
uint16_t ADPins[7];
};
// A struct "ADPinsLeft" guarda os valores restantes de AD, sendo de A7 a A15. Possui um tamanho de 19 bytes
struct ADPinsLeft
{
uint8_t type = 3;
uint16_t ADPins[9];
};
Setup
void setup()
{
// Iniciamos a velocidade da serial em 115200 para debug
Serial.begin(115200);
// Caso seja necessário saber qual o tamanho da estrutura basta descomentar o código abaixo e visualizar no monitor serial
/*
Serial.println((int)sizeof(digitalPinsUntil33)); // 32
Serial.println((int)sizeof(digitalAndADPins)); //31
Serial.println((int)sizeof(ADPinsLeft)); //19
*/
// Iniciamos a SPI
SPI.begin();
// Setamos o pino do botão como entrada
pinMode(eventPin, INPUT);
// Setamos o pino slave select como saída
pinMode(GPIO_SS, OUTPUT);
// Setamos o pino Clock como saída pinMode(GPIO_SCK, OUTPUT); // Setamos o pino Clock para baixo (SPI_MODE0) digitalWrite(GPIO_SCK, LOW); // Deixamos o slave desabilitado (quando low significa que uma nova transação será feita) digitalWrite(GPIO_SS, HIGH); delay(10); }
Loop
void loop()
{
// Se o botão foi pressionado
if(digitalRead(eventPin) == HIGH)
{
Serial.println("Lendo pinos");
// Efetuamos as leituras de todos os pinos
readPins();
Serial.println("Enviando parte 1");
// Enviamos a primeira estrutura em uma transação
sendToESP(DPins1);
Serial.println("Enviando parte 2");
// Enviamos a segunda estrutura em outra transação
sendToESP(Dpins2_ADPins1);
Serial.println("Enviando parte 3");
// Enviamos a terceira estrutura em outra transação
sendToESP(ADPins2);
Serial.println("OK");
// Enquanto o botão continuar pressionado, aguardamos
// Impedindo múltiplos envios se o botão ficar pressionado por muito tempo
while(digitalRead(eventPin) == HIGH)
delay(1);
}
else
delay(10);
}
ReadPins
// Função que lê os pinos e guarda seus valores nas estruturas
void readPins()
{
// Estrutura do tipo 1: digitalPinsUntil33
DPins1.digitalPins[0] = digitalRead(2);
DPins1.digitalPins[1] = digitalRead(3);
DPins1.digitalPins[2] = digitalRead(4);
DPins1.digitalPins[3] = digitalRead(5);
DPins1.digitalPins[4] = digitalRead(6);
DPins1.digitalPins[5] = digitalRead(7);
DPins1.digitalPins[6] = digitalRead(8);
DPins1.digitalPins[7] = digitalRead(9);
DPins1.digitalPins[8] = digitalRead(10);
DPins1.digitalPins[9] = digitalRead(11);
DPins1.digitalPins[10] = digitalRead(12);
// O Botao está ligado na gpio13, então não faz parte dos pinos válidos
DPins1.digitalPins[11] = digitalRead(14);
DPins1.digitalPins[12] = digitalRead(15);
DPins1.digitalPins[13] = digitalRead(16);
DPins1.digitalPins[14] = digitalRead(17);
DPins1.digitalPins[15] = digitalRead(18);
DPins1.digitalPins[16] = digitalRead(19);
DPins1.digitalPins[17] = digitalRead(20);
DPins1.digitalPins[18] = digitalRead(21);
DPins1.digitalPins[19] = digitalRead(22);
DPins1.digitalPins[20] = digitalRead(23);
DPins1.digitalPins[21] = digitalRead(24);
DPins1.digitalPins[22] = digitalRead(25);
DPins1.digitalPins[23] = digitalRead(26);
DPins1.digitalPins[24] = digitalRead(27);
DPins1.digitalPins[25] = digitalRead(28);
DPins1.digitalPins[26] = digitalRead(29);
DPins1.digitalPins[27] = digitalRead(30);
DPins1.digitalPins[28] = digitalRead(31);
DPins1.digitalPins[29] = digitalRead(32);
DPins1.digitalPins[30] = digitalRead(33);
// Estrutura do tipo 2: digitalAndADPins
Dpins2_ADPins1.digitalPins[0] = digitalRead(34);
Dpins2_ADPins1.digitalPins[1] = digitalRead(35);
Dpins2_ADPins1.digitalPins[2] = digitalRead(36);
Dpins2_ADPins1.digitalPins[3] = digitalRead(37);
Dpins2_ADPins1.digitalPins[4] = digitalRead(38);
Dpins2_ADPins1.digitalPins[5] = digitalRead(39);
Dpins2_ADPins1.digitalPins[6] = digitalRead(40);
Dpins2_ADPins1.digitalPins[7] = digitalRead(41);
Dpins2_ADPins1.digitalPins[8] = digitalRead(42);
Dpins2_ADPins1.digitalPins[9] = digitalRead(43);
Dpins2_ADPins1.digitalPins[10] = digitalRead(44);
Dpins2_ADPins1.digitalPins[11] = digitalRead(45);
Dpins2_ADPins1.digitalPins[12] = digitalRead(46);
Dpins2_ADPins1.digitalPins[13] = digitalRead(47);
Dpins2_ADPins1.digitalPins[14] = digitalRead(48);
Dpins2_ADPins1.digitalPins[15] = digitalRead(49);
Dpins2_ADPins1.ADPins[0] = analogRead(A0);
Dpins2_ADPins1.ADPins[1] = analogRead(A1);
Dpins2_ADPins1.ADPins[2] = analogRead(A2);
Dpins2_ADPins1.ADPins[3] = analogRead(A3);
Dpins2_ADPins1.ADPins[4] = analogRead(A4);
Dpins2_ADPins1.ADPins[5] = analogRead(A5);
Dpins2_ADPins1.ADPins[6] = analogRead(A6);
// Estruturas do tipo 3: ADPinsLeft
ADPins2.ADPins[0] = analogRead(A7);
ADPins2.ADPins[1] = analogRead(A8);
ADPins2.ADPins[2] = analogRead(A9);
ADPins2.ADPins[3] = analogRead(A10);
ADPins2.ADPins[4] = analogRead(A11);
ADPins2.ADPins[5] = analogRead(A12);
ADPins2.ADPins[6] = analogRead(A13);
ADPins2.ADPins[8] = analogRead(A14);
ADPins2.ADPins[7] = analogRead(A15);
}
sendToESP – Struct 1
// Função que envia os valores da estrutura do tipo 1 via SPI
void sendToESP(struct digitalPinsUntil33 dados)
{
// Selecionamos o slave (esp)
digitalWrite(GPIO_SS,LOW);
// Iniciamos uma transação
SPI.beginTransaction(settings);
// Criamos um ponteiro que aponta para o primeiro byte da estrutura 'dados'
uint8_t *p = (uint8_t*)&dados;
// Percorremos byte a byte da estrutura, enviando-os um por vez, até que todos os 32 bytes sejam enviados
for(int i=0; i<=sizeof(digitalPinsUntil33); i++)
SPI.transfer(*p++);
// Finalizamos a transação
SPI.endTransaction();
// Desativamos o slave (esp)
digitalWrite(GPIO_SS,HIGH);
}
sendToESP – Struct 2
// Função que envia os valores da estrutura do tipo 2 via SPI
void sendToESP(struct digitalAndADPins dados)
{
// Selecionamos o slave (esp)
digitalWrite(GPIO_SS,LOW);
// Iniciamos uma transação
SPI.beginTransaction(settings);
// Criamos um ponteiro que aponta para o primeiro byte da estrutura 'dados'
uint8_t *p = (uint8_t*)&dados;
// Percorremos byte a byte da estrutura, enviando-os um por vez, até que todos os 31 bytes sejam enviados
for(int i=0; i<=sizeof(digitalAndADPins); i++)
SPI.transfer(*p++);
// Finalizamos a transação
SPI.endTransaction();
// Desativamos o slave (esp)
digitalWrite(GPIO_SS,HIGH);
}
sendToESP – Struct 3
// Função que envia os valores da estrutura do tipo 3 via SPI
void sendToESP(struct ADPinsLeft dados)
{
// Selecionamos o slave (esp)
digitalWrite(GPIO_SS,LOW);
// Iniciamos uma transação
SPI.beginTransaction(settings);
// Criamos um ponteiro que aponta para o primeiro byte da estrutura 'dados'
uint8_t *p = (uint8_t*)&dados;
// Percorremos byte a byte da estrutura, enviando-os um por vez, até que todos os 19 bytes sejam enviados
for(int i=0; i<=sizeof(ADPinsLeft); i++)
SPI.transfer(*p++);
// Finalizamos a transação
SPI.endTransaction();
// Desativamos o slave (esp)
digitalWrite(GPIO_SS,HIGH);
}
CÓDIGO ESP
Declarações e variáveis
// Biblioteca SlaveSPI para ESP32
#include <SlaveSPI.h>
// Biblioteca SPI
#include <SPI.h>
#include "esp_task_wdt.h"
// Pinos SPI utilizados
#define _MISO (gpio_num_t)19
#define _MOSI (gpio_num_t)23
#define _SCK (gpio_num_t)18
#define _SS (gpio_num_t)5
// Objeto SlaveSPI, setamos para virtual SPI (VSPI)
SlaveSPI slave(VSPI_HOST);
// Variável usada para que duas tasks não exibam na serial ao mesmo tempo, evitando que os valores se embaralhem durante suas exibições
bool serialIsBusy = false;
// Variáveis que correspondem ao código do Arduino Mega (explicação no código do Arduino Mega)
struct digitalPinsUntil33
{
uint8_t type = 1;
uint8_t digitalPins[31];
};
struct digitalAndADPins
{
uint8_t type = 2;
uint8_t digitalPins[16];
uint16_t ADPins[7];
};
struct ADPinsLeft
{
uint8_t type = 3;
uint16_t ADPins[9];
};
struct digitalPinsUntil33 DPins1;
struct digitalAndADPins Dpins2_ADPins1;
struct ADPinsLeft ADPins2;
Setup
void setup()
{
// Desabilitamos o watchdog de hardware do core 0
//disableCore0WDT(); // Desnecessário
// Setamos a velocidade da serial para 115200
Serial.begin(115200);
// Setamos o pino Slave Select como entrada
pinMode(_SS, INPUT);
// Setamos o pino Clock como saída
pinMode(_SCK, OUTPUT);
// Setamos o pino Clock para baixo (SPI_MODE0)
digitalWrite(_SCK, LOW);
// Iniciamos a SPI setando os pinos, tamanho máximo de transmissões (32 bytes) e setando a função callback (opcional)
slave.begin(_MISO, _MOSI, _SCK, _SS, 32, callback_after_slave_tx_finish);
}
Loop
void loop()
{
// Verificamos se existem dados a serem lidos na SPI
if(slave.getInputStream()->length() && digitalRead(_SS) == HIGH)
{
uint8_t type;
// Obtemos o primeiro byte, que indica o tipo de struct recebida
slave.getInputStream()->getBytes(&type, sizeof(uint8_t));
// Efetuamos a leitura de acordo com o seu tipo
readData(type);
// Limpamos o buffer
slave.flushInputStream();
}
}
ReadData
// Função que lê os dados, atribuindo aos endereços das structs
void readData(uint8_t type)
{
// Executamos um switch/case para o tipo (type)
switch(type)
{
case 1:
// Atribuimos ao endereço da struct global DPins1, os valores em bytes recebidos por SPI
slave.getInputStream()->getBytes(&DPins1, sizeof(digitalPinsUntil33));
break;
case 2:
// Atribuimos ao endereço da struct global Dpins2_ADPins1, os valores em bytes recebidos por SPI
slave.getInputStream()->getBytes(&Dpins2_ADPins1, sizeof(digitalAndADPins));
break;
case 3:
// Atribuimos ao endereço da struct global ADPins2, os valores em bytes recebidos por SPI
slave.getInputStream()->getBytes(&ADPins2, sizeof(ADPinsLeft));
break;
default:
// Se o tipo não for 1, 2 ou 3, exibimos erro e abortamos a função
Serial.println("Erro ao obter primeiro byte!");
return;
break;
}
// Executamos uma task no core 0 para exibirmos na serial os valores das structs
// Assim podemos voltar para a rotina de leitura SPI mais rapidamente
xTaskCreatePinnedToCore(taskPrintValues, "taskPrintValues", 10000, (void*)type, 2, NULL, 0);
}
TaskPrintValues
// Task que exibe os valores dos pinos de acordo com o tipo de estrutura
void taskPrintValues(void *p)
{
// Recebemos o tipo por parâmetro
// 1 - primeira struct
// 2 - segunda struct
// 3 - terceira struct
// Obs. Não é obrigatório enviar as estruturas na ordem, desde que seus tipos sejam enviados corretamente
uint8_t type = *((uint8_t*)(&p));
// Flag que evita imprimir os dados embaralhados (por mais de uma task)
while(serialIsBusy)
delay(1);
// Setamos a flag para true
serialIsBusy = true;
// Exibimos na serial
Serial.println("\nTipo:"+String(type));
PrintStatePins
// Para cada tipo, chamamos a função printStatePins enviando uma determinada struct
switch(type)
{
case 1:
printStatePins(DPins1);
break;
case 2:
printStatePins(Dpins2_ADPins1);
break;
case 3:
printStatePins(ADPins2);
break;
default:
// Se o tipo não for 1, 2 ou 3, exibimos erro
Serial.println("taskPrintValues: Tipo invalido");
break;
}
// Setamos a flag para false
serialIsBusy = false;
// Encerramos a task
vTaskDelete(NULL);
}
printStatePins – Struct 1
// Função que exibe na serial os valores da primeira estrutura (D2 até D33)
void printStatePins(struct digitalPinsUntil33 DPins1)
{
// Percorremos o vetor de pinos digitais até o GPIO12 exibindo na serial
// Exemplo de exibição: "D3 = 0"
for(int i=0; i<11; i++)
Serial.println("D"+String(i+2)+" = "+String(DPins1.digitalPins[i]));
// Sabemos que o GPIO13 não faz parte dos pinos válidos, portanto na exibição pulamos do D12 para o D14
for(int i=11; i<31; i++)
Serial.println("D"+String(i+3)+ " = "+String(DPins1.digitalPins[i]));
}
printStatePins – Struct 2
// Sobrecarga da função acima
// Função que exibe na serial os valores da segunda estrutura (D34 até D49 e A0 até A6)
void printStatePins(struct digitalAndADPins Dpins2_ADPins1)
{
// Percorremos o vetor com o restante dos pinos digitais exibindo na serial do D34 ao D49
// Exemplo de exibição: "D34 = 0"
for(int i=0; i<16; i++)
Serial.println("D"+String(i+34)+" = "+String(Dpins2_ADPins1.digitalPins[i]));
// Percorremos o vetor com os valores AD do A0 ao A6 e exibimos na serial
for(int i=0; i<7; i++)
Serial.println("AD"+String(i)+ " = "+String(Dpins2_ADPins1.ADPins[i]));
}
printStatePins – Struct 3
// Sobrecarga da função acima
// Função que exibe na serial restante dos valores AD (do A7 ao A15)
void printStatePins(struct ADPinsLeft ADPins2)
{
// Percorremos o vetor com os valores AD do A7 ao A15 e exibimos na serial
for(int i=0; i<9;i++)
Serial.println("AD"+String(i+7)+" = "+String(ADPins2.ADPins[i]));
}
Função call-back setada no setup (opcional)
// Função callback que é chamada logo após uma transação SPI
int callback_after_slave_tx_finish()
{
//Serial.println("[slave_tx_finish] slave transmission has been finished!");
//Serial.println(slave[0]);
return 0;
}


































7 Comentários
Boa tarde Fernando.... tudo bem com você, parabéns pelo projeto e pelo grande profissional que você é,eu assisto seus videos no youtube, e acho suas explicações excelentes e de ótimo conteúdo
ResponderExcluirParabéns! - pelo projeto, muito interessante.
ResponderExcluirA titulo de experiência, segue algumas questões:
- Seria muito complicado eu adaptar este projeto para o Arduino Uno e ESP32 30 pinos?
- As bibliotecas utilizadas teriam que ser modificadas?
Saudações, aguardo retorno.
Bom dia. parabéns pela explicação e conteudo. gostaria de tirar umas duvidas, eu estava montando um sistema baseado em i2c, e estou avaliando a possibilidade de uso via spi. obrigado.
ResponderExcluirBoa noite Fernando, eu possuo um projeto com ESP32 que está programado para ser controlado pela Alexa (Amazon Echo Dot), utilizando a biblioteca do fauxmo. Eu gostaria de enviar o status da porta do ESP32 para o Arduino Mega. Isso seria possível de se adaptar com esse seu projeto?
ResponderExcluirObrigado e parabéns pelo canal!
Boa Noite Mestre! Que ótima idéia de projeto! Parabéns pela aula! Gostei muito e estou tentando fazer, mas estou com problemas, onde aparece o mesmo erro na compilaçao do ESP32: 'class SlaveSPI' has no member named 'getInputStream'. Acho que o problema é library, mas não estou conseguindo resolver, tem alguma idéia do que está acontecendo? Obrigado!
ResponderExcluirEste comentário foi removido pelo autor.
ExcluirHugo, estou com o mesmo problema. Como tu resolveu?!
ExcluirForte Abraço.