Arduino SD -> File extension - c

I found this example to list all files on the SD Card:
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');
}
if (entry.type
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 I want to list only the files with an explicit extension and save them into an array. Any one of you an Idea? I can't found any Function to get the file extension by the SD Class.

Unfortunately you will need to loop through each of the filenames individually.
please look at an example of my code that does just this. Similar to the above comment by "A Person".
Here are links to my actual use WHERE I use it and THE filter function
Note that my above use is using the SdFatLib, a more advanced version that SD (the IDE provided) library. Below I have adapted the same function for SD. Should work as it merely inspects the pointed char arrary's last 4 characters.
FYI - Note that the SD and SdFatLib only support 8.3 format.
void printDirectory(File dir, int numTabs) {
while(true) {
File entry = dir.openNextFile();
if (! entry) {
// no more files
break;
}
for (uint8_t i=0; i<numTabs; i++) {
Serial.print('\t');
}
if ( isFnMusic(entry.name()) ) { // Here is the magic
Serial.print(entry.name());
}
if (entry.isDirectory()) { // Dir's will print regardless, you may want to exclude these
Serial.print(entry.name());
Serial.println("/");
printDirectory(entry, numTabs+1);
} else {
// files have sizes, directories do not
Serial.print("\t\t");
Serial.println(entry.size(), DEC);
}
entry.close();
}
}
bool isFnMusic(char* filename) {
int8_t len = strlen(filename);
bool result;
if ( strstr(strlwr(filename + (len - 4)), ".mp3")
|| strstr(strlwr(filename + (len - 4)), ".aac")
|| strstr(strlwr(filename + (len - 4)), ".wma")
|| strstr(strlwr(filename + (len - 4)), ".wav")
|| strstr(strlwr(filename + (len - 4)), ".fla")
|| strstr(strlwr(filename + (len - 4)), ".mid")
// and anything else you want
) {
result = true;
} else {
result = false;
}
return result;
}

Related

Fatfs string comparison for fno.fname

I have an issue regarding reading the size of a file on my SD card. The sizes of these files will vary in the application, I therefore, need to get the size of the file. If I run the below code I can see the files in my directory along with their size.
What I need to do is store the size of the DATA.CSV file as a variable.
How do I add a comparision to get the fno.fsize when the listing is "data.csv
This prints out:
00> Listing directory: /00> 0 EVENTLOG.CSV <DIR> SYSTEM~1 183600 DATA.CSV ```
void Get_data_csv_file_size()//of the data csv
{
if(Logging_UART_SD_CARD == true){NRF_LOG_INFO("\r\n Listing directory: /");}
ff_result = f_opendir(&dir, "/");
if (ff_result)
{
if(Logging_UART_SD_CARD == true){NRF_LOG_INFO("Directory listing failed!");}
}
do
{
ff_result = f_readdir(&dir, &fno);
if (ff_result != FR_OK)
{
if(Logging_UART_SD_CARD == true){NRF_LOG_INFO("Directory read failed.");}
}
if (fno.fname[0])
{
if (fno.fattrib & AM_DIR)
{
if(Logging_UART_SD_CARD == true){NRF_LOG_RAW_INFO(" <DIR> %s",(uint32_t)fno.fname);}
}
else
{
if(Logging_UART_SD_CARD == true){NRF_LOG_RAW_INFO("%9lu %s", fno.fsize, (uint32_t)fno.fname);}
if(strcmp((uint32_t)fno.fname, "data.csv")==0)//Convert both to a uint32_t
{
Size_of_file = fno.fsize;//Set the size of the file
//Does not get to here
}
}
}
}
while (fno.fname[0]);
}
Note this is programmed in C using a arm board. What operation do I need to do so I can get the file size?
I want something like:
if(fno.name == "data.csv")
{
Size_of_file = fno.fsize;//Set the size of the file
}
Just in case you determine using an implementation of stricmp() would be useful, here is one that I have used:
//case insensitive string compare
int cb_stricmp(const char *a, const char *b)
{
if(!a) return -1;
if(!b) return -1;
int ch_a = 0;
int ch_b = 0;
while ( ch_a != '\0' &&ch_a == ch_b)
{
ch_a = (unsigned char) *a++;
ch_b = (unsigned char) *b++;
ch_a = tolower(toupper(ch_a));
ch_b = tolower(toupper(ch_b));
}
return ch_a - ch_b;
}
Found a solution using snprintf neeeded to convert fno.fname to a string to compare the result.
char string_test[9] = "DATA.CSV";
char name_test[9]={0};
snprintf(name_test, sizeof(name_test),"%s",(uint32_t)fno.fname);
NRF_LOG_INFO("Result is: %s",name_test);
int result = strcmp(name_test, string_test);
if(result==0)//Convert both to a uint32_t
{
Size_of_file = fno.fsize;//Set the size of the file
NRF_LOG_INFO("Size of file using is: %9lu",Size_of_file);
}

Linux Kernel Module : Problem with kernel_write function

I have a problem using kernel_write function while I am developing LKM for Linux 4.14.73 version.
This is the part of my module where I am facing the issue :
void change_led_state(char *led_path, int led_value)
{
printk("we are in change_led_state\n");
size_t len = sizeof(led_value);
char lpath[64];
ssize_t rk=0;
strncpy(lpath, led_path, sizeof(lpath) - 1);
lpath[sizeof(lpath) - 1] = '\0';
f_led = filp_open(lpath, O_WRONLY , 0);
if (f_led == NULL) {
printk("Unable to access led\n");
return;
}
rk = kernel_write(f_led, led_value, len, &f_led->f_pos);
printk("rk = %d\n", rk);
filp_close(f_led, NULL);
}
rk is usually getting a negative value. I think it must be positive. What's wrong with the function please ? How can I correct it ?
For more informations, I have the same code but in user-space program, and it's working fine, I just want to replace it to kernel space. This is the user space program :
void change_led_state(char *led_path, int led_value)
{
char lpath[64];
FILE *led_fd;
strncpy(lpath, led_path, sizeof(lpath) - 1);
lpath[sizeof(lpath) - 1] = '\0';
led_fd = fopen(lpath, "w");
if (led_fd == NULL) {
fprintf(stderr, "simplekey: unable to access led\n");
return;
}
fprintf(led_fd, "%d\n", led_value);
fclose(led_fd);
}
void reset_leds(void)
{
change_led_state(LED_PATH "/" green "/brightness", 0);
}
void eval_keycode(int code)
{
static int green_state = 0;
switch (code) {
case 260:
printf("BTN pressed\n");
// figure out red state
green_state = green_state ? 0 : 1;
change_led_state(LED_PATH "/" green "/brightness", green_state);
break;
default :
printf("Wrong Button was pressed\n");
break;
}
}
In the main fuction I just call eval_keycode function and it's working. What mistakes I have made ?
In your call:
rk = kernel_write(f_led, led_value, len, &f_led->f_pos);
The second parameter is supposed to be a char *, but you are passing an int.
In addition, when writing to the "/sys/class/leds/[device]/brightness" file, the code that handles this expects a pointer to a string of decimal digits, terminated by some non-digit character (such as a newline) or a null terminator.
You need to "print" your led_value to a char array as a decimal number and write the array contents to the file:
char led_valstr[20]; /* should be large enough */
len = scnprintf(led_valstr, sizeof(led_valstr), "%d\n", led_value);
rk = kernel_write(f_led, led_valstr, len, &f_led->f_pos);
(EDIT: corrected call to scnprintf.)

Waiting for character in string

I am currently working on a project that will be used to test whether an instrument is within tolerance or not. My test equipment will put the DUT (Device Under Test) into a "Test Mode" where it will repeatedly send a string of data every 200ms. I want to receive that data, check is is within tolerance and give it a pass or fail.
My code so far (I've edited a few things out like .h files and some work related bits!):
void GetData();
void CheckData();
char Data[100];
int deviceId;
float a;
float b;
float c;
void ParseString(const char* stringValue)
{
char* token = NULL;
int tokenPlace = 0;
token = strtok((char *) stringValue, ",");
while (token != NULL) {
switch (tokenPlace) {
case 0:
deviceId = atoi(token);
break;
case 1:
a= ((float)atoi(token)) / 10.0f;
break;
case 2:
b= ((float)atoi(token)) / 100.0f;
break;
case 3:
c= ((float)atoi(token)) / 10.0f;
break;
}
tokenPlace++;
token = strtok(NULL, ",");
}
}
void GetData()
{
int x = UART.scanf("%s,",Data);
ParseString(Data);
if (x !=0) {
UART.printf("Device ID = %i\n\r", deviceId);
UART.printf("a= %.1f\n\r", a);
UART.printf("s= %.2f\n\r", b);
UART.printf("c= %.1f\n\n\r", c);
}
if (deviceId <= 2) {
CheckData();
} else {
pc.printf("Device ID not recognised\n\n\r");
}
}
void CheckData()
{
if (a >= 49.9f && a< = 50.1f) {
pc.printf("a Pass\n\r");
} else {
pc.printf("a Fail\n\r");
}
if (b >= 2.08f && b <= 2.12f) {
pc.printf("b Pass\n\r");
} else {
pc.printf("b Fail\n\r");
}
if (c >= 20.0f && c <= 25.0f) {
pc.printf("c Pass\n\n\r");
} else {
pc.printf("c Fail\n\n\r");
}
if (deviceId == 0) {
(routine1);
} else if (deviceId == 1) {
(routine2);
} else if (deviceId == 2) {
(Routine3);
}
}
int main()
{
while(1) {
if(START == 0) {
wait(0.1);
GetData();
}
}
}
And this works absolutely fine. I am only printing the results to a serial terminal so I can check the data is correct to make sure it is passing and failing correctly.
My issue is every now and then the START button happens to be pressed during the time the string is sent and the data can be corrupt, so the deviceId fails and it will say not recognised. This means I then have to press the start button again and have another go. A the moment, it's a rare occurrence but I'd like to get rid of it if possible. I have tried adding a special character at the beginning of the string but this again gets missed sometimes.
Ideally, when the start button is pressed, I would like it to wait for this special character so it knows it is at the beginning of the string, then the data would be read correctly, but I am unsure how to go about it.
I have been unsuccessful in my attempts so far but I have a feeling I am overthinking it and there is a nice easy way to do it. Probably been staring at it too long now!
My microcontroller is STM32F103RB and I am using the STM Nucleo with the mBed IDE as it's easy and convenient to test the code while I work on it.
You can use ParseString to return a status indicating whether a complete string is read or not.
int ParseString(const char* stringValue)
{
/* ... your original code ... */
/* String is complete if 4 tokens are read */
return (tokenPlace == 4);
}
Then in GetData use the ParseString return value to determine whether to skip the string or not.
void GetData()
{
int x = UART.scanf("%s,",Data);
int result = ParseString(Data);
if (!result) {
/* Did not get complete string - just skip processing */
return;
}
/* ... the rest of your original code ... */
}

Missing events on ReadConsoleInput in windows shell?

As I proceed in my (possibly vain) attempt to reimplement a curses style library that supports both *nix and windows under an MIT license, I've stumbled onto a problem reading terminal import using the windows api.
Basically, I don't get all the events I expect to, and I don't know why.
First I setup the terminal to be in non-buffering mode:
DWORD mode;
HANDLE hstdin = GetStdHandle( STD_INPUT_HANDLE );
// Save old mode
GetConsoleMode(hstdin, &mode);
// Set to no line-buffering, no echo, no special-key-processing
SetConsoleMode(hstdin, 0);
Then I use PeekConsoleInput and ReadConsoleInput in a loop to have a non blocking key press input; the equivalent of using termios.h and select on stdin in linux:
__EXPORT int sterm_read(void *state) {
DWORD dwRead;
INPUT_RECORD inRecords[1];
PeekConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 1, &dwRead);
if (dwRead > 0) {
ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &inRecords[0], 1, &dwRead);
if (inRecords[0].EventType == KEY_EVENT) {
if (inRecords[0].Event.KeyEvent.bKeyDown) {
return inRecords[0].Event.KeyEvent.wVirtualKeyCode;
}
}
}
return -1;
}
Ignore the state variable; that's so the api can accept an arbitrary state struct on various platforms.
Now if I try to use this code:
#include <sterm.h>
#include <stdio.h>
#define assert(v, msg) if (!v) { printf("FAILED! %s", msg); return 1; }
int main(void) {
void *state = sterm_init();
int i;
char c;
for (;;) {
if ((c = sterm_read(state)) == 81) { // ie. press q to exit
break;
}
if (c != -1) {
sterm_write(state, &c, 1); // This is a thin wrapper around _write(1, ...)
}
}
sterm_shutdown(state);
return 0;
}
It almost works. I get the input character I press pushed out to the terminal... mostly.
Probably every 10th character press is recorded. If I type quickly, the API 'loses' events, and I get "HEO WLD" instead of "HELLO WORLD".
What's going on? Does ReadConsoleInput somehow clear the input buffer?
Am I doing something wrong? It seems almost like I'm only getting events based on a race condition which is 'is key pressed when PeekConsoleInput is called'.
...but surely that shouldn't be the case? The point of using these buffered I/O interfaces (instead of GetAsyncKeyState) is that the events should be buffered right?
Help!
I also have discovered that events are not guaranteed to stay around to be read.
This makes sense because otherwise the OS would need to provide lots and lots of buffering space.
The best I can do to deal with this is this code to do my own buffering
but clearly pastes of more than 128 characters will often fail :
static int g_eaten_ct = 0; /* Re-eaten char */
static int g_eaten_ix = -1;
static int g_eaten[128];
void reeat(int c)
{ g_eaten_ct += 1;
g_eaten[g_eaten_ix + g_eaten_ct] = c; /* save the char for later */
}
void flush_typah()
{
g_eaten_ct = 0;
g_eaten_ix = -1;
while (_kbhit())
(void)ttgetc();
}
int ttgetc()
{ if (g_eaten_ct > 0)
{ g_eaten_ct -= 1;
return g_eaten[++g_eaten_ix];
}
{ int totalwait = g_timeout_secs;
int oix = -1;
while (1)
{ int got,need;
const DWORD lim = 1000;
INPUT_RECORD rec[32];
int cc = WaitForSingleObject(g_ConsIn, lim);
switch(cc)
{ case WAIT_OBJECT_0:
need = sizeof(g_eaten)/sizeof(g_eaten[0]) - oix;
if (need > 32)
need = 32;
cc = ReadConsoleInput(g_ConsIn,&rec[0],need,(DWORD*)&got);
if (cc && got > 0)
break;
#if _DEBUG
{ DWORD errn = GetLastError();
if (errn != 6)
mlwrite("%pError %d %d ", cc, errn);
}
#endif
continue;
case WAIT_TIMEOUT:
#if _DEBUG
if (g_got_ctrl)
{ g_got_ctrl = false;
return (int)(CTRL | 'C');
}
#endif
if (--totalwait == 0) // -w opt
exit(2);
// drop through
default:continue;
}
{ int ix = -1;
while (++ix < got)
{ INPUT_RECORD * r = &rec[ix];
if (r->EventType == KEY_EVENT && r->Event.KeyEvent.bKeyDown)
{ int ctrl = 0;
int keystate = r->Event.KeyEvent.dwControlKeyState;
if (keystate & (RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED))
{ ctrl |= CTRL;
g_chars_since_ctrl = 0;
}
{ int chr = r->Event.KeyEvent.wVirtualKeyCode;
if (in_range(chr, 0x10, 0x12))
continue; /* shifting key only */
if (keystate & (RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED))
ctrl |= ALTD;
else
chr = r->Event.KeyEvent.uChar.AsciiChar & 0xff;
if (/*chr != 0x7c && */ (chr | 0x60) != 0x7c) // | BSL < or ^ BSL
{ int vsc = r->Event.KeyEvent.wVirtualScanCode;
if (in_range(vsc, SCANK_STT, 0x58))
{ ctrl |= SPEC;
chr = scantokey[vsc - SCANK_STT];
}
// else if (in_range(vsc, 2, 10) && chr == 0)
// chr = '0' - 1 + vsc;
}
if ((keystate & SHIFT_PRESSED) && ctrl) // exclude e.g. SHIFT 7
ctrl |= SHFT;
g_eaten[++oix] = ctrl | (chr == 0xdd ? 0x7c : chr);
++g_chars_since_ctrl;
}}
else if (r->EventType == MENU_EVENT)
{ /*loglog1("Menu %x", r->Event.MenuEvent.dwCommandId);*/
}
}
if (got == need && oix < sizeof(g_eaten) / sizeof(int))
{ PeekConsoleInput(g_ConsIn, &rec[0], 1, (DWORD*)&got);
if (got > 0)
continue;
}
if (oix >= 0)
{ g_eaten_ct = oix;
g_eaten_ix = 0;
return g_eaten[0];
}
}}
}}

C function to calculate relative path

on input: two paths like
inFrom: /usr/share/lib
inTo: /usr/bin
on output:
a path like
oRelPath == ../../bin
Are there any standard or near standard functions ? OSes of interests are: windows, mac, linux
It looks weird that for such standard trivial task it seems there is no any standard function (or they called in an non-obvious way)
I converted the C# code that #Armali posted to C++:
#include <vector>
#include <string>
#include <algorithm>
std::vector<std::string> str_split(const std::string& in, const std::string& delim=" \t\r\n") {
std::vector<std::string> out;
auto firstPos = in.find_first_not_of(delim);
auto secondPos = in.find_first_of(delim, firstPos);
out.clear();
if(firstPos != std::string::npos)
{ out.push_back( in.substr( firstPos, secondPos - firstPos ) ); }
while( secondPos != std::string::npos ) {
firstPos = in.find_first_not_of(delim, secondPos);
if(firstPos == std::string::npos)
{ break; }
secondPos = in.find_first_of( delim, firstPos );
out.push_back( in.substr( firstPos, secondPos - firstPos ) );
}
return out;
}
int str_compare_no_case(std::string a, std::string b) {
std::transform(a.begin(), a.end(), a.begin(), ::tolower);
std::transform(b.begin(), b.end(), b.begin(), ::tolower);
return a.compare(b);
}
static std::string MakeRelativePath(std::string absPath, std::string relTo) {
#ifdef _WIN32
const char directorySeparator = '\\';
#else
const char directorySeparator = '/';
#endif
std::string DirectorySeparatorChars = {directorySeparator, '\0'};
auto absParts = str_split(absPath, DirectorySeparatorChars);
auto relParts = str_split(relTo, DirectorySeparatorChars);
// Get the shortest of the two paths
size_t len = std::min(absParts.size(), relParts.size());
// Use to determine where in the loop we exited
int lastCommonRoot = -1;
int index;
// Find common root
for (size_t index = 0; index < len; index++) {
if (str_compare_no_case(absParts[index], relParts[index])==0) {
lastCommonRoot = index;
} else {
break;
}
}
// If we didn't find a common prefix "c:\xx", "D:\"
if (lastCommonRoot == -1) {
// The path of the two files doesn't have any common base.
return absPath;
}
// Build up the relative path
std::string relativePath;
// Add on the ..
for (size_t index = lastCommonRoot + 1; index < relParts.size(); index++) {
relativePath += "..";
relativePath += directorySeparator;
}
// Add on the folders
for (size_t index = lastCommonRoot + 1; index+1 < absParts.size(); index++) {
relativePath += absParts[index];
relativePath += directorySeparator;
}
if(!absParts.empty()) {
relativePath += absParts[absParts.size() - 1];
}
return relativePath;
}
This question is a duplicate, but in C#. Should not be impossible to port the code to C.
– Some programmer dude
thanks, from there it seems I found a WinAPI function: PathRelativePathTo – dev_null

Resources