ESP32: Usage of both SPI's / FATSD Library on HSPI

Hi all,
I have reseached all I could but it didnt get me any further.
Generally my project is to build a thermometer sending its data via an NRF24 and also logging it on board.

The NRF24 is to be run off of the VSPI on the ESP

The MAX6675 and the SD card should be run off of HSPI

As you may expect - it did not work.

Right now I am trying to get the SDFat run SD card to initialize. Wiring should be ok, using the arduino on-board SD library I can access it just fine. But, as it is know that the SD library is not to be used with any other device on the same SPI bus I had to switch to SDFat.

I tried to use the SDInfo.ino and adapted it. I dont use PIN12 for MISO as it (knownly) tends to give you problems during upload of the sketch.

So first I added 2 SPI Classes as explained in the SPI_MULTIPLE_BUSSES.ino example.

vspi = new SPIClass(VSPI);
hspi = new SPIClass(HSPI);
  //hspi->begin(); 
  //alternatively route through GPIO pins
hspi->begin(14, 26, 13,25); //SCLK, MISO, MOSI, SS

I dont begin VSPI because I dont use it atm.

On Github (GITHUB_SDFAT) is says:

Support for multiple SPI ports now uses a pointer to a SPIClass object.

See the STM32Test example.

explicit SdFat(SPIClass* spiPort);
\\ or
explicit SdFatEX(SPIClass* spiPort);

so I expected this to work:

  if (!sd.cardBegin(&hspi,25)) {
    sdErrorMsg("\ncardBegin failed");
    return;
  }

Sadly I get:

no matching function for call to 'SdFat::cardBegin(SPIClass*&, int)'

I suspect I just messed up something small but I just dont understand.

So in general I am trying to get the SD card (not the 3.3V China module, I dont have a Level adaption) and MAX6675 on Pins:

CLK,MISO,MOIS,SS
SD 14, 26, 13, 25
MAX 14, 26, 13, 27

Any help here? I am a bit lost right now...

The whole Sketch for reference:

/*
 * This program attempts to initialize an SD card and analyze its structure.
 */
#include <SPI.h>
#include "SdFat.h"
#include "sdios.h"

// Set USE_SDIO to zero for SPI card access. 
#define USE_SDIO 0
SPIClass * vspi = NULL;
SPIClass * hspi = NULL;
/*
 * SD chip select pin.  Common values are:
 *
 * Arduino Ethernet shield, pin 4.
 * SparkFun SD shield, pin 8.
 * Adafruit SD shields and modules, pin 10.
 * Default SD chip select is the SPI SS pin.
 */
const uint8_t SD_CHIP_SELECT = 25;
/*
 * Set DISABLE_CHIP_SELECT to disable a second SPI device.
 * For example, with the Ethernet shield, set DISABLE_CHIP_SELECT
 * to 10 to disable the Ethernet controller.
 */
const int8_t DISABLE_CHIP_SELECT = -1;

#if USE_SDIO
// Use faster SdioCardEX
SdFatSdioEX sd;
// SdFatSdio sd;
#else // USE_SDIO
SdFat sd;
#endif  // USE_SDIO

// serial output steam
ArduinoOutStream cout(Serial);

// global for card size
uint32_t cardSize;

// global for card erase size
uint32_t eraseSize;
//------------------------------------------------------------------------------
// store error strings in flash
#define sdErrorMsg(msg) sd.errorPrint(F(msg));
//------------------------------------------------------------------------------
uint8_t cidDmp() {
  cid_t cid;
  if (!sd.card()->readCID(&cid)) {
    sdErrorMsg("readCID failed");
    return false;
  }
  cout << F("\nManufacturer ID: ");
  cout << hex << int(cid.mid) << dec << endl;
  cout << F("OEM ID: ") << cid.oid[0] << cid.oid[1] << endl;
  cout << F("Product: ");
  for (uint8_t i = 0; i < 5; i++) {
    cout << cid.pnm[i];
  }
  cout << F("\nVersion: ");
  cout << int(cid.prv_n) << '.' << int(cid.prv_m) << endl;
  cout << F("Serial number: ") << hex << cid.psn << dec << endl;
  cout << F("Manufacturing date: ");
  cout << int(cid.mdt_month) << '/';
  cout << (2000 + cid.mdt_year_low + 10 * cid.mdt_year_high) << endl;
  cout << endl;
  return true;
}
//------------------------------------------------------------------------------
uint8_t csdDmp() {
  csd_t csd;
  uint8_t eraseSingleBlock;
  if (!sd.card()->readCSD(&csd)) {
    sdErrorMsg("readCSD failed");
    return false;
  }
  if (csd.v1.csd_ver == 0) {
    eraseSingleBlock = csd.v1.erase_blk_en;
    eraseSize = (csd.v1.sector_size_high << 1) | csd.v1.sector_size_low;
  } else if (csd.v2.csd_ver == 1) {
    eraseSingleBlock = csd.v2.erase_blk_en;
    eraseSize = (csd.v2.sector_size_high << 1) | csd.v2.sector_size_low;
  } else {
    cout << F("csd version error\n");
    return false;
  }
  eraseSize++;
  cout << F("cardSize: ") << 0.000512*cardSize;
  cout << F(" MB (MB = 1,000,000 bytes)\n");

  cout << F("flashEraseSize: ") << int(eraseSize) << F(" blocks\n");
  cout << F("eraseSingleBlock: ");
  if (eraseSingleBlock) {
    cout << F("true\n");
  } else {
    cout << F("false\n");
  }
  return true;
}
//------------------------------------------------------------------------------
// print partition table
uint8_t partDmp() {
  mbr_t mbr;
  if (!sd.card()->readBlock(0, (uint8_t*)&mbr)) {
    sdErrorMsg("read MBR failed");
    return false;
  }
  for (uint8_t ip = 1; ip < 5; ip++) {
    part_t *pt = &mbr.part[ip - 1];
    if ((pt->boot & 0X7F) != 0 || pt->firstSector > cardSize) {
      cout << F("\nNo MBR. Assuming Super Floppy format.\n");
      return true;
    }
  }
  cout << F("\nSD Partition Table\n");
  cout << F("part,boot,type,start,length\n");
  for (uint8_t ip = 1; ip < 5; ip++) {
    part_t *pt = &mbr.part[ip - 1];
    cout << int(ip) << ',' << hex << int(pt->boot) << ',' << int(pt->type);
    cout << dec << ',' << pt->firstSector <<',' << pt->totalSectors << endl;
  }
  return true;
}
//------------------------------------------------------------------------------
void volDmp() {
  cout << F("\nVolume is FAT") << int(sd.vol()->fatType()) << endl;
  cout << F("blocksPerCluster: ") << int(sd.vol()->blocksPerCluster()) << endl;
  cout << F("clusterCount: ") << sd.vol()->clusterCount() << endl;
  cout << F("freeClusters: ");
  uint32_t volFree = sd.vol()->freeClusterCount();
  cout <<  volFree << endl;
  float fs = 0.000512*volFree*sd.vol()->blocksPerCluster();
  cout << F("freeSpace: ") << fs << F(" MB (MB = 1,000,000 bytes)\n");
  cout << F("fatStartBlock: ") << sd.vol()->fatStartBlock() << endl;
  cout << F("fatCount: ") << int(sd.vol()->fatCount()) << endl;
  cout << F("blocksPerFat: ") << sd.vol()->blocksPerFat() << endl;
  cout << F("rootDirStart: ") << sd.vol()->rootDirStart() << endl;
  cout << F("dataStartBlock: ") << sd.vol()->dataStartBlock() << endl;
  if (sd.vol()->dataStartBlock() % eraseSize) {
    cout << F("Data area is not aligned on flash erase boundaries!\n");
    cout << F("Download and use formatter from www.sdcard.org!\n");
  }
}
//------------------------------------------------------------------------------
void setup() {
  Serial.begin(9600);


vspi = new SPIClass(VSPI);
hspi = new SPIClass(HSPI);
  //hspi->begin(); 
  //alternatively route through GPIO pins
hspi->begin(14, 26, 13,25); //SCLK, MISO, MOSI, SS





  
  // Wait for USB Serial 
  while (!Serial) {
    SysCall::yield();
  }

  // use uppercase in hex and use 0X base prefix
  cout << uppercase << showbase << endl;

  // F stores strings in flash to save RAM
  cout << F("SdFat version: ") << SD_FAT_VERSION << endl;
#if !USE_SDIO  
  if (DISABLE_CHIP_SELECT < 0) {
    cout << F(
           "\nAssuming the SD is the only SPI device.\n"
           "Edit DISABLE_CHIP_SELECT to disable another device.\n");
  } else {
    cout << F("\nDisabling SPI device on pin ");
    cout << int(DISABLE_CHIP_SELECT) << endl;
    pinMode(DISABLE_CHIP_SELECT, OUTPUT);
    digitalWrite(DISABLE_CHIP_SELECT, HIGH);
  }
  cout << F("\nAssuming the SD chip select pin is: ") <<int(SD_CHIP_SELECT);
  cout << F("\nEdit SD_CHIP_SELECT to change the SD chip select pin.\n");
#endif  // !USE_SDIO  
}
//------------------------------------------------------------------------------
void loop() {
  // Read any existing Serial data.
  do {
    delay(10);
  } while (Serial.available() && Serial.read() >= 0);

  // F stores strings in flash to save RAM
  cout << F("\ntype any character to start\n");
  while (!Serial.available()) {
    SysCall::yield();
  }

  uint32_t t = millis();

  if (!sd.cardBegin(&hspi,25)) {
    sdErrorMsg("\ncardBegin failed");
    return;
  }

  t = millis() - t;

  cardSize = sd.card()->cardSize();
  if (cardSize == 0) {
    sdErrorMsg("cardSize failed");
    return;
  }
  cout << F("\ninit time: ") << t << " ms" << endl;
  cout << F("\nCard type: ");
  switch (sd.card()->type()) {
  case SD_CARD_TYPE_SD1:
    cout << F("SD1\n");
    break;

  case SD_CARD_TYPE_SD2:
    cout << F("SD2\n");
    break;

  case SD_CARD_TYPE_SDHC:
    if (cardSize < 70000000) {
      cout << F("SDHC\n");
    } else {
      cout << F("SDXC\n");
    }
    break;

  default:
    cout << F("Unknown\n");
  }
  if (!cidDmp()) {
    return;
  }
  if (!csdDmp()) {
    return;
  }
  uint32_t ocr;
  if (!sd.card()->readOCR(&ocr)) {
    sdErrorMsg("\nreadOCR failed");
    return;
  }
  cout << F("OCR: ") << hex << ocr << dec << endl;
  if (!partDmp()) {
    return;
  }
  if (!sd.fsBegin()) {
    sdErrorMsg("\nFile System initialization failed.\n");
    return;
  }
  volDmp();
}

The first thing do is to find a library that accepts the SPI object, the SPI bus to be used, as an input parameter. If so, good. If not, time to use the ESP32 SPI API.

For most SPI projects on the ESP32, I take a library to use for the device and adapt it to,directly, use the ESP32 API. There are several advantages to using the ESP32 SPI API over the Arduino SPI library. SPI Master Driver - ESP32 - — ESP-IDF Programming Guide latest documentation. The ESP32 SPI API makes use of the ESP32's built in OS, freeRTOS. Which means Queues can be used, so that SPI operations occur in the background and unclaimed SPI messages can be held in a Queue till wanted. I found that having more then 7 queues is a performance hit, for most applications.


The device on the ESP32 SPI HSPI must not transmit any data during the program load. For instance the ADAfruit Ultimate GPS just starts sending data as soon as its started up. If placed on HSPI data line changes would cause program upload to fail.

Thanks for that very informative reply! Specially the info about what NOT to place on HSPI.

I would consider myself slightly advances in programming after working with arduino for many years but that stuff in your link was kinda over my head :slight_smile:
I will try to get into it further.
If it is not ot much could you show an example of how you manipulate the libraries? For instance, the MAX6675 does not take SPI Class Object, the pins are just defined in the sketch itself. Hoiw can I make that library accept a SPI Class Object?

Sme1986:
Thanks for that very informative reply! Specially the info about what NOT to place on HSPI.

If it is not ot much could you show an example of how you manipulate the libraries? For instance, the MAX6675 does not take SPI Class Object, the pins are just defined in the sketch itself. Hoiw can I make that library accept a SPI Class Object?

re write the library to accept a SPI class object.

Here is a LSM9DSI library modified to work with the ESP32 SPI API:

#include "ESP32_LSM9DS1.h"
///////////////////////////////////////////
spi_device_handle_t hAG;
spi_device_handle_t hM;
////
#define csPinAG 5
#define csPinM 32
#define spiCLK 25 // CLK module pin SCL
#define spiMOSI 26 // MOSI module pin SDA
#define spiMISO 27 // MISO module pin SDOAG tied to SDOM
//
float _accel_mg_lsb;
float _mag_mgauss_lsb;
float _gyro_dps_digit;
float aXbias = 0.0f;
float aYbias = 0.0f;
float aZbias = 0.0f;
float gXbias = 0.0f, gYbias = 0.0f, gZbias = 0.0f;
bool LSM9DS1_ID_OK = false;
bool M_ID_OK = false;
float  aX, aY, aZ, gX, gY, gZ, mX, mY, mZ;
int8_t lowB;
int16_t highB;
float ACCELrange = 0.0f;
//////////////////////////////
int getMh()
{
  return (int)hM;
}
int getDh()
{
  return (int)hAG;
}
//////////////////////////////////////////
bool fInitializeDevice( )
{
  esp_err_t intError;
  intError = fInitializeSPI_Channel( spiCLK, spiMOSI, spiMISO, HSPI_HOST, true);
  if ( intError == 0 )
  {
    return true;
  }
}
bool fInitializeAG()
{
  esp_err_t intError;
  intError = fInitializeSPI_Devices( hAG, csPinAG );
  if ( intError == 0 )
  {
    if ( (int)hAG != 0 )
    {
      return true;
    }
  }
} // bool fInitializeAG()
bool fInitializeM()
{
  esp_err_t intError;

  intError = fInitializeSPI_Devices( hM, csPinM );
  if ( intError == 0 )
  {
    if ( (int)hM != 0 )
    {
      return true;
    }
  }
} // bool fInitializeM()
////
int16_t returnHighBits()
{
  return highB;
}
////
int8_t returnLowBits()
{
  return lowB;
}
////
void fReboot()
{
  // soft reset & reboot accel/gyro
  fWriteSPIdata8bits( hAG, LSM9DS1_REGISTER_CTRL_REG8, 0x05 );
  // soft reset & reboot magnetometer
  fWriteSPIdata8bits( hM, LSM9DS1_REGISTER_CTRL_REG2_M, 0x0C );
}
////
bool fDO_AG_ID()
{
  fReadSPIdata16bits( hAG, LSM9DS1_REGISTER_WHO_AM_I_XG );

  highB = GetHighBits();
  lowB = GetLowBits();
  if ( highB == LSM9DS1_ID )
  {
    LSM9DS1_ID_OK = true;
    return true;
  } else
  {
    return false;
  }
}
//////
////
bool getLSM9DS1_ID_OK()
{
  return LSM9DS1_ID_OK;
}
//////
bool fDO_M_ID()
{
  //  uint8_t temp;
  fReadSPIdata16bits( hM, LSM9DS1_REGISTER_WHO_AM_I_M );
  highB = GetHighBits();
  lowB = GetLowBits();
  if ( highB == LSM9DS1_MAG_ID )
  {
    M_ID_OK = true;
    return true;
  } else
  {
    return false;
  }
}
////
bool getMAG_ID_OK()
{
  return M_ID_OK;
}
//////
bool setupAccelScale ( int _range )
{
  esp_err_t intError = 0;
  uint8_t range = _range;
  fReadSPIdata16bits( hAG, LSM9DS1_REGISTER_CTRL_REG6_XL );
  uint8_t reg = GetHighBits();
  reg &= ~(0b00011000);
  reg |= range;
  fWriteSPIdata8bits( hAG, LSM9DS1_REGISTER_CTRL_REG6_XL, reg );
  switch (range)
  {
    case LSM9DS1_ACCELRANGE_2G:
      _accel_mg_lsb = LSM9DS1_ACCEL_MG_LSB_2G;
      ACCELrange = 2.0f;
      break;
    case LSM9DS1_ACCELRANGE_4G:
      _accel_mg_lsb = LSM9DS1_ACCEL_MG_LSB_4G;
      ACCELrange = 4.0f;
      break;
    case LSM9DS1_ACCELRANGE_8G:
      _accel_mg_lsb = LSM9DS1_ACCEL_MG_LSB_8G;
      ACCELrange = 8.0f;
      break;
    case LSM9DS1_ACCELRANGE_16G:
      _accel_mg_lsb = LSM9DS1_ACCEL_MG_LSB_16G;
      ACCELrange = 16.0f;
      break;
  }
  if ( intError )
  {
    return true;
  }
  else
  {
    return false;
  }
} // void setupAccel ( uint8_t range )
//////
bool fEnableGandA()
{
  esp_err_t intError = 0;
  // high pass filter cutoff gyro
  intError = fWriteSPIdata8bits( hAG, LSM9DS1_REGISTER_CTRL_REG3_G, 0x48 );
  if ( intError > 0 )
  {
    return false;
  }
  //  intError = fWriteSPIdata8bits( hAG, LSM9DS1_REGISTER_CTRL_REG5_XL, 0x38 );  // enable X Y and Z axis
  //  if ( intError > 0 )
  //  {
  //    return false;
  //  }
  intError = fWriteSPIdata8bits( hAG, LSM9DS1_REGISTER_CTRL_REG7_XL, 0xE0 ); // High resolution on,
  if ( intError > 0 )
  {
    return false;
  }
  // enable accelerometer continous
  intError = fWriteSPIdata8bits( hAG, LSM9DS1_REGISTER_CTRL_REG6_XL, 0x27 ); //
  if ( intError > 0 )
  {
    return false;
  }
  // enable gyro continuous
  intError = fWriteSPIdata8bits( hAG, LSM9DS1_REGISTER_CTRL_REG1_G, 0xC0 );
  ////  intError = fWriteSPIdata8bits( hAG, LSM9DS1_REGISTER_CTRL_REG1_G, 0xC1 );

part b b

  if ( intError > 0 )
  {
    return false;
  }
  ////
  return true;
} // bool fEnableGandA()
////
bool fEnableM()
{
  esp_err_t intError = 0;
  fWriteSPIdata8bits( hM, LSM9DS1_REGISTER_CTRL_REG1_M, 0xFC );  // high perf XY, 80 Hz ODR
  if ( intError > 0 )
  {
    return false;
  }
  fWriteSPIdata8bits( hM, LSM9DS1_REGISTER_CTRL_REG3_M, 0x00 );  // continuous mode
  if ( intError > 0 )
  {
    return false;
  }
  fWriteSPIdata8bits( hM, LSM9DS1_REGISTER_CTRL_REG4_M, 0x0C );  // high perf Z mode
  if ( intError > 0 )
  {
    return false;
  }
  return true;
}
////
bool setupMagScale ( int _gain )
{
  uint8_t gain = _gain;
  float AtoDscaleFactor = 32767.5f;
  if ( fReadSPIdata16bits( hM, LSM9DS1_REGISTER_CTRL_REG2_M ) > 0 )
  {
    return true;
  }
  uint8_t reg = GetHighBits();
  reg &= ~(0b01100000); // ~ ones compliment
  reg |= gain;
  if ( fWriteSPIdata8bits( hM, LSM9DS1_REGISTER_CTRL_REG2_M, reg ) > 0 )
  {
    return true;
  }
  switch (gain)
  {
    case LSM9DS1_MAGGAIN_4GAUSS:
      // _mag_mgauss_lsb = LSM9DS1_MAG_MGAUSS_4GAUSS;
      _mag_mgauss_lsb = 4.0f / AtoDscaleFactor;
      break;
    case LSM9DS1_MAGGAIN_8GAUSS:
      // _mag_mgauss_lsb = LSM9DS1_MAG_MGAUSS_8GAUSS;
      _mag_mgauss_lsb = 8.0f / AtoDscaleFactor;
      break;
    case LSM9DS1_MAGGAIN_12GAUSS:
      // _mag_mgauss_lsb = LSM9DS1_MAG_MGAUSS_12GAUSS;
      _mag_mgauss_lsb = 12.0f / AtoDscaleFactor;
      break;
    case LSM9DS1_MAGGAIN_16GAUSS:
      _mag_mgauss_lsb = 16.0f / AtoDscaleFactor;
      break;
  }
  return false;
} // void setupMag ( lsm9ds1MagGain_t gain )
////
bool setupGyroScale ( int _gscale )
{
  // esp_err_t intError = 0;
  uint8_t gscale = _gscale;
  float AtoDscaleFactor = 32767.5f;
  if ( fReadSPIdata16bits( hAG, LSM9DS1_REGISTER_CTRL_REG1_G) > 0 )
  {
    return true;
  }
  uint8_t reg = GetHighBits();
  reg &= ~(0b00011000); // ~ ones compliment
  reg |= gscale;
  if ( fWriteSPIdata8bits( hAG, LSM9DS1_REGISTER_CTRL_REG1_G, reg ) > 0 )
  {
    return true;
  }
  //
  switch ( gscale )
  {
    case LSM9DS1_GYROSCALE_245DPS:
      //_gyro_dps_digit = LSM9DS1_GYRO_DPS_DIGIT_245DPS;
      _gyro_dps_digit =  245.0f / AtoDscaleFactor;;
      break;
    case LSM9DS1_GYROSCALE_500DPS:
      // _gyro_dps_digit = LSM9DS1_GYRO_DPS_DIGIT_500DPS;
      _gyro_dps_digit = 500.0f / AtoDscaleFactor;
      break;
    case LSM9DS1_GYROSCALE_2000DPS:
      // _gyro_dps_digit = LSM9DS1_GYRO_DPS_DIGIT_2000DPS;
      _gyro_dps_digit = 2000.0f / AtoDscaleFactor;
      break;
  }
  return false;
} // void setupGyro ( lsm9ds1GyroScale_t scale )
////
void calibrate()
{
  float _Xbias = 0.0f; // temp storage location
  float _Ybias = 0.0f; // temp storage location
  float _Zbias = 0.0f; // temp storage location
  int numberOfSamples = 33;
  // read 32 samples from each device and average those samples
  // do accelerometer bias
  for ( int i = 0; i < numberOfSamples; i++ )
  {
    fReadAccelerometers();
    _Xbias = _Xbias + (aX  * _accel_mg_lsb); // scale factor  * _accel_mg_lsb
    _Ybias = _Ybias + (aY * _accel_mg_lsb);
    _Zbias = _Zbias + (aZ * _accel_mg_lsb);
    vTaskDelay( 40 );
  } //for ( int i = 0; i < numberOfSamples; i++ )
  aXbias = _Xbias / (float)numberOfSamples;
  aYbias = _Ybias / (float)numberOfSamples;
  aZbias = _Zbias / (float)numberOfSamples;
  _Xbias = 0.0f;
  _Ybias = 0.0f;
  _Zbias = 0.0f;
  //   do gyro bias
  for ( int i = 0; i < numberOfSamples; i++ )
  {
    fReadGyros();
    _Xbias = _Xbias + (gX * _gyro_dps_digit); // scale factor  * _gyro_dps_digit
    _Ybias = _Ybias + (gY * _gyro_dps_digit);
    _Zbias = _Zbias + (gZ * _gyro_dps_digit);
    vTaskDelay( 40 );
  } // for ( int i = 0; i < numberOfSamples; i++ )
  gXbias = _Xbias / (float)numberOfSamples;
  gYbias = _Ybias / (float)numberOfSamples;
  gZbias = _Zbias / (float)numberOfSamples;
} // void calibrate()
////
float get_aXbias( )
{
  return -(aXbias);
}
////
float get_aYbias()
{
  return -(aYbias);
}
////
float get_aZbias()
{
  return -(aZbias);
}
////
float get_gXbias()
{
  return -(gXbias);
}
////
float get_gYbias()
{
  return -(gYbias);
}
////
float get_gZbias()
{
  return -(gZbias);
}
////
void fReadAccelerometers()
{
  int16_t temp = 0;
  // read status register
  fReadSPIdata16bits( hAG, STATUS_REG );
  // is new accel data available
  if ( GetHighBits() & AccelerometerDataReady )
  {
    fReadSPIdata16bits( hAG, LSM9DS1_REGISTER_OUT_X_L_XL ); // read x accel
    temp = GetHighBits(); temp <<= 8; temp |= GetLowBits();
    aX = (float)temp;
    aX = (aX * _accel_mg_lsb) - aXbias;
    temp = 0;
    //
    fReadSPIdata16bits( hAG, LSM9DS1_REGISTER_OUT_Y_L_XL ); // read Y accel
    temp = GetHighBits(); temp <<= 8; temp |= GetLowBits();
    aY = (float)temp;
    aY = (aY * _accel_mg_lsb) - aYbias;
    temp = 0;
    //
    fReadSPIdata16bits( hAG, LSM9DS1_REGISTER_OUT_Z_L_XL ); // read z accel
    temp = GetHighBits(); temp <<= 8; temp |= GetLowBits();
    aZ = (float)temp;
    aZ = (aZ * _accel_mg_lsb) - aZbias;
  }
}
////
float get_aX()
{
  return aX;
}
float get_aY()
{
  return aY;
}
float get_aZ()
{
  return aZ;
}
////
void fReadGyros()
{
  int16_t temp;

  fReadSPIdata16bits( hAG, STATUS_REG );
  // is new gyro data available
  if ( GetHighBits() & GyroDataReady )
  {
    gX = 0.0f; gY = 0.0f; gZ = 0.0f;
    fReadSPIdata16bits( hAG, LSM9DS1_REGISTER_OUT_X_L_G ); // read x gyro
    temp = GetHighBits(); temp <<= 8; temp |= GetLowBits();
    temp /= 1000.0f;
    gX = (float)temp * _gyro_dps_digit; // scale
    gX -= gXbias;
    temp = 0;
    fReadSPIdata16bits( hAG, LSM9DS1_REGISTER_OUT_Y_L_G ); // read Y gyro
    temp = GetHighBits(); temp <<= 8; temp |= GetLowBits();
    temp /= 1000.0f;
    gY = (float)temp * _gyro_dps_digit; // scale
    gY -= gYbias;
    temp = 0;
    fReadSPIdata16bits( hAG, LSM9DS1_REGISTER_OUT_Z_L_G ); // read Z gyro
    temp = GetHighBits(); temp <<= 8; temp |= GetLowBits();
    temp /= 1000.0f;
    gZ = (float)temp * _gyro_dps_digit; // scale
    gZ -= gZbias;
  }
}
////
float get_gX()
{
  return gX;
}
////
float get_gY()
{
  return gY;
}
////
float get_gZ()
{
  return gZ;
}
////
void fReadMagnetometer()
{
  int16_t temp;
  fReadSPIdata16bits( hM, STATUS_REG );
  if ( GetHighBits() & LSM9DS1_REGISTER_OUT_X_L_M )
  {
    fReadSPIdata16bits( hM, LSM9DS1_REGISTER_OUT_X_L_M ); // read X magnetometer
    temp = GetHighBits(); temp <<= 8; temp |= GetLowBits();
    mX = (float)temp * _mag_mgauss_lsb; // scale
    ////
    fReadSPIdata16bits( hM, LSM9DS1_REGISTER_OUT_Y_L_M ); // read Y magnetometer
    temp = GetHighBits(); temp <<= 8; temp |= GetLowBits();
    mY = (float)temp * _mag_mgauss_lsb; // scale
    //        ////
    fReadSPIdata16bits( hM, LSM9DS1_REGISTER_OUT_Z_L_M ); // read Z magnetometer
    temp = GetHighBits(); temp <<= 8; temp |= GetLowBits();
    mZ = (float)temp * _mag_mgauss_lsb; // scale
  }
}
////
float get_mX()
{
  return mX;
}
////
float get_mY()
{
  return mY;
}
////
float get_mZ()
{
  return mZ;
}
////

here is the code that the above uses to do the spi thing

#include "ESP32_SPI_API.h"
/////////////////////////////
///////////////////////////
uint8_t txData[2] = { };
uint8_t rxData[25] = { };
uint8_t low;
int8_t high;
//////
//////////////////////////////////
uint8_t GetLowBits()
{
  return low;
}
int8_t GetHighBits()
{
  return high;
}
////////////////////////////////////////
int fInitializeSPI_Channel( int spiCLK, int spiMOSI, int spiMISO, spi_host_device_t SPI_Host, bool EnableDMA)
{
  esp_err_t intError;
  spi_bus_config_t bus_config = { };
  bus_config.sclk_io_num = spiCLK; // CLK
  bus_config.mosi_io_num = spiMOSI; // MOSI
  bus_config.miso_io_num = spiMISO; // MISO
  bus_config.quadwp_io_num = -1; // Not used
  bus_config.quadhd_io_num = -1; // Not used
  intError = spi_bus_initialize( HSPI_HOST, &bus_config, EnableDMA) ;
  return intError;
}
//////
int fInitializeSPI_Devices( spi_device_handle_t &h, int csPin)
{
  esp_err_t intError;
  spi_device_interface_config_t dev_config = { };  // initializes all field to 0
  dev_config.address_bits     = 0;
  dev_config.command_bits     = 0;
  dev_config.dummy_bits       = 0;
  dev_config.mode             = 3 ;
  dev_config.duty_cycle_pos   = 0;
  dev_config.cs_ena_posttrans = 0;
  dev_config.cs_ena_pretrans  = 0;
  dev_config.clock_speed_hz   = 5000000;
  dev_config.spics_io_num     = csPin;
  dev_config.flags            = 0;
  dev_config.queue_size       = 1;
  dev_config.pre_cb           = NULL;
  dev_config.post_cb          = NULL;
  spi_bus_add_device(HSPI_HOST, &dev_config, &h);
  // return intError;
  // return h;
} // void fInitializeSPI_Devices()
///////////////////////////////////////////////////////////////
int fReadSPIdata16bits( spi_device_handle_t &h, int _address )
{
  uint8_t address = _address;
    esp_err_t intError = 0;
    low=0; high=0;
    spi_transaction_t trans_desc;
    trans_desc = { };
    trans_desc.addr =  0;
    trans_desc.cmd = 0;
    trans_desc.flags = 0;
    trans_desc.length = (8 * 3); // total data bits
    trans_desc.tx_buffer = txData;
    trans_desc.rxlength = 8 * 2 ; // Number of bits NOT number of bytes
    trans_desc.rx_buffer = rxData;
    txData[0] = address | 0x80;
    intError = spi_device_transmit( h, &trans_desc);
    low = rxData[0]; high = rxData[1];
  //  if ( intError != 0 )
  //  {
  //    Serial.print( " WHO I am LSM9DS1. Transmitting error = ");
  //    Serial.println ( esp_err_to_name(intError) );
  //  }
  return intError;
} // void fSendSPI( uint8_t count, uint8_t address, uint8_t DataToSend)
////
int fWriteSPIdata8bits( spi_device_handle_t &h, int _address, int _sendData )
{
  uint8_t address =  _address;
  uint8_t sendData = _sendData;
  esp_err_t intError;
  spi_transaction_t trans_desc;
  trans_desc = { };
  trans_desc.addr =  0;
  trans_desc.cmd = 0;
  trans_desc.flags = 0;
  trans_desc.length = (8 * 2); // total data bits
  trans_desc.tx_buffer = txData;
  trans_desc.rxlength = 0 ; // Number of bits NOT number of bytes
  trans_desc.rx_buffer = NULL;
  txData[0] = address  & 0x7F;
  txData[1] = sendData;
  intError = spi_device_transmit( h, &trans_desc);
  return intError;
//  //  if ( intError != 0 )
//  //  {
//  //    Serial.print( " LSM9DS1_REGISTER_CTRL_REG6_XL. Transmitting error = ");
//  //    Serial.println ( esp_err_to_name(intError) );
//  //  }
} // void fWriteSPIdata8bits(  spi_device_handle_t &h, uint8_t address, uint8_t sendData )
//

here is the spi api h file

#include <driver/spi_master.h>
#include "sdkconfig.h"
#include "esp_system.h" //This inclusion configures the peripherals in the ESP system.
///////////////////////////
 uint8_t GetLowBits();
 int8_t GetHighBits();
 int fReadSPIdata16bits( spi_device_handle_t &h, int address );
 int fWriteSPIdata8bits( spi_device_handle_t &h, int address, int sendData );
 int fInitializeSPI_Devices( spi_device_handle_t &h, int csPin);
// spi_device_handle_t fInitializeSPI_Devices( int csPin);
int fInitializeSPI_Channel( int spiCLK, int spiMOSI, int spiMISO, spi_host_device_t SPI_Host, bool EnableDMA);
Note the use of : [code spi_bus_config_t bus_config = { };

All array, sturcture, and the like created, using the ESP32 core from ESPRESSIF should be initialized to a default value. The ESP32 core does not clear the memory space to be ued for the memory assignment Creation of the structure, where the entire contents of the structure will be sent to the SPI API, may, without clearing the structure memories addresses, result in phantom or ghost data to show up as configuration parameters of the SPI configuration.

Ok, wow - thats a lot for me to unterstand!
Thanks very much, I will copy it together and try to get the grasp of it - but I think this one is too big for me to chew :o

Hope this helps others as well, havent found much info on this topic!

Will do may best - thanks again!

Try and use

 dev_config.mode             = 3 ;

Which is full duplex operations, if your device does not do full duplex, that's OK.

The SPI API object can be used by 3 SPI devices. if you use more then one SPI device on the same SPi bus make sure the highest settings works for the slowest device.