Problems with SD-Card and FatFs (Mounting, Opening Files) - microcontroller

I've got some problems with the FATFs library and the STM32F7 Series.
I want to connect a SD-Card (32GB, SanDisk, FAT32) with the F746G-Discovery-Board to save some data on it. (at first .txt-Files, later read .csv Files to visualize some measuring data).
My problem is, that the f_mount function gives me a "FR_OK" but in the next step I can't open a file with f_open.
I located the problem by debugging step by step.
It's in the ff.c file of the FatFs lib.
res = find_volume(&path, &fs, mode);
At this point the program stops.
jumping into the function the problem is following line
stat = disk_initialize(fs->drv); /* Initialize the physical drive */
So the SD-Card is not initialized/mounted? But why does the f_mount giving me the FR_OK?
It would be really nice if someone could help. Thanks!
I tried following things:
-formatted SD-Card (exFat,..)
-other Paths
in main:
FIL fil;
FRESULT fr;
FATFS FatFs;
fr=f_mount(&FatFs,(TCHAR const*)SDPath,1);
if(fr!= FR_OK)
{
Error_Handler();
}
else if (fr == FR_OK)
{
char path[13] = "testfile.txt";
fr = f_open(&fil, (char*)path, FA_WRITE | FA_CREATE_ALWAYS);
// File should be created
//here begins the main problem
if ( fr == FR_OK ) // File opened
{
f_printf(&fil, "%d", 1234);
}
else if (fr != FR_OK)
{
Error_Handler();
}
}
f_close(&fil);
f_mount(0,"SD",0);

Related

How can I call random jpg from sd card and display using drawSdJpeg

In the TFT_ESPI (Bodmer) Example/Generic/ESP32_SDcard_jpeg
I have run it on my esp32 on a 3.5" ili9341 with no problems.
I have changed the sd file names to numbers and called them as such ...
drawSdJpeg("/1.jpg", 0, 0); and as expected, runs the same.
I have used this code to display the images one after another ...
`File file;
File root = SD.open("/");
if(!root){
Serial.println("Failed to open root directory");
return;
}
file = root.openNextFile(); // Opens next file in root
while(file)
{
if(!file.isDirectory())
{
drawSdJpeg(file.name(), 0, 0); // This draws a jpeg pulled off the SD Card
delay(4000);
}
file = root.openNextFile(); // Opens next file in root
}
root.close();`
But I can't find a way to call a RANDOM file (image) and display it.
Can anyone help please, I would appreciate it.
Thank you.
And thank you to Bodmer for making many things possible !
SOLVED
Thanks to Bodmer
Yes, the drawSdJpeg function expects a string, so you need to create a string with the number in it plus the .jpg extension. For example:
char filename [20]; // 19 chars max + terminating null
int i = 42;
itoa(i,filename,10); // Put string version of a number in filename,
number base 10
strcat(filename, ".jpg"); // add .jpg to end of string
drawSdJpeg(filename, 0, 0);

arduino, setup ethernet & network using data from SD config file

Im try to add to my sketch a dynamic way to setup the ethernet info (mac, ip, gateway, subnet) from a configuration file (config.txt). So running a webserver and serving htm files from sd card, user can go to setting page, fill a form with these info and when posted , the webserver parse the http form and save (update) the config.txt file. After that system do a restart, in order to start with the new settings (by read the config.txt file)
I have create succesfully all the parts (sd, ethernet, webserver, webclient, create the config file from posted form data) except the get params by reading the config.txt file.
I can read line by line the config, I can split the line to param & value, and now I need to fill some byte variables with the readed data. I can (after a month of google searching) to read IPs (decimal values) to byte array. Im stack to read the MAC ADDRESS hex into byte array. The config file contains the:
mac=8f:2c:2b:19:e0:b7;
ip=192.168.1.200;
netmask=255.255.255.0;
gateway=192.168.1.254;
dns=8.8.8.8;
posturl=192.168.1.157;
postport=8080;
postscript=/itherm/update.php;
interval=60000;
and the code that I use to read is:
byte myMAC[6];
byte myIP[4];
File fset;
fset = SD.open("config.txt");
if (fset){
char ff[40];
while (fset.available()>1){
bool eol=false;
for (int i=0; !eol;i++){
ff[i]=fset.read();
if (ff[i]=='\n'){
eol=true;
}
}
String par="";
bool DONE=false;
for (int i=0; !DONE;i++){
par+=ff[i];
if (ff[i]== '='){DONE=true;}
}
String pval="";
DONE=false;
//------------------------
if (par=="ip=" ){
int x=0;
while(!DONE){
for(int i=3;i<=i+21;i++){
if(ff[i]=='.'){
myIP[x]=pval.toInt();
x++;
i++;
pval="";
}
else if(ff[i]==';' || i>20){
myIP[x]=pval.toInt();
DONE=true;
break;
}
pval+=ff[i];
}
}
}
} //while (fset.available()>1)
} //if (fset)
I will appreciate any help. Please no answers with simple use of Serial.print(). I have found hundreds of suggestions but none, that work properly to read all the parameters (dec, hex, strings). After a month of effort & searching, I wonder why something so necessary and useful does not exist as an example in the community, completely functional !!
Best regards
Okay so here is a complete set of routines to do what you want -I think you misunderstood the concept of char arrays vs a single char[0] The routines are documented and self explanatory. I recomend not to finish lines with ; but with '\n' which in your example is there anyway (also you can not see the new line terminator) To get the mac address I need three lines:
if (strncmp(cfgLine, "mac=", 4) == 0) {
strcpy (macAddr, cfgLine + 4);
}
line one compares the first 4 characters and if it is 0 (meaning its a fit)
line two copies the chars from the fifth to the last char from the lineBuffer to the target array, which can actually be used as param for functions.
The file structure should be with no ; as you would have to parse ; and \n
mac=8f:2c:2b:19:e0:b7
ip=192.168.1.200
....
postport=8080
To convert a char array to eg int we use atoi(), to convert a single char[0] to a single number we use int singleDigit = char[0]-48;
const char configurationFilePath [] = "/someconfig.txt";
char cfgLine[128] = {'\0'}; // this is a global temp char array to hold the read lines (lenght= chars longest line +1)
char numBuffer[16] = {'\0'}; // this is a global temo char array to help to convert char to number
char macAddr [18] = {'\0'}; // this is a global char array to hold the mac address
char ipAddr [16] = {'\0'}; // this is a global char array to hold the IP address - max xxx.xxx.xxx.xxx
int postport=0;
// .... you can easyly implement for all other data you want to store/retrieve
// Counts the lines of a file
uint16_t countLines() {
uint16_t currentLineCount = 0;
File cfgFile = SD.open(configurationFilePath, "r");
if (!cfgFile) {
Serial.println(F("Config file open failed on read"));
} else {
while (cfgFile.available()) {
/** Lets read line by line from the file */
if (cfgFile.read() == '\n') currentLineCount ++; // Lines are delimited by '\n'
}
cfgFile.close();
}
return currentLineCount;
}
//Load the config file from SD/SPIFFS/LittleFS
bool loadConfigFile() {
uint16_t lineCounter = countLines();
if (lineCounter <= 0) {
Serial.print(F("No config data stored in file ")); Serial.println(configurationFilePath);
return false;
}
else {
File cfgFile = SD.open(configurationFilePath, "r");
while (cfgFile.available()) {
strcpy (cfgLine, (cfgFile.readStringUntil('\n').c_str())); // normaly you use new line, we copy one line at a time
// Serial.println(cfgLine); /** Printing for debuging purpose */
while (cfgLine[0] != '\0') { /* Block refilling of cfgLine till processed */
loadSingleCfgLine();
}
}
cfgFile.close();
Serial.println(F("[Success] Loaded config !"));
return true;
}
}
//Load the data of a single line into a char array
void loadSingleCfgLine() {
if (strncmp(cfgLine, "mac=", 4) == 0) {
strcpy (macAddr, cfgLine + 4);
}
if (strncmp(cfgLine, "ip=", 3) == 0) {
strcpy (ipAddr, cfgLine + 3);
}
if (strncmp(cfgLine, "postport=", 9) == 0) {
strcpy (numBuffer, cfgLine + 9);
postport = atoi(numBuffer); // One extra step to convert to int
}
// ... easy to implement for all other data
}
I divided the routines into small independend functions, so its easy adaptable for different uses. I'm sorry for not digging into your code as it is hard to follow and unclear what you want todo.As an added bonus we do not use the String class. These Strings tend to fragment heap - causing resets/crashes while the global char arrays are compiled to flash and don't show this behavior.

how to recheck file in Arduino SD card

I am trying to create a list of files on my SD card this is easy enough to do once but the moment I run the program more than once the list become either shortened or the program say there is no files at all.
To make this as easy as possible I am using the SD example that comes with the arduino SD library and just putting the setup part ( that would normally run once ) in the loop part.
Heres what I have with that.
#include <SD.h>
File root;
void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}
Serial.print("Initializing SD card...");
// On the Ethernet Shield, CS is pin 4. It's set as an output by default.
// Note that even if it's not used as the CS pin, the hardware SS pin
// (10 on most Arduino boards, 53 on the Mega) must be left as an output
// or the SD library functions will not work.
pinMode(10, OUTPUT);
if (!SD.begin(10)) {
Serial.println("initialization failed!");
return;
}
Serial.println("initialization done.");
}
void loop()
{
Serial.println("hit any key then enter to run the list");
while(!Serial.available())
{;}
Serial.read();
root = SD.open("/");
printDirectory(root, 0);
Serial.println("done!");
// nothing happens after setup finishes.
}
void printDirectory(File dir, int numTabs) {
while(true) {
File entry = dir.openNextFile();
if (! entry) {
// no more files
//Serial.println("**nomorefiles**");
break;
}
for (uint8_t i=0; i<numTabs; i++) {
Serial.print('\t');
}
Serial.print(entry.name());
if (entry.isDirectory()) {
Serial.println("/");
printDirectory(entry, numTabs+1);
} else {
// files have sizes, directories do not
Serial.print("\t\t");
Serial.println(entry.size(), DEC);
}
}
}
But then I get this weird output after running it
Initializing SD card...initialization done.
hit any key then enter to run the list
HFBVYRG.TXT 7
THBVFG.TXT 7
WAZXDSQ.TXT 7
QAZXSW.TXT 21
WSXZAQ.TXT 7
1478523.TXT 7
QWSDFRE.TXT 7
ZXCVBNM.TXT 7
MKOLIJY.TXT 7
done!
hit any key then enter to run the list
HFBVYRG.TXT 7
THBVFG.TXT 7
WAZXDSQ.TXT 7
QAZXSW.TXT 21
WSXZAQ.TXT 7
1478523.TXT 7
QWSDFRE.TXT 7
ZXCVBNM.TXT 7
MKOLIJY.TXT 7
done!
hit any key then enter to run the list
HFBVYRG.TXT 7
THBVFG.TXT 7
WAZXDSQ.TXT 7
QAZXSW.TXT 21
done!
hit any key then enter to run the list
done!
hit any key then enter to run the list
done!
hit any key then enter to run the list
done!
hit any key then enter to run the list
////////////////////////////////////////////////////////////////////////////////////
as you can see it gets shorter and shorter then just stops altogether.
Does anyone have any ideas why ?
I have tried playing around with pointers and closing and reopening the file but I have come up with nothing.
Any ideas would be greatly appreciated.
No good can come from mismatched open()'s and close()'s. You open the root directory every pass through the loop:
root = SD.open("/");
but never
root.close();
First, fix this error and also check that when you open the root that you succeed before trying to print a listing:
root = SD.open("/");
if(root) {
printDirectory(root, 0);
Serial.println("done!");
root.close();
}
else {
Serial.println("failed to open directory");
}
Second, close the files that are opened in the directory walk
void printDirectory(File dir, int numTabs) {
while(true) {
File entry = dir.openNextFile();
...
entry.close();
}
return;
}
Lastly, consider not blocking in the loop(). Other things can happen outside your loop() and they will be blocked indefinitely depending on how long you wait to hit a key. A more typical code pattern is to spin wildly through the loop() as opposed to blocking waiting for user input. For example:
boolean bNeedPrompt = true;
void loop() {
// Show the prompt once, then mark as displayed so that text does not
// continuously scroll on the screen
if(bNeedPrompt) {
Serial.println("hit any key then enter to run the list");
bNeedPrompt = false;
}
if(Serial.available()) {
Serial.read();
// do action
// set flag so that prompt will display again
bNeedPrompt = true;
}
return;
}
jdr5ca's answer addresses a lot of the problems with your setup - by not closing files you are going to be leaking resources which won't end well. It is particularly important on such a low memory setup as the Arduino, which has only 2k RAM for the original chips, so worth bearing in mind throughout your code.
Short answer for your remaining question: it won't be entirely predictable, but low memory is likely the cause of your problem. Read on for more details.
I've had some similar experiences with the SD library recently, though not with such an obvious cause. I found that if the free memory on my system got below about 500 bytes (a quarter of the total RAM) then the SD library starts to behave very oddly when trying to list directories.
I saw similar things to you, for example:
list entries under root directory and find two sub-directories
list entries in one of those sub-directories, worked fine
list entries under root directory and now I find only one sub-directory! The one I had listed the contents of has vanished!
I could still list the contents of the sub-directory fine, ie. it is still there!
resetting the Arduino and starting again gave the same results, again showing that the files were still present on SD card
I think all of this comes down to SD library being both quite memory hungry (#include SD.h uses up 500 bytes off the bat!), and not handling low memory errors particularly obviously.
So if things are acting strangely then:
check your memory usage (see eg. freeRam() from http://playground.arduino.cc/Code/AvailableMemory)
reduce your memory usage where ever possible. In rough order:
remove libraries you don't need
get rid of any hard coded strings that you don't need (eg. in calls to Serial.print())
for hard coded strings you do need make them as short as possible, and store them in flash (eg. Serial.print(F("String in flash"));) rather than RAM
you can't stick format strings (eg. for snprintf) in flash currently, so avoid using those and just build up the output manually (it is more long handed but saves memory)
make sure you are using appropriate variable types everywhere (ie. the smallest sized type that will suit your purposes) - especially important if you have any arrays, for obvious reasons!

Xcode 4.5.1 - C programming - executable not producing output file

I'm using Xcode 4.5.1 (4G1004) on OSX 10.8.2. I've written a simple C program that reads scores from a .txt file and stores some of them in a new .txt file. What's happening is that if I build the program in Xcode (command-b) then go directly to the location of the executable it created and run that in a terminal, the program runs but does not output the new .txt file. However, if instead of building (command-b) and running the program in the described way above, if I run it through Xcode by clicking the arrow in the upper left of the toolbar, the new .txt IS created afterwards.
Why does this occur? I tried building this program using Sublime Text 2, then manually running the executable and that method works correctly by creating the output .txt file as well.
*edit - forgot to attach the code!
//func decs
void getScores();
void printScores(int);
int main()
{
getScores();
getchar();
return 0;
}//main
void getScores()
{
FILE* spscoresIn;
FILE* spscoresOut;
spscoresIn = fopen("scores_in.txt", "r");
spscoresOut = fopen("scores_out.txt", "w");
int score;
int count = 0;
while ((fscanf(spscoresIn, "%d", &score)) == 1) //writes only scores higher than 90 to the new file
if (score > 90)
{
fprintf(spscoresOut, "%d\n", score);
count++;
}
printScores(count);
return;
}//getScores
void printScores(int count)
{
printf("%d scores were higher than 90:\t", count);
printf("Press Enter to exit");
return;
}//printScores

Unix Networking Programming - Client and Server. List Function That wait for input after 40 lines

I am currently in the process of making a Client and Server in the Unix/Windows environment but right now I am just working on the Unix side of it. One of the function we have to create for the program is similar to the list function in Unix which shows all files within a dir but we also have to show more information about the file such as its owner and creation date. Right now I am able to get all this information and print it to the client however we have to also add that once the program has printing 40 lines it waits for the client to push any key before it continues to print.
I have gotta the program to sort of do this but it will cause my client and server to become out of sync or at least the std out to become out of sync. This means that if i enter the command 'asdad' it should print invalid command but it won't print that message until i enter another command. I have added my list functions code below. I am open to suggestions how how to complete this requirement as the method I have chosen does not seem to be working out.
Thank-you in advance.
Server - Fork Function: This is called when the list command is enter. eg
fork_request(newsockfd, "list", buf);
int fork_request(int fd, char req[], char buf[])
{
#ifndef WIN
int pid = fork();
if (pid ==-1)
{
printf("Failed To Fork...\n");
return-1;
}
if (pid !=0)
{
wait(NULL);
return 10;
}
dup2(fd,1); //redirect standard output to the clients std output.
close(fd); //close the socket
execl(req, req, buf, NULL); //run the program
exit(1);
#else
#endif
}
Here is the function used to get all the info about a file in a dir
void longOutput(char str[])
{
char cwd[1024];
DIR *dip;
struct dirent *dit;
int total;
char temp[100];
struct stat FileAttrib;
struct tm *pTm;
int fileSize;
int lineTotal;
if(strcmp(str, "") == 0)
{
getcwd(cwd, sizeof(cwd));
}
else
{
strcpy (cwd, str);
}
if (cwd != NULL)
{
printf("\n Using Dir: %s\n", cwd);
dip = opendir(cwd);
if(dip != NULL)
{
while ((dit = readdir(dip)) != NULL)
{
printf("\n%s",dit->d_name);
stat(dit->d_name, &FileAttrib);
pTm = gmtime(&FileAttrib.st_ctime);
fileSize = FileAttrib.st_size;
printf("\nFile Size: %d Bytes", fileSize);
printf("\nFile created on: %.2i/%.2i/%.2i at %.2i:%.2i:%.2i GMT \n", (pTm->tm_mon + 1), pTm->tm_mday,(pTm->tm_year % 100),pTm->tm_hour,pTm->tm_min, pTm->tm_sec);;
lineTotal = lineTotal + 4;
if(lineTotal == 40)
{
printf("40 Lines: Waiting For Input!");
fflush(stdout);
gets(&temp);
}
}
printf("\n %d \n", lineTotal);
}
else
{
perror ("");
}
}
}
At here is the section of the client where i check that a ! was not found in the returned message. If there is it means that there were more lines to print.
if(strchr(command,'!') != NULL)
{
char temp[1000];
gets(&temp);
}
Sorry for the long post but if you need anything please just ask.
Although, I didn't see any TCP/IP code, I once had a similar problem when I wrote a server-client chat program in C++. In my case, the problem was that I didn't clearly define how messages were structured in my application. Once, I defined how my protocol was suppose to work--it was a lot easier to debug communication problems.
Maybe you should check how your program determines if a message is complete. In TCP, packets are guaranteed to arrive in order with no data loss, etc. Much like a conversation over a telephone. The only thing you have to be careful of is that it's possible to receive a message partially when you read the buffer for the socket. The only way you know to stop reading is when you determine a message is complete. This could be as simple as two '\n' characters or "\n\r".
If you are using UDP, then that is a completely different beast all together (i.e. messages can arrive out of order and can be lost in transit, et cetera).
Also, it looks like you are sending across strings and no binary data. If this is the case, then you don't have to worry about endianess.

Resources