Go Down

Topic: Frequency table on a SD card how to use in sketch (Read 1 time) previous topic - next topic

StanCh

I am building an Arduino based frequency generator with an AD9850 module. It ought to generate a set of a frequencies in the time intervals (3 minutes).
I combined a code using tutorials and an examples from an Arduino library because I am not a programmer.
A code for store a frequency set for execution is as follows:
Code: [Select]

 case 11:      // funkcja obslugi opcji detox
 {
 unsigned long freq[] = {
33408, 41984, 45568, 49152, 40000, 50816, 48640, 56320, 50368, 48064,
46528, 43264, 40640, 33408, 59520, 56832, 38912, 39168, 37888, 37376,
31129, 30822, 42434, 40960, 32071, 51609};
wykonaj("Detox", 26, freq);
 }
 break;


It consists of a frequency table and an execution of a function "wykonaj" which takes a frequency one by one, sends a comand to an AD9850 and display informations for an user. Number "26" it is a number of a frequencies inside this table.
Because a Nano (Pro Mini) modules have only a 2kB RAM I can store only about 200 frequencies for use.
It is too small number of a sets for use. So I decided to use a microSD card module to store frequency tables as a files on a SD card. So this generated a problem for me :-( about how to take a numbers from a file and write them into a table for use with a function "wykonaj".
I found an example about how to count a comma delimiter and I wrote something like that:

Code: [Select]

//funkcja odczytu liczby czestotliwosci w pliku z karty SD
int ilefreq(char Name[10], int n)
{
 card.init(SPI_HALF_SPEED, 10);
 File Preset;
 SD.begin(10);   
 Preset = SD.open(Name,FILE_READ);                           
   while (Preset.available())  //wykonuj pętlę dopókiwszystkie dane
    {                                 //nie zostaną zczytane
  if (Preset.read() == ',')
 {
 n += 1;       
 } 
   }
  Preset.close();
  return n + 1;
  }



At the return is a "n+1" because last frequency on the file at the card has no comma after.
This code works OK and returns a number of a frequencies in a file "Name".
Now I ought to read a frequencies from a file and put them into a table for next execution.
I found at this forum a thread about how to read CSV files from a SD card and tried to use an example by a fatlib16 user where we can read a CSV file into an array 5x4. I modified a code slightly to achieve a single row of a frequencies, use long type numbers instead of int type and it works OK when displaying the frequencies on a serial monitor but I don't know how to use them as a table for frequency generation. A code looks like:

Code: [Select]

int ile = 43; // number of the frequencies in a file
// 1 X ile  rray
#define ROW_DIM 1
#define COL_DIM ile

File Preset;

/*
   Read a file one field at a time.

   file - File to read.

   str - Character array for the field.

   size - Size of str array.

   delim - String containing field delimiters.

   return - length of field including terminating delimiter.

   Note, the last character of str will not be a delimiter if
   a read error occurs, the field is too long, or the file
   does not end with a delimiter.  Consider this an error
   if not at end-of-file.

*/
// funkcja okreslajaca dlugosc rekordu

size_t readField(File* file, char* str, size_t size, char* delim) {
  char ch;
  size_t n = 0;
  while ((n + 1) < size && file->read(&ch, 1) == 1) {
    // Delete CR.
    if (ch == '\r') {
      continue;
    }
    str[n++] = ch;
    if (strchr(delim, ch)) {
      break;
    }
  }
  str[n] = '\0';
  return n;
}

//------------------------------------------------------------------------------
#define errorHalt(msg) {Serial.println(F(msg)); while(1);}
//------------------------------------------------------------------------------
void setup() {

 
  Serial.begin(9600);
lcd.begin();
// oczyt danych z karty do tablicy
  // Initialize the SD.
  if (!SD.begin(CS_PIN)) {
    errorHalt("begin failed");
  }
  // Create or open the file.
  Preset = SD.open("Tinitus", FILE_READ); // file Tinitus for test
  if (!Preset) {
    errorHalt("open failed");
  }
  // Rewind file so test data is not appended.
 
  // Array for data.
  long array[ROW_DIM][COL_DIM];
  int i = 0;     // First array index.
  int j = 0;     // Second array index
  size_t n;      // Length of returned field with delimiter.
  char str[10];  // Must hold longest field with delimiter and zero byte.
  char *ptr;     // Test for valid field.

  // Read the file and store the data.

  for (i = 0; i < ROW_DIM; i++) {
    for (j = 0; j < COL_DIM; j++) {
      n = readField(&Preset, str, sizeof(str), ",\n");
      if (n == 0) {
        errorHalt("Too few lines");
      }
      array[i][j] = strtoul(str, &ptr, 10);
      if (ptr == str) {
        errorHalt("bad number");
      }
      if (j < (COL_DIM - 1) && str[n - 1] != ',') {
        errorHalt("line with too few fields");
      }
    }
    // Allow missing endl at eof.
    if (str[n - 1] != '\n' && Preset.available()) {
      errorHalt("missing endl");
    }
  }

  // Print the array.
  for (i = 0; i < ROW_DIM; i++) {
    for (j = 0; j < COL_DIM; j++) {
      if (j) {
        Serial.print(',');
        lcd.print(',');
      }
      Serial.print(array[i][j]);
      lcd.print(array[i][j]);
    }
    Serial.println();
  }
  Serial.println("Done");
  Preset.close();
}



This code works OK writing to a serial monitor display a list of a frequencies stored in a Tinitus file with 43 frequencies inside.
But I still don't know how to use this examples to achieve a table of a frequencies for use by a next function (wykonaj) similar to that which I put into a code manually.
Please someone help me to  resolve this problem.
Arduino Mega256,Nano,Pro Mini
It's very hard to start with.

PaulS

Quote
But I still don't know how to use this examples to achieve a table of a frequencies for use by a next function (wykonaj) similar to that which I put into a code manually.
What the readField() function does is read ONE of the comma-separated values from a field, and return the length of the string (which is in the str argument).

You would then need to call atoi() or strtoul() to convert the string to a number, which you could then store in an array.

Without seeing the wykonaj() function (or having a clue what wykonaj means), that's the best I can do.
The art of getting good answers lies in asking good questions.

StanCh

Thank You for an answer!
A function "strtoul" is used in a code by fat16lib (modified by me) so at the end of this code we have a numbers (frequencies) written to an array printed at the serial monitor window.
function wykonaj mean execute and looks like below:

Code: [Select]

void wykonaj(char Name[10], int n, unsigned long freq[])
{
   for (int i=0; i<n; i++)
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(Name);
  lcd.print(" ");
  lcd.print(n-i);
  digitalWrite(ledpin, HIGH);
  dds.setFrequency(freq[i]);
  lcd.setCursor(0, 1);
  lcd.print(freq[i]);
  lcd.print(" Hz");
  delay(180000);
  if (i == n);
  {
    digitalWrite(ledpin, LOW);
    dds.init();
    lcd.clear();
    lcd.print("Koniec");
  }
}
}



 where "Name" is a name of a frequency set, "n" is the number of the frequencies to generate and "unsigned long freq[]" is a table with the frequencies to generate.
A problem is how to take a frequencies (numbers) from a fatlib16 code and import them into a table
"unsigned log freq[]" for execution?
I attach a display photo when working. A number right to a Name is a number of the frequencies left to end.
Maybe a solution is simple but I cannot see it.
Arduino Mega256,Nano,Pro Mini
It's very hard to start with.

PaulS

Quote
A problem is how to take a frequencies (numbers) from a fatlib16 code and import them into a table
"unsigned log freq[]" for execution?
What IS the problem?

Code: [Select]
unsigned long freq[10];
int index = 0;

   char str[10];
   char *delims = ",";

   size_t len = readField(someFile, str, fileSize, delims);
   if(len > 0)
   {
      freq[index++] = strtoul(str, NULL, 10);
   }
The art of getting good answers lies in asking good questions.

StanCh

#4
Sep 20, 2017, 02:38 pm Last Edit: Sep 20, 2017, 02:46 pm by StanCh
Hi!
For expierienced software engineer that is not a problem but I don't know C++ language and can have a problem with anything. I can slightly modify a code when I understand what it does. So back to Your help code. I think that it is not a "standalone" code but a replacement of a part of a fat16lib code. I must replace a part of a fat16lib code with Your version instead of printing this numbers at the serial monitor?
I must think about how to acomodate fat16lib and Your code to make a function with name "readfile"  for example because it may be used many times with change a frequency set to generate. What type of a return value it must give at the end? Array, unsigned long?
Sorry for inconvenience but I am a very beginner in software.
I plan to make it simple so:
-count a number of a frequencies in a given file on a SD card (function)
-read CSV data from a file, convert them into a unsigned long freq[] table (function)
-execute generation using a values from a table and a number of frequencies in a file (function)
I have first and last functions ready for use but must workout a second. This will be performed for any file chosen from a list of a stored sets at the SD card.
Arduino Mega256,Nano,Pro Mini
It's very hard to start with.

PaulS

Quote
I must replace a part of a fat16lib code with Your version instead of printing this numbers at the serial monitor?
Yes.

It would be possible to alter your code, if you posted more than snippets.
The art of getting good answers lies in asking good questions.

StanCh

#6
Sep 21, 2017, 06:14 pm Last Edit: Oct 06, 2017, 09:31 am by StanCh
Hi Paul!
I try to save a space and post only a code I have a problem with.
I tried to build a function for reading frequency list from a file using Your code but next function "wykonaj" (at the provious post) takes only a first frequency from a list and next  frequency is "0".
This is this function:

Code: [Select]

//funkcja odczytu pliku z karty SD function for reading a frequency set from a SD card
unsigned long odczytpliku(char Name[10])
{
 card.init(SPI_HALF_SPEED, 10);
 SD.begin(10);   
  File Preset;
 Preset = SD.open(Name,FILE_READ);                           

// replacement instead of printing code from Paul
//unsigned long freq[43]; I must commented this line because of a compiler error
int index = 0;

   char str[10];
   char *delims = ",";
   size_t len = readField(&Preset, str, sizeof(str), ",\n");
   if(len > 0)
   {
      freq[index++] = strtoul(str, NULL, 10);
   }

   Preset.close(); 
  return freq[ile];



I am calling this function that way:

Code: [Select]

  freq[ile] = {odczytpliku("Tinitus")};



where "ile" is a number of the frequencies in a file "Tinitus"
When I run this 3 functions described earlier the last function "wykonaj" takes only a first frequency from a list and next numbers are "0". I see this on a 16x2 display.
For testig purpose i wrote a sketch which uses all 3 functions together and displays a results at 16x2 display. Many lines are commented because I tried other options too.
Here is the code:

Code: [Select]

#include <DDS.h>
// Instantiate the DDS...
DDS dds(5, 4, 3, 2);
//include LCD library
#include <LiquidCrystal_I2C.h>
#include <Wire.h>
// include the SD library:
#include <SPI.h>
#include <SD.h>
// set up variables using the SD utility library functions:
Sd2Card card;
const int chipSelect = 10;
unsigned long freq[100];
//inicjalizacja wyswietlacza
LiquidCrystal_I2C lcd(0x3F,16,2);
int ledpin = 13;
int ile;
//------
// funkcja okreslajaca dlugosc rekordu
size_t readField(File* file, char* str, size_t size, char* delim) {
  char ch;
  size_t n = 0;
  while ((n + 1) < size && file->read(&ch, 1) == 1) {
    // Delete CR.
    if (ch == '\r') {
      continue;
    }
    str[n++] = ch;
    if (strchr(delim, ch)) {
      break;
    }
  }
  str[n] = '\0';
  return n;
}
//---------
//------
//funkcja generowania presetow
void wykonaj(char Name[10], int ile, long freq[])
{
   for (int i=0; i<ile; i++)
{
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(Name);
  lcd.print(" ");
  lcd.print(ile-i);
  digitalWrite(ledpin, HIGH);
  dds.setFrequency(freq[i]);
  lcd.setCursor(0, 1);
  lcd.print(freq[i]);
  lcd.print(" Hz");
  delay(180000);
  if (i == ile);
  {
    digitalWrite(ledpin, LOW);
    dds.init();
    lcd.clear();
    lcd.print("Koniec");
  }
}
}
//-------
//funkcja odczytu liczby czestotliwosci w pliku z karty SD
int ilefreq(char Name[10])
{
  int n=0;
 card.init(SPI_HALF_SPEED, 10);
 File Preset;
 SD.begin(10);   
 Preset = SD.open(Name,FILE_READ);                           
   while (Preset.available())  //wykonuj pętlę dopókiwszystkie dane
    {                                 //nie zostaną zczytane
  if (Preset.read() == ',')
 {
 n += 1;       
 } 
   }
  Preset.close();
  return n + 1;
  }
//----------------------------------------------
//funkcja odczytu pliku z karty SD
unsigned long odczytpliku(char Name[10])
{
 card.init(SPI_HALF_SPEED, 10);
 SD.begin(10);   
  File Preset;
 Preset = SD.open(Name,FILE_READ);                           
 lcd.print(Name);
int index = 0;
   char str[10];
   char *delims = ",";
 size_t len = readField(&Preset, str, sizeof(str), ",\n");
   if(len > 0)
   {
      freq[index++] = strtoul(str, NULL, 10);
   }
   Preset.close(); 
  return freq[ile];
}
//-------------------------------------------
void setup()
{
//inicjalizacja wyswietlacza LCD
LiquidCrystal_I2C lcd(0x3F,16,2);
  Wire.begin();
  lcd.begin();
  lcd.backlight();
 ile = ilefreq("Tinitus");
  lcd.clear();
  lcd.print(ile);
  delay(5000);
  lcd.clear();   
  freq[ile] = {odczytpliku("Tinitus")};
  lcd.clear();
  lcd.write(freq[1]);
  delay(5000);
  wykonaj("Tinitus", ile, freq);
}

void loop(void) {

}


Maybe You can correct my error and make it work?
Arduino Mega256,Nano,Pro Mini
It's very hard to start with.

PaulS

You are writing to a global array using an index that is local to the function. Every time the function is called, index is 0, so, every time the function is called, it writes to the same place in the array.

Make index global, too.
The art of getting good answers lies in asking good questions.

StanCh

#8
Sep 22, 2017, 08:56 am Last Edit: Sep 22, 2017, 09:29 am by StanCh
I made this variable global and this works the same way, read only a first number from a list. My error must be at the other place inside a code.
Maybe I am not clear with my words but I want to read all the frequencies from a file into a table in a memory and then use them by a next function.


Arduino Mega256,Nano,Pro Mini
It's very hard to start with.

PaulS

Quote
Maybe I am not clear with my words but I want to read all the frequencies from a file into a table in a memory and then use them by a next function.
So, odczytpliku() needs a loop that calls readField() over and over, until readField() returns 0, meaning no more data on the record, and another (outer) loop that reads all the records in the file.

Do not post code without commented out code. Keep that for your records. Delete commented out code before posting.
The art of getting good answers lies in asking good questions.

StanCh

I am trying to follow Your instructions and made such a change in a sketch (function) which ought to fill a table with a numbers read from a CSV file at the SD card.
That's it:

Code: [Select]

//funkcja odczytu pliku z karty SD
unsigned long odczytpliku(char Name[10], byte ile)
{
 card.init(SPI_HALF_SPEED, chipSelect);
 SD.begin(chipSelect);   
  File Preset;
  Preset = SD.open(Name,FILE_READ);
   Preset.seek(0);     // added but don't know is it necessary                     
 while (Preset.available())
 {
  char str[10];
 size_t len = readField(&Preset, str, sizeof(str), ",\n");
   if(len > 0)
   {
     for (byte index=0; index<ile; index++)
     {
      freq[index] = strtoul(str, NULL, 10);
     }
   }
 }
   Preset.close(); 
  return freq[index];
}


This function is compiled without errors but I fired a hardware (Nano and Ad9850 module) so I cannot test it for a while when I rebuild a hardware.
Is it this what You told me to change?
Function is called this way:

Code: [Select]

  freq[ile] = {odczytpliku("Tinitus", ile)};


Thanks in advance!
Arduino Mega256,Nano,Pro Mini
It's very hard to start with.

PaulS

I don't understand why that function populates a global array, and then returns the last element in the array.

I don't understand why that function puts the SAME value in many positions in the array.

The function should NOT take a second argument. It already reads the entire file. It SHOULD set index to 0 at the beginning, and should return nothing.
The art of getting good answers lies in asking good questions.

StanCh

I don't understand those too :-). The last display I remember before a fire was the last number from the list displayed on a LCD. Probably they sold me a 3,3V version of a SD card module among a 5V ones and it took too much current and fired a voltage regulator on a Nano module.
A curiosity is that before I went to a dinner I wrote a while loop and a for loop and run them. After a dinner I started to clean a code from a commented lines and deleted something important from this function because when I looked at the running display I stated that all is OK, numbers decreased and frequencies were read from a table. I don't remember what I deleted with this cleaning operation and not done a backup copy.
SD card reader modules aren't written for which voltage they are made and I think I must check a current at the 5V to be shure for what voltage they are. One I tested was pretty hot when connected to a 5V DC.
Inside this function was something important I deleted by an accident. I don't know what it was unfortunately and must look for once more when I will rebuild a hardware.
Thanks in advance!

Arduino Mega256,Nano,Pro Mini
It's very hard to start with.

Go Up