SD.h vs SD_MMC.h

Hi,

I want to support using an sd but for 2 boards, the examples in one use SD.h, like this:

#include <SD.h>

const int chipSelect = 10;
File root;

void setup() {
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
  // wait for Serial Monitor to connect. Needed for native USB port boards only:
  while (!Serial);

  Serial.print("Initializing SD card...");

  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed. Things to check:");
    Serial.println("1. is a card inserted?");
    Serial.println("2. is your wiring correct?");
    Serial.println("3. did you change the chipSelect pin to match your shield or module?");
    Serial.println("Note: press reset button on the board and reopen this Serial Monitor after fixing your issue!");
    while (true);
  }

  Serial.println("initialization done.");

  root = SD.open("/");

  printDirectory(root, 0);

  Serial.println("done!");
}

The other board uses SD_MMC.h (esp32), which actually is pretty much the same: arduino-esp32/SDMMC_Test.ino at master · espressif/arduino-esp32 · GitHub

The only difference is that the FAT stuff is in FS.h

I wonder how would be the cleanest way to support both boards in the same code?

Thanks

Bueller?  

This thread’s kind of off-topic, but…


Option 1:

The easy option.

Write a ‘thin’ wrapper library that has all the features you need and uses the other libraries as a back-end, then select the implementation you want based on which board is being used.

E.g.

// EriedSD.h
#pragma once

#if defined(BOARD_A)
#include "EriedSD_BoardA.h"
#elif defined(BOARD_B)
#include "EriedSD_BoardB.h"
#else
#error "Unsupported board"
#endif
// EriedSD_BoardA.h
#pragma once

#include <SD.h>

#include <stdint.h>

namespace Filesystem
{
	inline bool begin()
	{
		return SD.begin(chipSelect);
	}

	enum class CardType : uint8_t
	{
		Unknown,
		SD,
		SDHC,
		MMC,
	};

	inline CardType getCardType()
	{
		return CardType::Unknown;
	}

	// ...

	class SDFile
	{
	private:
		File file;

	public:
		SDFile(const char * path)
		{
			this->file = SD.open(path);
		}

		~SDFile()
		{
			this->file.close();
		}

		// ...
	};

	// ...
}
// EriedSD_BoardB.h
#pragma once

#include <SD_MMC.h>

#include <stdint.h>

namespace Filesystem
{
	inline bool begin()
	{
		return SD_MMC.begin();
	}

	enum class CardType : uint8_t
	{
		Unknown,
		SD = CARD_SD,
		SDHC = CARD_SDHC,
		MMC = CARD_MMC,
	};

	inline CardType getCardType()
	{
		return SD_MMC.cardType();
	}

	// ...

	class SDFile
	{
	private:
		File file;

	public:
		SDFile(const char * path)
		{
			this->file = SD_MMC.open(path);
		}

		~SDFile()
		{
			this->file.close();
		}

		// ...
	};

	// ...
}

Ideally you’d hide the internal library in a .cpp file so it never leaks out to the code using the library (that way you could reuse names like File without worrying about clashes), but there are some things that are easier to do if you include it in the header (e.g. the getCardType trick above).

(It’s times like this I wish C++'s include system were as sophisticated as Haskell’s module system. Or at the very least that people would always put their libraries in namespaces instead of littering the global namespace like a C programmer.)


Option 2:

Instead of a wrapper, butcher both libraries to create a new library that uses the internal code of both libraries and behaves appropriately for both boards.

Probably more efficient and less like trying to fit a square peg into a round hole, but would require far more effort and testing.

2 Likes

Thanks @Pharap, maybe the option 2 is the best, that way I dont need to change the original code at all. The project I wanted to fork to add SD_MMC support already has a light wrapper.