Arduino Nano ESP32 - Can't get SPI slave to work

Hello,
I am new to building Arduino projects and wanted to learn a bit about SPI using an Arduino. I have been trying to establish a connection between two Arduino Nano ESP32 devices. The Master device seems to be working right, and sends an array of numbers every 2 seconds. However, the Slave device doesn't seem to be working properly.

I've been following this demo from an ESP32SPISlave library. The master and slave both are supposed to send the numbers 1-8 and verifies that they each correctly received the data. The master pulls the SS line every 2 seconds to initiate the transfer, however when I set it up on my devices the returned buffer's only have 0's in them.

As well, the slave device doesn't seem to be waiting for the SS line, as it prints unexpected difference found between master/slave data nonstop. I have been trying to figure out if I wired things wrong or simply don't have a good understanding on how SPI is meant to work on the Nano ESP32. Below is the code I've been using (which is from the example code on the aforementioned library), as well as my current wiring.

Any help is appreciated as I want to better understand how to work with SPI.

helper.h

#pragma once

#include <Arduino.h>
#include <cstdint>
#include <cstddef>

void dumpBuffers(const char *title, const uint8_t *buf, size_t start, size_t len) {
  // show title and range
  if (len == 1)
    Serial.printf("%s [%d]: ", title, start);
  else
    Serial.printf("%s [%d-%d]: ", title, start, start + len - 1);

  // show data in the range
  for (size_t i = 0; i < len; i++) {
    Serial.printf("%02X ", buf[start + i]);
  }
  Serial.printf("\n");
}

bool verifyAndDumpDifference(const char *a_title, const uint8_t *a_buf, size_t a_size, const char *b_title, const uint8_t *b_buf, size_t b_size) {
  bool verified = true;

  if (a_size != b_size) {
    Serial.printf("received data size does not match: expected = %d / actual = %d\n", a_size, b_size);
    return false;
  }

  for (size_t i = 0; i < a_size; i++) {
    // if a_buf and b_buf is same, continue
    if (a_buf[i] == b_buf[i]) {
      continue;
    }

    verified = false;

    // if a_buf[i] and b_buf[i] is not same, check the range that has difference
    size_t j = 1;
    while (a_buf[i + j] != b_buf[i + j]) {
      j++;
    }

    // dump different data range
    dumpBuffers(a_title, a_buf, i, j);
    dumpBuffers(b_title, b_buf, i, j);

    // restart from next same index (-1 considers i++ in for())
    i += j - 1;
  }
  return verified;
}

void initializeBuffers(uint8_t *tx, uint8_t *rx, size_t size, size_t offset = 0) {
  if (tx) {
    for (size_t i = 0; i < size; i++) {
      tx[i] = (i + offset) & 0xFF;
    }
  }
  if (rx) {
    memset(rx, 0, size);
  }
}

master code:

#include <SPI.h>
#include "helper.h"

SPIClass master(HSPI);

static constexpr size_t BUFFER_SIZE = 8;
uint8_t tx_buf[BUFFER_SIZE]{ 1, 2, 3, 4, 5, 6, 7, 8 };
uint8_t rx_buf[BUFFER_SIZE]{ 0, 0, 0, 0, 0, 0, 0, 0 };

void setup() {
  Serial.begin(9600);

  delay(2000);

  pinMode(SS, OUTPUT);
  digitalWrite(SS, HIGH);
  master.begin(SCK, MISO, MOSI, SS);

  delay(2000);

  Serial.println("start spi master");
}

void loop() {
  // initialize tx/rx buffers
  initializeBuffers(tx_buf, rx_buf, BUFFER_SIZE);

  master.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));
  digitalWrite(SS, LOW);
  master.transferBytes(tx_buf, rx_buf, BUFFER_SIZE);
  digitalWrite(SS, HIGH);
  master.endTransaction();

  // verify and dump difference with received data
  if (verifyAndDumpDifference("master", tx_buf, BUFFER_SIZE, "slave", rx_buf, BUFFER_SIZE)) {
    Serial.println("successfully received expected data from slave");
  } else {
    Serial.println("unexpected difference found between master/slave data");
  }

  delay(2000);
}

Slave Code:

#include <ESP32SPISlave.h>
#include "helper.h"

ESP32SPISlave slave;

static constexpr size_t BUFFER_SIZE = 8;
static constexpr size_t QUEUE_SIZE = 1;
uint8_t tx_buf[BUFFER_SIZE]{ 1, 2, 3, 4, 5, 6, 7, 8 };
uint8_t rx_buf[BUFFER_SIZE]{ 0, 0, 0, 0, 0, 0, 0, 0 };

void setup() {
  Serial.begin(9600);

  delay(2000);

  slave.setDataMode(SPI_MODE0);    // default: SPI_MODE0
  slave.setQueueSize(QUEUE_SIZE);  // default: 1

  // begin() after setting
  slave.begin(HSPI, SCK, MISO, MOSI, SS);  // default: HSPI (please refer README for pin assignments)

  Serial.println("start spi slave");
}

void loop() {
  // initialize tx/rx buffers
  initializeBuffers(tx_buf, rx_buf, BUFFER_SIZE);

  // start and wait to complete one BIG transaction (same data will be received from slave)
  const size_t received_bytes = slave.transfer(tx_buf, rx_buf, BUFFER_SIZE);

  // verify and dump difference with received data
  if (verifyAndDumpDifference("slave", tx_buf, BUFFER_SIZE, "master", rx_buf, received_bytes)) {
    Serial.println("successfully received expected data from master");
  } else {
    Serial.println("unexpected difference found between master/slave data");
  }
}

Wiring
Arduino Nano ESP32 - SPI Slave Wiring

I thought I'd give an update, after a little bit of tinkering I realized the problem and I feel a bit foolish now :sweat_smile:. It was indeed an issue with the wiring on the slave.

I was aware that the GPIO and Arduino pin numbers differ, and that some libraries may interpret them the wrong way, I just hadn't considered that the library I was using may be doing that. Anyways, by using the predefined SCK, MISO, MOSI, and SS vars, the pin numbers mapped to them were being read as GPIO numbers, and I should have caught that. Anyways, below is the revised code if anyone else stumbles across this problem.

Slave code:

#include <ESP32SPISlave.h>
#include "helper.h"

// GPIO pin mappings
#define SLAVE_SCK 48
#define SLAVE_MISO 47
#define SLAVE_MOSI 38
#define SLAVE_SS 21

ESP32SPISlave slave;

static constexpr size_t BUFFER_SIZE = 8;
static constexpr size_t QUEUE_SIZE = 1;
uint8_t tx_buf[BUFFER_SIZE]{ 1, 2, 3, 4, 5, 6, 7, 8 };
uint8_t rx_buf[BUFFER_SIZE]{ 0, 0, 0, 0, 0, 0, 0, 0 };

void setup() {
  Serial.begin(9600);

  delay(2000);

  slave.setDataMode(SPI_MODE0);    // default: SPI_MODE0
  slave.setQueueSize(QUEUE_SIZE);  // default: 1

  // begin() after setting
  slave.begin(HSPI, SLAVE_SCK, SLAVE_MISO, SLAVE_MOSI, SLAVE_SS);  // default: HSPI (please refer README for pin assignments)

  Serial.println("start spi slave");
}

void loop() {
  // initialize tx/rx buffers
  initializeBuffers(tx_buf, rx_buf, BUFFER_SIZE);

  // start and wait to complete one BIG transaction (same data will be received from slave)
  const size_t received_bytes = slave.transfer(tx_buf, rx_buf, BUFFER_SIZE);

  // verify and dump difference with received data
  if (verifyAndDumpDifference("slave", tx_buf, BUFFER_SIZE, "master", rx_buf, received_bytes)) {
    Serial.println("successfully received expected data from master");
  } else {
    Serial.println("unexpected difference found between master/slave data");
  }
}

Now it's working and I can sleep.

3 Likes

You have no idea how long I have been looking for this answer. You are the absolute best, thank you.