Acquisire e processare stringhe da seriale

Ciao a tutti,
Ho l'intenzione di leggere i valori di una stringa creata da processing su arduino. Fin'ora ho creato questo codice che ovviamente non fa cambiare i valori dei pin:

char serialin[] = "";

String motor1[] = "";
String motor2[] = "";
String motor3[] = "";
String motor4[] = "";

int motor1val = 0;
int motor2val = 0;
int motor3val = 0;
int motor4val = 0;

int i = 0;

void setup() {
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(9, OUTPUT);
}
void loop() {
  if (Serial.available() > 0) {
    while (Serial.available() > 0) {
      serialin[i] = char(Serial.read());
      i++;
    }
    motor1 += int(serialin[0]);
    motor1 += int(serialin[1]);
    motor1 += int(serialin[2]);
    
    motor2[0] = serialin[4];
    motor2[1] = serialin[5];
    motor2[2] = serialin[6];
    
    motor3[0] = serialin[8];
    motor3[1] = serialin[9];
    motor3[2] = serialin[10];
    
    motor4[0] = serialin[12];
    motor4[1] = serialin[13];
    motor4[2] = serialin[14];
    
    motor1val = atoi(motor1);
    motor2val = atoi(motor2);
    motor3val = atoi(motor3);
    motor4val = atoi(motor4);
    
    Serial.flush();
    }
    
    analogWrite(3, motor1val);
    analogWrite(5, motor2val);
    analogWrite(6, motor3val);
    analogWrite(9, motor4val);
}

esattamente la stringa seriale che processing produce e invia è:

Ü,´,?,?

o qualcosa di simile, ovviamente i caratteri cambiano in base al numero di input dell'utente e devono essere convertiti mediante la tabella ascii per trovare l'esatto valore.

Qualcuno che sappia darmi una mano :)?

while (Serial.available() > 0) {
      serialin[i] = char(Serial.read());
      i++;
    }

così stai dando per scontato che tutta la stringa ti arrivi in blocco. Non è vero: per la seriale, ogni lettera (o meglio, ogni byte) è un discorso a parte. Certo SPESSO queste due cose coincidono, ma partire da una falsa assunzione è la via privilegiata per fare una brutta fine. Un pò come costruire una casa senza fondamenta, quando inizia a cascare c'è poco da fare. Quindi sta a te inventarti una lettera che indichi al tuo ciclo di lettura che la stringa è stata completata.

Da quì nasce il problema che arduino potrebbe leggere (per varie problematiche più rare delle precedenti) solo una parte della stringa: sta a te capire se una stringa è valida, o usando un carattere di "inizio stringa" oppure, se conosci a priori la lunghezza della stringa, a controllare che il numero di byte letti coincida.

poichè tu vuoi lavorare con i valori grezzi, e fregartene altamente delle tabelle ascii, non vedo perchè tiri in mezzo atoi: Ü per esempio, corrisponde ad un valore numerico, però se ci fai una atoi ottieni che Ü non corrisponde ad alcun numero, e quindi atoi ritorna un valore standard oppure un errore.

uhh se ti ho messo confusione non fare problemi a chiedere. diciamo che il motivo principale per cui non funziona è il numero 3, ma anche gli altri 2 sono importanti e te ne accorgeresti quando risolvi il punto 3 e ti ritrovi con i motori che ogni tanto (o sempre, dipende dal baudrate impostato e dalla durata del loop) che impazziscono lo stesso

Hey ti devo fare un ringraziamento speciale per la fedeltà con cui mi aiuti :smiley: :smiley:
Comunque, non ho ben capito, la seriale per me è un po' incasinata... Comunque ho provato a cambiare l'output di processing portandolo a una cosa del genere:

207,213,215,244

ma tutti i pin rimangono a 0 volt :\

ah ok, passimo per i char. va bene però quando fai

motor1 += int(serialin[0]);
    motor1 += int(serialin[1]);
    motor1 += int(serialin[2]);

metti che arriva 207 succede che fai

motor1 += 2; //errore, se motor != 0!
motor1 += 0;
motor1 += 7;
motor1 quindi è = a 9!!

questo errore è su tutte le letture. considera che poi non stai mettendo 2 0 o 7, perchè quelli sono il corrispettivo ashii: sti mettendo il valore nella tabella ascii che corrisponde a 2, 0 o 7. è quì che dovresti usare atoi, non alla fine!

bhè, almeno per la virgola va bene che ti sei ricordato di saltarla

Non è esattamente quello di cui ho bisogno ma mi accodo sperando di poter capirci qualcosa sulla seriale, che davvero, è una parte davvero mistica/oscura per me....

Allora, ho tolto quel pezzo di codice che hai preso in esame in quanto dava errore durante la compilazione(non chiedermi perchè). E sono rimasto con il codice che vedevi subito sotto al pezza da te esaminato.
Ora ho questo:

char serialin[] = "";

char motor1[] = "";
char motor2[] = "";
char motor3[] = "";
char motor4[] = "";

int motor1val = 0;
int motor2val = 0;
int motor3val = 0;
int motor4val = 0;

int i = 0;

void setup() {
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(9, OUTPUT);
}
void loop() {
  if (Serial.available() > 0) {
    while (Serial.available() > 0) {
      serialin[i] = char(Serial.read());
      i++;
    }
    motor1[0] = atoi(serialin[0]);
    motor1[1] = atoi(serialin[1]);
    motor1[2] = atoi(serialin[2]);
    
    motor2[0] = atoi(serialin[4]);
    motor2[1] = atoi(serialin[5]);
    motor2[2] = atoi(serialin[6]);
    
    motor3[0] = atoi(serialin[8]);
    motor3[1] = atoi(serialin[9]);
    motor3[2] = atoi(serialin[10]);
    
    motor4[0] = atoi(serialin[12]);
    motor4[1] = atoi(serialin[13]);
    motor4[2] = atoi(serialin[14]);
    
    motor1val = int(motor1);
    motor2val = int(motor2);
    motor3val = int(motor3);
    motor4val = int(motor4);
    
    Serial.flush();
    }
    
    analogWrite(3, motor1val);
    analogWrite(5, motor2val);
    analogWrite(6, motor3val);
    analogWrite(9, motor4val);
}

tuttavia mi restituisce error: invalid conversion from 'char' to 'const char*'

p.s. se non sai neanche le basi della seriale, se vuoi ho un buon video da consigliarti :wink:

Beh forse ho esagerato!
Scrivo sulla porta seriale cosa ritornano i componenti che uso e queste cose qua ma se devo inviare io qualcosa su porta seriale in modo da aggiorare una variabile su Arduino, beh, qui proprio zero!

Se mi vuoi linkare il video me lo guardo lo stesso, non si sa mai !

a si, scusa, atoi vuole in input una stringa non una lettera.
se osservi la tabella ascii, per trasformare una lettera nel corrispettivo valore inttero basta sottrarre il valore asseganto al carattere 0.

altrimenti potresti "rompere" la tua megastringa in tante piccole stringhe da 4 caratteri (3 cifre + '\0', ovvero carattere di fine stringa) e darle in pasto ad atoi. in questo modo avresti:
char a[4];
a[0] = '2'; a[1]= '0';a[2] = '9'; a[3] = '\0';
int num = atoi(a);

char serialin[] = "";

char motor1[4] = "";
char motor2[4] = "";
char motor3[4] = "";
char motor4[4] = "";

int motor1val = 0;
int motor2val = 0;
int motor3val = 0;
int motor4val = 0;

int i = 0;

void setup() {
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(9, OUTPUT);
}
void loop() {
  if (Serial.available() > 0) {
    while (Serial.available() > 0) {
      serialin[i] = char(Serial.read());
      i++;
    }
    motor1[0] = serialin[0]; 
    motor1[1] = serialin[1];
    motor1[2] = serialin[2]; 
    motor1[3] = '\0';
    motor1val = atoi(motor1);

    motor1[0] = serialin[4]; 
    motor1[1] = serialin[5];
    motor1[2] = serialin[6]; 
    motor2[3] = '\0';
    motor2val = atoi(motor3);

    motor1[0] = serialin[8]; 
    motor1[1] = serialin[9];
    motor1[2] = serialin[10]; 
    motor3[3] = '\0';
    motor3val = atoi(motor3);

    motor1[0] = serialin[12]; 
    motor1[1] = serialin[13];
    motor1[2] = serialin[14]; 
    motor4[3] = '\0';
    motor4val = atoi(motor4);

    Serial.flush();
  }

  analogWrite(3, motor1val);
  analogWrite(5, motor2val);
  analogWrite(6, motor3val);
  analogWrite(9, motor4val);
}

Ho modificato il codice in questo modo ma pur dando via seriale 220,220,220,220 gli output dell'arduino rimangono a 0v :\

ok, ora la conversione dovrebbe essere giusta... ora dobbiamo assicurarci che i caratteri che arrivano via seriale siano giusti!

questo loop avviene con una velocità di (sparo un valore) 100microSec. un carattere con baud a 9600 impiega 1/960 secondi ad essere inviato.

quindi ogni loop sei fosrtunano se leggi anche solo UN carattere... come ti dicevo devi trovare un modo per rallentare il loop fino al ricevimento di tutta la stringa (non usare un delay, ma un carattere di fine stringa), oppure che aggiunge la lettera eventualmente letta ogni loop ad una stringa, e quando la stringa è abbastanza lunga (quindi completa) esegua la conversione.

in pratica immagina se io anzichè inviare solo questa risposta ti invii questa risposta in tanti messaggi da 3 o 5 o X caratteri a caso. Perchè tu possa ricavare un senso logico o aspetti che io finisca di scrivere e leggi tutto, oppure leggi pezzo per pezzo mano a mano che i messaggi arrivano e crei nella mente il significato.

una cosa di questo tipo potrebbe andar bene?

char serialin[] = "";

char motor1[4] = "";
char motor2[4] = "";
char motor3[4] = "";
char motor4[4] = "";

int motor1val = 0;
int motor2val = 0;
int motor3val = 0;
int motor4val = 0;

int i = 0;

void setup() {
  Serial.begin(9600);
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(9, OUTPUT);
}
void loop() {
  if (Serial.available() > 0) {
    while (Serial.available() > 0) {
      serialin[i] = char(Serial.read());
      i++;
    }
    if (sizeof(serialin) >= 15) {
    motor1[0] = serialin[0]; 
    motor1[1] = serialin[1];
    motor1[2] = serialin[2]; 
    motor1[3] = '\0';
    motor1val = atoi(motor1);

    motor1[0] = serialin[4]; 
    motor1[1] = serialin[5];
    motor1[2] = serialin[6]; 
    motor2[3] = '\0';
    motor2val = atoi(motor3);

    motor1[0] = serialin[8]; 
    motor1[1] = serialin[9];
    motor1[2] = serialin[10]; 
    motor3[3] = '\0';
    motor3val = atoi(motor3);

    motor1[0] = serialin[12]; 
    motor1[1] = serialin[13];
    motor1[2] = serialin[14]; 
    motor4[3] = '\0';
    motor4val = atoi(motor4);

    Serial.flush();
    }
  }

  analogWrite(3, motor1val);
  analogWrite(5, motor2val);
  analogWrite(6, motor3val);
  analogWrite(9, motor4val);
}

perchè ora i pin sono a 1v (non ci capisco più nullaaaaa D:)

quasi, scordati sizeof che in questo caso ti da sì la dimensione dell'array, ma essendo un array in realtà un puntatore alla cella di ram che possiede il primo elemento, ti restituisce la dimensione in byte del puntatore, ovvero 16 byte. confuso? vabbè lascia perdere.

devi usare una variabile contatore: aumenta di 1 ogni lettera letta e aggiunta alla stringona, e viene azzerata quando la stringona cancellata. diciamo che non esiste un altro metodo (bugia bugia bugia! ma prima studiati i puntatori)

perfetto, ora funziona :smiley: Il problema è solo uno, dato che processing invia i valori relativi a una barra di selezione, è possibile che invii valori a una o due cifre il che incasina un po' tutto, come mi regolo :/?

visto che i valori sono divisi da virgola, puoi fare un algoritmo che "smonta" i valori in base ad essa. oppure cambi l'algoritmo processing per fare in modo che invii sempre valori a 3 cifre, mettendo uno 0 davanti

Beh lesto, intanto grazie per la parte arduino :smiley:
Stavo pensado di fare una serie di if che controllano la grandezza del numero facendo una cosa simile a:
se (numero minoreougualea 9) { numero = 00numero }
se (numero minoreougualea 99 && maggiore di 9) { numero = 0numero }
ma come faccio a unire un int a un'altro int? o meglio come faccio fisicamente a aggiungere due zeri?

non puoi sugli int, devi giocare con le stringhe.

L'ho fatto mentre che mi rispondevi aggiungiendo questo pezzo di codice:

  if (motor1val >= 0 && motor1val <= 9) {
    String stringa="";
    stringa += char('0');
    stringa += char('0');
    stringa += int(motor1val);
    motor1val = int(stringa);
  }
  
  if (motor2val >= 0 && motor2val <= 9) {
    String stringa="";
    stringa += char('0');
    stringa += char('0');
    stringa += int(motor2val);
    motor2val = int(stringa);
  }
  
  if (motor3val >= 0 && motor3val <= 9) {
    String stringa="";
    stringa += char('0');
    stringa += char('0');
    stringa += int(motor3val);
    motor3val = int(stringa);
  }
  
  if (motor4val >= 0 && motor4val <= 9) {
    String stringa="";
    stringa += char('0');
    stringa += char('0');
    stringa += int(motor4val);
    motor4val = int(stringa);
  }

facendolo diventare:

import processing.serial.*;
Serial myPort;  // Create object from Serial class

int motor1val = ' ';
int motor2val = ' ';
int motor3val = ' ';
int motor4val = ' ';

PFont f;

float percent1 = 0;
float percent2 = 0;
float percent3 = 0;
float percent4 = 0;

void setup() {
  size(220, 210);
  f = loadFont("DEF.vlw");  
  myPort = new Serial(this, "COM3", 9600);
}
void draw() {
  background(100);

  if (mousePressed && mouseX > 10 && mouseX < 210 && mouseY > 30 && mouseY < 50) {
    percent1 = map(int(mouseX), 10, 210, 0, 100);
  }
  if (mousePressed && mouseX > 10 && mouseX < 210 && mouseY > 80 && mouseY < 100) {
    percent2 = map(int(mouseX), 10, 210, 0, 100);
  }
  if (mousePressed && mouseX > 10 && mouseX < 210 && mouseY > 130 && mouseY < 150) {
    percent3 = map(int(mouseX), 10, 210, 0, 100);
  }
  if (mousePressed && mouseX > 10 && mouseX < 210 && mouseY > 180 && mouseY < 200) {
    percent4 = map(int(mouseX), 10, 210, 0, 100);
  }

  //trasformo float in int
  int perc1 = int(percent1);
  int perc2 = int(percent2);
  int perc3 = int(percent3);
  int perc4 = int(percent4);

  //Disegno quadrati neri per indicare una possibile barra
  fill(50);
  stroke(0);
  rect(10, 30, 200, 20);
  rect(10, 80, 200, 20);
  rect(10, 130, 200, 20);
  rect(10, 180, 200, 20);

  //Disegno le barre di stato dei motori
  fill(175);
  stroke(0);
  rect(10, 30, perc1*2, 20);
  rect(10, 80, perc2*2, 20);
  rect(10, 130, perc3*2, 20);
  rect(10, 180, perc4*2, 20);

  //Stampo i testi di identificazione del motore
  fill(255);
  stroke(255);
  text("Valore motore 1", 10, 20);
  text("Valore motore 2", 10, 70);
  text("Valore motore 3", 10, 120);
  text("Valore motore 4", 10, 170);

  //Stampo i testi delle percentuali
  text(perc1+"%", 185, 20);
  text(perc2+"%", 185, 70);
  text(perc3+"%", 185, 120);
  text(perc4+"%", 185, 170);

  motor1val = int(map(perc1, 0, 100, 0, 225));
  motor2val = int(map(perc2, 0, 100, 0, 225));
  motor3val = int(map(perc3, 0, 100, 0, 225));
  motor4val = int(map(perc4, 0, 100, 0, 225));

  if (motor1val >= 0 && motor1val <= 9) {
    String stringa="";
    stringa += char('0');
    stringa += char('0');
    stringa += int(motor1val);
    motor1val = int(stringa);
  }
  
  if (motor2val >= 0 && motor2val <= 9) {
    String stringa="";
    stringa += char('0');
    stringa += char('0');
    stringa += int(motor2val);
    motor2val = int(stringa);
  }
  
  if (motor3val >= 0 && motor3val <= 9) {
    String stringa="";
    stringa += char('0');
    stringa += char('0');
    stringa += int(motor3val);
    motor3val = int(stringa);
  }
  
  if (motor4val >= 0 && motor4val <= 9) {
    String stringa="";
    stringa += char('0');
    stringa += char('0');
    stringa += int(motor4val);
    motor4val = int(stringa);
  }

  myPort.write(motor1val+","+motor2val+","+motor3val+","+motor4val);
  println(motor1val+","+motor2val+","+motor3val+","+motor4val);
  myPort.clear();
}

tuttavia i numeri continuano ad essere di 1/2/3 cifre :\

int(stringa)?

ricordati che stringa è un oggetto Stringa, che al suo interno contiene un sacco di cose tra cui un ARRAY di char che dovrebbe essere convertito con atoi...

a dir la verità ho provato prima con atoi ma non sembra essere compreso nel linguaggio di processing, quindi ho provato con int che comunque non dava errore... Tuttavia toglie gli zeri che metto =(

perchè in processing (leggi java) la classe possiede già i metodi per autotrasformarsi.

PERò se abbiamo detto che gli int non possono avere 0 a sinistra, se tu prendi l'int, lo trasformi in char, aggiunge gli zeri a sinistra, e lo ritrasformi in int, secondo te gli zeri rimangono? Implicitamente hai risposto sì, e hai sbagliato ]:smiley:

una volta che hai la stringa con gli 0, stampa direttamente quella!

purtoppo quello che queste librerie precotte ti nascondono è tanto, per esempio la println() di nascosto trasforma tutti i numeri in stringa, altrimenti verrebbero scritti come nel tuo primo post, ovvero la loro vera rappresentazione lato PC, e non in forma Ascii umana