Puerto serie C++. Mejorar el programa.

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

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

Meta

Buenas:

Tengo un programa aquí que no está bien hecho del todo.

Envía datos al puerto seria, concretamente llega a Arduino. Los datos llegan bien, al recibir, ya no tanto.

Si pulsas la tecla, por ejemplo 5, primero se ve reflejado y luego tengo que pulsar Enter. Lo que quiero lograr es que si pulse el número 5, automáticamente ejecuta la instruacción del case 5 del Switch.

Otra cosa, es que no me llegan los datos desde Arduino cuando le pulso un botón. C++ tiene que ser capaz, desde que le llegue algún dato, reflejarlo en pantalla.

La librería a usar es esta.

https://github.com/Gmatarrubia/LibreriasTutoriales

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

void main()
{
SetConsoleTitle("Control Led 13 de Arduino UNO y saludos.");

Serial* Puerto = new Serial("COM4");

while (Puerto -> IsConnected())
{

// Comandos para Arduino.
char L_ON[] = "Led13_ON";
char L_OFF[] = "Led13_OFF";
char Saludar[] = "Hola Arduino. Saludos desde el PC con C++ bajo  Win32.";
char Luz_ON[] = "Luz_ON";
char Luz_OFF[] = "Luz_OFF";
char lectura[50];

int opc;

cout << "Introduzca la opcion deseada: ";

cin >> opc;

switch (opc)
{
case 1:
cout << "caso 1\n";
// Enviar encender Led.
cout << "Enviando: " << L_ON << endl;
Puerto -> WriteData(L_ON, sizeof(L_ON) - 1);
break;

case 2:
cout << "caso 2\n";
// Enviar apagar Led.
cout << "Enviando: " << L_OFF << endl;
Puerto -> WriteData(L_OFF, sizeof(L_OFF) - 1);
break;

case 3:
cout << "caso 3\n";
// Mensaje saludar.
cout << "Enviando: " << Saludar << endl;
Puerto -> WriteData(Saludar, sizeof(Saludar) - 1);
break;

case 4:
cout << "caso 4\n";
// Mensaje saludar.
cout << "Enviando: " << Luz_ON << endl;
Puerto->WriteData(Luz_ON, sizeof(Luz_ON) - 1);
break;

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

default:
cout << "Puse del 1 al 5.";
}

Puerto -> ReadData(lectura, 50);
cout << "Recibido: " << lectura << endl;

cout << "-------------------" << endl;

// system("PAUSE");
}
}


¿Alguna ayuda?

Un cordial saludo.

PD: Uso Visual Studio Community 2017, lenguaje C++ bajo Win32.
Tutoriales Electrónica y PIC: http://electronica-pic.blogspot.com/

ivancea96

Utilizando el input normal de la consola no puedes hacer esto que comentas.

Lo que sí puedes hacer, es utilizar funciones de WinAPI para obtener las teclas pulsadas, como GetAsyncKeyState. Entonces ya tendrás total control de la consola sin bloqueos para entrada del usuario. Incluso multi-thread si lo necesitases.

Meta

Hola:

Muchas gracias. Si te digo la verdad, no entiendo ni papa. Jajaajjajajaja. Soy de C# y este C de Win32 o en WinAPI.

Lo que tiene que hacer el programa es enviar este comando que lo hace bien.
Código (cpp) [Seleccionar]
cout << "Enviando: " << L_ON << endl;
Puerto -> WriteData(L_ON, sizeof(L_ON) - 1);


Arduino lo detecta.

Luego recibir este comando que no se si lo hace bien. Cuando Arduino le envía mensajes a C++ del PC.
Código (cpp) [Seleccionar]
Puerto -> ReadData(lectura, 50);
cout << "Recibido: " << lectura << endl;


Para los que me respondieron en privado lo pongo en público.
CitarSi hay que usar el Dev C++.


Pues a suarlo.
https://sourceforge.net/projects/orwelldevcpp/

Otros con el Code Blocks, que no se ponen de acuerdo. Para mi solo son IDE y me quedo con Visual studio 2017 que me gusta más. ;)

¿Alguna ayuda?

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

ivancea96

"No sabes si lo hace bien"? Pues yo menos ._.

En cualquier caso, mi respuesta iba orientada a:

Cita de: Meta en 16 Marzo 2017, 22:21 PM
Si pulsas la tecla, por ejemplo 5, primero se ve reflejado y luego tengo que pulsar Enter. Lo que quiero lograr es que si pulse el número 5, automáticamente ejecuta la instruacción del case 5 del Switch.
Para lo cual, puedes usar la función GetAsyncKeyState de WinAPI.
En al documentación explica su retorno:
CitarIf the function succeeds, the return value specifies whether the key was pressed since the last call to GetAsyncKeyState, and whether the key is currently up or down. If the most significant bit is set, the key is down

Por tanto, comprobamos el bit:
Código (cpp) [Seleccionar]
if(GetAsyncKeyState(tecla) & 0x8000){
    // Tecla pulsada
}


El código de la tecla es un VirtualKey Code: https://msdn.microsoft.com/es-es/library/windows/desktop/dd375731(v=vs.85).aspx. En cualquier caso, decirte que los codigos de las letras coinciden con los caracteres de las letras mayúsculas:

Código (cpp) [Seleccionar]
if(GetAsyncKeyState('A') & 0x8000){
    // Tecla 'A' pulsada
}



Y con respecto a lo del IDE que comentaste, no sé de qué hablabais, pero por supuesto que no importa el IDE.

y bueno, lo dicho:
CitarLuego recibir este comando que no se si lo hace bien.
Describe qué es lo que no sabes si hace bien y por qué.

Meta

#4
Hola Señor:

He hecho el proyecto desde el principio. Como me lía en C++ de Win32, jajajajajja. Y eso que me lo recomendaron mucha gente para aprenerlo, ni viniendo de C#. ;) A lo que íabmos.

La parte donde pone if (GetAsyncKeyState('A') & 0x8000) , precisamente 0x8000 no lo entiendo y no lo pone aquí.

Con este código me aparece el mensaje en pantalla pero no es capaz de enviar algo a Arduino o datos al puerto serie.
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");

// Variables. Comandos para enviar a Arduino por puerto serie.
char L_ON[] = "Led13_ON";
char L_OFF[] = "Led13_OFF";
char Saludar[] = "Hola Arduino. Saludos desde el PC con C++ bajo  Win32.";
char Luz_ON[] = "Luz_ON";
char Luz_OFF[] = "Luz_OFF";

// Para almacenar datos desde el puerto serie.
char lectura[50];

// 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)
{
cout << "Enviando: " << Luz_ON << endl;
Puerto->WriteData(Luz_ON, sizeof(Luz_ON) - 1);
}

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

system("PAUSE");
}


Parece ser que ignora esta instrucción.
Código (cpp) [Seleccionar]
Puerto->WriteData(Luz_OFF, sizeof(Luz_OFF) - 1);

Aquí una captura cuando pulsa solo la B o la A. Se repite muchas veces. (Si es posible hacer el tamaño de la ventana en la programación de C++, mejor que mejor).


Lo de recibir comandos desde el peurto serie o Arduino es este el que me refería:
Código (cpp) [Seleccionar]
Puerto -> ReadData(lectura, 50);
cout << "Recibido: " << lectura << endl;


Que este programa al mismo tiempo que envía comandos, también los puede recibir. Si el programa C++ no le hago nada, solo lo dejo arrancado, desde el puerto serie puede venir cualquier dato y C++ debe leerlo sin problemas.

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

MAFUS

Y de parte del arduino cómo lo tienes?
Funciona con hypeterminal?

ivancea96

Cita de: Meta en 17 Marzo 2017, 02:57 AM
La parte donde pone if (GetAsyncKeyState('A') & 0x8000) , precisamente 0x8000 no lo entiendo y no lo pone aquí.
No, no lo pone ahí. Lo pone aquí: https://msdn.microsoft.com/es-es/library/windows/desktop/ms646293(v=vs.85).aspx
CitarIf the function succeeds, the return value specifies whether the key was pressed since the last call to GetAsyncKeyState, and whether the key is currently up or down. If the most significant bit is set, the key is down
El bit más significativo en un short es, digamos, ""el de la izquierda"". 0x8000 en binario es 0b1000_0000_0000_0000. Haciendo un and (&), sacamos si ese bit está activo o no. Si está activo, el resultado será diferente de 0, así que se ejecutará el contenido del if. Si no está pulsada, el bit estará a 0, y el AND retornará 0. El if, en ese caso, no se ejecutará.

Cita de: Meta en 17 Marzo 2017, 02:57 AM
Parece ser que ignora esta instrucción.
Código (cpp) [Seleccionar]
Puerto->WriteData(Luz_OFF, sizeof(Luz_OFF) - 1);

Aquí una captura cuando pulsa solo la B o la A. Se repite muchas veces. (Si es posible hacer el tamaño de la ventana en la programación de C++, mejor que mejor).

Sobre el tema de que se repita muchas veces es porque el GetAsyncKeyState te dice que está pulsada, no si ha sido pulsada. Por tanto, si mantienes la tecla,  entrará muchas veces. Tendrías que controlar si ha sido pulsada ahora o si ya estaba pulsada.

Cita de: Meta en 17 Marzo 2017, 02:57 AM
Lo de recibir comandos desde el peurto serie o Arduino es este el que me refería:
Código (cpp) [Seleccionar]
Puerto -> ReadData(lectura, 50);
cout << "Recibido: " << lectura << endl;


Que este programa al mismo tiempo que envía comandos, también los puede recibir. Si el programa C++ no le hago nada, solo lo dejo arrancado, desde el puerto serie puede venir cualquier dato y C++ debe leerlo sin problemas.

Como dice MAFUS, esto es un protocolo con 2 programas. Conocemos el de Windows, pero no conocemos el de Arduino. ¿No podría ser problema del programa del Arduino?

Meta

Buenos días Señor:

Estuve haciendo pruenas a mi manera, la de poner un número y luego pulsa Enter. Hace cosas raras todavía. Lo raro que hace es almacenar información que viene de Arduino. Lo ideal sería, que si viene algo desde Arduino, se muestre en pantalla de C++, luego se borre, preparado para que llegue otro nuevo mensaje. El truco de las teclas, todavía no me sale.
Código (cpp) [Seleccionar]
#include <iostream>
#include <fstream>
#include <Windows.h>
#include "SerialClass.h"
using namespace std;

void main()
{
SetConsoleTitle("Control Led 13 de Arduino UNO y saludos.");

Serial* Puerto = new Serial("COM4");

while (Puerto -> IsConnected())
{

// Comandos para Arduino.
char L_ON[] = "Led13_ON";
char L_OFF[] = "Led13_OFF";
char Saludar[] = "Hola";
char Luz_ON[] = "Luz_ON";
char Luz_OFF[] = "Luz_OFF";
char lectura[50] = "\0";

int opc;

cout << endl;
cout << "Introduzca la opcion deseada: " << endl << endl;

cin >> opc;

switch (opc)
{
case 1:
cout << "caso 1\n";
// Enviar encender Led.
cout << "Enviando: " << L_ON << endl;
Puerto -> WriteData(L_ON, sizeof(L_ON) - 1);
break;

case 2:
cout << "caso 2\n";
// Enviar apagar Led.
cout << "Enviando: " << L_OFF << endl;
Puerto -> WriteData(L_OFF, sizeof(L_OFF) - 1);
break;

case 3:
cout << "caso 3\n";
// Mensaje saludar.
cout << "Enviando: " << Saludar << endl;
Puerto -> WriteData(Saludar, sizeof(Saludar) - 1);
break;

case 4:
cout << "caso 4\n";
// Mensaje saludar.
cout << "Enviando: " << Luz_ON << endl;
Puerto->WriteData(Luz_ON, sizeof(Luz_ON) - 1);
break;

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

default:
cout << "Puse del 1 al 5.";
}

cout << endl;
Puerto->ReadData(lectura, 50);
cout << "Recibido: " << lectura << endl;
lectura[0] = 0; // Limpiar.
cout << "-------------------" << endl;

//system("PAUSE");
}
}


Código de Arduino perfectamente funcional con su LCD Keypad Shield incluido.
#include <LiquidCrystal.h>

// Inicializa la librería con sus pines indicados.
// RS, RW, Enable, D4, D5, D6, D7.
LiquidCrystal lcd(8, NULL, 9, 4, 5, 6, 7);

// Pin 10 para saber que es luz de fondo.
const byte LuzFondo = 10;

bool estadoActual = false, estadoUltimo = false, contador = false;
const byte pinLed = 13;   // Declaramos la variable pin del Led.
const byte boton = 2;
char caracter;
String comando;

void setup()
{
  pinMode(pinLed, OUTPUT);  // Inicializa el pin del LED como salida:
  pinMode(boton, INPUT);    // Pulsador del pin 2.
  Serial.begin(115200);     // Puerto serie 115200 baudios.
  lcd.begin(16, 2);         // Formato de pantalla.
  //digitalWrite(LuzFondo, HIGH);
  lcd.setCursor(0, 0);      // Posición en la pantalla.
  lcd.print("    Arduino     ");
  delay(1000);
}

void loop()
{
  /* Leyendo carácter a carácter lo que se resive por el canal del puerto serie
    y los voy concatenando uno tras otro en una cedena. */
  while (Serial.available() > 0)
  {
    caracter = Serial.read();
    comando.concat(caracter);
    delay(10);
  }

  estadoActual = digitalRead(boton); // Guarda el estado del pulsador.
  delay(50); // Retardo de 50 mili segundos par evitar antirebotes.

  // ¿Pulsador y estadoActual y negación del estadoUltimo es verdadero?
  if (estadoActual && !estadoUltimo)
  {
    contador = !contador;   // Cambio el estado tipo boleano.

    if (contador)
    {
      digitalWrite(LuzFondo, HIGH);
      Serial.write("ON");
      lcd.setCursor(15, 0);
      lcd.print("1");
    }
    else
    {
      digitalWrite(LuzFondo, LOW);
      Serial.write("OFF");
      lcd.setCursor(15, 0);
      lcd.print("0");
    }
  }

  // Pasa del estadoActual a estadoUltimo.
  estadoUltimo = estadoActual;

  // Si los carácteres es recibido y verdadero.
  if (comando.equals("Led13_ON") == true)
  {
    digitalWrite(pinLed, HIGH); // Enciende el Led.
    Serial.write("Led 13 encendido.");
    lcd.setCursor(0, 1);
    lcd.print("Led 13 encendido");
  }

  // Si los carácteres es recibido y verdadero.
  if (comando.equals("Led13_OFF") == true)
  {
    digitalWrite(pinLed, LOW); // Apaga el Led.
    Serial.write("Led 13 apagado  ");
    lcd.setCursor(0, 1);
    lcd.print("Led 13 apagado. ");
  }

  // Mensaje de saludo.
  if (comando.equals("Hola") == true)
  {
    Serial.write("Hola C++ de Win32. Saludos desde Arduino.");
    lcd.setCursor(0, 1);
    lcd.print("Hola C++ de Win32. Saludos desde Arduino.");
  }

  if (comando.equals("Luz_ON") == true)
  {
    digitalWrite(LuzFondo, HIGH); // Apaga el Led.
    Serial.write("Luz ON.");
    lcd.setCursor(0, 1);
    lcd.print("Luz ON.         ");
  }


  if (comando.equals("Luz_OFF") == true)
  {
    digitalWrite(LuzFondo, LOW); // Apaga el Led.
    Serial.write("Luz OFF.");
    lcd.setCursor(0, 1);
    lcd.print("Luz OFF.        ");
  }

  // Limpiamos la cadena para volver a recibir el siguiente comando.
  comando = "";
}


Si tengo la ventana de C++, pulso el botón desde Arduino, me tiene que llegar un mensaje, solo llega un ON o un OFF. No muestra nada en C#, solo se acumula en la variable lenguaje, que si envío algo, se suma el ON o OFF más otros mensajes.

Si no entiendes a lo que hablo, me lo dices y lo explico de otra manera.

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

ivancea96

El "truco" d elas teclas, es lo mismo que el digitalRead que tienes ahí. Puedes guardar su estado anterior, no más.

CitarNo muestra nada en C#, solo se acumula en la variable lenguaje
¿C#? ¿Variable lenguaje?

En cualquier caso. ¿Tu problema es que no recibes datos del Arduino? Entonces comenta el switch y testealo por separado.

Meta

#9
Buenas:

Quiero decir C++. ;)

Empecé otro proyecto más sencillo, primero corregir lo de recepción del puerto.
Código (cpp) [Seleccionar]
// Para crear conexión con los puertos COM1 - COM9.
// Serial* Arduino = new Serial("COM7");

// Para crear conexión con los puertos COM10 en adelante.
// Serial* Arduino = new Serial("\\\\.\\COM10");

#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");

while (Puerto->IsConnected())
{

// Comandos para Arduino.
char Luz_ON[] = "Luz_ON"; // Envía "Luz_ON" al puerto serie.
char Luz_OFF[] = "Luz_OFF";
char lectura[50]; // Guardan datos de entrada del puerto.
int opc; // Guarda un 1 o 2 tipo entero queintroduces desde la consola.

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.
Puerto->ReadData(lectura, 50); // Recibe datos desde el puerto y lo almacena en la variable "lectura".
cout << "Recibido: " << lectura << endl; // Muestra en pantalla el texto o datos almacenado en la variable "lectura".
cout << "-------------------" << endl;
}
}


Lo ejecuto y muestra exactamente esto:


¿Por qué en recibido aparece esos caracteres raros?

La manera para que no me salga es esto, cambiando de esta manera la variable lectura, colocarle un \0.
Código (cpp,4) [Seleccionar]

// 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.


Quiero ir por partes, resolver cada problema. Ahora, no se si lo que hice arriba es lo más indicado. Creo que no, porque si pulso 1 me muestra este resultado:


Si vuelvo a pulsar 1, ya me muestra el Recibido: bien como indica abajo:


Tiene que funcionar todo a la primera. Antes de hacer lo de las teclas, primero resolver este acertijo.  ;)

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