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