Puerto serie C++. Mejorar el programa.

Iniciado por Meta, 16 Marzo 2017, 22:21 PM

0 Miembros y 10 Visitantes están viendo este tema.

ivancea96

Para empezar, es interesante saber qué hace exactamente ReadData: Retorna el número de caracteres leídos (y -1 si hubo un error o si leyó 0 caracteres). Dicho esto, ten en cuenta que las cadenas en C++ no son como en C#. De esos 50 caracteres, si solo lees 10, quedarán 40 como basura. Para evitar leerlos, basta poner un '\0' en el undécimo caracter:

Código (cpp) [Seleccionar]
int n = Puerto->ReadData(lectura, 49);
lectura[n + 1] = '\0';


Eso en primer lugar. Luego, si el retorno de ReadData es -1, no deberías mostrar el Recibido (deberías esperar primero a que haya bytes para leer, pues no tienen por qué trabajar a la vez el Arduino y el PC (viene siendo como sincronizar varios threads, vaya))

Y bueno, hecho eso, a ver qué problemas te da.

Meta

#11
Hola:

Enviar me envia todo bien, Arduino lo lee.

Recibir, seguí tu consejo, creo que no lo seguí como debería. C++ no lee nada. Seguro que hice algo pero muy mal.
Código (cpp) [Seleccionar]
#include <iostream>
#include <fstream>
#include <Windows.h>
#include "SerialClass.h"
using namespace std;

void main()
{
// Título de la ventana
SetConsoleTitle("Control Led Arduino.");

// Puerto serie.
Serial* Puerto = new Serial("COM4");

// Comandos para Arduino.
char Luz_ON[] = "Luz_ON"; // Envía "Luz_ON" al puerto serie.
char Luz_OFF[] = "Luz_OFF";
char lectura[50] = "\0"; // Guardan datos de entrada del puerto.

int opc; // Guarda un 1 o 2 tipo entero queintroduces desde la consola.

while (Puerto->IsConnected())
{
cout << endl; // Dejamos un retorno.
cout << "Introduzca la opcion deseada: " << endl << endl; // Muestra texto en pantalla.

cin >> opc; // Aquí introduces un número, el 1 o el 2.

switch (opc) // Espera recibir un 1 o un 2.
{
case 1:
// Mensaje saludar.
cout << "Enviando: " << Luz_ON << endl; // Muestra en pantalla textos.
Puerto->WriteData(Luz_ON, sizeof(Luz_ON) - 1); // Envía al puerto el texto "Luz_ON".
break;

case 2:
// Mensaje saludar.
cout << "Enviando: " << Luz_OFF << endl;
Puerto->WriteData(Luz_OFF, sizeof(Luz_OFF) - 1);
break;

default: // Si haz pulsado otro número distinto del 1 y 2, muestra
cout << "Puse del 1 al 2."; // este mensaje.
}

cout << endl; // Dejamos un retorno.
int n = Puerto->ReadData(lectura, 49); // Recibe datos desde el puerto y lo almacena en la variable "lectura".
lectura[n + 1] = '\0';
cout << "Recibido: " << lectura << endl; // Muestra en pantalla el texto o datos almacenado en la variable "lectura".
cout << "-------------------" << endl;
}
}


Lo que hace es, que si pulso uno, no lee nada. Si vuelvo a pulsar el 1, lee y lo indica bien. Eso si, por segunda vez.

Reinicio la aplicación. Pulso 1, no lee nada, ahora pulso el 2, me lee como si hubiera pulsado el 1.

Tiene que verse Luz OFF cuando pulso el 2.



:)
Tutoriales Electrónica y PIC: http://electronica-pic.blogspot.com/

ivancea96

Pero, lo dicho. Desde que pulsas enter y escribes al arduino hasta que lees del arduino... ¿Crees que le da tiempo al Arduino a procesar y responder?
Vamos a comprobar que no sea eso. Coloca un Sleep(100); antes del ReadData.

Meta

Va, me olvidé lo del Sleep. Jajajajjaaja.

Por cierto. No me funciona en 100, si en 500.
Código (cpp,2) [Seleccionar]
cout << endl; // Dejamos un retorno.
Sleep(500);
int n = Puerto->ReadData(lectura, 49); // Recibe datos desde el puerto y lo almacena en la variable "lectura".
lectura[n + 1] = '\0';
cout << "Recibido: " << lectura << endl; // Muestra en pantalla el texto o datos almacenado en la variable "lectura".
cout << "-------------------" << endl;

El Sleep a 150 funciona bien. Aunque hay gente que lo dejan a 500 pero se ve un poco lento. ;)

Código (cpp) [Seleccionar]
#include <iostream>
#include <fstream>
#include <Windows.h>
#include "SerialClass.h"
using namespace std;

void main()
{
// Título de la ventana
SetConsoleTitle("Control Led Arduino.");

// Puerto serie.
Serial* Puerto = new Serial("COM4");

// Comandos para Arduino.
char Luz_ON[] = "Luz_ON"; // Envía "Luz_ON" al puerto serie.
char Luz_OFF[] = "Luz_OFF";
char lectura[50] = "\0"; // Guardan datos de entrada del puerto.

int opc; // Guarda un 1 o 2 tipo entero queintroduces desde la consola.

while (Puerto->IsConnected())
{
cout << endl; // Dejamos un retorno.
cout << "Introduzca la opcion deseada: " << endl << endl; // Muestra texto en pantalla.

cin >> opc; // Aquí introduces un número, el 1 o el 2.

switch (opc) // Espera recibir un 1 o un 2.
{
case 1:
// Mensaje saludar.
cout << "Enviando: " << Luz_ON << endl; // Muestra en pantalla textos.
Puerto->WriteData(Luz_ON, sizeof(Luz_ON) - 1); // Envía al puerto el texto "Luz_ON".
break;

case 2:
// Mensaje saludar.
cout << "Enviando: " << Luz_OFF << endl;
Puerto->WriteData(Luz_OFF, sizeof(Luz_OFF) - 1);
break;

default: // Si haz pulsado otro número distinto del 1 y 2, muestra
cout << "Puse del 1 al 2."; // este mensaje.
}

cout << endl; // Dejamos un retorno.
Sleep(150);
int n = Puerto->ReadData(lectura, 49); // Recibe datos desde el puerto y lo almacena en la variable "lectura".
lectura[n + 1] = '\0';
cout << "Recibido: " << lectura << endl; // Muestra en pantalla el texto o datos almacenado en la variable "lectura".
cout << "-------------------" << endl;
}
}


Por lo que se ve, ahora toca lo del botón. He hace esto y no devuelve nada.


Código de ahora C++:
Código (cpp) [Seleccionar]
#include <iostream>
#include <fstream>
#include <Windows.h>
#include "SerialClass.h"
using namespace std;

void main()
{
// Título de la ventana.
SetConsoleTitle("Control Led 13 de Arduino UNO y saludos.");

// Nombre del puerto selecconado.
Serial* Puerto = new Serial("COM4");

// Comandos para Arduino.
char Luz_ON[] = "Luz_ON"; // Envía "Luz_ON" al puerto serie.
char Luz_OFF[] = "Luz_OFF";
char lectura[50] = "\0"; // Guardan datos de entrada del puerto.

// Mostrar texto en pantalla.
cout << "Pulse letra 'A' para endender Led y 'B' para apagar: " << endl;

while (Puerto->IsConnected())
{
// Tecla 'A' pulsada
if (GetAsyncKeyState('A') & 0x8000)
{
// Encener luz.
cout << "Enviando: " << Luz_ON << endl; // Muestra en pantalla textos.
Puerto->WriteData(Luz_ON, sizeof(Luz_ON) - 1); // Envía al puerto el texto "Luz_ON".
}

// Tecla 'B' pulsada
if (GetAsyncKeyState('B') & 0x8000)
{
// Apagar luz.
cout << "Enviando: " << Luz_OFF << endl;
Puerto->WriteData(Luz_OFF, sizeof(Luz_OFF) - 1);
}
}

cout << endl; // Dejamos un retorno.
Sleep(500); // Funciona a 150 para asegurarme en estas pruebas lo dejo a 500 por ahora.
int n = Puerto->ReadData(lectura, 49); // Recibe datos desde el puerto y lo almacena en la variable "lectura".
lectura[n + 1] = '\0';
cout << "Recibido: " << lectura << endl; // Muestra en pantalla el texto o datos almacenado en la variable "lectura".
cout << "-------------------" << endl;
}


Saludos.
Tutoriales Electrónica y PIC: http://electronica-pic.blogspot.com/

ivancea96

Bien. De todos modos, un Sleep es un parche para testear, pero no es correcto. Lo que quieres no es esperar 500 milisegundos; lo que quieres es esperar hasta que el Arduino te contexte (o si tarda mucho, poner un timeout, pero esto ya más adelante).
Podrías hacer un while hasta recibir la respuesta completa del Arduino. Un while con un Sleep(1) si tal, para no sobrecargar. Y en el while, pues eso, leer hasta recibirlo todo.

Meta

Buenas:

Sleep(1) como que no. ;)

Código (cpp,21) [Seleccionar]
#include <iostream>
#include <fstream>
#include <Windows.h>
#include "SerialClass.h"
using namespace std;

void main()
{
// Título de la ventana
SetConsoleTitle("Control Led Arduino.");

// Puerto serie.
Serial* Puerto = new Serial("COM4");

// Comandos para Arduino.
char Luz_ON[] = "Luz_ON"; // Envía "Luz_ON" al puerto serie.
char Luz_OFF[] = "Luz_OFF";
char lectura[50] = "\0"; // Guardan datos de entrada del puerto.

int opc; // Guarda un 1 o 2 tipo entero queintroduces desde la consola.
while (1)
{
while (Puerto->IsConnected())
{
cout << endl; // Dejamos un retorno.
cout << "Introduzca la opcion deseada: " << endl << endl; // Muestra texto en pantalla.

cin >> opc; // Aquí introduces un número, el 1 o el 2.

switch (opc) // Espera recibir un 1 o un 2.
{
case 1:
// Encener luz.
cout << "Enviando: " << Luz_ON << endl; // Muestra en pantalla textos.
Puerto->WriteData(Luz_ON, sizeof(Luz_ON) - 1); // Envía al puerto el texto "Luz_ON".
break;

case 2:
// Apagar luz.
cout << "Enviando: " << Luz_OFF << endl;
Puerto->WriteData(Luz_OFF, sizeof(Luz_OFF) - 1);
break;

default: // Si haz pulsado otro número distinto del 1 y 2, muestra
cout << "Puse del 1 al 2."; // este mensaje.
}


cout << endl; // Dejamos un retorno.
Sleep(500);
int n = Puerto->ReadData(lectura, 49); // Recibe datos desde el puerto y lo almacena en la variable "lectura".
lectura[n + 1] = '\0';
cout << "Recibido: " << lectura << endl; // Muestra en pantalla el texto o datos almacenado en la variable "lectura".
cout << "-------------------" << endl;
}
}
}


Acordarse que Arduino tiene un botón, si lo pulso, envía un ON o un OFF. Por ahora no me aparece en pantalla. C++ lo ignora. Pero nos estamos centrando en que si envío un 1 o un 2, responde Arduino también.

;)
Tutoriales Electrónica y PIC: http://electronica-pic.blogspot.com/

ivancea96

#16
No. Hablo de un while a la hora de recibir datos. No sirve solo "enviar y luego recibir". En un protocolo, hay que asegurarse de que se completa la comunicación. Si tratas de leer datos del puerto y no recibes nada, hay un problema. El Arduino no es instantáneo, probablemente trabajará mucho más lento que tu PC. Hay que esperar los datos:

Código (cpp) [Seleccionar]
while(true){
   int n = Puerto->ReadData(lectura, 49);
   if(n > 0){
       lectura[n + 1] = '\0';
       break;
   }
   Sleep(1);
}
cout << "Recibido: " << lectura << endl;
cout << "-------------------" << endl;


Por ejemplo.

Meta

Funciona bien mientras ocurre una cosa rara.
Código (cpp) [Seleccionar]
#include <iostream>
#include <fstream>
#include <Windows.h>
#include "SerialClass.h"
using namespace std;

void main()
{
// Título de la ventana
SetConsoleTitle("Control Led Arduino.");

// Puerto serie.
Serial* Puerto = new Serial("COM4");

// Comandos para Arduino.
char Luz_ON[] = "Luz_ON"; // Envía "Luz_ON" al puerto serie.
char Luz_OFF[] = "Luz_OFF";
char lectura[50] = "\0"; // Guardan datos de entrada del puerto.

int opc; // Guarda un 1 o 2 tipo entero queintroduces desde la consola.

while (Puerto->IsConnected())
{
cout << endl; // Dejamos un retorno.
cout << "Introduzca la opcion deseada: " << endl << endl; // Muestra texto en pantalla.

cin >> opc; // Aquí introduces un número, el 1 o el 2.

switch (opc) // Espera recibir un 1 o un 2.
{
case 1:
// Encener luz.
cout << "Enviando: " << Luz_ON << endl; // Muestra en pantalla textos.
Puerto->WriteData(Luz_ON, sizeof(Luz_ON) - 1); // Envía al puerto el texto "Luz_ON".
break;

case 2:
// Apagar luz.
cout << "Enviando: " << Luz_OFF << endl;
Puerto->WriteData(Luz_OFF, sizeof(Luz_OFF) - 1);
break;

default: // Si haz pulsado otro número distinto del 1 y 2, muestra
cout << "Puse del 1 al 2."; // este mensaje.
}


while (true) {
int n = Puerto->ReadData(lectura, 49);
if (n > 0) {
lectura[n + 1] = '\0';
break;
}
Sleep(1);
}
cout << "Recibido: " << lectura << endl;
cout << "-------------------" << endl;
}

}


Si pulso una tecla que no está en el Switch, se queda el programa bloqueado. Hablando de pulsar números, porque si pulsas letras se vuelve loco, ya que la variable de entrada es tipo entero, pero esto, ya es otra historia.

;)
Tutoriales Electrónica y PIC: http://electronica-pic.blogspot.com/

ivancea96

¡Desde luego!
Si no envías nada al Arduino, intuyo que el Arduino, en principio, no le envía nada al PC. por tanto, se va a quedar esperando.
Everything as expected
Tendrás que cambiar el flujo del programa si quieres que eso no ocurra. Además, imagínate que agregas una opción que envía datos al Arduino pero no los recibe. ¿Para qué esperar la salida del Arduino?
En fin, eso ya no es un error, solo un flujo incorrecto. Depende de cómo quieras ampliar el programa, modifícalo como más te convenga.

Meta

Hola:

El programa que quiero es:

1. Si pulso un número 1 o un 2 en C++, envía un mensaje a Aduino, luego Arduino le devuelve un mensaje.

2. Si pulso otro número que no está en el Switch Case, te salta elmensaje Default inficando que solo hay dos opciones para pulsar, tecla 1 y 2.

3. Si no se pulsa nada desde C++, Arduino tiene un botón, que al pulsarlo, envía mensajes llamado, ON o un OFF, C++ debe leer.

Por eso en C++ Win32 me cuesta mucho, en .net de C++ del CLI no cuesta tanto, más fácil pero no tiene ejecutable nativo, el Win32 si. Hay ventajas enuno y en la otra. ;)

Saludos.
Tutoriales Electrónica y PIC: http://electronica-pic.blogspot.com/