Envio de informação via Serial para outro arduino

Boas malta.

Tou aqui com umas duvidas.
O que pretendo é o seguinte.
Tenho 2 Arduinos. O 1º lê um certo valor de uma entrada analógica. Em seguida envia esse valor para o outro Arduino, que depois escreve o mesmo em um lcd. O meu problema esta na transmissão entre os dois.

No 1º

....
loop()
{
valor = analogRead(0);
Serial.println(valor);
}

Até aqui tudo bem e agora no segundo como faço o tratamento da informação?

Desde já obrigado.

Primeiro ligas o RX ao TX e vice-versa. Depois fazes isto no segundo:

void loop(){
char valor[5];
unsigned int fromOther = 0; 

 
   if (Serial.available() > 3) { //não é ideal...
      for (int i = 0; i <5; i++) {
          if ((valor[i] = Serial.read()) != '\n') {
             valor[i] = '\0'; 
             break; //sai deste for.
          }//end if 
      }//end for
   fromOther = atoi(valor);
   }
}

Não sei o que se vai passar com os chips USB ainda ligados nos dois controladores...

Há uma maneira mais simples... sem teres de enviar dados em ASCII.

Como os dois arduinos estão ligados, por cabo nos pinos TX/RX -> RX/TX?

Presumo que já sabes como imprimir no LCD, então o que te falta é ler a serial?

se for isso, testa o abaixo:

String SerialBuffer = "";

void setup(){
  SerialBuffer.reserve(200);

  Serial1.begin(9600); //os dois devem ter o mesmo rate
}

void loop(){
  if (Serial1.available() > 0){
    while (Serial1.available() != 0) {
      char inChar = (char)Serial1.read(); 
      
      if (inChar == '\n') {
        //Imprime no LCD a string do SerialBuffer
        SerialBuffer = "";
      }
      else {
        SerialBuffer += inChar; \\Add to buffer
      }
    }
  }
}

Assim o primeiro arduino imprime uma linha na serial e o segundo recebe todos os bytes, armazena no buffer (string) e quando encontrar a quebra de linha você pega o conteúdo do buffer e imprime no LCD.

bubulindo, the flash again kkkk Ou eu é que sou lento? lol

String SerialBuffer = "";

String , de todo evitavel sendo que o pretendido é apenas ler uns bytes da Serial
Usa apenas isso em ultimo recurso a tudo.
Nao sei se sabes mas isso e um objecto e nao um tipo de dados nativo do C!

fromOther = atoi(valor);

Visto ele querer colocar isso num LCD e o LCD apenas recebe strings se calhar aqui será desnecessario converter para int

HugoPT:

String SerialBuffer = "";

String , de todo evitavel sendo que o pretendido é apenas ler uns bytes da Serial
Usa apenas isso em ultimo recurso a tudo.
Nao sei se sabes mas isso e um objecto e nao um tipo de dados nativo do C!

Olá HugoPT!

Estás correto! Eu uso o acima para alguns comandos e variáveis via serial, o uso de string nesse caso facilita a comparação, exemplo SerialBuffer == "IrSensor(99,99,99)".

Neste último caso, você sugere um char array no lugar de string?

Neste último caso, você sugere um char array no lugar de string?

Sim sem duvida.Para fazer comparaçoes podes sempre usar o strcmp() para fazeres comparaçoes entre strings

HugoPT:
Sim sem duvida.Para fazer comparaçoes podes sempre usar o strcmp() para fazeres comparaçoes entre strings

Valeu a Dica!!! Farei testes com strcmd() :smiley:

Quando programamos no ambiente Windows temos certos hábitos que levamos para outra linguagens.

Obrigado!

Mortis:
Como os dois arduinos estão ligados, por cabo nos pinos TX/RX -> RX/TX?

Presumo que já sabes como imprimir no LCD, então o que te falta é ler a serial?

se for isso, testa o abaixo:

String SerialBuffer = "";

void setup(){
 SerialBuffer.reserve(200);

Serial1.begin(9600); //os dois devem ter o mesmo rate
}

void loop(){
 if (Serial1.available() > 0){
   while (Serial1.available() != 0) {
     char inChar = (char)Serial1.read();
     
     if (inChar == '\n') {
       //Imprime no LCD a string do SerialBuffer
       SerialBuffer = "";
     }
     else {
       SerialBuffer += inChar; \Add to buffer
     }
   }
 }
}




Assim o primeiro arduino imprime uma linha na serial e o segundo recebe todos os bytes, armazena no buffer (string) e quando encontrar a quebra de linha você pega o conteúdo do buffer e imprime no LCD.

Sim estão ligados TX/RX -> RX/TX
Desculpem a minha ignorância mas sou novo nestas coisas... :slight_smile:

Quanto ao codigo, Serial1 é por algum motivo em especial? As placas que estou a usar são Uno, não será apenas Serial?

Não existe uma maneira mais simples?
Por exemplo...

Arduino 1 lê valor de 975 na analógica 0
leituraa = analogRead(0);
envia para Arduino 2
Serial.println(leituturaa);

No Arduino 2 algo tipo do género
leiturab = Serial.read(); sendo o valor de "leituraa" igual a 975...
lcd.setCursor(0,0);
lcd.print(leiturab,1);

Isto é possível?

leiturab = Serial.read(); sendo o valor de "leituraa" igual a 975...

975 ao serem enviados pela porta serie cada digito ocupa um byte.
logo tens de ler todos os bytes e reconstruir o numero.
Nao é possivel duma só vez

joaquim_lopes:
Quanto ao codigo, Serial1 é por algum motivo em especial? As placas que estou a usar são Uno, não será apenas Serial?

Perdoa-me, mas eu copiei partes do meu código e eu uso o Mega, que possui mais portas seriais. :roll_eyes:

Sobre ser novato, eu também sou, então não se preocupe! Mas logo mais deixaremos de ser :wink:

joaquim_lopes:
Arduino 1 lê valor de 975 na analógica 0
leituraa = analogRead(0);
envia para Arduino 2
Serial.println(leituturaa);

No Arduino 2 algo tipo do género
leiturab = Serial.read(); sendo o valor de "leituraa" igual a 975...
lcd.setCursor(0,0);
lcd.print(leiturab,1);

Isto é possível?

Bom, primeiro você precisa saber se tem dados na serial, então não tem como fugir do Serial.available()

O segundo o HugoPT já respondeu, você terá de passar pelo Serial.read() três vezes, uma para cada byte, logo não tem como fazer de forma diferente daquela que o bubulindo exemplificou.

Ok... depois de ler um pouco sobre o Serial... experimenta algo assim:

Arduino 1

void loop() {

unsigned int valor = 0;

valor = analogRead(A0);
Serial.write(valor >> 8);
Serial.write(valor);
Serial.println(); //importante
}

no Arduino 2

void loop() {
  unsigned int value = 0;
  if (Serial.available() >=3) {//dois bytes do int mais carriage return
    value = (Serial.read()<<8) + Serial.read();
    Serial.read();//skip \n
    Serial.print("received ");
    Serial.println(value);
  }
}

Assim é melhor porque são sempre apenas enviados 2 bytes. Se enviares em ASCII podes enviar entre 2 a 5 bytes (ou 6 se olhares ao tamanho dum int) e isso é bem mais complicado de programar. Também é mais rápido...

Nota que não coloquei aqui protecções para sincronizar ambos os arduinos. Ou seja, têm de ser ligados ambos ao mesmo tempo para garantir que um está a ouvir o que o outro está a mandar, mas isso é simples de fazer colocando o Arduino 2 à espera do \n.

Esta é uma maneira mais simples e correcta de fazer isto. :slight_smile:

Bom, abaixo como ficaram meus códigos, para o Joaquim Lopes pode ser um próximo passo, passar o valor de vários sensores e ou comandos entre os arduinos (?).

Eu executo uma série de comandos via bluetooth, esse é para eu gravar os valores de referencia para sensores IR de um seguidor de linhas, valores usados para aumentar ou reduzir a sensibilidade das condições.

Lembrando que ainda sou muito noob! :disappointed_relieved:

char SerialBuffer[100]; //sem String aqui rs
byte CharIndex = 0;

int paramR = 0; //Right Sensor ref
int paramC = 0; //Center Sensor ref
int paramL = 0; //Left Sensor ref

void setup(){
  Serial1.begin(57600);
}

void loop(){
  if (Serial1.available() > 0){
    while (Serial1.available() != 0) {
      char inChar = (char)Serial1.read(); 
      
      if (inChar == '\n') {
        ExecuteCommand(SerialBuffer);

        CharIndex = 0;
        memset(SerialBuffer, 0, 100);
      }
      else {
        SerialBuffer[CharIndex] = inChar;
        CharIndex++;
      }
    }
  }
}

void ExecuteCommand(char Value[]){
  char Command[100];

  strcpy(Command, Value);

  char *chpt = strtok(Value, "("); //Split string into tokens
  if (chpt == NULL) {
      Serial1.println("No Command in the First strok");
      return;
  }
  
  if(strcmp(chpt, "SetSensorParams") == 0)
    SetSensorParams(Command);
}

void SetSensorParams(char Params[]){
  int newParamL,newParamC,newParamR;

  Serial1.print("Values: ");
  Serial1.println(Params);

  int errors = 0;
  
  char *chpt = strtok(Params, "("); //Split string into tokens
  if (chpt == NULL) {
      Serial1.println("Params strok returns NULL");
      ++errors;
  }
  
  if (errors == 0) {
      chpt = strtok(NULL, ",");
      if (chpt == NULL) {
          Serial1.println("First param strok returns NULL");
          ++errors;
      }
      else {
          newParamL = atoi(chpt); //Convert to integer
      }
  }
  
  if (errors == 0) {
      chpt = strtok(NULL, ",");
      if (chpt == NULL) {
          Serial1.println("Second param strok returns NULL");
          ++errors;
      }
      else {
          newParamC = atoi(chpt);
      }
  }
  
  if (errors == 0) {
      chpt = strtok(NULL, ")");
      if (chpt == NULL) {
          Serial1.println("Third Param strok returns NULL");
          ++errors;
      }
      newParamR = atoi(chpt);
  }
  
  if (errors == 0) {
    paramL = newParamL;
    paramC = newParamC; 
    paramR = newParamR;
  }
}

o que vocês acham?

HugoPT, esta melhor assim?

PS: Lembrando que eu uso o Serial1 do arduino mega, assim posso trabalhar com a serial usb e bluetooth simultaneamente

Assim por uma vista de olhos rapida parece que está la tudo.
Ha algumas coisas que podem ser melhoradas a titulo de uma boa programaçao.
Vejamos aqui:

void ExecuteCommand(char Value[]){

Estas a passar um parametro com o Value[].Quando passas um array ele por si só é um ponteiro para a primeira posiçao do vector.Eu teria colocado ou esperar um ponteiro em vez de Value[]
Eu modificava para:

ExecuteCommand(char * Value)

Agora ha aqui outra questao. O vector que estas a passar é global o que poderá nao fazer muito sentido passa-lo como argumento na funçao...
Irá funcionar na mesma assim.

char Command[100];

strcpy(Command, Value);

Se passas o vector por parametro porque é que depois o copias para outro array dentro da funçao?
A idea é usalo la dentro nao copia-lo.
Assim comeste mais 100 bytes de RAM para nada.Faz uso dos ponteiros!

Hmm, acho que nos estamos a deixar levar por dois problemas diferentes.

O do Mortis em que uma pessoa comunica com o Arduino (já agora, tens um código extremamente complexo para algo tão simples) e o do joaquim em que dois arduinos comunicam entre si.
No problema do Mortis, os humanos preferem ASCII então a comunicação é feita assim e como tal tem um overhead maior e é mais complexo.
No caso do Joaquim é escusado colocar duas máquinas a falar linguagem de humanos (ASCII) já que elas se entendem melhor em binário e a programação é bem menos complexa como exemplifiquei em cima.

A única coisa que me preocupa é o efeito que o chip FTDI tem na comunicação... Alguém experimentou os dois códigos?

Antes de mais obrigado a todos.

Tal como disse, sou novato nestas coisas e alguma linguagem passa-me ao lado...
mas vou testar os vários códigos e depois dou o meu feedback.

HugoPT:

char Command[100];

strcpy(Command, Value);

Se passas o vector por parametro porque é que depois o copias para outro array dentro da funçao?
A idea é usalo la dentro nao copia-lo.
Assim comeste mais 100 bytes de RAM para nada.Faz uso dos ponteiros!

HugoPT,

Eu copio o array porque após o strtok eu via apenas "SetSensorParams" exemplo:
Value passado "SetSensorParams(200,300,200)"

char *chpt = strtok(Value, "(");

O resultado era
chpt = "SetSensorParams"
Value = "SetSensorParams"

Farei mais testes e seguirei teus conselhos.

Muitíssimo obrigado.

bubulindo:
Hmm, acho que nos estamos a deixar levar por dois problemas diferentes.

Desculpem-me, acho que me empolguei e não tinha a intenção de complicar =/

Por favor, voltemos ao problema do tópico:

bubulindo:
A única coisa que me preocupa é o efeito que o chip FTDI tem na comunicação... Alguém experimentou os dois códigos?

A qual efeito te referes?

joaquim_lopes:
Antes de mais obrigado a todos.

Tal como disse, sou novato nestas coisas e alguma linguagem passa-me ao lado...
mas vou testar os vários códigos e depois dou o meu feedback.

Joaquim, desculpa-me se compliquei muito para ti, o bubulindo esta correto, os códigos que coloco estão muito longe da linguagem de comunicação entre máquinas (estou habituado a programar para Windows :(), diga-nos o que ocorreu por ai e se podemos ajudar!

Malta após alguns teste eis o que consegui até agora.

No Arduino 1

loop()
{
val = analogRead(0);
Serial.write("T");
Serial.write(val);
delay(1500);
}

No Arduino 2

loop()
{
if ((incomingByte)=='T')
{
valor = Serial.read();
lcd.setCursor(0,1);
lcd.print(valor);
}
}

O que acontece é que isto funciona desde que o valor enviado (neste caso val) não seja superior a 255, caso isso aconteça o Arduino 2 lê outro valor
Por exemplo

Arduino 1 - valor enviado -> Arduino 2 - Valor LCD
0 -> 0
1 -> 1
... -> ...
255 -> 255
256 -> 0
257 -> 1
258 -> 2
259 -> 3
260 -> 4
261 -> 5
262 -> 6
263 -> 7
264 -> 8
265 -> 9
266 -> 10
267 -> 11
268 -> 12
269 -> 13
.... - > ....

Ou seja, de 255 em 255 ele reinicia a contagem... :frowning: alguma ideia?

O que acontece é que isto funciona desde que o valor enviado (neste caso val) não seja superior a 255, caso isso aconteça o Arduino 2 lê outro valor
Por exemplo

Arduino 1 - valor enviado -> Arduino 2 - Valor LCD
0 -> 0
1 -> 1

Logico o valor maximo de um byte é 255 no caso de ser unsigned !
Como escreves alem disso ele da a volta e recomeça de 0

Serial.write(val);

Voltamos a mesma questao.Nao consegues passar o valor todo num só byte!
O valor maximo que pode ser lido no ADC é 1023 ou seja necessitas de no minimo 4 bytes e talvez mais um para usar um caracter terminador