Hi David ,
Do you think it’s possible to read the directory structure from an sdcard ?
Only the root would be enough.
Kind regards,
Ronny
Hi David ,
Do you think it’s possible to read the directory structure from an sdcard ?
Only the root would be enough.
Kind regards,
Ronny
The Arduino SD library provides two functions to help with this:
File entry = dir.openNextFile();
opens the next file in the current directory, or returns 0 if there are no more files, and:
entry.name()
returns the name of the current file. I could provide uLisp equivalents of these functions.
Alternatively, a better solution might be to provide something more like Common Lisp:
(directory)
which could return a list of the filenames of the files in the current directory.
Hi David ,
I searched on this and found an example on following link on the arduino site :
I used the ‘list files’ program , did the chip select en spi1 change and it works fine on my challenger board !
Initializing SD card…initialization done.
sunrise-sunset.fs 7567
Greeting.txt 9
ULISP.IMG 320
test.fs 26
done!
Maybe this could be implemented in ulisp ?
What do you think.
Kind regards,
Ronny
Funnily enough I was just putting something together as a uLisp extension. Here it is:
/*
SD Card Extension
*/
object *fn_directory (object *args, object *env) {
(void) env;
SD.begin(SDCARD_SS_PIN);
File root = SD.open("/");
if (!root) error2(PSTR("problem reading from SD card"));
object *result = cons(NULL, NULL);
object *ptr = result;
while (true) {
File entry = root.openNextFile();
if (!entry) break;
object *filename = lispstring(entry.name());
cdr(ptr) = cons(filename, NULL);
ptr = cdr(ptr);
};
root.close();
return cdr(result);
}
// Symbol names
const char stringdirectory[] PROGMEM = "directory";
// Documentation strings
const char docdirectory[] PROGMEM = "(directory)\n"
"Reads the directory at the top level of an SD card and returns\n"
"a list of the filenames.";
// Symbol lookup table
const tbl_entry_t lookup_table2[] PROGMEM = {
{ stringdirectory, fn_directory, 0200, docdirectory },
};
// Table cross-reference functions
tbl_entry_t *tables[] = {lookup_table, lookup_table2};
const unsigned int tablesizes[] = { arraysize(lookup_table), arraysize(lookup_table2) };
const tbl_entry_t *table (int n) {
return tables[n];
}
unsigned int tablesize (int n) {
return tablesizes[n];
}
To use this:
An example of listing the files on an SD card:
> (directory)
("CARDS2.GIF" "SPOTLI~1" "YELLOW.GIF" "CARDS8.GIF" "_YELLO~1.GIF")
Hi David ,
Thanks , but it seems to throw an error :
Compilation error: invalid conversion from ‘const char*’ to ‘char*’ [-fpermissive]
for this line in directory.ino
object *filename = lispstring(entry.name());
What platform are you using it on? I tested it on an Arduino MRRZero and it was fine.
Oh yes, I remember now - it’s the RP2040 Challenger. I’ll test it on RP2040 and see if I get the same problem.
David ,
I even tried the original 4.5 version for rpi pico , but always the same error …
I don’t really have a clue what causes this.
Regards,
Ronny
I think you just have to change:
object *filename = lispstring(entry.name());
to:
object *filename = lispstring((char*)entry.name());
Let me know if that works!
Oh, in your case you have to change:
SD.begin(SDCARD_SS_PIN);
to:
SD.begin(SDCARD_SS_PIN,SPI1);
Hi David ,
That change object filename = lispstring((char)entry.name()); did it !
I already had changed the spi1 , so that was not a problem.
Thanks again , you’re marvelous as always :-)
uLisp 4.5
22820> (directory)
(“sunrise-sunset.fs” “Greeting.txt” “ULISP.IMG” “test.fs”)
22820>
Hi David ,
One more question :
Is it possible to print the files each on a new line ?
Kind regards,
Ronny
I thought it would be best for (directory)
to return a list of filenames that you can do what you want with. So you could do:
(progn (mapc #'print (directory)) nothing)
David, thank you very much!
Dear friends, i decided adding search pattern parameter near to standard. Maybe someone is interested. I suggest to use it.
/*
(directory [pattern])
Returns a list of the filenames of the files on the SD card.
Pattern is string which contains '*' symbols.
*/
// (directory "/home/*/") - search directories
// (directory "/home/*") ("/home/*.*") ("/home/*.txt") search files
object *fn_directory (object *args, object *env)
{
int selection(char *name, char *filemask );
#if defined(sdcardsupport)
(void) env;
int type = 0x4 | 0x8 ; // Files and directories
char pattern_string[256] = "*" ;
char dirname_string[256] = "/";
if (args != NULL)
{ // Directory name
if(stringp(car(args)))
{
cstring(car(args), dirname_string, 256) ;
if(dirname_string[strlen(dirname_string)-1] == '/')
{
dirname_string[strlen(dirname_string)-1] = 0x0 ;
type = 0x4 ;
}
else type = 0x8 ;
char *pattern_bgn = strchr(dirname_string,'*') ;
if(pattern_bgn)
{ // There is pattern string with '*'-symbols
while((pattern_bgn!=dirname_string)&&(*pattern_bgn!='/')) pattern_bgn -- ;
if(*pattern_bgn=='/')
{
pattern_bgn ++ ;
strcpy(pattern_string, pattern_bgn);
*pattern_bgn = 0x0 ; // set 0x00 into dirname_string
}
else
{
strcpy(pattern_string, pattern_bgn);
strcpy(dirname_string, "/");
}
if(!(*dirname_string))
strcpy(dirname_string, "/"); // Dir name "/" restore
}
}
}
object *result = cons(NULL, NULL);
object *ptr = result;
SD.begin(SDCARD_SS_PIN);
File root = SD.open(dirname_string);
if (!root){ pfstring("problem reading from SD card", pserial); return nil; }
while (true) {
File entry = root.openNextFile();
if(!entry) break;
if( (entry.isDirectory() && (type&0x4)) || (!entry.isDirectory() && (type&0x8)) )
if(selection((char*)entry.name(), pattern_string ))
{
object *filename = lispstring((char*)entry.name());
cdr(ptr) = cons(filename, NULL);
ptr = cdr(ptr);
}
}
root.close();
return cdr(result);
#else
(void) args, (void) env;
error2("not supported");
return nil;
#endif
}
// File search using '*'-type patterns
int fillpattern(char *mask, char *pattern){
int i = 0 ;
if(*mask==0) return -1 ;
while((*mask!=0)&&(*mask!='*')) {
*pattern++ = *mask++ ;
i++ ;
}
*pattern = 0 ;
return i ;
}
int findpattern(char *pattern, char *name) {
int i = 0 , lenp, lenn ;
if(*pattern==0) return -1 ;
lenp = strlen(pattern) ;
lenn = strlen(name) ;
while(lenp<=lenn) {
if(strncmp(name,pattern,lenp)==0) return i;
name++ ;
lenn-- ;
i++ ;
}
return -1 ;
}
// use file_pattern
int selection(char *name, char *filemask )
{
char file_pattern[256] ;
int i;
int imaskpos = 0, inamepos = 0 ;
i = fillpattern(filemask, file_pattern);
if(i==-1) return -1 ;
if(i>0)
{
if(strncmp(file_pattern,name,i)!=0) return 0 ;
}
imaskpos += i+1 ; // next position after '*'
inamepos += i ;
do{
// take mask next fragment between '*' symbols
i = fillpattern(&filemask[imaskpos], file_pattern);
if(i == -1 ) return 1 ;
int k = findpattern(file_pattern, &name[inamepos]);
if(k==-1) return 0 ;
imaskpos += i ;
inamepos += k ;
if(filemask[imaskpos] == '*') imaskpos++ ;
else
if(name[inamepos+i] != 0) return 0 ;
// the end of pattern but not end of name
}while(1) ;
return 0;
}
Dear friends, any functions may be useful for somedody. These function can be placed to “extensions.ino”. You can use its for you needs.
(probe-file name) function tests exists specified file or not.
(delete-file name) remove file (or directory).
(rename-file oldname newname) rename or move a file
(ensure-directories-exist dir) test and create directory
const char string_probefile[] = "probe-file";
const char doc_probefile[] = "(probe-file pathspec)\n"
"tests whether a file exists.\n"
" Returns nil if there is no file named pathspec,"
" and otherwise returns the truename of pathspec.";
object *fn_probefile (object *args, object *env) {
#if defined(sdcardsupport)
(void) env;
char pattern_string[256] ;
if (args != NULL)
{ // file name
if(stringp(car(args))) {
cstring(car(args), pattern_string, 256) ;
} else {
pfstring("\nprobe-file: First argument must be string.", pserial);
return nil; }
}
SD.begin(SDCARD_SS_PIN);
if(SD.exists(pattern_string)) return tee;
return nil;
#else
(void) args, (void) env;
error2("not supported");
return nil;
#endif
}
const char string_deletefile[] = "delete-file";
const char doc_deletefile[] = "(delete-file pathspec)\n"
"delete specified file.\n"
" Returns true if success and otherwise returns nil.";
object *fn_deletefile (object *args, object *env) {
#if defined(sdcardsupport)
(void) env;
char pattern_string[256] ;
if (args != NULL)
{ // Directory name
if(stringp(car(args))) {
cstring(car(args), pattern_string, 256) ;
}
else {
pfstring("\ndelete-file: First argument must be string.",pserial);
return nil; }
}
SD.begin(SDCARD_SS_PIN);
if(SD.exists(pattern_string))
{
if(SD.remove(pattern_string)) return tee;
}
else return tee;
return nil;
#else
(void) args, (void) env;
error2("not supported");
return nil;
#endif
}
const char string_renamefile[] = "rename-file";
const char doc_renamefile[] = "(rename-file filespec newfile)\n"
"rename or moving specified file.\n"
" Returns true if success and otherwise returns nil.";
object *fn_renamefile (object *args, object *env) {
#if defined(sdcardsupport)
(void) env;
char filename_string[256] ;
char newname_string[256] ;
if (args != NULL)
{ // Directory name
if(stringp(car(args)))
{
cstring(car(args), filename_string, 256) ;
args = cdr(args);
if (args != NULL){
if(stringp(car(args)))
cstring(car(args), newname_string, 256) ;
else {
pfstring("\nrename-file: Second argument must be string.", pserial);
return nil; }
}
else {
pfstring("\nrename-file: First argument must be string.", pserial);
return nil; }
}
SD.begin(SDCARD_SS_PIN);
if (!SD.exists(filename_string)) {
pfstring("file not exists", pserial); return nil; }
File fp_source = SD.open(filename_string, FILE_READ);
if (SD.exists(newname_string)) SD.remove(newname_string) ;
File fp_dest = SD.open(newname_string, FILE_WRITE);
if (!fp_dest) {
pfstring("Cannot open destination file.\n", pserial); return nil; }
uint32_t i, sz ;
sz = fp_source.size();
for(i=0; i<sz;i++) fp_dest.write(fp_source.read()) ;
fp_source.close();
fp_dest.close();
SD.remove(filename_string) ;
return tee;
#else
(void) args, (void) env;
error2("not supported");
return nil;
#endif
}
const char string_ensuredirectoriesexist[] = "ensure-directories-exist";
const char doc_ensuredirectoriesexist[] = "(ensure-directories-exist pathspec)\n"
"Tests whether the directories containing the specified file actually exist,"
" and attempts to create them if they do not.\n"
" Returns true if success and otherwise returns nil.";
object *fn_ensuredirectoriesexist(object *args, object *env) {
#if defined(sdcardsupport)
(void) env;
char pattern_string[256] ;
if(stringp(car(args))) cstring(car(args), pattern_string, 256) ;
else { pfstring("\nensure-directories-exist: argument must be string\n", pserial); return nil; }
SD.begin(SDCARD_SS_PIN);
if(!SD.exists(pattern_string))
{
if(SD.mkdir(pattern_string)) return tee;
}
else return tee;
return nil;
#else
(void) args, (void) env;
error2("not supported");
return nil;
#endif
}
Next strings must be add
to lookup table:
{ string_probefile, fn_probefile, 0211, doc_probefile },
{ string_renamefile, fn_renamefile, 0222, doc_renamefile },
{ string_deletefile, fn_deletefile, 0211, doc_deletefile },
{ string_ensuredirectoriesexist, fn_ensuredirectoriesexist, 0211, doc_ensuredirectoriesexist },
Dear friends, i found one difficulty with adding new functions if their total number exceeds 255. To solve this we can to correct the line
#define fntypef(x) (getminmax((uint8_t)(x))>>6)
replace it with
#define fntypef(x) (getminmax((uint16_t)(x))>>6)
Dear friends, I found one difficulty with adding new functions if their total number exceeds 255.
Yes, that’s a bug - thanks for spotting it. Will be corrected …