Go Down

Topic: Enviar datos al monitor serial con SD  (Read 3510 times) previous topic - next topic

Lucario448

salvo que quieras guardarlas en una especie de buffer y mostrar tantos nombres seguidos como pulsaciones.
Es uno de los pocos casos donde la interrupción sí facilitaría la implementación.

Sólo que en vez de ser un buffer como tal, sería un contador que incrementa su valor al dispararse la ISR (productor), y decrementa al accionarse lo del contacto (consumidor).
Preferiblemente una variable de 8 bits ya que las operaciones de incrementación sobre un único byte, se realizan en una sola instrucción máquina; por lo tanto, es una "operación atómica" y no tendrá problemas de condición de carrera (interrupción que puede acabar en un valor no determinístico y/o incorrecto).

CaleBit

Bueno al menos no estaba tan equivocado, pero suena muy complicado  :smiley-confuse:, me gusta la idea la voy a estar masticando, agradezco tu apreciable colaboración. Gracias brother

noter

#32
Apr 22, 2018, 12:58 pm Last Edit: Apr 22, 2018, 01:10 pm by noter
Como te dije, tengo problemas para compilar con la librería liquidcrystal, así que no sé ni si compila bien esto, pero prueba a ver. Esto sería sin utilizar interrupciones (eliminando todos los delay).
Code: [Select]


#include <SD.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(10, 9, 8, 7, 6, 5);
File myFile;

const byte VO = 3;
const byte chipSelect = 4;
const byte Coin = 2;

unsigned long millis_inicio=0, millis_espera=0;
bool demo=true; // si estamos en modo demo (o mostrando registro)
int sigFase=0; // siguiente fase de la demo a ejecutar
int pulsaciones = 0; // pulsaciones pendientes de ejecutar en modo registro

bool estadoAnterior = LOW;
char nom[17]; // buffer de almacenamiento temporal
char tel[17]; // buffer de almacenamiento temporal

void setup() {

  pinMode (VO, OUTPUT);
  analogWrite (VO, 5);
  pinMode(Coin, INPUT_PULLUP);

  lcd.begin(16, 2);
  Serial.begin (9600);

  lcd.print (" COMPROBANDO");
  delay(1000);

  lcd.setCursor (0, 1);
  lcd.print ("   HARDWARE...");
  delay (5000);
  lcd.clear ();
  delay (1000);

  Serial.println ("Inicializando SD card...");
  pinMode (chipSelect, OUTPUT);
  lcd.print ("Inicializando SD");
  delay (2000);
  lcd.clear();

  if (!SD.begin(chipSelect)) {
    Serial.println ("Fallo lectura de tarjeta SD");
    lcd.print("ERROR al iniciar SD");
    delay (1000);

    lcd.setCursor (0, 1);
    lcd.print ("Inserta la SD");
    delay (5000);
    lcd.clear ();
    delay (1000);
    return;

  }

  Serial.println ("SD inicializada correctamente");
  lcd.setCursor (0, 0);
  lcd.print("BOOT exitoso...");
  delay (2000);
  lcd.clear ();

  lcd.print(" Bienvenidos a: ");
  delay(2000);
  lcd.setCursor(0, 1);
  lcd.print("ARDUINO");
  delay(7000);
  lcd.clear();
  delay(1000);

  lcd.print(" APRENDE");
  delay(1000);
  lcd.setCursor(0, 0);

  lcd.setCursor(0, 1);
  lcd.print("CADA DIA");
  delay(7000);
  lcd.clear();
  delay(1000);

  lcd.print("*NO ESPERES MAS*");
  delay(2000);

  lcd.setCursor(0, 1);
  lcd.print("!!!!!!");
  delay(5000);
  lcd.clear();
  delay(900);

  myFile = SD.open ("Contactos.txt"); // ¿Seguro que abre con ese nombre? Según mis "cálculos", al ser uno que no cumple con el formato 8.3; se deberá referenciar por "CONTAC~1.TXT"
  if (myFile) {
    lcd.print ("NOMBRE:");
    lcd.setCursor (0, 1);
    lcd.print ("TEL:");
  } else lcd.print ("No se pudo abrir");
}

void loop() {

  if (digitalRead(Coin) != estadoAnterior) {
    estadoAnterior = !estadoAnterior;
    if (estadoAnterior == LOW) {
      if (demo) {
        demo = false;
        millis_espera=0; // si estábamos en ciclo demo, salimos y hacemos "resume"
      }
      pulsaciones++;
    }
  }

  if ( (millis() - millis_inicio) >= millis_espera ) {
    if (demo) {
      // Aquí tratamos la demo
      switch (sigFase){
        case 0:
          Serial.println("Bienveniddos a:");
          millis_espera = 2000;
        break;
        case 1:
          Serial.println("Arduino.");
          millis_espera = 7000;
        break;
        case 2: // Aprovechamos para poner todos los casos en los que hay lo mismo (borrado de pantalla y espera 1000)
        case 5:
        case 8:
          Serial.println("(borrado pantalla)");
          millis_espera = 1000;
        break;
        case 3:
          Serial.println("Aprender a");
          //millis_espera = 1000; // no es necesrio , porque ya es 1000 desde el case anterior.       
        break;
        case 4:
          Serial.println("Programar");
          millis_espera = 7000;
        break;
        // caso 5 igual que 2
        case 6:
          Serial.println("Bienvenidos");
          millis_espera = 2000;
        break;
        case 7:
          Serial.println("¡¡¡Todos!!!");
          millis_espera = 5000;
        break;
        // caso 8 igual que 2 y 5
      }
      sigFase++;
      if (sigFase > 8) sigFase=0;
    }
    else {
      // modo no demo, o muestra de registro
      if (!pulsaciones) {
        demo = true; // si hemos llegado a cero pulsaciones, volvemos a modo mdemo
        return; // y salimos del loop para que reentre inmediatamente en modo demo
      }
      // si hay pulsaciones pendientes, ejecutamos y descontamos
      pulsaciones--;
      if (myFile) {
        if (myFile.available()) {
          nom[myFile.readBytesUntil(',', nom, 16)] = '\0';
          tel[myFile.readBytesUntil('\n', tel, 16)] = '\0';
          Serial.println(nom);
          Serial.println(tel);
          lcd.clear();
          lcd.print ("NOMBRE:"); // entiendo que este paso va aquí, no tras el delay
          lcd.print (nom);
          lcd.setCursor (0, 1);
          lcd.print ("TEL:"); // entiendo que este paso va aquí, no tras el delay
          lcd.print (tel);
        } else {
          Serial.println ("Final del Archivo");
          myFile.seek (0L);
          lcd.clear ();
          lcd.print ("Final del Archivo");
        }
      } else {
        lcd.clear();
        lcd.print ("ERROR: no hay SD");
        lcd.setCursor (0, 1);
        lcd.print ("o archivo no exs");
      }
      millis_espera = 5000;
    }
    millis_inicio = millis(); // tanto en demo como tras muestra de registro
  }
}


Si aun así hubiera alguna pérdida de pulsación, el cambio para utilizar interrupción sería mínimo. Intentemos primero sin interrupciones. Dime si compila y en qué falla.

CaleBit

Hola noter, si compiló el programa pero me manda un error al cargar el sketch... "poca memoria disponible, se pueden producir problemas de estabilidad",  y también al tener cargado el programa muestra bien el inicio pero en el demo esta corriendo muy rápido que ni se ve lo que está pasando...

noter

Code: [Select]

#include <SD.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(10, 9, 8, 7, 6, 5);
File myFile;

const byte VO = 3;
const byte chipSelect = 4;
const byte Coin = 2;

unsigned long millis_inicio=0, millis_espera=0;
bool demo=true; // si estamos en modo demo (o mostrando registro)
int sigFase=0; // siguiente fase de la demo a ejecutar
int pulsaciones = 0; // pulsaciones pendientes de ejecutar en modo registro

bool estadoAnterior = LOW;
char nom[17]; // buffer de almacenamiento temporal
char tel[17]; // buffer de almacenamiento temporal

void setup() {

  pinMode (VO, OUTPUT);
  analogWrite (VO, 5);
  pinMode(Coin, INPUT_PULLUP);

  lcd.begin(16, 2);
  Serial.begin (9600);

  lcd.print (" COMPROBANDO");
  delay(1000);

  lcd.setCursor (0, 1);
  lcd.print ("   HARDWARE...");
  delay (5000);
  lcd.clear ();
  delay (1000);

  Serial.println ("Inicializando SD card...");
  pinMode (chipSelect, OUTPUT);
  lcd.print ("Inicializando SD");
  delay (2000);
  lcd.clear();

  if (!SD.begin(chipSelect)) {
    Serial.println ("Fallo lectura de tarjeta SD");
    lcd.print("ERROR al iniciar SD");
    delay (1000);

    lcd.setCursor (0, 1);
    lcd.print ("Inserta la SD");
    delay (5000);
    lcd.clear ();
    delay (1000);
    return;

  }

  Serial.println ("SD inicializada correctamente");
  lcd.setCursor (0, 0);
  lcd.print("BOOT exitoso...");
  delay (2000);
  lcd.clear ();

  lcd.print(" Bienvenidos a: ");
  delay(2000);
  lcd.setCursor(0, 1);
  lcd.print("ARDUINO");
  delay(7000);
  lcd.clear();
  delay(1000);

  lcd.print(" APRENDE");
  delay(1000);
  lcd.setCursor(0, 0);

  lcd.setCursor(0, 1);
  lcd.print("CADA DIA");
  delay(7000);
  lcd.clear();
  delay(1000);

  lcd.print("*NO ESPERES MAS*");
  delay(2000);

  lcd.setCursor(0, 1);
  lcd.print("!!!!!!");
  delay(5000);
  lcd.clear();
  delay(900);

  myFile = SD.open ("Contactos.txt"); // ¿Seguro que abre con ese nombre? Según mis "cálculos", al ser uno que no cumple con el formato 8.3; se deberá referenciar por "CONTAC~1.TXT"
  if (myFile) {
    lcd.print ("NOMBRE:");
    lcd.setCursor (0, 1);
    lcd.print ("TEL:");
  } else lcd.print ("No se pudo abrir");
}

void loop() {

  if (digitalRead(Coin) != estadoAnterior) {
    estadoAnterior = !estadoAnterior;
    if (estadoAnterior == LOW) {
      if (demo) {
        demo = false;
        millis_espera=0; // si estábamos en ciclo demo, salimos y hacemos "resume"
      }
      pulsaciones++;
    }
  }

  if ( (millis() - millis_inicio) >= millis_espera ) {
    if (demo) {
      // Aquí tratamos la demo
      switch (sigFase){
        case 0:
          lcd.clear();
          lcd.setCursor(0, 0 );
          lcd.print("Bienveniddos a:");
          millis_espera = 2000;
        break;
        case 1:
            lcd.setCursor(0, 1);
            lcd.print("ARDUINO ");
            millis_espera = 7000;
        break;
        case 2: // Aprovechamos para poner todos los casos en los que hay lo mismo (borrado de pantalla y espera 1000)
        case 5:
        case 8:
          lcd.clear();
          millis_espera = 1000;
        break;
        case 3:
          lcd.setCursor(0, 0 );
          lcd.print("Aprender a");
          //millis_espera = 1000; // no es necesrio , porque ya es 1000 desde el case anterior.       
        break;
        case 4:
          lcd.setCursor(0, 1 );
          lcd.print("Programar");
          millis_espera = 7000;
        break;
        // caso 5 igual que 2
        case 6:
          lcd.setCursor(0, 0 );
          lcd.print("Bienvenidos");
          millis_espera = 2000;
        break;
        case 7:
          lcd.setCursor(0, 1 );
          lcd.print("¡¡¡Todos!!!");
          millis_espera = 5000;
        break;
        // caso 8 igual que 2 y 5
      }
      sigFase++;
      if (sigFase > 8) sigFase=0;
    }
    else {
      // modo no demo, o muestra de registro
      if (!pulsaciones) {
        demo = true; // si hemos llegado a cero pulsaciones, volvemos a modo mdemo
        return; // y salimos del loop para que reentre inmediatamente en modo demo
      }
      // si hay pulsaciones pendientes, ejecutamos y descontamos
      pulsaciones--;
      if (myFile) {
        if (myFile.available()) {
          nom[myFile.readBytesUntil(',', nom, 16)] = '\0';
          tel[myFile.readBytesUntil('\n', tel, 16)] = '\0';
          Serial.println(nom);
          Serial.println(tel);
          lcd.clear();
          lcd.print ("NOMBRE:"); // entiendo que este paso va aquí, no tras el delay
          lcd.print (nom);
          lcd.setCursor (0, 1);
          lcd.print ("TEL:"); // entiendo que este paso va aquí, no tras el delay
          lcd.print (tel);
        } else {
          Serial.println ("Final del Archivo");
          myFile.seek (0L);
          lcd.clear ();
          lcd.print ("Final del Archivo");
        }
      } else {
        lcd.clear();
        lcd.print ("ERROR: no hay SD");
        lcd.setCursor (0, 1);
        lcd.print ("o archivo no exs");
      }
      millis_espera = 5000;
    }
    millis_inicio = millis(); // tanto en demo como tras muestra de registro
  }
}


Supongo que tienes un arduino uno. Si te está dando el aviso de memoria escasa, por el momento, quita todos los lcd y serial print del setup (ya veremos si los volvemos a poner cuando solucionemos el loop, que es lo que interesa).
A ver, porque tengo un poco de lío entre serial y lcd. Prueba este código e intenta explicarme lo mejor que puedas qué es lo que se muestra en lcd tras el setup. Si crees que está pasando muy deprisa, mete un pequeño delay (200, por ejemplo) al principio del loop.

CaleBit

Acabo de hacer los cambios que me pediste, agregue el delay al inicio del loop y ya corre la "demo" a velocidad normal, también borre los serial print dentro de los casos millis, y ya no me arrojó la alerta de falta de memoria.

Y tienes razón es un arduino uno.

También hice un cambio borre la demo del setup, por que es muy repetitiva, la agregue antes  porque me daba cuenta que tal vez los delays estaban comiéndose algunos pulsos, y al menos mostrarla al inicio. Después de abrir el archivo de la SD puse esos dos campos de registro vacíos para que fueran cambiando al mostrar el pulso, dejando la demo fuera sin mostrarse ya, por los problemas de los delays.

Pero si estaría genial que las mostrará todo el tiempo como se planteó al principio.

No sabes lo mucho que te agradezco tu tiempo para ayudarme.

He hecho las pruebas de este código que me enviaste y corre bien el setup...


Esta parte del código muestra los mensajes de la lcd un poco descompuestas, solo se muestra desacomodada con el mensaje del BOOT éxitoso...
Code: [Select]


myFile = SD.open ("Contactos.txt"); // ¿Seguro que abre con ese nombre? Según mis "cálculos", al ser uno que no cumple con el formato 8.3; se deberá referenciar por "CONTAC~1.TXT"
  if (myFile) {
    lcd.print ("NOMBRE:");
    lcd.setCursor (0, 1);
    lcd.print ("TEL:");
  } else lcd.print ("No se pudo abrir");


De ahí la demo corre normal, pero no lee ningún pulso ni muestra nada ni en el monitor serial ni en la lcd.


Gracias brother!!

Lucario448

A todo esto, no sé si deberías pero... desearía que explicaras, con lujo de detalle, cómo quieres que todo se realice.

En todo el trayecto de este hilo, me pareció que quierías empezar con una cosa, luego cambiaste a otra, que el demo solo al principio y que luego cuando no se pulsa nada.

A toda esta confusión mejor decide con claridad y seguridad lo que planeas hacer, así se podrá modificar el código, de una buena vez por todas, de manera que todos los objetivos sean cumplidos (y por qué no, en un futuro cercano, optimizarlo y mejorar su "mantenibilidad").

CaleBit

Si tienes razón Lucario, gracias por tu comentario.

Bueno estos son los objetivos;

Al comenzar a correr el programa muestre los mensajes en la LCD de inicializacion y que verifique que esta presente la SD y por ende muestre al final el BOOT exitoso(Este objetivo ya esta implementado y corre de manera eficiente).

Después de esto el siguiente paso es comenzar a correr la demo, al detectarse el pulso de entrada mostrará un registro en el monitor serial (Esto es porque deseo tomar esos datos e imprimirlos en una impresora térmica), y también en la LCD pero si fuera posible no los muestre solo por 5 segundos, más bien los deje congelados hasta que se apreté un botón y este limpie pantalla y nos devuelva a la demo.

El siguiente paso que la verdad no tenía contemplado, como algunos otros y por eso les pido disculpas por que también he ido comprendiendo varias cosas en este proyecto, es la de ¿Que pasaría si reinicio el arduino o simplemente lo apagó, porque no estará en función todo el tiempo?, ¿Me mostrará el siguiente registro donde se quedó la última vez?,  haciendo las pruebas me he dado cuenta que me regresa al principio nuevamente, y como me comentabas Lucario no es necesario comprar una EPROM arduino trae una por defecto donde se podría hacer que al reinicio me muestre el siguiente registro a partir del último que mostró.


Bueno disculpen si los confundí  :smiley-confuse:, no se si fui claro pero con seguridad es esto lo que busco.
 
Mil gracias.

Lucario448

Ahora sí, una vez definidos los objetivos ya puedo darte pistas más concretas:

el siguiente paso es comenzar a correr la demo, al detectarse el pulso de entrada mostrará un registro en el monitor serial (Esto es porque deseo tomar esos datos e imprimirlos en una impresora térmica), y también en la LCD pero si fuera posible no los muestre solo por 5 segundos, más bien los deje congelados hasta que se apreté un botón y este limpie pantalla y nos devuelva a la demo.
Veamos... ¿estamos hablando de dos pulsaciones en un botón, o una en dos?

Para ejecutar la rutina del contacto con inmediatez mientras el "demo" está en juego, la implementación de una máquina de estados sigue siendo necesaria. noter había dejado una sugerencia al respecto.

Ahora, si la LCD debe "congelarse" indefinidamente hasta que cierto evento ocurra, entonces sería una pulsación adicional en el único botón, o realizarlo en un segundo botón.
En caso de recurrir a esto último, para no perder pulsos accionadores de lo del contacto (durante el congelado de la LCD), la interrupción externa es factible (existe también la interrupción por cambio de estado de pin, pero es más complicado de manejar y configurar).


¿Que pasaría si reinicio el arduino o simplemente lo apagó, porque no estará en función todo el tiempo?, ¿Me mostrará el siguiente registro donde se quedó la última vez?,  haciendo las pruebas me he dado cuenta que me regresa al principio nuevamente, y como me comentabas Lucario no es necesario comprar una EPROM arduino trae una por defecto donde se podría hacer que al reinicio me muestre el siguiente registro a partir del último que mostró.
Exacto. Solo que esa discución no quedó registrada en este hilo porque fue por PM.

Lo que puedes almacenar en memoria no volátil es el valor del "puntero de archivo" cada vez que recuperas el par nombre-teléfono; se recupera con position()
Para que el Arduino recuerde por donde iba, en el arranque se usa seek() después de abrir el archivo. El valor a introducir proviene de lo que se recupere (lea) de esa memoria no volátil.



PD:
no es necesario comprar una EPROM
Te faltó una E.

No es una simple corrección, las "EPROM" realmente existen. Son chips de memoria programable no volátil, solo que tienen una pequeña ventana donde se les ve la circuitería interna. Esto porque son borrables, pero sólo con luz ultravioleta.

CaleBit

Ok si son 2  pulsaciones diferentes, (dos botones).

A "congelarse", me refiero aque al detectar el pulso del primer botón (Llamemosle botón mostrar contacto),  este me muestre en el monitor (eso ya esta), el registro y también en la lcd (ya está), solo que la lcd dejaría los valores en la pantalla hasta que el botón2 limpiará y nos volviera al Demo.

Y gracias por el dato pensé que con una "E", me refería a esta.

Lucario448

A "congelarse", me refiero aque al detectar el pulso del primer botón (Llamemosle botón mostrar contacto),  este me muestre en el monitor (eso ya esta), el registro y también en la lcd (ya está), solo que la lcd dejaría los valores en la pantalla hasta que el botón2 limpiará y nos volviera al Demo.
¿Y en el caso de presionar "botón mostrar contacto" dos veces consecutivas? ¿Actualiza la pantalla o no ocurre nada la segunda vez?

CaleBit

El botónMostrar contacto solo ira mostrando los registros, el botónLimpiar lcd solo nos borrará el registro mostrado y nos llevará de nuevo a la demo.

Si se presionará el botón mostrar contacto dos veces consecutivas, lo que pasaría es que me mostraría un contacto y al volver a presionar el siguiente contacto, solo eso haría este botón, Aunque no se pulse el botón limpiar lcd y el registro aun este en pantalla, el botón mostrar contacto me seguiría mostrando el siguiente registro.


noter

Bueno. Debo decir, entonces, que bien no he entendido hasta ahora lo que querías hacer, o bien han ido cambiando los objetivos. Lo que yo estaba intentando hacer era que al pulsar botón de mostrar contacto, inmediatamente lo mostrara por un tiempo (cinco segundos, creo) y tras ese tiempo continuara con la demo; excepto que se hubiera producido nueva pulsación durante ese tiempo, que encadenaría el siguiente registro otros cinco segundos, etc. Vamos; una especie de buffer de pulsación. Esa temporización de la muestra de los registros, creo que precisamente complicaba un poco el código.

CaleBit

Así es noter pido mil disculpas, lo he ido adecuando un poco para que sea 100% funcional, pero me gustaría que quedará así como lo he explicado ahora.

2 botones una muestra un contacto, y lo deja en pantalla de forma indeterminada, hasta que el segundo boton limpia pantalla y nos vuelve a la demo.

El botón uno de mostrar contacto, solo mostrará registros, y el otro botón solo borrará ese registro de pantalla y nos volverá al demo. Bueno si es posible.



Lucario448

Ya lo capto...

La sección del demo sigue siendo necesario hacerlo con máquina de estados; eso que acabas de mencionar se hace con if y while. Nada de interrupciones entonces...
Sería importante que separas ambas secciones en funciones aparte:

Code: [Select]
void loop() {
  if (!digitalRead(2)) contacto();
  else demo();
}

Go Up