sábado, 6 de octubre de 2012

Graficar entrada analogica (potenciometro) usando Arduino y C#

Bien, el pequeño proyecto que les traigo el día de hoy es de graficar la entrada analógica de nuestro Arduino, es decir, el Convertidor Analógico Digital.

Este proyecto lo presenté hace varios meses como una práctica de laboratorio en mi querídisima (ROFL)  facultad de mi Universidad, FIME UANL.

Vamos a graficarlo en la PC usando una aplicación creada por mi mismo en C#.

Bien, puedo resumir todo esto en unas cuantas etapas:

  1. Armamos un circuito sencillo donde tengamos conectad a forma de divisor de voltaje nuestro potenciómetro, tu sabes man, una terminal de un extremo a voltaje, la otra a tierra y la terminal de enmedio a una de las entradas analogicas del Arduino.
    1. Esto sencillamente para al momento de variar la resistencia en el potenciometro, en la terminal de en medio varía el voltaje.
    2. NOTA: si tenemos un potenciometro logaritmico (los "normales jaja", los mas baratos) nuestra salida sera una función logaritmica (aah en serio!!) de la variación  de la resistencia, si requieres un poco de más "precisión" usa uno lineal.
  2. Nuestro Arduino estará conectado a la PC por el USB
  3. Por medio de comunicación Serial mandaremos el resultado de la conversión Analógica - Digital cada vez que se requiera refrescar la gráfica
  4. La aplicación en la PC hecha en C# recibe los datos por Serial y refresca la gráfica.


Circuito

Esquematico del circuito (NOTA: La terminal superior del potenciometro tambien esta conectada a 5V así como a AREF)
Imágenes del Circuito

Usando un Arduino Mega

1.- Usando un Arduino UNO

2.- Usando un Arduino UNO




Sketch de Arduino:
void setup()
{
  Serial.begin(115200);
  analogReference(EXTERNAL);
  Inicializa();
}

void loop()
{
  Get_Instruccion();
}



int Get_Instruccion()
{
  if(Serial.available())
  {
      switch(Serial.read())
      {
         case 'r'://Leer entrada analogica y mandar dato
           Analog_Read();
         break; 
         
         case 's': //En espera a nueva inicializacion
           Salir();
         break;
      }
  }
}

void Salir()
{
  Inicializa(); //Loop en inicializa esperando nueva inicializacion
}

void Analog_Read()
{
    int resultado = 0;
    resultado = analogRead(0); //Leemos el pin analogico 0 en 10bits
    Serial.write(resultado / 4); //Mandamos el resultado en 8bits por Serial
}



//Inicializamos la comunicacion serial
//Debe mandar una secuencia de caracteres tal que cumplan con el criterio "square"
void Inicializa()
{
  int caracter[6];
  int i = 0;
  caracter[0] = 's';
  caracter[1] = 'q';
  caracter[2] = 'u';
  caracter[3] = 'a';
  caracter[4] = 'r';
  caracter[5] = 'e';
  
  while(1)
  {//Mientras no se inicialize no sale del ciclo
     if(Serial.available())
    {
        if(Serial.read() == caracter[i])
        {
          if(i == 5)
          {//Si es la ultima iteracion
            Serial.write('a');//escribimos caracter de confirmacion
            break; //Salimos del while
          }
          i++;
        }
      } 
  }
  //Termina While
}

Explicaré un poco el código para el Arduino:

Al inicio se encuentra la función Inicializa(), esta se encarga de inicializar al Arduino, por así decirlo, lo que hace es esperar a recibir cierta cadena de caracteres por Serial, si esa cadena de caracteres es la correcta entonces se pondrá en marcha el programa principal confirmando esto por mandando un carácter de confirmación por Serial

La función principal Get_Instruccion() ejecuta cierta operación de acuerdo a lo recibido por Serial, si recibe "r" quiere decir que nuestra aplicación en C# esta requiriendo una lectura delo convertidor analógico digital, si recibe "s" es que se debe salir al al bucle de Inicialización.




Parte del Codigo para C#:


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;

namespace Practica2_LabDSM
{
    public partial class Form_Main : Form
    {
        Arduino arduino = null;
        int[] muestreo;

        public Form_Main()
        {
            InitializeComponent();
            muestreo = new int[50];
        }

        private void groupBox_PuertoSerial_Enter(object sender, EventArgs e)
        {

        }

        private void Form_Main_Load(object sender, EventArgs e)
        {
            serial_combo(this.comboBox_Puertos);
            for (int i = 0; i < 50; i++)
            {
                //this.chart_Datos.Series[0].Points.AddY(i);
                muestreo[i] = 0;
            }
           
        }

        private void button_Iniciar_Click(object sender, EventArgs e)
        {
            if (this.comboBox_Puertos.Items.Count > 0)
            {
                if (this.comboBox_Puertos.SelectedIndex > -1)
                {//Si esta seleccionado algun puerto
                    arduino = new Arduino(this.comboBox_Puertos.Text, 115200);//Inicializamos arduino a 115200 bauds
                    arduino.Inicializa_Comunicacion();
                    this.button_Iniciar.Enabled = false;
                    this.button_Stop.Enabled = true;
                    timer.Enabled = true;
                    timer.Start();
                }
            }
        }


        //Metodo que primero limpia y despues llena la comboBox pasada como
        //parametro con los puertos serie disponibles
        private void serial_combo(ComboBox combo)
        {
            combo.Items.Clear();
            string[] ports = SerialPort.GetPortNames();

            foreach (string port in ports)
            {
                combo.Items.Add(port);
            }
        }

        private void Form_Main_FormClosed(object sender, FormClosedEventArgs e)
        {
            try
            {
                arduino.CierraPuerto();
            }
            catch
            {
                ;
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            Get_Lectura();
        }

        private void button_Stop_Click(object sender, EventArgs e)
        {
            try
            {
                arduino.Stop();
                arduino.CierraPuerto();
                arduino = null;
                this.button_Stop.Enabled = false;
                this.button_Iniciar.Enabled = true;
                this.timer.Stop();
                this.timer.Enabled = false;
            }
            catch
            {
                arduino = null;
            }
        }

        private void Get_Lectura()
        {
            int punto = 0;
            if (arduino != null)
            {
                punto = arduino.GetLectura();
                this.textBox_caracter.Text = Convert.ToChar(punto).ToString();
                ActualizaGrafica(punto);
            }
        }

        private void ActualizaGrafica(int punto)
        {
            RecorreArray(punto);
            this.chart_Datos.Series[0].Points.Clear();
            for (int i = 0; i < 50; i++)
            {
                this.chart_Datos.Series[0].Points.AddY(muestreo[i]);
            }
        }

        private void RecorreArray(int punto)
        {
            int[] muestreoAux = new int[50];
            for (int i = 0; i < 49; i++)
            {
                muestreoAux[i + 1] = muestreo[i];
            }
            muestreoAux[0] = punto;
            muestreo = muestreoAux;
        }

        private void button_Refresh_Click(object sender, EventArgs e)
        {
            serial_combo(this.comboBox_Puertos);
        }

    }
}

Este código en sí no ayuda en mucho, sólo se ve bonito =). Es mejor si descargas la solución completa (Tip, está mas abajo)



Screenshot de la Interfaz en C# de la aplicación.
Básicamente lo que trata de hacer esta aplicación es por medio de un Timer, cada cierto tiempo una lectura analógica digital al arduino vía Serial, esta lectura la coloca en un array de hasta 50 muestras la cuál usa para construir la gráfica.


***********************_________________________________________********************

DESCARGAS:
  • Solución de C# para Visual Studio 2010 y Sketch Arduino: aqui (compresión RAR)
  • Esquematico: pdf, png

Si cargas el sketch al Arduino y Corres la solución de VS2010 todo debe funcionar perfecto, pero te coloco unas instrucciones para que lo hagas bien y sin dudas:


  1. Abre con Arduino el archivo prac2_2.ino
    1. Carga el Sketch al Arduino
  2. Abre la solcion de VS2010 (Practica2_LabDSM.sln)
  3. Conecta el Arduino a la PC por USB
  4. Corre la aplicación de C# (Run)
  5. Una vez se esté ejecutando el programa selecciona el puerto serie que corresponde a tu arduino, si no aparece da click en Refrescar
  6. Click en Iniciar

Esto mismo puede ser usado, para muchos propósitos, por ejemplo, si cambias el potenciómetro por un LM35 tendrás un graficador de Temperatura etc...

Usando un poco de imaginación y programación tendrás lo que necesitas!!






Tags:


  • Arduino
  • Arduino y C#
  • Graficar Arduino
  • Graficar Convertidor Analogico Digital
  • Analog Digital Converter Arduino with C#
  • Graficar potenciometro con Arduino en la PC

45 comentarios:

  1. Hola. muy buen post.

    En este momento estoy realizando un trabajo similar, con una PIC18 y codigo en C#. Quisiera preguntarte con que frecuencia se da tu evento Tick del Timer, ya que en este momento tengo problemas con ello (con 50 ms mi aplicación funciona, pero si agrego una sola linea de codigo en C#, mi grafica ya no se actualiza)

    Muchas gracias! mi correo es j.santacruz.leon@gmail.com

    ResponderEliminar
    Respuestas
    1. Saludos, el intervalo del Tick es de 100ms.
      Habría que ver mas o menos tu codigo en C# amigo para pensarle en una solución!
      Un Saludo!!

      Eliminar
  2. disculpa me podrias decir como debe de ser la conexión al Arduino Uno ya que al tratar de graficar en c# no lo puedo hacer

    ResponderEliminar
    Respuestas
    1. Podrías explicarte mejor amigo Anonimo?
      Simplemente conectas por USB el Arduino a la PC (te debe crear el puerto virtual COM1 sobre Windows).

      Saludos

      Eliminar
  3. de hecho mi pregunta es como va la conexión del potenciometro al arduino ya que se me a dificultado eso un poco.....!!!!!

    ResponderEliminar
    Respuestas
    1. A ver, creo que no me explique bien en el esquemático de arriba:
      pin 1 pot -> 5V
      pin 2 pot (pin de en medio) -> A0
      pin 3 pot -> GND

      Y también conectas los 5V a AREF.

      Listo

      Saludos

      Eliminar
  4. Hola tengo una duda, por que inicia la comunicacion serial con "Serial.begin(115200)", me parece creer que la comunicacion serial es de 9200 bauds esto no altera sus datos?, otra duda al momento de hacer la comunicacion al pasar el sensor por un amplificador seguidor y de hay al puerto de la arduino esxiste la posibilidad de que haya perdidas de voltaje esto es mi salida del amplificador mide .21 y recibo en la arduino .20 o .19 esto me parece muy raro no se si tiene alguna idea del porque, gracias

    ResponderEliminar
    Respuestas
    1. Hola Ada Espinoza.
      1.- Hay diferentes velocidades, 115200bds es buena velocidad, mucho mayor a 9200bds, si Arduino la soporta no veo el inconveniente en usarla, y por supuesto no altera en nada los datos (solo se comunican mas rápido, lo cual es mejor). Si quisieras usar otra velocidad (como 9600 ó 9200) tienes que modificar el codigo Arduino y el de C# para sincronizarlos a la misma velocidad (recordando que UART es Asincrono).

      2.- Perdidas de voltaje (no creo que eso sea posible allí), lo mas seguro es que el ADC no esté bien calibrado, segura que conectaste el ARef del Arduino?, tienes que calcular también la amplificación. Me explico.

      Si tu ARef lo colocas a 5V significa que tienes un rango de 0V a 5V para el ADC de 10 bits del arduino por tanto 5V/1023bit = 4.88mV/bit.
      Es decir cuando obtengas un 1 en el ADC quiere decir que estas midiendo 4.88mV cuando obtengas 10 en el ADC estas midiendo 48.8mV y así hasta que obtengas 1023 en el ADC que seran 5V.

      etc...

      Saludos.

      Eliminar
    2. Hola, si es lo que estaba observando que conecto a 5v Aref, pero estaba leyendo que por default esta a 5V por eso considere que podria no conectarlo, pero lo conectare solo que tengo otra duda tambien encontre que si se conecta Aref hay que cambiar a EXTERNAL, cuando se llame la funcion analogRead() para evitar posibles daños a la arduino eso en donde se hace? es que no lo veo en su codigo

      Eliminar
    3. No la usé porque el valor por default que usa Arduino para la referencia del ADC es 5V, el cableado solo estaba para ejemplificar.
      Si usas referencia a 5V igual no lo conectes, pero si necesitas por ejemplo 3V3, sí deberas conectarlo y agregar la linea de codigo en void setup():

      analogReference(EXTERNAL);

      http://arduino.cc/es/Reference/AnalogReference

      Toda la documentación está en la pagina de Arduino incluyendo como se usan las funciones principales loop y setup.

      Saludos

      Eliminar
    4. Pues si es de hay de donde saque la informacion que le comentaba y por eso le preguntaba si tenia una idea de por que me pasaba eso, por que estoy considerando igual el ADC por default como viene en la documentacion de 5 volts y 10 bits pero aun asi leo con multimetro en el circuito y me aparece .21 y ya lo que aparece en la arduino haciendo la conversion con el ADC es de .20 o .19, estoy usando un sensor lm35 asi como comenta que de igual forma se puede estaba pensando igual graficarla pero quise primero ver si enviaba los datos bien, pero no puedo pasar de ese paso

      Eliminar
    5. Recuerda que observar afecta lo observado!! Es decir, si mides algo afectarás lo que estas midiendo.

      Y si me preguntas a mí obtener de un ADC .20V cuando el valor "real" (entre comillas porque el multímetro puede fallar) es .21V es un excelente resultado, quieres que platiquemos de eso por otro medio, esta conversación esta muy larga para un blog...

      Si en verdad deseas precisión en serio tendras que usar mejores instrumentos, amplifica la señal de tu LM25 por un factor de 10 con un Amplificador de Instrumentación (recomiendo el AD624) para obetener 2.1V en lugar de 0.21, referenciar a 5V, y de esa manera poder reducir el error (podrías llegar a medir hasta 5V entonces lo que si mal no recuerdo con un lm35 serían 50ºC)

      Eliminar
    6. Si por favor me podria proporcionar un correo electrónico o usted dira, gracias

      Eliminar
    7. En la parte superior izquierda de este blog estan los links a mi facebook o a mi Google+, el que gustes. (Mi Google+ es mi correo de Gmail).

      Eliminar
  5. oye lo que se va graficando es dinamico, es decir el eje de tiempo sigue graficando siempre como este video????? http://www.youtube.com/watch?v=k9axoFqIjNY

    ResponderEliminar
    Respuestas
    1. Es parecido sí, pruebalo, no ocupas mas que un potenciometro, un arduino y una pc con windows, es todo...

      Eliminar
  6. hola hazael estoy haciendo un proyecto con pic y c#, soy nuevo en c# y quiero comunicar con usb-hid, no tengo claro el codigo para que los datos del CAD los capture el pc,
    si me puedes orientar te lo agradeceria

    ResponderEliminar
    Respuestas
    1. Lo siento richard zerpa, aun no he trabajado con comunicación nativa USB (HID, Bulk...) por falta de tiempo, siempre he comunicado con la PC usando UART (sea con FTDI, sea RS232).

      Pero en cuanto tenga un chance empezaré con eso y agregaré una entrada al blog.

      Saludos.

      Eliminar
  7. ayudame con un compilador en ensamblador o tienes alguno t lo agradecere

    ResponderEliminar
  8. excelente post, para utilizar el sensor de temp. lm35, debo agregar una conversion al programa para que de la lectura correcta?

    ResponderEliminar
    Respuestas
    1. Sí, es muy sencillo agregar un LM35 solo necesitas multiplicar los bits obtenidos (0 -> 1023) por un factor para obtener el Voltaje dado por el LM35 o los grados C, si gustas puedo pasarte uno ya hecho

      Eliminar
  9. Hola amigo, podrias explicar o si sabes donde como en un chart (grafico de windows form9 en c#) poder poner el eje en X el tiempo en segundos (o milisengundos) y el valor de Y que sea el valor del potenciometro? graciassss

    ResponderEliminar
    Respuestas
    1. Antes que nada. Puedes modificar este ejemplo para hacer todo lo que desees, está hecho para eso.

      1.- En este ejemplo el eje X es el tiempo solo camibiale el nombre del Eje a algo como "tiempo (s)" o algo así y de acuerdo a tu tiempo de muestreo (en el timer) cada cuadrícula valdrá tantos segundos o milisegundos.

      2.- En este ejemplo el eje Y va de 0 a 256 de acuerdo al valor del voltaje a la salida del potenciometro que pasa por el ADC del Arduino, debido a que el valor del ADC de 0 corresponde a 0V de voltaje hay una relación de Línea Recta entre ambos y se puede resolver por regla de tres simple.
      Por tanto para obtener el voltaje V para un valor ADC (entre 0 a 256) la formula sería V = (ADC)(5) / 256.

      Aplica lo anterior y podrás covnertir este ejemplo en algo que te sea más útil.

      Saludos.

      Eliminar
  10. sera complicado hacer todo este proyecto con un arduino uno y graficar estos datos en visual basic 6.0?????????????

    ResponderEliminar
    Respuestas
    1. No lo creo, puedo apostar a que es muy parecido.
      Pero..... para que querrias usar tecnologia de hace 20 años?
      (Te lo piden asi en tu universidad supongo :P)

      Lo unico que necesitas para tener algo parecido a este proyecto pero en VB 6.0 es:
      1. Una libreria para controlar el Puerto Serial.
      2. Librerias para crear graficas (tengo entendido que VB6.0 no posee librearias para graficos de manera nativa pero he visto proyectos -y hecho uno- que usa directamente las .dll que vienen al instalar Office.

      Suerte.

      Eliminar
    2. Corrijo, librearias para graficas (NO graficos) "chart libraries" en gringo.

      Eliminar
  11. private void Form_Main_Load(object sender, EventArgs e)
    {
    serial_combo(this.comboBox_Puertos);
    for (int i = 0; i < 50; i++)
    {
    muestreo[i] = 0;
    }
    }

    Puedes explicarme este bucle?

    ResponderEliminar
    Respuestas
    1. Pone en 0 todo el array muestreo.
      (que en teoría debería ya estar en 0)
      Me parece que puedes eliminar ese bucle y el programa seguiría funcionando bien.
      Saludos

      Eliminar
  12. Hola, Quiero realizar este proyecto pero en ves de usar un arduino con un chipKit Max32, que en pocas palabras es lo mismo que un Mega pero con mayor velocidad de reloj, Que debo cambiarle en el codigo de c# y en el del el arduino,

    Salu2

    ResponderEliminar
    Respuestas
    1. Hola Luis.
      Para usar el chipkit en teoria no deberias tener que modificar nada.
      Just as is.

      Carga el codigo de Arduino al chipKit por medio de la IDE que proporciona digilent (la roja) y listo.

      Saludos

      Eliminar
  13. Buenas tardes, alguien que tenga el .rar que me lo pueda pasar de favor D: me urge y no lo puedo bajar, no sé si es mi PC o el internet :v

    ResponderEliminar
  14. Puedes enviar todos los datos que desees. Pero la forma en la que los envías depende de ti, usando un arduino conectado a la PC estarás ocupando un solo puerto Serie, por ese puerto serie puedes enviar todo un byte a la vez, dependerá de como programes los algoritmos de envío y recepcion de datos. No es algo que se pueda ver demasiado directo pero es posible hacerlo por supuesto.

    ResponderEliminar
  15. Una pregunta: como podria enviar y visualizar mas de 255 osea no dividir el valor del potenciometro sobre 4.....

    revicir Serial.write(resultado / 4); //Mandamos el resultado en 8bits por Serial ......

    como podria ver en el textbox de 0 a 2014.... si le quito /4 quiero que muestre 1024. pero no lo muestra

    ResponderEliminar
    Respuestas
    1. Hola.

      Una forma interesante de poder enviar datos mayores a un byte sería creando una especie de ciclo. Tanto en el Arduino como en la PC.
      Dicho ciclo enviará tantos datos desees.
      Algo como:
      Arduino:
      while(existen_datos)
      {
      send(dato);
      }
      PC:
      while(not_finish)
      {
      datos += read(dato);
      }

      Puedes crear tu propio "protocolo" de transmisión, algo que le indique a la PC cuantos datos va a recibir O enviar un dato específico para romper el ciclo (como un cero).

      Saludos.

      Eliminar
  16. hola amigo muy interesante el proyecto pero tengo una duda si deseo conectar un potenciometro a un pic y que los datos sean enviados a arduino! como podria recibir esos datos enviados en arduino??? para luego ser enviados a c#.

    Saludos

    ResponderEliminar
  17. No entiendo el cometario de potenciómetro logarítmico(los normales) de toda la vida los dos son potenciómetros normales, tanto los lineales como los logarítmicos, otra cosa es que tu uses los logarítmicos para todo y por ello digas que son los normales es decir los que normalmente usas tu

    ResponderEliminar
  18. buenas, con una molestia si se quisiera duplicar o triplicar la grafica que se tendria que modificar???

    ResponderEliminar
  19. Excelente proyecto pero yo queria hacer lo mismo pero con 5 graficas y poner 5 potenciometros y de vez de medir datos quiero graficar voltajes de 0 a 5v esto es posible pero como ?

    ResponderEliminar
    Respuestas
    1. Completamente posible, pero vas a requerir bastante programación

      Eliminar
  20. amigo si deseo cambiar el potenciometro por un LM35 como quedaria el codigo modificad

    ResponderEliminar
    Respuestas
    1. No tienes que modificar nada si lo único que quieres es graficar la forma de la onda. Habrá que hacer variaciones si necesitas traficar otro tipo de datos, como temperatura por ejemplo.

      Eliminar
  21. como se puede unir el codigo del otro sensor arduino al archivo en c shart

    ResponderEliminar
  22. como se puede unir el codigo del otro sensor MPU6050 arduino al archivo en c sharp, que parte del codigo se debiese modificar?

    ResponderEliminar
  23. Si dicho sensor tiene una salida Analógica de entre 0 y 5v es perfectamente compatible. Solo tienes que quitar el pot y reemplazarlo por tu sensor.

    ResponderEliminar