The SDK has already formatted the SD card as FAT32, and the card is mounted successfully, but atomvm:posix_opendir("/sdcard") still returns an error. {error,enoent} Why is this happening?
Thank you for your for your interest in AtomVM, and reaching out to us with this problem.
Can you provide a little more information? Is this using the spi diver or sdmmc, for instance? I discovered a bug (or rather an unimplemented feature) in the sdmmc driver where the sdmmc peripheral is not configurable and the driver defaults to sdmmc 0, several dev boards connect the sd card to sdmmc 1.
We need to add a configuration parameter to allow selecting the sdmmc device to use. We also need to investigate why the driver does not send an error when initializing a device that does not exist. I don’t think I opened an issue for this yet, I only recently discovered this myself and have been extra busy for the last month.
Thanks for your reply,I tried this tutorial: Mounting SPI SD card (Programmers Guide — AtomVM 0.6.6 documentation) . use esp32s3 atomvm 0.6.6 esp-idf 5.4 otp27
I tried the following ways of writing
1.
SPIConfig = [{bus_config, [{miso, 39}, {mosi, 14}, {sclk, 40}, {peripheral, "spi3"}]}],
SPI = spi:open(SPIConfig),
{ok, F} = esp:mount("sdspi", "/sdcard", fat, [{spi_host, SPI}, {cs, 12}]),
{ok, Fd} = atomvm:posix_opendir("/sdcard"),
R = atomvm:posix_readdir(Fd),
io:format("R ~p~n", [R]).
R 返回 {error,ebadf}
2.
SPIConfig = [{bus_config, [{miso, 39}, {mosi, 14}, {sclk, 40}, {peripheral, "spi3"}]}],
SPI = spi:open(SPIConfig),
{ok, F} =
case esp:mount("sdspi", "/sdcard", fat, [{spi_host, SPI}, {cs, 12}]) of
{ok, MountRef} ->
{ok, Fd} = atomvm:posix_opendir("/sdcard"),
R = atomvm:posix_readdir(Fd),
io:format("R ~p~n", [R]),
io:format("SD card mounted successfully~n"),
{ok, MountRef};
{error, Reason} ->
%
io:format("Failed to mount SD card: ~p~n", [Reason]),
{error, Reason}
end.
R return R {ok,{dirent,0,<<"HELLO">>}}
3.
SPIConfig = [{bus_config, [{miso, 39}, {mosi, 14}, {sclk, 40}, {peripheral, "spi3"}]}],
SPI = spi:open(SPIConfig),
{ok, F} =
case esp:mount("sdspi", "/sdcard", fat, [{spi_host, SPI}, {cs, 12}]) of
{ok, MountRef} ->
io:format("SD card mounted successfully~n"),
{ok, MountRef};
{error, Reason} ->
%
io:format("Failed to mount SD card: ~p~n", [Reason]),
{error, Reason}
end,
R = atomvm:posix_opendir("/sdcard"),
%R = atomvm:posix_readdir(Fd),
io:format("R ~p~n", [R]).
atomvm:posix_opendir("/sdcard"), return {error, enoent}
4.
SPIConfig = [{bus_config, [{miso, 39}, {mosi, 14}, {sclk, 40}, {peripheral, "spi3"}]}],
SPI = spi:open(SPIConfig),
case esp:mount("sdspi", "/sdcard", fat, [{spi_host, SPI}, {cs, 12}]) of
{ok, MountRef} ->
{ok, Fd} = atomvm:posix_opendir("/sdcard"),
R1 = atomvm:posix_readdir(Fd),
io:format("R ~p~n", [R1]),
io:format("SD card mounted successfully~n"),
{ok, MountRef};
{error, Reason} ->
%
io:format("Failed to mount SD card: ~p~n", [Reason]),
{error, Reason}
end,
{ok, Fd1} = atomvm:posix_opendir("/sdcard/hello"),
R = atomvm:posix_readdir(Fd1),
io:format("R ~p~n", [R]).
R1 return {error,ebadf} and atomvm:posix_opendir("/sdcard/hello") return{error, enoent}
I don’t know the reason, it’s weird.
I tried to use the example given by esp-idf and wrote a nif
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "esp_vfs_fat.h"
#include "sdmmc_cmd.h"
#include <stdio.h>
#include "esp32_sys.h"
#include <erl_nif_priv.h>
#include "trace.h"
#include <atom.h>
#include <memory.h>
#include <nifs.h>
#include <term.h>
#include <defaultatoms.h>
#define PIN_NUM_MISO 39
#define PIN_NUM_MOSI 14
#define PIN_NUM_CLK 40
#define PIN_NUM_CS 12
#define MOUNT_POINT "/sdcard"
static const char *TAG = "example";
void mount(){
esp_err_t ret;
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.allocation_unit_size = 16*1024
};
sdmmc_card_t *card;
ESP_LOGI(TAG, "Initializing SD card");
const char mount_point[] = MOUNT_POINT;
spi_bus_config_t bus_cfg = {
.mosi_io_num = PIN_NUM_MOSI,
.miso_io_num = PIN_NUM_MISO,
.sclk_io_num = PIN_NUM_CLK,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 4000,
};
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
ret = spi_bus_initialize(host.slot, &bus_cfg, SDSPI_DEFAULT_DMA);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to initialize bus.");
return;
}
sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT();
slot_config.gpio_cs = PIN_NUM_CS;
slot_config.host_id = host.slot;
ESP_LOGI(TAG, "Mounting filesystem");
ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &card);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount filesystem. "
"If you want the card to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
} else {
ESP_LOGE(TAG, "Failed to initialize the card (%s). "
"Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
}
return;
}
ESP_LOGI(TAG, "Filesystem mounted");
}
static term sd_mount_nif(Context *ctx, int argc, term argv[])
{
mount();
return OK_ATOM;
}
static const struct Nif mount_nif =
{
.base.type = NIFFunctionType,
.nif_ptr = sd_mount_nif
};
const struct Nif *sd_get_nif(const char *nifname)
{
if (strcmp("sd:mount/0", nifname) == 0) {
TRACE("Resolved platform nif %s ...\n", nifname);
return &mount_nif;
}
return NULL;
}
REGISTER_NIF_COLLECTION(sd_mount_nif, NULL, NULL, sd_get_nif)
init_sd() ->
ok = sd:mount(),
{ok, Fd1} = atomvm:posix_opendir("/sdcard/hello"),
R = atomvm:posix_readdir(Fd1),
{ok, Fd2} = atomvm:posix_opendir("/sdcard"),
R1 = atomvm:posix_readdir(Fd2),
io:format("R ~p~n", [R]),
io:format("R ~p~n", [R1]).
R {ok,{dirent,0,<<"HELLO.TXT">>}}
R {ok,{dirent,0,<<"HELLO">>}}
Looks like the posix module is ok?
I think I’ve found the correct way to use it — it seems that as long as the mount statement doesn’t end, it works normally.
It is feasible
start() ->
Pid = self(),
spawn(fun() -> init_sd(Pid) end),
receive
{_, mount} ->
ok
end,
spawn(fun() ->
{ok, Fd} = atomvm:posix_open("/sdcard/audio/0001.wav", [o_rdonly]),
read_file(Fd)
end),
wait_forever().
wait_forever() ->
receive
stop ->
ok
end.
init_sd(Pid) ->
SPIConfig = [{bus_config, [{miso, 39}, {mosi, 14}, {sclk, 40}, {peripheral, "spi3"}]}],
SPI = spi:open(SPIConfig),
case esp:mount("sdspi", "/sdcard", fat, [{spi_host, SPI}, {cs, 12}]) of
{ok, MountedRef} ->
io:format("SD card mounted successfully~n"),
Pid ! {self(), mount},
receive
stop ->
ok
end,
{ok, MountedRef};
{error, Reason} ->
io:format("Failed to mount SD card: ~p~n", [Reason]),
{error, Reason}
end.
read_file(Fd) ->
read_file(Fd, 0).
read_file(Fd, S) ->
case atomvm:posix_read(Fd, 512) of
{ok, Bin} ->
read_file(Fd, S + size(Bin));
eof ->
io:format("read size ~p~n", [S]),
io:format("eof~n");
{error, _R} ->
io:format("error~n")
end.
is that so?
Believe it’s supposed to just work with
this makes it sound like the mount ref is garbage collected/deallocated or something, I know our esp32 tests currently doesn’t test the erlang mount functions, I’ll look into fixing that..
Meanwhile can you check against main(0.7)?, you can flash latest with chrome here AtomVM Flasher
Your nif is using the sdmmc interface, but your Erlang code uses the sdspi interface, this may be part of your problem. If your nif works this means your board uses sdmmc slot 0, which the storage driver does support.
I would suggest trying:
esp:mount("sdmmc", "/sdcard", fat, [])
I do agree with @outlog it sounds like the ref is being garbage collected after the ref is no longer in use. This would be a good candidate for putting behind a gen_server, you can keep the ref in state so it is not garbage collected until you are done with it and the card is unmounted.
Sorry, my previous suggestion would not work in your situation. It looks like your board does not use the default sdmmc pins, which are not configurable yet. I just discovered one of my devices also uses non standard pins. I just started working on changes to make the sdmmc pins configurable too. Hopefully I will get a PR submitted soon. The performance should be better with sdmmc as it uses more pins for a higher bit width.