Pages: [1] 2   Go Down
Author Topic: [Dúvida] *Protocolo* de comunicação  (Read 2755 times)
0 Members and 1 Guest are viewing this topic.
Bom Princípio - RS
Offline Offline
Sr. Member
****
Karma: 0
Posts: 263
SOFTWARE DEVELOPER, HACKER, RASPBERRY/ARDUINO/QT ENTHUSIAST & METALLICA FAN
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Estou querendo criar um mini protocolo de comunicação entre meu arduino e o C++ onde estou a desenvolver uma interface básica para testes, só que estou com algum problema na hora de ler valores 'string' para poder mandar pro arduino.

Eu vou ter uma interface que vai ter que ficar lendo a temperatura do arduino, MAS o arduino atuaria como um terminal, ele recebe um comando e devolve um valor, então eu mandaria pro arduino
"temp" e ele responderia o valor lido pelo sensor, ...

o problema é que ele esta lendo t,e,m,p ... até aí beleza dava pra criar um "buffer" de leitura, só que cada comando vai ter uma quatidade de caracteres, então eu não sei a largura do buffer, dai estou tentando implementar algo que funcione assim:

comando - palavra sem espaços "temp","led" ...
parametros - "numeros ou palavras separados por 1 espaço"
caracater final - caracter que define o fim do parametro ";"

um exemplo:
ligaled 13; (arduino não devolve nada, só vai ligar o led na porta 13)
desligaled 13; (arduino não devolve nada, só vai desligar o led na porta 13)
temperatura; (arduino envia o valor da temperatura);


Com este mini protocolo eu sei dos 2 lados qual o caracter que define o fim de um comando, e também fica mais fácil pra saber no C++ quando vem a temperatura, e talz...

eu fiz esta função para retornar uma string com o valor lido, mas ainda retorna quebrando linha por estar ...
Code:
String serialRead(){
  char actualChar;
  String finalString;
  while (Serial.available() > 0) {
     actualChar = Serial.read();
     finalString.concat(actualChar);
  }
  return finalString;
}


Finalmente eu andei estudando um pouco esta lib: http://interactive-matter.eu/how-to/ajson-arduino-json-library/
Pra trabalhar com JSON, minha ideia era ter alguns comandos neste meu protocolo, que retornassem json pela serial, tipo
"read -general;"
iria retornar uma leitura dos sensores catalogados:
{"temperature":27.8,"trimpot":300}

Mas também não estou conseguindo mandar esta para serial a string final montada, como eu mostrei...
Só consegui fazer ao contrário, ao interpretar a string e separar os valores...






Logged

My Toys:
Raspberry Pi (Model B)
Arduino MEGA 1280: ( Shield LCD 16x2, Ethernet, RFID )
Freescale Kinetis KL25Z [/

'round the world...
Offline Offline
Faraday Member
**
Karma: 42
Posts: 3213
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Muita gente gosta de pensar que o arduino é humano... Não é! Entre temp ou AB a diferença é abismal para ti, memória e tamanho de programa e para o arduino não faz diferença nenhuma. Logo, se o que pretendes é comunicar entre o pc e o arduino o mais fácil que tens é mesmo fazer essa mudança.

Agora se pretendes ter o protocolo todo em ascii e compreensivel por humanos, vais ter mais dificuldades.
Um conselho, deixa a String de lado. Usar strings de C para isso sair de forma decente.

Não entendi em concreto o teu problema. Importas-te de explicar melhor?
Logged

Eu não sou o teu criado. Se respondo no fórum é para ajudar todos mediante a minha disponibilidade e disposição. Responder por mensagem pessoal iria contra o propósito do fórum e por isso evito-o.
Se realmente pretendes que eu te ajude por mensagem pessoal, então podemos chegar a um acordo e contrato onde me pagas pela ajuda que eu fornecer e poderás então definir os termos de confidencialidade do meu serviço. De forma contrária toda e qualquer ajuda que eu der tem de ser visível a todos os participantes do fórum (será boa ideia, veres o significado da palavra fórum).
Nota também que eu não me responsabilizo por parvoíces escritas neste espaço pelo que se vais seguir algo dito por mim, entende que o farás por tua conta e risco.

Dito isto, mensagens pessoais só se forem pessoais, ou seja, se já interagimos de alguma forma no passado ou se me pretendes convidar para uma churrascada com cerveja (paga por ti, obviamente).

Portugal
Offline Offline
Edison Member
*
Karma: 37
Posts: 1531
Pretending you know everything then you will learn nothing.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
ligaled 13; (arduino não devolve nada, só vai ligar o led na porta 13)
Muitos colegas aparecem com esta duvida.A minha resposta é para quê lhe mandar ligaled se o poderias fazer enviando para o arduino apenas 3 bytes : 0x0D 0X6F 0x3B  por exemplo.Como o Bubilindo te disse o microcontrolador nao fala como nós.
Se olhares para a ascii table (ja que pelo que dizes queres basear o teu protocolo em códigos ASCII) 0x0d representa 13 em decimal, 0x6F representa o carácter 'o' de on e para carácter terminador o 0x3B que equivale a ';'
Assim passando esses 3 bytes apenas consegues ligar e desligar o led 13, ou outros pinos, manipulando os byte.(para desligar podias usar o carácter que representa um ´f´
Repara que isto e muito mais fácil de lidar e é mais eficiente do que mandares ligaled13;
Do lado do c++ envias os comandos hex e do lado do arduino reconstroes a instruçao.Do lado do arduino a coisa é fácil de se fazer com uns cast ...
Idealmente devias ter um carácter também para marcar o inicio do comando ...

« Last Edit: May 19, 2013, 04:49:58 pm by HugoPT » Logged

Debian,Mint,Ubuntu
Arduino Mega 2560
Arduino Nano
Arduino Duemilanove
MAC OS Montain Lion
Raspberry PI Model B


Bom Princípio - RS
Offline Offline
Sr. Member
****
Karma: 0
Posts: 263
SOFTWARE DEVELOPER, HACKER, RASPBERRY/ARDUINO/QT ENTHUSIAST & METALLICA FAN
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Não entendi em concreto o teu problema. Importas-te de explicar melhor?

Meu problema é a interrupção de código, pois ele manda por exemplo a letra A e ele "encerra" o envio, dai abre outro "envio" ´para enviar a próxima letra, tipo ele não fica preso no
Code:
while (Serial.available() > 0) {   }

Daí ele não 'junta' a string para me enviar o comando, eu queria montar tipo uma string inteira de comando ex: 'meucomando parametro1 parametroN', dai depois disto em iria separar os 'blocos' de string pelo espaço ficando com um array de strings sendo:
Code:
[0] -> comando
[1] -> parametro 1
[N] -> parametro N

Depois de separados o comando e os parâmetros eu iria procurar o método dele em um switch, e só ia chamar os parâmetros existentes, tipo,

Code:
  switch code[0]:
     case 'ligaled':
        digitalWrite(code[1].toInt(), HIGH);
        break;
     case 'desligaled':
       digitalWrite(code[1].toInt(), LOW);
       break; 

e assim por diante...
Logged

My Toys:
Raspberry Pi (Model B)
Arduino MEGA 1280: ( Shield LCD 16x2, Ethernet, RFID )
Freescale Kinetis KL25Z [/

'round the world...
Offline Offline
Faraday Member
**
Karma: 42
Posts: 3213
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

mas isso e o computador, correcto? não o arduino, ou estyou a confundir?
Logged

Eu não sou o teu criado. Se respondo no fórum é para ajudar todos mediante a minha disponibilidade e disposição. Responder por mensagem pessoal iria contra o propósito do fórum e por isso evito-o.
Se realmente pretendes que eu te ajude por mensagem pessoal, então podemos chegar a um acordo e contrato onde me pagas pela ajuda que eu fornecer e poderás então definir os termos de confidencialidade do meu serviço. De forma contrária toda e qualquer ajuda que eu der tem de ser visível a todos os participantes do fórum (será boa ideia, veres o significado da palavra fórum).
Nota também que eu não me responsabilizo por parvoíces escritas neste espaço pelo que se vais seguir algo dito por mim, entende que o farás por tua conta e risco.

Dito isto, mensagens pessoais só se forem pessoais, ou seja, se já interagimos de alguma forma no passado ou se me pretendes convidar para uma churrascada com cerveja (paga por ti, obviamente).

Bom Princípio - RS
Offline Offline
Sr. Member
****
Karma: 0
Posts: 263
SOFTWARE DEVELOPER, HACKER, RASPBERRY/ARDUINO/QT ENTHUSIAST & METALLICA FAN
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Na verdade, o software do computador escreve este parâmetro, monta o código e manda para o arduino, pois eu não sei quando vou precisar de tal coisa, e não sei se seria correto ficar enviando a todo momento...

Por exemplo:
Um Arduino + LM35 (Sensor de Temperatura), e no PC Software(C++) mostra temperatura, e tem um botão atualizar, cada vez que eu clico no botão atualizar eu mandaria via serial para o arduino:
Code:
getTemperature

O Arduino iria passar este código pelo switch até encontrar um "match" e devolver para o software, via serial, o valor da temperatura:
Code:
28.7

Dai o software quando mandou o getTemperature já sabe que o arduino vai responder a temperatura, e fica esperando, é como se fosse um identificador...

A ideia é que eles 'conversem', um pede algo, o outro responde, para mim saber o que o arduino irá mandar, meio orientado a evento, só responde quando pedir, ...

Outra forma seria o arduino enviar a string em XML ou JSON com um resultado parcial de tudo que ele estivesse fazendo, só que eu não consegui fazer ele montar isso, até achei inviável ficar mandando tudo ...
MAS em alguns casos de informações que precisam ser atualizadas a todo momento, o arduino teria que simplesmente mandar em de alguma forma um array de informações, em algum padrão, no caso JSON ou XML...

Code:
{
 temperature:{
    sensor1:28.7,
    sensor2:29.2
  },
 outputs:{
   1:0,
   2:1,
   3:1
  }
}
ou
Code:
<xml>
<temperature>
    <sensor1>28.7</sensor1>
    <sensor2>29.2</sensor2>
</temperature>
<outputs>
    <1>0</1>
    <2>1</2>
    <3>1</3>
</outputs>
</xml>

 
« Last Edit: May 20, 2013, 01:14:58 pm by MarceloBoeira » Logged

My Toys:
Raspberry Pi (Model B)
Arduino MEGA 1280: ( Shield LCD 16x2, Ethernet, RFID )
Freescale Kinetis KL25Z [/

Aracaju, SE, Brasil
Offline Offline
Sr. Member
****
Karma: 4
Posts: 323
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Eu também estava com problema de comunicação nesse mesmo modelo (Master / Slave), e descobri que boa parte dos meus problemas estavam ligados a não ter um protocolo robusto o suficiente. Dá uma olhada no meu tópico.

Resolvi o problema utilizando o protocolo descrito neste link. O protocolo é dessa forma:

Code:
Byte 1: Start Byte ( 0 hexadecimal ).
Byte 2-3: ASCII Arduino's address.
Byte 4: Byte ENQ, ACK or NAK (0x05h, 0x06h y 0x15h) .
Byte 5: ASCII Requested command.
Byte 6 y 7: ASCII Function number.
Byte 8: Sign byte (Positive 0x20h y Negative 2D)
Byte 9-12: ASCII of data bytes (0x00h-0xFFFFh)
Byte 13: Byte EOT (End of Text) (0x03h)
Byte 14-15: Checksum (addition from 2nd byte to 13th byte)

Em resumo: o protocolo define que cada mensagem tem 15 bytes. Isso é fechado, toda mensagem tem que caber em 15 bytes. E no Arduino, só leio os valores do Serial quando Serial.available == 15. Daí leio tudo, e cada byte tem o seu significado.

O que o HughPT falou é válido: define um protocolo mais simples, e com o número de bytes sempre igual (3, 4, 5, quantos você conseguir fazer), e deixa as regras do seu protocolo escritas. Esse protocolo que postei acima, de 15 bytes, tem uma possibilidade quase infinita de comandos, e ainda conta com verificação de dados (checksum).
Logged

Bom Princípio - RS
Offline Offline
Sr. Member
****
Karma: 0
Posts: 263
SOFTWARE DEVELOPER, HACKER, RASPBERRY/ARDUINO/QT ENTHUSIAST & METALLICA FAN
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

O que eu estou a fazer varia, pois eu quero passar uma string do código, a fins de criar uma interface do tipo 'terminal', então varia quantidade de caracteres de cada operação.

Eu estava pensando e vou fazer algo assim:

Code:

void readSerialPort() {
  unsigned int bytesAmount = Serial.available(); 
 
  if (bytesAmount> 0) {
    char buffer[bytesAmount];
    for(int i = 0; i < bytesAmount; i++) {
        buffer[i] = Serial.read();
    }
    // Separar o buffer em  um array de comando e parâmetros, onde o espaço vai ser o caracter separador, o comando vai ser o índice 0 e os outros índices vão ser parâmetros
    // http://blog.prsolucoes.com/c/separando-ou-explodindo-uma-string-em-c/


    //varrer o switch com todos os parâmetros, e executar o necessário
    switch (command) {
      case "getTemperature":
            Serial.print(analogRead(A0);
            break;
      case "setLedPWM": // primeiro parâmetro deve ser o pino do led e o segundo deve ser o valor do pwm (0-255)
            analogWrite(params[0].toInt(),params[1].toInt());
            break;
      ... outros comandos ...

    }

  }
}

Claro que futuramente penso em jogar cada comando para uma void, e o switch só vai chamar o método daquele comando com os parâmetros corretos ...
____________________________

O que eu estou na dúvida é, o  Serial.available() já define mesmo a quantidade de caracteres enviados? tipo se eu enviar o comando "getTemperature" o  Serial.available() seria 14 ?
Se for desta forma eu não preciso necessariamente usar um carácter que vai me definir o fim do comando ...
Logged

My Toys:
Raspberry Pi (Model B)
Arduino MEGA 1280: ( Shield LCD 16x2, Ethernet, RFID )
Freescale Kinetis KL25Z [/

Aracaju, SE, Brasil
Offline Offline
Sr. Member
****
Karma: 4
Posts: 323
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

O que eu estou na dúvida é, o  Serial.available() já define mesmo a quantidade de caracteres enviados? tipo se eu enviar o comando "getTemperature" o  Serial.available() seria 14 ?
Se for desta forma eu não preciso necessariamente usar um carácter que vai me definir o fim do comando ...

Sim, desde que você não use o Serial.read(). Quando você usa o Serial.read(), o Serial.available() diminui em 1, porque você já leu o primeiro. No próximo Serial.read(), ele vai pegar o próximo char.

No seu caso, acho que o readBytesUntil é o mais apropriado.
Logged

Portugal
Offline Offline
Edison Member
*
Karma: 37
Posts: 1531
Pretending you know everything then you will learn nothing.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Claro que futuramente penso em jogar cada comando para uma void
O que é para ti uma void?
Quote
void readSerialPort() {
  unsigned int bytesAmount = Serial.available();  
  
  if (bytesAmount> 0) {
    char buffer[bytesAmount];
Ao ler o teu excerto de código fiquei a coçar a cabeça ao pensar o que ira acontecer na criação desse array. Nao devias usar antes um malloc ?
Nunca vi tal coisa, não vou dizer que isso não funcione porque nunca usei uma declaração assim mas estou com muitas duvidas que isso aloque memoria correctamente já que para criares um array tens de saber  à partida quantos elementos necessitas.Nessa função isso não é conhecido pois irá depender do que seja retornado pela funçao Serial.available()
Ja testaste isso?
Nao sei que comportamento o compilador ira gerar ...

« Last Edit: May 20, 2013, 06:25:04 pm by HugoPT » Logged

Debian,Mint,Ubuntu
Arduino Mega 2560
Arduino Nano
Arduino Duemilanove
MAC OS Montain Lion
Raspberry PI Model B


Bom Princípio - RS
Offline Offline
Sr. Member
****
Karma: 0
Posts: 263
SOFTWARE DEVELOPER, HACKER, RASPBERRY/ARDUINO/QT ENTHUSIAST & METALLICA FAN
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Claro que futuramente penso em jogar cada comando para uma void
O que é para ti uma void?
Quote
void readSerialPort() {
  unsigned int bytesAmount = Serial.available(); 
 
  if (bytesAmount> 0) {
    char buffer[bytesAmount];
Ao ler o teu excerto de código fiquei a coçar a cabeça ao pensar o que ira acontecer na criação desse array. Nao devias usar antes um malloc ?
Nunca vi tal coisa, não vou dizer que isso não funcione porque nunca usei uma declaração assim mas estou com muitas duvidas que isso aloque memoria correctamente já que para criares um array tens de saber  à partida quantos elementos necessitas.Nessa função isso não é conhecido pois irá depender do que seja retornado pela funçao Serial.available()
Ja testaste isso?
Nao sei que comportamento o compilador ira gerar ...


Void:
 Eu penso em ter voids para não deixar o código 'bruto' dos comandos no meio do switch ficando bagunçado o código ....
dentro do switch
Code:
case 'getTemperature':
      getTemperature();
      break;

E a void separada:
Code:
void getTemperature(){
   Serial.print(analogRead(A0));
}


Não testei nada ainda, são só suposições, e o código que coloquei é apenas um esboço, justamente para identificar as falhas e pensar antes de sair gravando código.

Como seria uma declaração correta HugoPT ?

Logged

My Toys:
Raspberry Pi (Model B)
Arduino MEGA 1280: ( Shield LCD 16x2, Ethernet, RFID )
Freescale Kinetis KL25Z [/

Portugal
Offline Offline
Edison Member
*
Karma: 37
Posts: 1531
Pretending you know everything then you will learn nothing.
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Eu penso em ter voids para não deixar o código 'bruto' dos comandos no meio
Eu fiz te essa pergunta porque da maneira que tu te referes a keyword void é como que void fosse a criaçao de uma funçao, o que nao é sabes disso correcto?
void é apenas um tipo de retorno de uma funçao( void quer dizer que a funçao declarada nao retorna nenhum valor )
Quote
void readSerialPort() {
  unsigned int bytesAmount = Serial.available();  
  
  if (bytesAmount> 0) {
    char buffer[bytesAmount];

Nesta funçao que chamaste readSerialPort que nao retorna nenhum valor (porque tens la void ) estas tentar criar um array dinamicamente, que do que eu sei nao é possivel, para isso tens de usar o malloc e depois o free para alocar memoria e depois a libertar.
« Last Edit: May 23, 2013, 09:43:46 am by HugoPT » Logged

Debian,Mint,Ubuntu
Arduino Mega 2560
Arduino Nano
Arduino Duemilanove
MAC OS Montain Lion
Raspberry PI Model B


Bom Princípio - RS
Offline Offline
Sr. Member
****
Karma: 0
Posts: 263
SOFTWARE DEVELOPER, HACKER, RASPBERRY/ARDUINO/QT ENTHUSIAST & METALLICA FAN
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Eu me refiro a void no sentido de "procedure"/procedimento, justamente por ela não ter retorno.

Vou tentar procurar um exemplo, mas eu já criei arrays assim 'dinâmicos' em C para desktop e eu inicializava ele com o tamanho do array de origem sempre, vou dar uma pesquisada...

Logged

My Toys:
Raspberry Pi (Model B)
Arduino MEGA 1280: ( Shield LCD 16x2, Ethernet, RFID )
Freescale Kinetis KL25Z [/

Bom Princípio - RS
Offline Offline
Sr. Member
****
Karma: 0
Posts: 263
SOFTWARE DEVELOPER, HACKER, RASPBERRY/ARDUINO/QT ENTHUSIAST & METALLICA FAN
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Testei este código, mas não funcionou corretamente:

Code:
void setup() {
  Serial.begin(9600);
}

void loop() {
  readSerialPort();
  delay(10);
}

void readSerialPort() {
  unsigned int bytesAmount = Serial.available(); 
  if (bytesAmount > 0) {
    Serial.print("Starting ReadPortSerial with: ");   
    Serial.println(bytesAmount);   
    char buffer[bytesAmount];
    for(int i = 0; i < bytesAmount; i++) {
        buffer[i] = Serial.read();
    }
    for(int i = 0; i < bytesAmount; i++){
       Serial.println("buffer[" + (String)i + "] = " + (String)buffer[i]);
    }
  }
}


Em teoria este código deveria pegar a quantidade de caracteres enviadas por serial, mostrar e depois listar o array que contém estes mesmos caracteres, o problema é que ele quebra na maioria das vezes a palavra e eu não faço ideia por que.

Eu enviei a palavra teste e ele quebrou e me retornou isto:
Code:
Starting ReadPortSerial with: 3
buffer[0] = t
buffer[1] = e
buffer[2] = s
Starting ReadPortSerial with: 3
buffer[0] = t
buffer[1] = e
buffer[2] =

O pior de tudo é que ele não quebra a palavra sempre no mesmo lugar, ele quebra aleatoriamente, tem vezes que ele mostra certo a palavra, e tem vezes que não.

Alguém sabe o motivo?
Logged

My Toys:
Raspberry Pi (Model B)
Arduino MEGA 1280: ( Shield LCD 16x2, Ethernet, RFID )
Freescale Kinetis KL25Z [/

Bom Princípio - RS
Offline Offline
Sr. Member
****
Karma: 0
Posts: 263
SOFTWARE DEVELOPER, HACKER, RASPBERRY/ARDUINO/QT ENTHUSIAST & METALLICA FAN
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Consegui dar uma melhorada, agora ele não quebra mais a string, MAS ele deixa sujeira no buffer, tipo se eu envio uma palavra de tipo AMARELO, e depois TESTE ele me manda na segunda vez TESTELO, acredito eu que o LO tenha ficado no buffer da Serial. E eu estou chamando Serial.flush para tentar limpar, mas nada.

Segue o código:

Code:

const int SERIAL_BUFFER_MAX_SIZE = 64;

void setup() {
  lcd.begin(16,2);
  Serial.begin(9600);
}

void loop() {
  readSerialPort();
  delay(10);
}

void readSerialPort() {
  unsigned int bytesAmount = Serial.available(); 
  if (bytesAmount > 0) {
    char buffer[SERIAL_BUFFER_MAX_SIZE];
    Serial.readBytesUntil('\n', buffer, SERIAL_BUFFER_MAX_SIZE);
    Serial.println(buffer);
    Serial.flush();
  }
}
Logged

My Toys:
Raspberry Pi (Model B)
Arduino MEGA 1280: ( Shield LCD 16x2, Ethernet, RFID )
Freescale Kinetis KL25Z [/

Pages: [1] 2   Go Up
Jump to: