SD Card Example?
# help
o
Hi, I'm trying to store some data on an SD Card. I have found the flash library, which provides
Mount
, which allows me to mount (and format, if necessary) an SD Card. But how do I read and write with it? Are there any examples that I could use as a starting off point?
f
The
host
package has file and directory operations.
I haven't tested it myself, but @mikkel.damsgaard implemented it, and the
host
package's functions should map to sdcard accesses. We still need to update the package description...
m
We discussed renaming the host package filesystem. Maybe it should even be considered to move it into the standard library, seeing that we keep getting this question?
o
So I just mount the SD card, and then treat it just like a file system on my Linux machine?
m
yes
i
I used it like this:
Copy code
sdcard = SDCard 
    --miso=gpio.Pin 19
    --mosi=gpio.Pin 23
    --clk=gpio.Pin 18
    --cs=gpio.Pin 5

sdcard.openw filename
sdcard.write "something"
sdcard.close
using this abstraction file:
Copy code
import flash
import gpio 
import spi
import host.file

class SDCard:

  csvfile/file.Stream := ?

  constructor --miso/gpio.Pin --mosi/gpio.Pin --clk/gpio.Pin --cs/gpio.Pin --mount_point/string="/sd":
    bus := spi.Bus
      --miso=miso
      --mosi=mosi
      --clock=clk

    sdcard := flash.Mount.sdcard
      --mount_point=mount_point
      --spi_bus=bus
      --cs=cs

    csvfile = file.Stream.for_write "/sd/test"
    csvfile.close
    print "SDCard initialised"

  write line/string filename/string:
    print "write \"$line\" into file: $filename"
    csvfile = file.Stream.for_write filename
    csvfile.write line
    csvfile.close

  openw filename:
    csvfile = file.Stream.for_write filename

  openr filename:
    csvfile = file.Stream.for_read filename

  write line/string:
    csvfile.write line

  read_file filename -> string:
    return (file.read_content filename).to_string

  close:
    csvfile.close
o
Thanks, will try that out!
OK, I've now tried this, but I'm getting an error:
Copy code
E (175187) sdmmc_sd: sdmmc_init_sd_if_cond: send_if_cond (1) returned 0x108
E (175187) vfs_fat_sdmmc: sdmmc_card_init failed (0x108).

******************************************************************************
Decoding by `jag`, device has version <2.0.0-alpha.55>
******************************************************************************
EXCEPTION error. 
UNKNOWN ERROR 0x108(264)
  0: init_sdcard_              <sdk>/flash.toit:141:3
  1: Mount.sdcard              <sdk>/flash.toit:44:14
  2: SDCard                    sdtest.toit:18:15
  3: main                      sdtest.toit:55:13
******************************************************************************
Pins: MISO-32, MOSI-23, CS-25, CLOCK-33
Any idea what is going wrong? Am I using a pin I shouldn't be using?
i
unknown error is not very helpfull tbh 🙂 what esp are you using and how did you connected the sd reader?
o
I'm using a TTGO T-BEAM, and have just wired it to the respective pins.
i
is the sdcard formatted and working?
o
It should be formatted on initialisation.
Ah, if I switch the MOSI and MISO pins, I'm getting a different error:
Copy code
E (22087) sdmmc_sd: sdmmc_check_scr: send_scr returned 0x107
E (22087) vfs_fat_sdmmc: sdmmc_card_init failed (0x107).

******************************************************************************
Decoding by `jag`, device has version <2.0.0-alpha.55>
******************************************************************************
EXCEPTION error. 
UNKNOWN ERROR 0x107(263)
  0: init_sdcard_              <sdk>/flash.toit:141:3
  1: Mount.sdcard              <sdk>/flash.toit:44:14
  2: SDCard                    sdtest.toit:18:15
  3: main                      sdtest.toit:56:13
******************************************************************************
Right, now I'm using MISO/4, MOSI/13, CLOCK/14, and CS/2 (which I found someone else on the web use), and the first error is gone; now I only get
Copy code
E (58017) vfs_fat_sdmmc: sdmmc_card_init failed (0x107).

******************************************************************************
Decoding by `jag`, device has version <2.0.0-alpha.55>
******************************************************************************
EXCEPTION error. 
UNKNOWN ERROR 0x107(263)
  0: init_sdcard_              <sdk>/flash.toit:141:3
(I'm using an 8GB SD card, which works fine on my Linux machine)
And it's formatted as FAT32
i
🤔
m
#define ESP_ERR_TIMEOUT             0x107   /*!< Operation timed out */
I think that is what you see. That would suggest that the SPI protocol receives a timeout. I would double check the pins and their connections.
o
I’m pretty sure they are fine, as I switched pins several times. I’ve got another reader module, so will try with that one.
i
For SPI you have this two options with the pins. In your case the MOSI should be 13 instead of 4. I think only the CS pin can be whatever you wish but would also make it 15 for testing.
it doesn't really matter which board you are using - as long as it is an ESP32 the pins should be the same. Of course you can also use software SPI with defining any pin but I am not sure how good this will work out
VSPI is the default
you could also test it using arduino code (they have example code for SDCards) just to make sure SPI works.
o
My problem is that on the board I’m using (T-BEAM), not all those pins are exposed, so I have no choice but use other ones 🙁
Is there any example of doing software SPI in toit?
f
What do you mean with "software SPI" ? Edit. Ah. I see InformaticOre's response now.
The ESP32 can use pretty much any pin for the SPI. It might not be able to run at full speed when it has to reroute the pins, but that's almost never a problem.
Here is a simplified version of the SPI allocation:
Copy code
// Check if there is a preferred device.
  if ((mosi == -1 || mosi == 13) &&
      (miso == -1 || miso == 12) &&
      (clock == -1 || clock == 14)) {
    host_device = HSPI_HOST;
  }
  if ((mosi == -1 || mosi == 23) &&
      (miso == -1 || miso == 19) &&
      (clock == -1 || clock == 18)) {
    host_device = VSPI_HOST;
  }
So if you use these pins, you would get the optimal assignment, but it's really not necessary.
i
thats what I meant 🙂 afaik using the dedicated pins it is doing hardware SPI and using any other Pins will do a software SPI. Some devices might have issues with that but I also don't know any.
f
It's not a software SPI.
It's just running the pin through a matrix.
There should be no CPU overhead doing that.
o
So can I use arbitrary pins for mosi/miso/clock? I'm confused now. I can't use either the HSPI or VSPI pins.
f
From the docs.
Copy code
Most of ESP32’s peripheral signals have direct connection to their dedicated IO_MUX pins. However, the signals can also be routed to any other available pins using the less direct GPIO matrix. If at least one signal is routed through the GPIO matrix, then all signals will be routed through it.

The GPIO matrix introduces flexibility of routing but also brings the following disadvantages:

- Increases the input delay of the MISO signal, which makes MISO setup time violations more likely. If SPI needs to operate at high speeds, use dedicated IO_MUX pins.

- Allows signals with clock frequencies only up to 40 MHz, as opposed to 80 MHz if IO_MUX pins are used.
o
Ah, would that be why I'm getting a time-out on the SD card?
f
I doubt it.
i
is there a default clock speed in toit? can you set it to 40k?
f
I think you can set the frequency when getting a device.
i
yea, from the toit docs:
Copy code
import gpio
import spi

main:
  bus := spi.Bus
    --miso=gpio.Pin 12
    --mosi=gpio.Pin 13
    --clock=gpio.Pin 14

  device := bus.device
    --cs=gpio.Pin 15
    --frequency=10_000_000
f
Try with a really low frequency, like 1MHz.
That should eliminate hardware issues.
Also: do you use any adapter for the SD card?
(I'm using it on 5V, as I have read that 3.3V can lead to problems)
f
yes. that should work.
I would start by trying to run it a lower speed.
I think there is no default speed. You have to specify the frequency.
Which pins are you currently using?
o
I’ve tried a variety. Will have another go this evening with lower speed. It’s only going to be a data logger, so speed is not important anyway.
How do I pass a frequency to the
Mount
? Do I create a
Device
with the frequency (which I then ignore)? SD Card is the only option which hasn't got a frequency parameter.
(If I read the docs right, it's the device config)
f
You are right. The sdcard part doesn't have any frequency. @mikkel.damsgaard is there maybe a way to test the sdcard with the other flash functions? Like opening it as nand and reading from it? Wouldn't need to understand the filesystem for that.
o
Now getting error 0x102. Not long until I've got them all through 🙂
This was with defining a
bus.device
with a frequency of 1MHz
With 2MHz it's 0x109.
f
That's an "invalid argument" error. (102)
o
Is there a list of error codes somewhere that I can check?
(I did have a look in the toit-master directory)
o
So 109 (invalid CRC) might be speed related...
I'm trying a different card reader module; I have taken off all other peripherals, and now I consistently get error
ESP_ERR_INVALID_CRC
If I take the card out of the reader, then I'm getting a timeout (0x107)
The same happens whether I try
Mount.sdcard
or
Mount.sdcard_unformatted
😦
Using some other pins (mosi=23, miso=32, clock=33, cs=25) I'm now getting 0x108 (invalid response). I give up.
m
What i think is happening here is that the sd-card has been formatted with something that is not recognised completely by the ESP-IDF, but almost. So it will try to mount it (instead of formatting). We only format if it is blank(ish). At the moment there is no way to force a format. The sdcard readers are normally so dumb that they should not pose an issue. Trying a completely blank sdcard might be worth a try.
o
It’s one I newly bought, but it was formatted already (in FAT32)…
No, I reformatted it, and even created a smaller (2GB) partition instead of the single 8GB one. Still no success.
i
strange for me it worked pretty wel. it was even woorrking better then any other SDCard implementation of arduino or micrropython where I had also a loott of troubles. The ony difference is, I am using the SDCard directy without any level shifting or adapter. just plain SPI. You could try and skip the module and solder some cables to a SD Card adapter, the small things for micro SD Cards which look like normal big SDCards. Or do you have a plain ESP32 DEV Board? where you can use the dedicated SPI pins? just for the sake of testing because my board is using the exact pins. If I remember correctly I also had issues with not using the dedicated SPI pins for SPI but it is already some time ago.
o
Are you saying I could use an adapter instead of the card module?!
f
I think @Informatic0re means that you can interface the sdcard directly to the pins: https://alexlubbock.com/micro-sd-adapter-esp8266-esp32
Most of the time I have seen a pull-up on MISO, though.
Fundamentally, your board is doing exactly the same.
It also takes the 5V and regulates it down to 3.3V, but that's clearly not necessary if you already have a 3.3V supply.
not sure if the board also does level shifting.
Looks like it has a LVC125 buffer, though.
That separates the sdcard from the microcontroller.
I wouldn't be surprised if that chip acts as a level shifter.
o
Yes, looking at the SD pins, they can be used in SPI mode. I could always try that, though the adapter board doesn’t really do anything else.
The one thing I haven’t tried is using 3.3V instead of 5V; so I will try that this evening.
And then maybe solder a spare adapter straight to the ESP 😶
f
good luck 🙂
o
Tack!
Some positive news: I have taken an SD card adapter, and soldered wires straight onto it, instead of using the adapter with a break-out board. And 3.3V instead of the 5V the board used (which also doesn't work woith 3.3V either). And, it works. The first error I got was a "File not found", as I had mistyped a pathname. But now it works. And I can even read the file when connecting the SD card to my Linux machine. Happy days.
8 Views