SPI communication problem with two ESP32 - Page 1
Forum section: electronics / general

author: fedimakni
author_rank: Regular Contributor
timestamp: September 24, 2022, 03:49:52 am
content: Hello,I am currently trying to implement an SPI communication between 2 ESP32 using arduino IDE but i am facing a problem.I set 1 as a master using the normal SPI library and the second one as slave using ESP32SPISlave.Master SW is basically writing 0 or 1 to slave device and wait for a responseif the master send a 0 the slave will send back 2 in the next communication,if the master send 1 the slave will send back 1 in the next communication.the problem that sometimes even if the master send 1 i receive the number 2 instead of 1 so it seems that it doesn't override the previous value.I am not sure exactly what's the problem. could you please support me?I will paste both SWYou can find the picture of the issue in the link belowhttps://drive.google.com/file/d/1K08ntvOcR7fH9SyWb-RZu7XXCOKP2msH/view?usp=sharingMaster SWCode:#include <SPI.h>// Define ALTERNATE_PINS to use non-standard GPIO pins for SPI bus#ifdef ALTERNATE_PINS#define VSPI_MISO 26#define VSPI_MOSI 27#define VSPI_SCLK 25#define VSPI_SS 32#else#define VSPI_MISO MISO#define VSPI_MOSI MOSI#define VSPI_SCLK SCK#define VSPI_SS SS#endifstatic const int spiClk = 1000000; // 1 MHzint data1;//uninitalised pointers to SPI objectsSPIClass * vspi = NULL;void setup() {Serial.begin(115200);delay(2000);//initialise instance of the SPIClass attached to HSPIvspi = new SPIClass(VSPI);//clock miso mosi ss#ifndef ALTERNATE_PINS//initialise hspi with default pins//SCLK = 14, MISO = 12, MOSI = 13, SS = 15vspi->begin();#else//alternatively route through GPIO pinsvspi->begin(VSPI_SCLK, VSPI_MISO, VSPI_MOSI, VSPI_SS); //SCLK, MISO, MOSI, SS#endif//set up slave select pins as outputs as the Arduino API//doesn't handle automatically pulling SS lowpinMode(VSPI_SS, OUTPUT); //HSPI SS}// the loop function runs over and over again until power down or resetvoid loop() {vspi_send_command();delay(1000);}void vspi_send_command() {byte data_on = 0b00000001; // data 1 to turn on LED of slavebyte data_off = 0b0000000; // data 0 to turn off LED of slavevspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));digitalWrite(VSPI_SS, LOW);data1 = vspi->transfer(data_on);digitalWrite(VSPI_SS, HIGH);vspi->endTransaction();Serial.print("data sent is 1 and data received is ");Serial.println(data1);delay(1000);vspi->beginTransaction(SPISettings(spiClk, MSBFIRST, SPI_MODE0));digitalWrite(VSPI_SS, LOW);data1 = vspi->transfer(data_off);digitalWrite(VSPI_SS, HIGH);vspi->endTransaction();Serial.print("data sent is 0 and data received is ");Serial.println(data1);delay(1000);}Slave SWCode:#include <ESP32SPISlave.h>ESP32SPISlave slave;static constexpr uint32_t BUFFER_SIZE {32};uint8_t spi_slave_tx_buf;uint8_t spi_slave_rx_buf;#define LED 2void setup() {Serial.begin(115200);delay(2000);pinMode(LED, OUTPUT);// begin() after setting// HSPI = CS: 15, CLK: 14, MOSI: 13, MISO: 12 -> default// VSPI = CS: 5, CLK: 18, MOSI: 23, MISO: 19slave.setDataMode(SPI_MODE0);//slave.begin();slave.begin(VSPI); // you can use VSPI like this// clear buffersmemset(spi_slave_tx_buf, 0, BUFFER_SIZE);memset(spi_slave_rx_buf, 0, BUFFER_SIZE);}void loop() {// block until the transaction comes from masterslave.wait(spi_slave_rx_buf, spi_slave_tx_buf, BUFFER_SIZE);// if transaction has completed from master,// available() returns size of results of transaction,// and buffer is automatically updatedchar data;while (slave.available()) {// show received dataSerial.print("Command Received: ");Serial.println(spi_slave_rx_buf);data = spi_slave_rx_buf;slave.pop();}if(data == 1 ){Serial.println("Setting LED active HIGH ");digitalWrite(LED, HIGH);memset(spi_slave_tx_buf, 1, BUFFER_SIZE);}else if(data == 0 ){Serial.println("Setting LED active LOW ");digitalWrite(LED, LOW);memset(spi_slave_tx_buf, 2, BUFFER_SIZE);}Serial.println("");}Thank you again for your cooperation.

author: ledtester
author_rank: Super Contributor
timestamp: September 24, 2022, 04:37:00 am
content: In your slave code try replacing:Code:char data;with:Code:char data = 3;The point is that you are potentially using an uninitialized value. Suppose that slave.wait() returns but there aren't any transactions to process. Then the variable data will contain an uninitialized value which could possibly screw things up.You could also add a final else clause to the if-else block that checks the value of data to see if this ever happens:Code:else { // data is not 0 or 1Serial.println("*** data is not 0 or 1 ***")}

author: fedimakni
author_rank: Regular Contributor
timestamp: September 24, 2022, 05:02:00 am
content: Thank you very much for the response.but i think the problem comes from the master because the data received back in the master is what contain the errorinitializing "data" will just fix the first iteration but the problem happened during the execution of the SW as you can see in the picture (link attached in the main post)I was really thinking that it's a timing issue but i couldn't figure it out

author: fedimakni
author_rank: Regular Contributor
timestamp: September 24, 2022, 05:33:17 am
content: I checked something right now,I actually found that i receive the number 2 in the slave when i only send 0 or 1 something weird but i don't know how to fix it.Do you have an idea?

author: DavidAlfa
author_rank: Super Contributor
timestamp: September 24, 2022, 08:12:03 am
content: Or use static char data, so it holds the value.

author: fedimakni
author_rank: Regular Contributor
timestamp: September 24, 2022, 08:33:27 am
content: That cannot help because actually i need that value to change depending on the received value. so static is not an option

author: rstofer
author_rank: Super Contributor
timestamp: September 24, 2022, 01:17:06 pm
content: In the past, I noted that sometimes SS goes high before the last byte is completely transmitted. I found it necessary to actually check that the byte had been transmitted and the output buffer empty before raising SS.

author: DavidAlfa
author_rank: Super Contributor
timestamp: September 25, 2022, 01:26:28 am
content: Static doesn't mean constant!Basicaly it's initialized only once and its last value is always kept.

author: fedimakni
author_rank: Regular Contributor
timestamp: September 26, 2022, 02:56:13 am
content: Do you have an idea how we can check that the SS went high before the last byte??

author: JustMeHere
author_rank: Frequent Contributor
timestamp: September 26, 2022, 10:39:29 am
content: Do you have an oscilloscope? Need to see your signals.

author: mikerj
author_rank: Super Contributor
timestamp: September 26, 2022, 12:10:21 pm
content: Code:data1 = vspi->transfer(data_off);This is your problem area. This class method sends out 8 bits of data and simultaneously receives 8 bits of data. The slave cannot determine what value to place in it's TX buffer until it's received and processed the data you have sent it, so it's impossible for your value returned by this function to be correct.This is a general problem with SPI and some other synchronous protocols when implemented on a microcontroller, the master has to give sufficient turnaround time for the slave to process data it has received and form a response and get it ready for the master to read it. Even with very fast micros there isn't typically enough time to do this in half an SPI clock cycle with highish SPI clock speeds (10-20MHz), so either you send dummy bytes during the turn around period, or you implement some kind of service request pin on the slave to let the master know data is ready for reading.Note that in your SPI slave you have also committed the cardinal sin of putting printf functions in the receive handling code which will greatly slow the slave's response time.As a simple but ugly proof of concept you could just repeat the SPI transfer after a suitable delay e.g.Code:data1 = vspi->transfer(data_off);delay(10);data1 = vspi->transfer(data_off);By the time the second transfer method is run, the slave should have processed the value received from the first and put the correct value in it's TX buffer. Using a delay like this is horribly ugly, you either have to use an excessively long delay, slowing down the SPI comms or do a bunch of work to find out the worst case times and repeat this whenever you change the code. You should implement a better solution.

author: ledtester
author_rank: Super Contributor
timestamp: September 26, 2022, 01:09:45 pm
content: Quote from: mikerj on September 26, 2022, 12:10:21 pmHi mikerj, I don't think this is the problem. If you look at the google drive file the OP linked to in the first post you'll see that when a 1 is sent a 1 is received on the next transfer -- except in the circled area where you can see an instance of a 2 being returned instead.Here's an excerpt:Code:...data sent is 0 and data received is 1data sent is 1 and data received is 2data sent is 0 and data received is 2 <-- problem heredata sent is 1 and data received is 2data sent is 0 and data received is 1...So one of the transfers glitched.