Mandare una lunga stringa dal pc ad arduino

Salve ragazzi,

In un altro post sto costruendo un braccio meccanico con stepper pilotato da arduino.

Dal mio pc ho creato un programma in C# per il calcolo di tutte le formule matematiche e geometriche in essenziali per farlo funzionare come si deve.

Il mio software del pc manda una stringa lunghissima di questo tipo:

50,50,49,48,48,48—-200,201,220,230

Dove la prima successione di numeri sono i step che deve seguire il motore e la seconda successione sono i microsecondi di delay.

Nel mio caso reale ho una stringa lunghissima, parliamo di oltre 1000 caratteri.
Dopo un certo numero di caratteri il mio serial.readstring di arduino non riesce a leggere tutto oppure salta qualcosa...

Quale sarebbe la via corretta per passare dal mio pc ad arduino 2 array di circa 1000 elementi per array?

Non sono molto pratico di comunicazione seriale, ma nel reference parla di timeout... sicuro che la trasmissione di così tanti caratteri non impieghi troppo tempo? e quindi la readString va in timeout...

fratt:
Non sono molto pratico di comunicazione seriale, ma nel reference parla di timeout... sicuro che la trasmissione di così tanti caratteri non impieghi troppo tempo? e quindi la readString va in timeout...

ho provato a mettere il timeout anche a 10 secondi, ma non è cambiato nulla.
Siamo sicuri che è l unica via per poter mandare tanti byte?

Ciao...nemmeno io sono un super esperto però, per test,ho eseguito questo codice invianto una serie di 1000 caratteri...la scelta del baud rate fa molta differenza per il timing; per esempio a me, con 9600 da tempi ciclo completo 4600 ms; con 250000 da 42ms... penso che il Baud rate sia influente per la qualità del segnale riferito alla distanza tra i due dispositivi.
i 1000 caratteri che ho inviato erano così composti: a234567890b234567890c234567890...etc
capirai perchè l'ho specificato leggendo il codice.

char c[1001];
int q;
boolean m;
unsigned long myMillis;
void setup() {
  Serial.begin(250000);
  for (int i = 0; i < sizeof(c); i++) {
    c[i] = 0;
  }
  q = 0;
  m = false;

}

void loop() {
  if (Serial.available()) {
    if (m == false) {
      myMillis = millis(); //memorizzo il tempo in cui è inziata la ricezione
      m = true;
    }
    q = Serial.readBytes(c, 1000); //leggo 1000 bytes
    Serial.println(millis()-myMillis);  // stampo quanto tempo ho impiegato a leggere 1000 bytes
  }
  if (q == 1000) {
    for (int i = 0; i < sizeof(c) - 1; i++) {
      if (c[i] >= 97 && c[i] <= 118) {  // se c[i] è tra 'a' e 'v'
        Serial.println(i);
        Serial.println(c[i]);
      }
      else {
        Serial.println(c[i]);
      }
    }
    Serial.println(millis() - myMillis); // stampo tempo ciclo completo tra inizio lettura e fine stampa
    q = 0;
    m = false;
  }
}

@fratt: penso che il timer si resetti ogni volta che c'è un byte in ingresso...quindi fino a che c'è qualche cosa che arriva, entro il timeout, tutto funziona.

... ma stai tenendo conto di quanta SRAM ha Arduino UNO ? ? ? :o

In TUTTO hai 2KB di SRAM ... dove devono entrare le variabili, lo stack, ecc. ecc. ... dove pensi di mettere 2 array da 1000 caratteri ciascuno ? ? ? ... e, appena sfori con la SRAM è ovvio che il programma faccia quello che vuole o Arduino si resetti.

Come minimo ti serve passara ad Arduno MEGA che almeno ha 8KB di SRAM ... ::slight_smile:

Guglielmo

P.S.: Il timeout di quei metodi si imposta con la setTimeout(), ma giustamente, finché arrivano caratteri NON inizia a contare.

gpb01:
... ma stai tenendo conto di quanta SRAM ha Arduino UNO ? ? ? :o

In TUTTO hai 2KB di SRAM ... dove devono entrare le variabili, lo stack, ecc. ecc. ... dove pensi di mettere 2 array da 1000 caratteri ciascuno ? ? ?

Come minimo ti serve passara ad Arduno MEGA che almeno ha 8KB di SRAM ... ::slight_smile:

Guglielmo

P.S.: Il timeout di quei metodi si imposta con la setTimeout()

credo sia proprio questo il problema, quindi come soluzione non va bene.
Visto che mando in una stringa tutte i step e ritardi che il motore deve fare (quidni 20 a 200, 21 a 200, 20 a 201.....) avevo pensato di mandare tutti i step dal mio programma c sharp in blocchi.

E' possibile fare che dal mio programma faccio un tipo 1000 write su arduino e arduino prende il coda i byte ricevuti e gli esegue uno dopo l'altro?

Altri workaround non ne ho in mente

... se non vuoi perdere dati ed essere sicuro, devi usare un piccolo protocollo ... ovvero, arduino invia un codice al PC che significa "pronto a ricevere", il PC trasmette un primo blocco, direi massimo 128 caratteri, Arduino lo esegue e il ciclo si ripete. In pratica trasmetti a blocchetti di 128 charatteri ogni volta che Arduno ti manda un carattere che significa sono pronto.

Guglielmo

gpb01:
... se non vuoi perdere dati ed essere sicuro, devi usare un piccolo protocollo ... ovvero, arduino invia un codice al PC che significa "pronto a ricevere", il PC trasmette un primo blocco, direi massimo 128 caratteri, Arduino lo esegue e il ciclo si ripete. In pratica trasmetti a blocchetti di 128 charatteri ogni volta che Arduno ti manda un carattere che significa sono pronto.

Guglielmo

come logica mi sembra possa andare bene, pensi ci possa essere un ritardo con questo protocollo?
Io devo mandare per esempio 500 messaggi dove ogni messaggio contiene il numero di step del motore.
Se li ha tutti in un array e li cicla va liscio una favola, ma non è possibile avere array cosi lunghi.
Se uso questo protocollo per eseguire un messaggio alla volta pensi potranno esserci microritardi?
Da tenere in considerazione che il mio stepper lavora con delay di 400 microsecondi, quindi se il ritardo c è anche di 100 microsecondi il mmio motore andrebbe a scatti e non va bene.

Lavorando a 115200 arrivi a trasmettere circa 11520 carateri al secondo il che significa che ogni carattere impiega circa 87 μsec ... ho idea che NON ce la puoi fare, devi prendere una scheda con più SRAM o provare velocité di trasmissioni superiori ... se il tuo PC te lo permette ma comunque stai molto stretto ... e anche a livello codice NON è banale ...

Guglielmo

gpb01:
Lavorando a 115200 arrivi a trasmettere circa 11520 carateri al secondo il che significa che ogni carattere impiega circa 87 μsec ... ho idea che NON ce la puoi fare, devi prendere una scheda con più SRAM o provare velocité di trasmissioni superiori ... se il tuo PC te lo permette ma comunque stai molto stretto ... e anche a livello codice NON è banale ...

Guglielmo

ho preparato il codice, vediamo se 'ascoltando' il motore ci sono delle pausa o meno:

 private void button1_Click(object sender, EventArgs e)
        {

            for (int i = 800; i <= 3000; i++)
            {
                lst.Add(i.ToString()); 
            }

            port = new SerialPort();
            port.BaudRate = 250000;
            //port.Handshake = Handshake. ;
            port.PortName = "/dev/cu.usbserial-A603RW9S";//textBox4.Text;
            port.Open();
            int ind = 0;
            port.WriteLine("PARTI");
        
            while (true)
            {
                String s = port.ReadLine();
                if (s != null)
                {
                    port.WriteLine(lst[ind].ToString());
                    ind = ind + 1;
                }
            }
            port.Close();
           
        }
void loop() 
{
  if (Serial.available()) {
      

          digitalWrite(3, HIGH);    digitalWrite(6, HIGH); 
           int delays = Serial.readString().toInt();
           for(int stepx = 0; stepx < 100; stepx++)
              {
                 
               digitalWrite(7, HIGH); digitalWrite(5, HIGH);
                  delayMicroseconds(delays);
                digitalWrite(7, LOW);  digitalWrite(5, LOW);
                 delayMicroseconds(delays);
              }
         Serial.println("VAI");
        
      }
  
}

Mah ... già noto che usi la stringa "VAI" ... sono 3 caratteri, anche a 250Kbps fanno più di 100 μsec. ... perché mandare 3 caratteri? Non ti basta mandare, ad esempio, un singolo '*' ?

Guglielmo

Perdono ma, da come lo vedo io, non hai soluzione mantenendo questa scheda e questa lunghezza di stringa, in quanto quei dati che ricevi dovrai comunque utilizzarli in qualche modo, e la cosa userà comunque una quantità di RAM. Facendo due conti a freddo dico che se ho 1000 cifre posso ottenere 250 numeri di 4 cifre, sapendo che un int occupa 2 byte fanno 500 byte. Sapendo che ho 2 sequenze di questo genere fanno 1000 byte (1k) di RAM necessaria in più. Quindi già solo il fatto di avere due array da 250 numeri occupano mezza RAM, oltre a quella necessaria per leggerli, per usarli e per fare operazioni accessorie.
Se PoI strutturi la comunicazione come suggerito e fai trasmettere un tot di byte ogni segnale la cosa si complica.
Come uniche soluzioni, quindi, vedo una rivisitazione del progetto (ammesso che sia possibile) in modo da ridurre i dati presenti in contemporanea (trovo un modo in cui dimezzò le dimensioni degli array o altro) e/o l'utilizzo di una quantità maggiore di RAM.

Io_me: ... essendo il tuo primo post, nel rispetto del regolamento (… punto 13, primo capoverso), ti chiedo cortesemente di presentarti IN QUESTO THREAD (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con MOLTA attenzione il su citato REGOLAMENTO ... Grazie.

Guglielmo

Io_me:
Perdono ma, da come lo vedo io, non hai soluzione mantenendo questa scheda e questa lunghezza di stringa, in quanto quei dati che ricevi dovrai comunque utilizzarli in qualche modo, e la cosa userà comunque una quantità di RAM. ....

Non è esatto, appunto scrivevo che il programma si complicava ...
... lui dovrebbe fare una struttura per cui due cose lavorano i "parallelo"... la seriale, lavora ad interrupt e quindi, mentre riceve, lui con il blocco ricevuto in precedenza deve pilotare il motore, terminato di pilotare il motore, copia il nuovo blocco ricevuto dalla seriale in un suo buffer, da il via alla nuova ricezione e, mentre la ISR della seriale riceve, lui pilota il motore ... e così via.
Il problema è fare tutto piuttosto velocemente in modo da stare nella tempistica.
Guglielmo

gpb01:
Non è esatto, appunto scrivevo che il programma si complicava ...
... lui dovrebbe fare una struttura per cui due cose lavorano i "parallelo"... la seriale, lavora ad interrupt e quindi, mentre riceve, lui con il blocco ricevuto in precedenza deve pilotare il motore, terminato di pilotare il motore, copia il nuovo blocco ricevuto dalla seriale in un suo buffer, da il via alla nuova ricezione e, mentre la ISR della seriale riceve, lui pilota il motore ... e così via.
Il problema è fare tutto piuttosto velocemente in modo da stare nella tempistica.
Guglielmo

niente ragazzi, ho stampato i timestap il momento prima di inviare il messaggio ad arduino e il momento iin curi riceve il VAI sostituito con l'asterisco.

Invia il primo al secondo 1.697205, arduino fa 100 cicli con doppio delay da 100, quindi 10.000, e rilegge il VAI (*) al secondo 1.765255.

Quindi 1.765255 - 1.697205 - 10000(tempo che impiega a far girare il motore) = 58050.
Non va bene :frowning:

PS: io uso un arduino mega

Se usi un Arduno MEGA allora NON hai problemi a ricevere tutti e 2000 caratteri che ti servono, solo che devi farlo NON con la "pappa fatta" :smiling_imp: , ma ti devi fare tu il ciclo per la lettura a carattere della seriale e memorizzazione nell'array altrimenti NON ci riuscirai mai.

Guglielmo

In che senso devo farlo? Io lo già fatto che data la lunga stringa lo divide in due array e poi li cicla. L unico problema è che potrei avere array molto lunghi

Altrimenti ecco la soluzione facendo fare tutti i calcoli ad arduino.
Per adesso il pc non gli manda niente, ma potrei semplicemente mandare i parametri della funzione "move" dove gli dico al motore vai da 30 a 80 gradi tutto qui.

Quindi il mio arduino fa tutti i calcoli necessari e nel for popola i due array che per adesso sono di 600 elementi ciascuno.

Nella speranza di non superare mai i 600 ciascuno pensate che come approccio comunque vada bene?
Cosi di certo non ho problema di delay della seriale

int valD = 0;  
int valS = 0;  


        int l1a = 43;
        double l2a = 10;

        double cmStep = 1400;
  
        double uFatgrad = 17500;//1 secondo fa 10 gradi per millimetro 10
   double steparray[600]; 
           double delayarray[600]; 




int indexarray = 0;


void setup() {
  
   pinMode(7, OUTPUT);    pinMode(5, OUTPUT);
   pinMode(2, OUTPUT);


   digitalWrite(7, LOW); //ATtIVA MOTORE
   
   digitalWrite(5, LOW); //ATtIVA MOTORE
   
   digitalWrite(2, HIGH);

   pinMode(13, OUTPUT); //alimento lettura 13
   digitalWrite(13, HIGH); 
   pinMode(12, INPUT); //leggo il 12
   pinMode(8, OUTPUT); //alimento lettura 8
   digitalWrite(8, HIGH); 
   pinMode(9, INPUT); //leggo il 12


   pinMode(3, OUTPUT); //DIREZIONEEE
    pinMode(6, OUTPUT); //DIREZIONEEE

  Serial.begin(9600);
 pinMode(A0, INPUT);




 
}



   void move(double startDegree, double endDegree)
        {
  

        
          
           double oldcm = 0;

          

            
            for (double i= startDegree; i<= endDegree; i = i + 0.1)
            {
                double diff = 0;
                double cm = sqrt((l1a * l1a) + (l2a * l2a) - (2 * l1a * l2a * (cos(i * (PI / 180)))));
                diff = cm - oldcm;

                

                if (i == startDegree)
                {

                }
                else
                {
               steparray[indexarray] = diff * cmStep;; 
                delayarray[indexarray] =  uFatgrad / steparray[indexarray] ; 
        
            indexarray = indexarray + 1;
          
               }
                oldcm = cm;

            } 
            
            
               

      digitalWrite(3, HIGH);    digitalWrite(6, HIGH); 
  
        for(int xx = 0; xx < 600; xx++)
          {
      
               for(int stepx = 0; stepx < steparray[xx]; stepx++)
              {
                 
               digitalWrite(7, HIGH); digitalWrite(5, HIGH);
                  delayMicroseconds((int)delayarray[xx]);
                digitalWrite(7, LOW);  digitalWrite(5, LOW);
                 delayMicroseconds((int)delayarray[xx]);
              }
          }
      
           Serial.print("end");




            
            }


        
void loop() 
{

 
    

   String content = "";
      if (Serial.available()) {
        while (Serial.available()) 
        {
 
          content = Serial.readString();
      move(30,80);
       
        }
         
      }
 
}

gpb01:
Se usi un Arduno MEGA allora NON hai problemi a ricevere tutti e 2000 caratteri che ti servono, solo che devi farlo NON con la "pappa fatta" :smiling_imp: , ma ti devi fare tu il ciclo per la lettura a carattere della seriale e memorizzazione nell'array altrimenti NON ci riuscirai mai.

Guglielmo

il problema che ho avuto quando mandavo dal mio programma la lunga stringa (circa 200o caratteri) era che appena li leggevo con il serial.readstring se li stampavo subito dopo senza fare operazioni mi mangiava l array :frowning:

tonyhhkx:
il problema che ho avuto quando mandavo dal mio programma la lunga stringa (circa 200o caratteri) era che appena li leggevo con il serial.readstring se li stampavo subito dopo senza fare operazioni mi mangiava l array :frowning:

... cioè usavi la "pappa fatta" ... Serial.readString() :smiling_imp: ... tocca che lavori a un livello più basso o con i tempi NON ci starai mai !

Guglielmo