Teensy 4.1 SD question: Start a function from SD and return a value to the program

Axelman8

Member
Hey there,

I am using the Teensy 4.1 with a peddle-board to control a digital guitar modeler. The board has 18 foot-switches and 15 ST7735 tft screens.
It is working perfectly and blazing fast and now I would like to make use of the SD card sloth.

My question is:
Can I start a function from the SD card and return a value to the program?
WHY?
Because then I would only have to edit the file on the SD card instead of flashing a new version on the Teensy.

Here is an example how it works:
On the modeler there are 1023 preset slots. The "active preset" is between 9 other presets. 0-9, 10-19, 20-29 etc.
To show all 10 preset names that are in the range of the "active preset" on the ST7735 tft screens, I am using a function to get these presetnames from a list.
NOTE: The active preset is called PresetNumb in this function.

Say "activepreset" PresetNumb is 7, then PresetNumb would change to PresetNumb 0, because it is between 0 an 9 in the switch case.


Code:
void presetRange()
{
  debug("\n"); debug("\n"); debug(" * function presetRange started on Preset_range.h"); 

switch ([COLOR="#800000"]PresetNumb[/COLOR])
{
case 0 ... 9:   {PresetNumb = 0;} break; case 10 ... 19: {PresetNumb = 10;} break; case 20 ... 29: {PresetNumb = 20;} break; 
case 30 ... 39: {PresetNumb = 30;} break; case 40 ... 49: {PresetNumb = 40;} break; case 50 ... 59:{PresetNumb = 50;} break; 
case 60 ... 69: {PresetNumb = 60;} break; case 70 ... 79: {PresetNumb = 70;} break; case 80 ... 89:{PresetNumb = 80;} break; 
case 90 ... 99: {PresetNumb = 90;} break; 

case 100 ... 109: {PresetNumb = 100;} break; case 110 ... 119: {PresetNumb = 110;} break; case 120 ... 129: {PresetNumb = 120;} break; 
case 130 ... 139: {PresetNumb = 130;} break; case 140 ... 149: {PresetNumb = 140;} break; case 150 ... 159: {PresetNumb = 150;} break; 
case 160 ... 169: {PresetNumb = 160;} break; case 170 ... 179: {PresetNumb = 170;} break; case 180 ... 189: {PresetNumb = 180;} break; 
case 190 ... 199: {PresetNumb = 190;} break;  etc.......

}
     debug("\n"); debug("\n");   debug(" * function preset_Names called on preset_range.h");
     [COLOR="#800000"]preset_Names();[/COLOR]
     debug("\n"); debug("\n"); debug(" * function presetRange succesfully loaded on  Preset_range.h"); 
}


The PresetNumb value of 0 is now passed to another function called preset_Names();

->This function preset_Names(); is the file I would like to switch to the SD card.


The function returns the presetnames for each preset in the range of the PresetNumb to the program.
This works very good because now I am only using/re-using 10 global variables for all 1023 presets (PresetName0 to PresetName9)


Code:
void preset_Names(void)
{
  debug("\n"); debug(" -> Preset names are beeing read from presets.ini.h");
  switch (PresetNumb)
  {
    
case 0: 
strcpy(PresetName0,("=/GIFT OF TONE\="));
strcpy(PresetName1,("LT Brown"));
strcpy(PresetName2,("LT Filth"));
strcpy(PresetName3,("LT Clean Amps"));
strcpy(PresetName4,("Mark Day IceCream"));
strcpy(PresetName5,("Fluff Drggd UndR"));
strcpy(PresetName6,("Marco Fanton"));
strcpy(PresetName7,("Chris Traynor"));
strcpy(PresetName8,("Andy Wood tele"));
strcpy(PresetName9,("Andy Wood Lead"));

break;

case 10: 
strcpy(PresetName0,("Moke Magic"));
strcpy(PresetName1,("Austin Buddy Liks"));
strcpy(PresetName2,("Austin Buddy  Strat"));
strcpy(PresetName3,("Austin Buddy EVH"));
strcpy(PresetName4,("Austin Buddy Vaughan"));
strcpy(PresetName5,("Austin Buddy DualRecto"));
strcpy(PresetName6,("Austin Buddy Machine"));
strcpy(PresetName7,("Cooper Carter"));
strcpy(PresetName8,("Fremen Dumbler"));
strcpy(PresetName9,("Fremen one 4all"));

break;

The 10 PresetName variables can now be used to print to the tft screens.


It would be a blessing if this function could be started from the SD card.
For any help or advise, thank you in advance!


Cheers
 
My question is: Can I start a function from the SD card and return a value to the program? WHY? Because then I would only have to edit the file on the SD card instead of flashing a new version on the Teensy.

I haven't tried to understand your program in detail, but I'll comment on your question. In general, no, you can't call a function that is stored on your SD card. You could conceivably store a form of "executable code" in a file on your SD card, but it would make a lot more sense to store data on your SD. Can you think of a way to store data in a file and write a function that can read the file and take the actions defined by the data in the file?
 
Seems as though you may benefit from a configuration file type of system. Eg;
You will have a config.cfg from where the dynamic data of your presets are configured. This file will be broken in to [sections] from which an [index] section will define the lookup table of user defined presets.
eg;

configset1.cfg:

[index]
preset1 = A Fools Folly
preset2 = The Quick Fox
preset3 = etc..

[A Fools Folly]
data = English
variable = whatever
somethingelse = yes

[The Quick Fox]
again = we go
etc...


[Another One]
......
 
If you are just looking to load the set of 10 preset names for the given PresetNumb from a file, here's a quick and dirty rudimentary working example of how you could do that, to get you going, with SdFat. I have tested this and it is functional. It is not robust (no real checking of file system available, range/bounds checking on the file, etc), and can leave a trailing newline char in the loaded preset name, but hopefully it will get you started.

- You will need a text file on the SDcard with all the preset names entered in, one line per preset name. I have called it "presetNames.txt" in the below example, but it could be anything you wish.
- The variable "bufSize" needs to be as long as the longest preset name, + 1 byte. Judging by your sample code posted, you are probably defining PresetName<n> this way, anyway.
- It loads into an array rather than your example code that uses 10 individual strings
- If I interpret the function of your "switch (PresetNumb)" statement, you can replace that with a single line: PresetNumb = floor(PresetNumb / 10) * 10; eg for 215, dividing it by 10 makes it 21.5, the floor makes it 21, and * 10 brings it up to 210.

Code:
#include "SdFat.h"

SdFs sd;
FsFile presetNameFile;

uint16_t PresetNumb;
const uint16_t bufSize = 60;      //This needs to be as long as your longest preset name + 1 byte

char presetName[10][bufSize];     //Array for storing the loaded preset names

void presetRange()
{
  //This will convert your PresetNumb in the same apparent way the large switch statement does
  PresetNumb = floor(PresetNumb / 10) * 10;

  preset_Names();
}

void preset_Names() {

  //Open file
  if (!presetNameFile.open("presetNames.txt", FILE_READ)) {
    Serial.println("Error, file not found");
    return;
  };

  //Read through preset names, skipping lines until we reach the right 'set'
  uint16_t presetCount = 0;
  while (presetCount < PresetNumb) {
    presetNameFile.fgets(presetName[0], bufSize);  //Dummy storage for skipped preset name
    presetCount += 1;
  }

  //Load the next 10 lines into presetName array
  loadPresetNames();
  
  //Close file
  presetNameFile.close();

  //Print loaded preset names
  for (uint8_t i=0; i<10; i++) {
    Serial.print(presetName[i]);
  }
}

void loadPresetNames() {
  for (uint8_t i=0; i<10; i++) {
    int n = presetNameFile.fgets(presetName[i], bufSize);
  }
}

void setup() {

  Serial.begin(9600);
    while (!Serial) {
  }

  sd.begin(SdioConfig(FIFO_SDIO));

  //Initial preset number to be converted, and names loaded
  PresetNumb = 21;

  presetRange();
}

void loop() {
  //
}
 
I haven't tried to understand your program in detail, but I'll comment on your question. In general, no, you can't call a function that is stored on your SD card. You could conceivably store a form of "executable code" in a file on your SD card, but it would make a lot more sense to store data on your SD. Can you think of a way to store data in a file and write a function that can read the file and take the actions defined by the data in the file?


Hey there,

Thank you for the response. It was key to me not to try to start a function from the SD card, if it is not possible at all. Your comment saved me a lot of time.

I got a working solution with a simple adjustment in the working proces.
I followed your suggestion to only store the data on the SD card and read from it. That got me going into another direction and now got a working solution.



Solution:
A file with just text on the SD card so I can search a number, like the PresetNumb


In my program I search and select the text and strcpy that to a global variable...

Code:
void preset_Names() 
{
  int i, number;
  number = PresetNumb;
  for (i = 0; i < 10; i++) 
  {
    int search_num = number + i;
    char search_str[12];
    sprintf(search_str, "%d ", search_num);

    // Open file on SD-card
    File file = SD.open("preset_Names.txt");
    if (!file) 
    {
      Serial.println("ERROR OPENING THE FILE");
      return;
    }
      bool found = false;
      while (file.available()) 
      {
        String line = file.readStringUntil('\n');
        char *cstr = &line[0u];
        char *match = strstr(cstr, search_str);
        if (match != NULL) 
        {
          found = true;
          char *text = match + strlen(search_str);
          switch (i) 
          {
            case 0: strcpy(PresetName0, text); break;
            case 1: strcpy(PresetName1, text); break;
            case 2: strcpy(PresetName2, text); break;
            case 3: strcpy(PresetName3, text); break;
            case 4: strcpy(PresetName4, text); break;
            case 5: strcpy(PresetName5, text); break;
            case 6: strcpy(PresetName6, text); break;
            case 7: strcpy(PresetName7, text); break;
            case 8: strcpy(PresetName8, text); break;
            case 9: strcpy(PresetName9, text); break;
          }
          break;
        }
      }
      file.close();
      if (!found) break;
    }
}



Cheers
 
Ok, glad it got you going! In your above code, looks like you open, parse through potentially hundred of lines, and close the file, for each one of the 10 preset names? Seems a little inefficient, if you have control over the file format? If you create the file with the preset names in numerical order, can't you just find the first number, and then read the next 10 lines, seeing as your original code seemed to indicate they are in blocks of 10? Anyway, glad you're up and running and good luck!
 
Ok, glad it got you going! In your above code, looks like you open, parse through potentially hundred of lines, and close the file, for each one of the 10 preset names? Seems a little inefficient, if you have control over the file format? If you create the file with the preset names in numerical order, can't you just find the first number, and then read the next 10 lines, seeing as your original code seemed to indicate they are in blocks of 10? Anyway, glad you're up and running and good luck!

Hey there

Good catch. Yes it is inefficient indeed. I understand your point.

I got the presetnames working that is a great start.. Your contribution will give me some thought how to make it more efficient and faster.

It works pretty fast and screen reshesh updating is good too, but getting above the +500 presets, the screen refreshing slows down some, but still fast enough to my likings. Even with the 1000+ presets, the screen refreshing is amazing fast.

Instead of starting all over again to the next line, you are right, I should just search the first one and copy the next 9 with it.
I just looked at your code, that could do it I guess. Thank you I will try to embed it in my program. Will report back if I had any luck

Cheers 🍻
 
If you are just looking to load the set of 10 preset names for the given PresetNumb from a file, here's a quick and dirty rudimentary working example of how you could do that, to get you going, with SdFat. I have tested this and it is functional. It is not robust (no real checking of file system available, range/bounds checking on the file, etc), and can leave a trailing newline char in the loaded preset name, but hopefully it will get you started.

- You will need a text file on the SDcard with all the preset names entered in, one line per preset name. I have called it "presetNames.txt" in the below example, but it could be anything you wish.
- The variable "bufSize" needs to be as long as the longest preset name, + 1 byte. Judging by your sample code posted, you are probably defining PresetName<n> this way, anyway.
- It loads into an array rather than your example code that uses 10 individual strings
- If I interpret the function of your "switch (PresetNumb)" statement, you can replace that with a single line: PresetNumb = floor(PresetNumb / 10) * 10; eg for 215, dividing it by 10 makes it 21.5, the floor makes it 21, and * 10 brings it up to 210.


Hey there,

Thank you for your contribution for my question and many thanks for your time to write a code that is just amazing. Its working right out of the box!
This is one for in my toolbox. Your floor() code is just awesome and very clever and makes me laugh really hard about my own approach how I would find the first preset from a range. You only needed just one minor line of code were I would need 101.


I adjusted the code so it would work with my program. Its a very little adjustment, but in basics its all your code.
Again, many thanks for your contribution on helping me with this question.

#Solved


Code:
#pragma once

#include "SdFat.h"

SdFs sd;
FsFile presetNameFile;


const uint16_t bufSize = 60;      //This needs to be as long as your longest preset name + 1 byte

char presetName[10][bufSize];     //Array for storing the loaded preset names


void loadPresetNames() 
{
  for (uint8_t i=0; i<10; i++) {
    int n = presetNameFile.fgets(presetName[i], bufSize);
  }
}


void preset_Names() 
{

  //Open file
  if (!presetNameFile.open("preset_Names.txt", FILE_READ)) {
    Serial.println("Error, file not found");
    return;
  };

  //Read through preset names, skipping lines until we reach the right 'set'
  uint16_t presetCount = 0;
  while (presetCount < PresetNumb) {
    presetNameFile.fgets(presetName[0], bufSize);  //Dummy storage for skipped preset name
    presetCount += 1;
  }

  //Load the next 10 lines into presetName array
  loadPresetNames();
  
  //Close file
  presetNameFile.close();

  //Print loaded preset names
  for (uint8_t i=0; i<10; i++) 
  {
    //Serial.println(presetName[i]);
    switch (i) 
          {
            case 0: strcpy(PresetName0, presetName[i]); break;
            case 1: strcpy(PresetName1, presetName[i]); break;
            case 2: strcpy(PresetName2, presetName[i]); break;
            case 3: strcpy(PresetName3, presetName[i]); break;
            case 4: strcpy(PresetName4, presetName[i]); break;
            case 5: strcpy(PresetName5, presetName[i]); break;
            case 6: strcpy(PresetName6, presetName[i]); break;
            case 7: strcpy(PresetName7, presetName[i]); break;
            case 8: strcpy(PresetName8, presetName[i]); break;
            case 9: strcpy(PresetName9, presetName[i]); break;
          }
  }
}

void presetRange()
{
  //This will convert your PresetNumb in the same apparent way the large switch statement does
  PresetNumb = floor(PresetNumb / 10) * 10;
  preset_Names();
}


Cheers
 
You don't need to use floor if PresetNumb is an integer value (int, short, char...), all integer division is rounded towards zero.

(In future please post ENTIRE code so all variable declarations are visible.)
 
You don't need to use floor if PresetNumb is an integer value (int, short, char...), all integer division is rounded towards zero.

Hey there

Thank you for the reply. I will try the code without the floor and see what happens. Good to know this is possible

Cheers
 
Back
Top