Bluetooth: quale grandezza massima dato da inviare?

Ciao a tutti,
sto giocando con il bluetooth e sto inviando i dati al cellulare sul quale vengono salvati in un file. Il sistema semplificato funziona egregiamente ma vorrei implementare un'app per cell un pochino più elegante in modo che possa ricevere ed "interpretare" "diversi" tipi di messaggi da arduino (messaggio e testo); ciò crea un pochino di problemi a causa della comunicazione di tipo seriale. Io devo leggere un file salvato su arduino in sd ed inviarne il contenuto al cell; ho due strade da poter seguire: leggere ed inviare subito dopo riga per riga oppure leggere tutto il file, creare una stringa che si incrementa ad ogni lettura di riga, quindi inviare l'intera stringa alla fine della lettura del file. Avendo impostato il baud rate a 9600 bps in teoria il bluetooth dovrebbe inviare 1200 byte in un secondo, cioè poco più di 1M byte, sbaglio? Nel caso avessi impostato il delay del loop ad un secondo ed i dati da inviare fossero 2M byte o più, come si comporterebbe il sistema? Ci sarebbe perdita di dati? Il timer dell'applicazione è impostato a 500ms comunque ho problemi nel gestire la ricezione dei dati.
Il bluetooth invia un pacchetto di 9600 bit ogni secondo a prescindere dal contenuto?
Aiuto....

micpic:
Avendo impostato il baud rate a 9600 bps in teoria il bluetooth dovrebbe inviare 1200 byte in un secondo

Tu NON devi pensare al Bluetooth ... devi pensare ad una semplice connessione seriale il cui il Bluetooth è solo un mezzo di trasmissione che NON interferisce (... e di suo a te NON trasmette nulla).

Se usi la seriale a 9600 baud arrivi si e no a trasmettere (tra startbit, stopbit, e ritardi vari) circa 900 caratteri al secondo.
Quando scrivi sulla seriale i dati vengono trasmessi a quella velocità quindi a te NON mandare caratteri in trasmissione più velocemente di quanto possano essere trasmessi, pena il rimanere bloccati fino a che non c'è di nuovo spazio nel buffer di trasmisisone (quindi i caratteri NON vengon persi, praticamnete viene fermato il tuo programma in attesa di svuotare il buffer).

La cosa è visibile esaminando il codice del "core" nel punto in cui effettua la trasmissione ...

  ...
  ...
  // If the output buffer is full, there's nothing for it other than to 
  // wait for the interrupt handler to empty it a bit
  while (i == _tx_buffer_tail) {
    if (bit_is_clear(SREG, SREG_I)) {
      // Interrupts are disabled, so we'll have to poll the data
      // register empty flag ourselves. If it is set, pretend an
      // interrupt has happened and call the handler to free up
      // space for us.
      if(bit_is_set(*_ucsra, UDRE0))
	_tx_udr_empty_irq();
    } else {
      // nop, the interrupt handler will free up space for us
    }
  }
  ...
  ...

Puoi sempre interrogare lo spazio disponibile nel buffer, così da non rimanere bloccato, tramite la Serial.AvailableForWrite() che appunto indica i bytes che rimangono liberi nel buffer prima che l'operazione venga bloccata.

Guglielmo

Grazie, molto utile! (brutta cosa l'ignoranza :-[ ) Adesso studio la cosa con calma.
Io ho utilizzato la funzione flush() per pulire un eventuale buffer il problema ....ecco, sto pensando che forse l'ho inserito nel posto sbagliato....
Un'altra domanda è la seguente:

  • se la lettura del file impiega più del delay impostato per il loop cosa succede? Chi ha la priorità? Continua la lettura del file finchè non arriva EOF oppure viene interrotta allo scadere del delay poi brutalmente ripresa?

Mic con il cervelletto un poco aggrovigliato :astonished:

.... il brutto dell'usare appinventor è che non si ha la possibilità di capire bene i dati come vengono gestiti/interpretati :confused:

La flush() lasciala stare NON fa quello che tu credi ... leggi il reference ...
... in merito alla seconda domanda, se sei nel delay() sei FERMO, NON c'è alcuna lettura da SD. Non sei su un PC o su un sistema Linux dove hai un multitask, sei su una piccola MCU, senza alcun sistema oprativo e dove le cose si fanno una alla volta e sei tu a farle. Se usi delay() fermi tutto per il tempo del delay(), allo scadere del delay riprendi da li.

Magari se spieghi esattamente quello che vuoi fare possiamo esaminare la cosa ...

Guglielmo

Acquisisco dati da un accelerometro, li salvo sulla SD nella forma
324 456 322
....
nel frattempo attraverso il bluetooth invio il valore di uno switch
0: arduino in lettura
1: errore in lettura
2: rimozione file
....
il problema è separare i valori del menu da quelli della lettura del file. Ho utilizzato caratteri di riconoscimento per attivare/disattivare scrittura file su cellulare ma i file ricevuti risultano "corrotti", sballati nelle posizioni, in quanto a volte due caratteri in una stessa riga ma il più delle volte uno (ovviamente, essendo byte to byte) a volte salta la riga, e sono generati dal seguente codice:

...
if (dataFile) {// apertura file ok
                    Serial.println("\nReading datalog.log...");
                    bluetooth.flush();
                    bluetooth.println("#");
                    while (dataFile.available()) {
                        bluetooth.println(char(dataFile.read())); // trasmetto le righe del file tramite bluetooth
                    }
                    bluetooth.flush();
                    bluetooth.println("|");
                    //delay(1000);
                    Serial.println("End reading datalog.log.");
                    sw = 5;

                    dataFile.close();
                }
...

Ho provato a creare un'unica stringa con tutte le righe del file ma dal cellulare vengono ricevuti i primi tot caratteri ed il resto tutto perso.
Se invece invio esclusivamente i dati del file, row to row, non ho alcun problema.

...posso utilizzare un interrupt per stoppare l'esecuzione dell'intero programma finchè non termina la lettura e l'invio dei dati dal file?

micpic:
...posso utilizzare un interrupt per stoppare l'esecuzione dell'intero programma finchè non termina la lettura e l'invio dei dati dal file?

NO. Gli Interrupts servono a tutt'altro e le ISR DEVONO essere le più brevi possibile.

Senti, metti tutto il codice che con i pezzetti non si capisce molto ...

Guglielmo

Il codice ha diversi commenti, sono tutte le varie prove che sto facendo :-[

#include <SPI.h>
#include <SD.h>
#include <SoftwareSerial.h>
///storage/sdcard0/Bluetooth
// dichiarazione variabili accelerometro
int pinX = A0;
int pinY = A1;
int pinZ = A2;

int indice;
int X;
int Y;
int Z;
float x;
float y;
float z;
int noiseX = 0;
int noiseY = 0;
int noiseZ = 0;

// settaggi per bluetooth
int rxPin = 2;
int txPin = 3;
SoftwareSerial bluetooth(rxPin, txPin);

// settaggio per SD
const int chipSelect = 10; // CS su pin 10

//settato a 1 per richiedere password ed avviare la scrittura (alla prima accensione)
int sw = 1; 
File dataFile;

// menu
String menu = "Insert: \n 0 for reading SD \n 1 for deleting SD \n 2 for writing SD";

// password per settaggio bluetooth
String passwd = "Tommaso2011";

// setto il delay
int tm = 1000;

// contatore di secondi da accensione
//int sec = 0;
//int sec = millis(); impostato qui sec è sempre uguale a zero

// tempo da quando acceso arduino
int sec = 0;

void setup(){

    // SERIAL MONITOR
  Serial.begin(9600);
  //Serial.println("Letture Accelerometro");
  analogReference(EXTERNAL);
  //Serial.println("Inserisci la password seguita dal simbolo @ per avviare il programma");
    Serial.println("Insert the password, followed by @, for starting the software");
  
      // SDCard
  //Serial.print("Initializing SD card...");
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed or not present");
    // don't do anything more:
    return;
  }

    // BLUETOOTH
  bluetooth.begin(9600);
    switch (sw) {
        case '0': 
                //bluetooth.println("\nWriting...");
                //bluetooth.println("0");
            break;
        case '1': 
                //bluetooth.println("Insert the password, followed by @, for starting the software");
                //bluetooth.println("1");
            break;
        case '2': 
                //bluetooth.println("Error opening datalog.log.");
                //bluetooth.println("2");
            break;

        case '3': 
                //bluetooth.println("File removed.");
                //bluetooth.println("3");
            break;
        case '4': 
                //bluetooth.println("\nNone file to remove.");
                //bluetooth.println("4");
            break;
        case '5': 
                //bluetooth.println("End reading datalog.log.");
                //bluetooth.println("5");
            break;
        case '6': 
                //bluetooth.println("Sorry, bad password.");
                //bluetooth.println("6");
            break;
        case '7': 
                //bluetooth.println("Reading datalog.log...");
                //bluetooth.println("7");
            break;
        //default: bluetooth.println(sw);
    }
  //bluetooth.println("Insert the password, followed by @, for starting the software");

    // PASSWORD
  int passWD();

}
  
void loop() {
  
if (sw != 7) {
    bluetooth.println(sw);
}
  
// configuro password per collegamento al bluetooth
if (sw == 1) { //righe 62 e 157

    sw = passWD();

} else if (sw == 0) { //riga 157

    // make a string for assembling the data to log:
    String dataStringForBluetooth = "";

    // read three sensors and append to the string:
    X = analogRead(pinX);
    dataStringForBluetooth += String(X);

    Y = analogRead(pinY);
    dataStringForBluetooth += " ";
    dataStringForBluetooth += String(Y);
    
    Z = analogRead(pinZ);
    dataStringForBluetooth += " ";
    dataStringForBluetooth += String(Z);
    
    // contatore di secondi da accensione
    dataStringForBluetooth += " ";
    dataStringForBluetooth += String(sec);

    // open the file. note that only one file can be open at a time,
    // so you have to close this one before opening another.
    File dataFile = SD.open("datalog.log", FILE_WRITE);

    // if the file is available, write to it:
    if (dataFile) {
        dataFile.println(dataStringForBluetooth);
        //dataFile.println(dataString, BIN);
        dataFile.close();
        Serial.println(dataStringForBluetooth);
    }
      // if the file isn't open, pop up an error:
    else {
        Serial.println("\nError opening datalog.log.");
        sw = 2;
// ATTENZIONE: mettere in app la condizione che il dato ricevuto (tipo byte quindi bisogna vedere la corrispondenza ASCII) per essere scritto su file deve essere compreso tra 48 e 57 inclusi, oppure uguale a 10, 13, 32 altrimenti deve essere visualizzato nel campo textreceived
    }
}

if (bluetooth.available() > 0) {
//if (Serial.available() > 0) {

        // visualizzo il menu
        Serial.println(menu);
        //bluetooth.println(menu);
        //byte command = Serial.read();
        byte command = bluetooth.read();
        //Serial.write(command);

        switch (command) { //controllo che sia un comando valido
           case '0': // trasferimento/lettura file
                sw = 7;
                dataFile = SD.open("datalog.log"); //Riapro il file
                if (dataFile) {//Se il file è stato aperto correttamente
                    Serial.println("\nReading datalog.log...");
                    //bluetooth.println("\nReading datalog.log...");
                    //tm = 10000;
                    //bluetooth.flush();
                    bluetooth.println("#");
                    //delay(1000);
                    //String stringXYZ = "";
                    while (dataFile.available()) {
                        //Serial.write(dataFile.read()); //deve essere commentato altrimenti i dati che arrivano al cell non sono corretti
                        bluetooth.println(char(dataFile.read())); // trasmetto le righe del file tramite bluetooth
                        //stringXYZ += char(dataFile.read()); // non si possono inviare in quanto i tempi di lettura del file possono essere superiori ai tempi del loop, più che altro la lunghezza del messaggio
                    }

                    //bluetooth.println(stringXYZ);
                    bluetooth.println("|");
                    //bluetooth.flush();
                    //delay(1000);
                    //tm = 1000;
                    Serial.println("End reading datalog.log.");
                    sw = 5;

                    dataFile.close();
                }
                else {
                    Serial.println("\nError in opening datalog.log");
                    sw = 2;
                }
              
              break;
           case '1': // eliminazione file
                // Check to see if the file exists:
                if (SD.exists("datalog.log")) {
                    // delete file:
                    Serial.println("\nRemoving datalog.log...");
                    //bluetooth.println("\nRemoving datalog.log...");
                    SD.remove("datalog.log");
                    Serial.println("File removed.");
                    sw = 3;
                    //bluetooth.println("File removed.");
                } else {
                    Serial.println("\nNone file to remove.");
                    sw = 4;
                    //bluetooth.println("\nNone file to remove.");
                }
              break;
            case '2': // eliminazione file
                sw = 0; // riattivo la scrittura su SD
                Serial.println("\nWriting...");
                //bluetooth.println("\nWriting...");
              break;
        }
}

// } riga 62
    sec++;
    delay(tm); // il bluetooth riceve un comando al secondo
} // fine loop

int passWD() { // richiede la password, se ok avvia la scrittura (per il primo avvio)

    String stringApp = "";

 if (bluetooth.available()) {
 //if (Serial.available()) {
        
        byte command;
        do {
          command = bluetooth.read();
          //command = Serial.read();
          if (command != 255 and command != 64) { 
            //Serial.println(char(command));
            stringApp += char(command);
          }
        } while (command != '@');

        //bluetooth.println(stringApp); solo per leggere la password inserita
        //Serial.println(stringApp);

        if (stringApp == passwd) {

            sw = 0; // se la password corrisponde avvia la scrittura su SD

        } else {
            bluetooth.write("\nSorry, bad password.");
            Serial.write("\nSorry, bad password.");
            sw = 6;
        }
 }
    return sw;
}

Non capisco il commento di questo pezzo ...

while (dataFile.available()) {
//Serial.write(dataFile.read()); //deve essere commentato altrimenti i dati che arrivano al cell non sono corretti
   bluetooth.println(char(dataFile.read())); // trasmetto le righe del file tramite bluetooth
   //stringXYZ += char(dataFile.read()); // non si possono inviare in quanto i tempi di lettura del file possono essere superiori ai tempi del loop, più che altro la lunghezza del messaggio
}

... che significa "non si possono inviare in quanto i tempi di lettura del file possono essere superiori ai tempi del loop" ? :o :o :o

Il loop() E' FERMO LI finché non hai inviato ... NON esiste che avvengano più cose allo stesso tempo (ovvero che il loop() continui a girare mentre tu leggi da SD e trasmetti su BT) !

Guglielmo

Benissimo, tutto ciò mi fa capire un sacco cose, ma allora per quale motivo se invio una sola stringa di 100000 caratteri quello che ricevo sono sempre i primi .... non ricordo di preciso quanti ma più dei circa 900? Dov'è il limite? Scusa l'ignoranza ma sono una neofita autodidatta di elettronica :stuck_out_tongue: :smiley:

Mic riconoscenti degli innumerevoli chiarimenti

Controlla dal lato dove ricevi ... perché NON vale la stessa cosa ... chi riceve ha un buffer limitato, e se tu continui a trasmettere prima che chi riceve svuoti completamente il buffer, perdi i caratteri.

Guglielmo

Ho fatto diverse prove, smontato e rimontato il codice lato Arduino lato cellulare svariate volte ma continuo ad avere lo stesso problema. Ora, tante cose si sono chiarite ma non è sufficiente, avrei bisogno di sapere:

  • si può regolare la dimensione del buffer di trasmissione? Se si, come? Devo accere alla libreria SoftwareSerial?
  • aggiungere il delay prima e dopo i dati da salvare su file a differenza di quelli che vanno messi a video e basta, può servire?
  • ho tentato di "svuotare" il buffer con questo codice:
void clearBuffer() {
  int byteBuffer = bluetooth.available(); // il buffer contiene 64byte?
  for (int i = 0; i < (byteBuffer+1); i++) {
    bluetooth.write("#");
  }
}

il carattere # mi serve come "riempimento", ma non mi sembra funzioni, quale può essere l'errore?

Grazie ancora per l'aiuto.

Mic ancora persa... :o :confused:

Perdona ... tutti i discorsi che ti ho fatto, li ho fatti avendo in mente la VERA seriale, NON la SoftwareSerial ... :-[

La SoftwareSerial ha un suo buffer di 64 charatteri per la ricezione, ma in trasmissione NON usa alcun buffer ... sfrutta quello che tu gli passi e trasmette, senza fermarsi, uno dopo l'altro, tutti i bytes che gli dai da trasmettere.

Quindi, se vuoi creare delle pause ogni X caratteri, sei tu che devi contare quanti caratteri hai trasmesso e fermarti per il tempo che vuoi prima di trasmettere i successivi.

Considera che sia la vera seriale che la SoftwareSerial, NON usano handshaking ... quindi ... NON c'è alcun controllo se chi deve ricevere può veramente ricevere o meno (... in pratica, non esistono i segnali di RTS/CTS).

Guglielmo