SD Card y biblioteca LedControl

Hola! Soy nuevo en arduino y viejo en programación. Quiero hacer un proyecto donde se lee un registro que contiene números en un archivo de texto en una tarjeta SD que tiene las conexiones:
** MISO - pin 12
** MOSI - pin 11
** CLK - pin 13
** CS - pin 10
La lógica funciona muy bien hasta que incluí el código para manejar un panel max7219 con las siguientes conexiones:

#include "LedControl.h"
const int DATAIN = 7; // PIN 7
const int CLK=6;      // PIN 6      
const int LOAD=5;     // PIN 5
int maxMatrix=2;
//LedControl lc=LedControl(DATAIN,CLK,LOAD,maxMatrix);

Si observan, la creación del objeto lc esta en comentarios. Así funciona muy bien la lógica para leer y cargar el archivo de números. Si la habilito, la lógica se comporta extraña. Solo carga el primero de los números.
Este es el código completo:

/*-- Librerías para el manejo de SD Card --*/
#include <SD.h>
#include <SPI.h>

/*-- Conexiones de la tarjeta SD --*/
int CS_PIN = 10;
/*--
    MISO -> PIN 12
    MOSI -> PIN 11
    SCK  -> PIN 13
    CS   -> PIN 10
--*/

/*-- Declaraciones para cargar las ciudades desde el archivo default 00.txt --*/
File file;             // define un objeto file
String fName="00.txt"; // nombre default del archivo
const int MAX_CIUDADES=512; // Máximo número de ciudaes que se pueden manejar
int Ciudades[MAX_CIUDADES]; // Arreglo que contiene los números de ciudad
int nCiudades;              // Cantidad de ciudades cargadas nCiudades <=MAX_CIUDADES

int parseCiudades(String datos) {
  int indice = 0;
  int i = 0;
  int ciudad;
  while (datos.length() > 0 && i < MAX_CIUDADES) {
    ciudad = datos.toInt();  // Convierte la parte inicial de la cadena a un entero
    Serial.println(ciudad);
    Ciudades[i++] = ciudad;  // Almacena la ciudad en el arreglo
    indice = datos.indexOf(',');  // Busca la próxima coma en la cadena
    if (indice != -1) {
      datos = datos.substring(indice + 1);  // Recorta la cadena desde la posición siguiente a la coma
    } else {
      datos = "";  // Si no hay más comas, termina el bucle
    }
  }
  return i;  // Retorna la cantidad de ciudades leídas
}

void setup()
{ Serial.begin(9600);

  initializeSD();
  openFile("00.txt");
  String cdStr=readBulk();
  Serial.println("----------");
  Serial.println("String ciudades = "+cdStr);
  nCiudades=parseCiudades(cdStr);
  Serial.print("Total de ciudades cargadas "); Serial.println(nCiudades);
}

/*-- Declaraciones para configuar matrices LED --*/
#include "LedControl.h"
const int DATAIN = 7; // PIN 7
const int CLK=6;      // PIN 6      
const int LOAD=5;     // PIN 5
int maxMatrix=2;
//LedControl lc=LedControl(DATAIN,CLK,LOAD,maxMatrix);
const int retardo = 500;// Contiene la cantidad de milisegundos de suspensión para producir parpadeo

void loop()
{
}


/*-- Rutinas para el manejo de la tarjeta SD --*/
void initializeSD()
{ Serial.println("Initializing SD card...");
  pinMode(CS_PIN, OUTPUT);
  if (SD.begin()){Serial.println("SD card is ready to use.");} 
  else { Serial.println("SD card initialization failed");
         return;}
}

int createFile(char filename[]){
  file = SD.open(filename, FILE_WRITE);
  if (file){
    Serial.println("File created successfully.");
    return 1;} 
  else {Serial.println("Error while creating file.");
        return 0;}
}

int writeToFile(char text[])
{ if (file)
  { file.println(text);
    Serial.println("Writing to file: ");
    Serial.println(text);
    return 1;} 
  else {Serial.println("Couldn't write to file");
        return 0;}
}

void closeFile()
{ if (file){file.close();
            Serial.println("File closed");}
}

int openFile(char filename[])
{ file = SD.open(filename);
  if (file) { Serial.println("File opened with success!");
              return 1; } 
  else { Serial.println("Error opening file...");
         return 0; }
}

String readLine()
{ String received = "";
  char ch;
  while (file.available())
  { ch = file.read();
    if (ch == '\n'){ return String(received); }
    else {received += ch;}
  }
  return "";
}

String readBulk()
{ String received = "";
  char ch;
  while (file.available())
  { ch = file.read();
    received += ch;}
  received.replace("\r\n",",");
  
  ch = received.charAt(received.length() - 1); // Obtener el último carácter del String
  if (ch == ',') {
     received.remove(received.length()-1);} // Verificar si el último carácter es una coma se remueve
  return String(received); 
}

¿Interfiere la librería LedControl.h con las librerías SD.h y SPI.h?
¿Alguien me puede orientar?

Moderador
Lineas editadas porque toda línea de código debe ir con etiquetas, aunque solo sea 1.

He trasladado su tema de una categoría de idioma inglés del foro a la categoría International > Español @quijotemx.

En adelante por favor usar la categoría apropiada a la lengua en que queráis publicar. Esto es importante para el uso responsable del foro, y esta explicado aquí la guía "How to get the best out of this forum".
Este guía contiene mucha información útil. Por favor leer.

De antemano, muchas gracias por cooperar.

¿Qué placa estás usando? ¿UNO, Nano?

Yo pienso que lo más probable es que sea un problema de memoria RAM.

Ocupas 1K solo para el array Ciudades, lo que deja disponibles unos 600 a 700 bytes (porque alrededor 400 bytes necesita el micro para trabajar).
Tienes al menos otros 300 bytes en las cadenas literales que envías por serial (deberías usar la macro F() para que se guarden en flash), te quedan disponibles poco más de 300 bytes, con suerte.
Y las strings son como pacmans para la memoria.

Gracias por la respuesta. Se trata de una placa ELEGOO Arduino UNO R3.
He hecho pruebas con difrerentes valores de max_Ciudades reduciendo su valor con los siguientes resultados tras la compilación:
Sin instanciar LedControl
Valor max_ciudades memoria del sketch memoria variables globales
10 13384 1062
100 13384 1062
512 13384 1062
Instanciando LedControl
Valor max_ciudades memoria del sketch memoria variables globales
10 13936 1150
100 13926 1150
512 13926 1150

por los números supondría que no es un problema de memoria, pero tengo muy poca experiencia en programación de arduino, así que puedo estar equivocado. En cuanto a los strings y el uso de la macro F(), dónde puedo documentarme?

Hola! como cualquier novato, estaba equivocado. Busqué la documentación para usar la macro F() en los strings constantes y avance. Ya puedo instanciar un objeto LedControl y cargar las ciudades contenidas en el archivo.
Pero, al parecer continúo (ya me convencí) teniendo un problema de memoria.
Los resultados de la compilación con la siguiente lógica en la función loop()
son 14334 bytes para el programa y 964 para las variables globales. La lógica funciona bien. Lee las 66 ciudades y enciende los leds como se espera.

void loop()
{
  //  nCiudades=random(1,513);
  //  for (int i=0; i<nCiudades;i++){
  //   Ciudades[i]=random(1,513);
  //   }
  //  for (int i=0;i<nCiudades;i++){
  //   ciudad=abs(Ciudades[i]);
  //   matriz=(ciudad-1)/64;
  //   fila=(ciudad-matriz*64-1)/8;
  //   columna=(ciudad-matriz*64-1)-fila*8;
  //   lc.setLed(matriz, fila, columna, true);
  // }
  // delay(retardo);
  // for (int i=0;  i < maxMatrix; i++){
  //   lc.clearDisplay(i);  /* and clear the display                 */
  // }
  // delay(retardo);
  for (int matriz=0;matriz<8;matriz++){
    for (int i=0;i<8;i++){
        lc.setLed(matriz, i, i, true);
        delay(10);
      }
    for (int j=6;j>0;j--){
        lc.setLed(matriz, 7, j, true);
        delay(10);
      }
  }


}

Si activo las instrucciones que están como comentario, los resultados de la compilación son los siguientes: memoria del programa 14612 y 1988 para variables globales. La lógica no lee las ciudades ni los leds se enciende.

Voy a quitar las funciones que no uso. ¿Qué otra cosa me sugieres?
¿Qué alternativas tengo?

Gracias por la ayuda hasta ahora. Ha sido muy valiosa.

Deja de pelear con un UNO. Elige un micro con mas RAM.
Tienes muchas opciones, cualquier Nano33 x ejemplo, los ESP32 desde ya, pero hay muchas opciones.
Pelear con algo con paca RAM no tiene sentido en estos dias.

Otra cosa, porque no usas la librería con los mismos pines tanto para SD como para el MAX7219?

Gracias. Buscaré un arduino de mayor capacidad. Otra opción es prescindir de la lectura de los números de las ciudades desde una SD, lo que daría menos versatilidad al programa, pero como inicio estará bien. De todas formas, quiero entender que está pasando. Ya estoy usando la función F() para strings constantes y logré que el código pueda leer el archivo de ciudades aún teniendo instanciado el objeto LedControl. El problema aparece cuando habilito las instrucciones para encender los leds. Estas instrucciones no son mas que un ciclo for, 3 asignaciones para decodificar el número de ciudad en matriz, fila y columna, y la instrucción de setLed. Sin ellas, la memoria usada por variables globales ronda en 950 bytes. Si las habilito se dispara a 1998. No me parece racional el comportamiento. ¿Alguna idea de porqué sucede?
Gracias ...

Estás malinterpretando lo que informa el compilador.
Seguramente en algún lado está la explicación de como calcula la RAM asignada en la compilación, pero como yo no la tengo solo te muestro algunos ejemplos.

Subo unos códigos (para UNO/Nano) que se puede decir que no tienen sentido, pero observa los resultados.

Ejemplo 1:

byte x; // declara una variable que ocupa 1 byte

void setup() {
}

void loop() {
}

Reporte del ejempo 1:

El Sketch usa 444 bytes (1%) del espacio de almacenamiento de programa. El máximo es 30720 bytes.
Las variables Globales usan 9 bytes (0%) de la memoria dinámica, dejando 2039 bytes para las variables locales. El máximo es 2048 bytes.

Ejemplo 2:

byte x[1024];  // un array que ocupa 1 Kbyte

void setup() {
}

void loop() {
}

Reporte del ejemplo 2:

El Sketch usa 444 bytes (1%) del espacio de almacenamiento de programa. El máximo es 30720 bytes.
Las variables Globales usan 9 bytes (0%) de la memoria dinámica, dejando 2039 bytes para las variables locales. El máximo es 2048 bytes.

Ejemplo 3:

byte x[10240];  // un array que ocupa 10 Kbytes!!!

void setup() {
}

void loop() {
}

Reporte del ejemplo 3:

El Sketch usa 444 bytes (1%) del espacio de almacenamiento de programa. El máximo es 30720 bytes.
Las variables Globales usan 9 bytes (0%) de la memoria dinámica, dejando 2039 bytes para las variables locales. El máximo es 2048 bytes.

¿Te das cuenta que hay algo que no está bien?
Siempre informa que las variables globales ocupan 9 bytes, incluso cuando el código crea un array que excede 4 veces el tamaño de la RAM del microcontrolador.

Lo que ocurre es que la RAM se asigna y ocupa en tiempo de ejecución, no en la compilación.

Creo que el informe indica la cantidad de memoria que el micro necesita para ejecutar ese código, no la que el código utiliza.


Ahora pongo los mismos ejemplos pero usando una instrucción que (aunque se usa en otro contexto) fuerza la asignación de la memoria, o al menos se refleja correctamente en el informe.

Ejemplo 4:

volatile byte x; // declara una variable que ocupa 1 byte

void setup() {
}

void loop() {
}

Reporte del ejempo 4:

El Sketch usa 444 bytes (1%) del espacio de almacenamiento de programa. El máximo es 30720 bytes.
Las variables Globales usan 10 bytes (0%) de la memoria dinámica, dejando 2039 bytes para las variables locales. El máximo es 2048 bytes.

Nota que ahora informa 10 bytes, o sea 9 que usa el micro para poder ejecutar el código más 1 byte de la variable.


Ejemplo 5:

volatile byte x[1024];  // un array que ocupa 1 Kbyte

void setup() {
}

void loop() {
}

Reporte del ejemplo 5:

El Sketch usa 444 bytes (1%) del espacio de almacenamiento de programa. El máximo es 30720 bytes.
Las variables Globales usan 1033 bytes (50%) de la memoria dinámica, dejando 1015 bytes para las variables locales. El máximo es 2048 bytes.

Fíjate que nuevamente la memoria ocupada es 9 bytes más los, ahora, 1024 bytes del array, total 1033 bytes.


Ejemplo 6:

volatile byte x[10240];  // un array que ocupa 10 Kbytes!!!

void setup() {
}

void loop() {
}

Reporte del ejemplo 6:

El Sketch usa 444 bytes (1%) del espacio de almacenamiento de programa. El máximo es 30720 bytes.
data section exceeds available space in board
Las variables Globales usan 10249 bytes (500%) de la memoria dinámica, dejando -8201 bytes para las variables locales. El máximo es 2048 bytes.

Not enough memory; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing your footprint.
Error compilando para la tarjeta Arduino Nano.

Fíjate que otra vez informa 9 bytes más la longitud del array (10 Kbytes), salvo que ahora sí el compilador "entendió" que el array no cabe en memoria y que le faltarían 8201 bytes.

En resumen, no te fíes de la cantidad de memoria RAM que informa la compilación, siempre ten en cuenta la capacidad del micro a la hora de programar.

Por otro lado, puedes usar MemoryFree para conocer la memoria disponible en diferentes secciones de tu código, pero siempre en tiempo de ejecución.

Muchas gracias. Tengo una semana con un kit de principiante en arduino. El proyecto que tengo en mente es iluminar con fibra óptica las ciudades de un mapa mundi que he visitado. El hardware es mínimo. Un arreglo de 5 matrices led 8x8 max7219 en cascada. La lógica es simple. Convertir el numero secuencial de ciudad en la combinación matriz, fila, columna y dar la directiva para encender con setLed.
El uso de la SD Card es para dar mayor funcionalidad al código al tener la posibilidad de cargar las ciudades visitadas desde un archivo. De esta forma bastaría enviar por bluetooth (:scream: ¡otro módulo!) el número de archivo deseado. Continuaré con sin la SD Card. De todas formas, la lógica y la disposición de fibras opticas son reutilizables.

Revisaré con detalle los códigos que haz enviado. Muchas gracias por las sugerencias.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.