Um Arduino Mega como master se
comunicando com um ESP32 e um Arduino Uno, ambos como slave. Toda a explicação
desta comunicação utilizando SPI, tratando do envio do estado dos pinos
Digitais e Analógicos (AD) do Arduino Mega para o ESP32 e do Arduino Mega para
o Arduino UNO, você vê neste vídeo acima. Pessoal, sei que SPI não é um assunto
tão bacaninha. Posso dizer até ligeiramente “árido”, mas é indispensável.
RECURSOS USADOS
·
ESP-WROOM32 (38 pinos)
·
Arduino Mega 2560
·
Arduino UNO
·
Protoboard
·
Conversor de nível lógico 3V3-5V
·
Jumpers
·
Push button
·
Resistor 10K ohm
PINOUT ARDUINO MEGA
PINOUT ARDUINO UNO
PINOUT ESP32
MONTAGEM
*Conecte utilizando conversor de nível:
D50 > H1 L1 < GPIO19
D51 > H2 L2 < GPIO23
D52 > H3 L3 < GPIO18
D53 > H4 L4 < GPIO5
CÓDIGO
Configurações
ESP32 - Bibliotecas
necessárias
VISUALIZAÇÃO NO MONITOR SERIAL (UNO)
VISUALIZAÇÃO NO MONITOR SERIAL (ESP)
CÓDIGO ESP32
Fluxograma
CÓDIGO MEGA
Fluxograma
CÓDIGO UNO
Fluxograma
CÓDIGO
CÓDIGO MEGA
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_ESP 53
#define GPIO_SS_UNO 49
// 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 30 bytes
struct digitalAndADPins
{
uint8_t type = 2;
uint8_t digitalPins[15];
uint16_t ADPins[7];
};
// Declaração das variáveis especiais (structs) globais
struct digitalPinsUntil33 DPins1;
struct digitalAndADPins Dpins2_ADPins1;
Setup
void setup()
{
// Iniciamos a velocidade da serial em 115200 para debug
Serial.begin(115200);
setupGPIOs();
/*
// 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)); // Tamanho de 32 bytes
Serial.println((int)sizeof(digitalAndADPins)); // Tamanho de 31 bytes
*/
// Iniciamos a SPI
SPI.begin();
// Setamos o pino do botão como entrada
pinMode(eventPin, INPUT);
// Setamos os pinos slave select como saída
pinMode(GPIO_SS_ESP, OUTPUT);
pinMode(GPIO_SS_UNO, 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 os slaves desabilitado (quando low significa que uma nova transação será feita)
digitalWrite(GPIO_SS_ESP, HIGH);
digitalWrite(GPIO_SS_UNO, HIGH);
Serial.println("Master Initialized");
delay(10);
}
Loop
void loop()
{
// Se o botão foi pressionado
if(digitalRead(eventPin))
{
// Aguarda soltar o botão
while(digitalRead(eventPin))
delay(1);
// Aguarda 1 segundo, se o botão for pressionado novamente enviamos para o UNO, senão, enviamos para o ESP32
if(buttonPressed(1000))
{
// Envia para o arduino
Serial.println("Enviando para o Arduino UNO");
sendValuesToUNO();
}
else
{
// Envia para o ESP
Serial.println("Enviando para o ESP32");
sendValuesToESP();
}
// Aguarda soltar o botão
while(digitalRead(eventPin))
delay(1);
}
else
delay(10);
}
SendValuesToUNO
// Função que envia as 2 structs para o Arduino UNO
void sendValuesToUNO()
{
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
sendStruct(DPins1, GPIO_SS_UNO);
delay(500);
Serial.println("Enviando parte 2");
// Enviamos a segunda estrutura em outra transação
sendStruct(Dpins2_ADPins1, GPIO_SS_UNO);
delay(500);
Serial.println("OK");
}
SendValuesToESP
// Função que envia as 2 structs para o ESP32
void sendValuesToESP()
{
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
sendStruct(DPins1, GPIO_SS_ESP);
delay(150);
Serial.println("Enviando parte 2");
// Enviamos a segunda estrutura em outra transação
sendStruct(Dpins2_ADPins1, GPIO_SS_ESP);
delay(150);
Serial.println("OK");
}
SendStruct
// Função que envia os valores da estrutura do tipo 1 via SPI
void sendStruct(struct digitalPinsUntil33 dados, int SSPin)
{
// Selecionamos o slave correspondente ao pino SSPin (arduino uno ou esp)
digitalWrite(SSPin, LOW);
delay(100);
// 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<(int)sizeof(digitalPinsUntil33); i++)
{
SPI.transfer(*p++);
delay(5);
}
delay(100);
// Desativamos o slave correspondente ao pino SSPin (arduino uno ou esp)
digitalWrite(SSPin, HIGH);
}
// Função que envia os valores da estrutura do tipo 2 via SPI
void sendStruct(struct digitalAndADPins dados, int SSPin)
{
// Selecionamos o slave correspondente ao pino SSPin (arduino uno ou esp)
digitalWrite(SSPin, LOW);
delay(100);
// 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<(int)sizeof(digitalAndADPins); i++)
{
SPI.transfer(*p++);
delay(5);
}
delay(100);
// Desativamos o slave correspondente ao pino SSPin (arduino uno ou esp)
digitalWrite(SSPin, HIGH);
}
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.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);
}
setupGPIOs
// Função que seta os pinos como input
void setupGPIOs()
{
for(int i=2; i<13; i++)
pinMode(i, INPUT);
for(int i=14; i<49; i++)
pinMode(i, INPUT);
pinMode(A0, INPUT);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
pinMode(A3, INPUT);
pinMode(A4, INPUT);
pinMode(A5, INPUT);
pinMode(A6, INPUT);
}
CÓDIGO ESP
Declarações e Variáveis
// Biblioteca SlaveSPI para ESP32
#include <SlaveSPI.h>
// Biblioteca SPI
#include <SPI.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;
uint8_t digitalPins[31];
};
struct digitalAndADPins
{
uint8_t type;
uint8_t digitalPins[15];
uint16_t ADPins[7];
};
struct digitalPinsUntil33 DPins1;
struct digitalAndADPins Dpins2_ADPins1;
void readData(uint8_t type);
Setup
void setup()
{
// Desabilitamos o watchdog de hardware do core 0
disableCore0WDT();
// 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 entrada
pinMode(_SCK, INPUT);
// Setamos o pino MISO como saída
pinMode(_MISO, OUTPUT);
// 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()
{
// Se existem dados a serem lidos
if(slave.getInputStream()->length() && digitalRead(_SS) == HIGH)
{
uint8_t type;
// Obtemos o primeiro byte, que indica o tipo de struct recebida
type = slave.getInputStream()->getBuffer()[0];
// Efetuamos a leitura de acordo com o seu tipo
readData(type);
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
memcpy(&DPins1, slave.getInputStream()->getBuffer(), sizeof(digitalPinsUntil33));
break;
case 2:
// Atribuimos ao endereço da struct global Dpins2_ADPins1, os valores em bytes recebidos por SPI
memcpy(&Dpins2_ADPins1, slave.getInputStream()->getBuffer(), sizeof(digitalAndADPins));
break;
default:
// Se o tipo não for 1 ou 2 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*)(int)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
// 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));
// 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;
default:
// Se o tipo não for 1 ou 2, exibimos erro
Serial.println("taskPrintValues: Tipo invalido");
break;
}
// Setamos a flag para false
serialIsBusy = false;
// Encerramos a task
vTaskDelete(NULL);
}
PrintStatePins (Struct 1 e Struct 2)
// 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]));
}
// 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<15; 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]));
}

Callback_after_slave_tx_finish
// Função callback que é chamada logo após uma transação SPI
int callback_after_slave_tx_finish()
{
//Serial.println("Size:"+String(slave.getInputStream()->length()));
//Serial.println("[slave_tx_finish] slave transmission has been finished!");
//Serial.println(slave[0]);
}
CÓDIGO UNO
Declarações e Variáveis
#include// Pinos SPI: // SCK: GPIO13 // MISO: GPIO12 // MOSI: GPIO11 // SS: GPIO10 // Variáveis que correspondem ao código do Arduino Mega (explicação no código do Arduino Mega) struct digitalPinsUntil33 { uint8_t type; uint8_t digitalPins[31]; }; struct digitalAndADPins { uint8_t type; uint8_t digitalPins[15]; uint16_t ADPins[7]; }; struct digitalPinsUntil33 DPins1; struct digitalAndADPins Dpins2_ADPins1; // Variável que indica se a primeira struct já foi recebida bool struct1Received = false; // Buffer que recebe os dados do Arduino Mega uint8_t buf[32]; // Variável referente ao tamanho do buffer int sizeBuff = 0;
Setup
void setup (void)
{
// Iniciamos a serial (debug)
Serial.begin (115200);
// Setamos o SPI para o modo Slave
SPCR |= bit (SPE);
// Setamos o pino MISO como saída
pinMode(MISO, OUTPUT);
// Setamos o pino SS como entrada
pinMode(SS, INPUT);
// Ativamos a função de interrupção (ISR)
SPI.attachInterrupt();
Serial.println("Slave ok");
}
Loop
void loop (void)
{
if (sizeBuff>0 && digitalRead(SS) == HIGH)
{
delay(50);
//Serial.println("Size: "+String(sizeBuff));
if(!struct1Received)
{
memcpy(&DPins1, buf, sizeof(DPins1));
struct1Received = true;
}
else
{
memcpy(&Dpins2_ADPins1, buf, sizeof(Dpins2_ADPins1));
process();
struct1Received = false;
}
sizeBuff = 0;
}
}
ISR
// Função de interrupção SPI
ISR(SPI_STC_vect)
{
// Atribuimos para o buffer o valor de SPDR (Recebido via SPI)
buf[sizeBuff++] = SPDR;
}
Process
// Função que exibe na Serial os valores das structs 1 e 2
// De acordo com o tipo (type)
void process()
{
if(DPins1.type == 1)
printStatePins(DPins1);
else
Serial.println("Erro ao obter primeiro byte (struct 1)");
if(Dpins2_ADPins1.type == 2)
printStatePins(Dpins2_ADPins1);
else
Serial.println("Erro ao obter primeiro byte (struct 2)");
}
printStatePins (Struct 1 e Struct 2)
// 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]));
}
// 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<15; 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]));
}







































0 Comentários