En algún momento del camino Jedi, un circuito falla más allá de lo que un Serial.print()
puede permitirnos debuguear: es el momento del osciloscopio. Los precios "oscilan" (XD broma no intencionada) entre unos pocos miles, y varias decenas. Sin embargo, si quieres analizar una señal de hasta 5V los pines analógicos de tu fiel Arduino pueden ser suficientes.
NOTA: Si necesitas algo sencillo, Arduino incluye la herramienta Serial Plotter.
Si buscas algo que después puedas integrar en una interfaz gráfica más completa, esta es tu guía ;)
Arremángate, que empezamos.
Hace unas semanas el microcontrolador del Isiukak, un ST Nucleo F401RE, dejó de comunicarse con los drivers de Pololu.
¿Y si los drivers de Pololu son de 5V y quemaron el puerto del ST, que sólo soporta 3.3V? La hoja de datos decía que eran de 3.3V, "tolerantes a 5V", y si su tolerancia es violenta?
Nos preguntamos. Para desahogar toda duda decidimos construir un osciloscopio hecho en casa con un Arduino UNO.
No buscábamos algo de mucha precisión simplemente que nos permitiera graficar la señal de voltaje del puerto serial y darnos una idea de cómo se estaba comportando.
El sistema se divide en dos programas. Un script en Arduino y un programa en C++ que corre en el computador, con el que leemos el puerto serial y graficamos la señal de los pines analógicos del Arduino UNO.
Así es, un osciloscopio de 6 canales; nada mal...
Esta es la parte sencilla, sólo leemos los pines A0 al A5 y los transmitimos al computador con la mayor frecuencia posible.
En teoría el UNO puede transmitir hasta a 2 millones de bits por segundo, sin embargo sólo logramos comunicación hasta 500,000 bps. Si conoces una forma de superar este umbral usando QSerialPlot, puedes compartirlo en los comentarios.
#include <Arduino.h>
int inputs[6];
int inputsReaded = 6;
int inputs_pins[6] = {A0, A1, A2, A3, A4, A5 };
char* inputs_headers[6] = {"A0", "A1", "A2", "A3", "A4", "A5"};
void setup()
{
//Serial.begin(115200); <- Funciona
//Serial.begin(250000); <- Funciona
//Serial.begin(300000); <- Funciona
//Serial.begin(400000); <- Funciona
Serial.begin(500000); // <- Funciona!
//Serial.begin(600000); // Wowowow, tranquilo, viejo...
}
void loop()
{
for(int index = 0; index < inputsReaded; index++)
{
// Leer cada canal
inputs[index] = analogRead(inputs_pins[index]);
// Desplegarlo en formato A0: 20, A1: 18, ...
Serial.print(inputs_headers[index]); Serial.print(": ");
Serial.print(inputs[index]);
if(index < inputsReaded-1)
Serial.print(", "); // Sólo los primeros 5 elementos llevan coma al final.
else
Serial.println(" "); // El último lleva un salto de línea.
}
delayMicroseconds(10);
}
NOTA: Si usas el IDE de Arduino no es necesario el #include <Arduino.h>
. Sólo es necesario si usas PlatformIO.
int inputsReaded
: Número de canales a leer (En nuestras pruebas inputsReaaded = 2).
inputs_pins[6]
: Un arreglo de 6 elementos con el nombre de los pines a leer. La idea es que el programa sea los más general posible en caso de que sólo ocupes medir 1 ó 2 canales, o incluso agregar fácilmente más canales si usaras otro microcontrolador.
inputs_headers[6]
: Un arreglo de 6 elementos con el nombre de cada canal, en el mismo orden que inputs_pins.
En el loop se leen los canales y se envian por Serial.
Este código tiene un poco más de gracia; se encarga de leer los valores que recibe por serial con QSerialPort
; separarlo en renglones (en ocasiones una lectura incluye más de 1 renglón); separar el valor de cada canal usando usando QRegularExpression
y graficarlo usando QCustomPlot
Puedes descargar el código completo, tanto de Arduino como de la Interfaz gráfica del respositorio en Github
La mayoría de la magia está en el constructor de la ventana principal MainWindow::MainWindow(QWidget *parent)
serialCom = new SerialCommunication();
parser = new DataParser();
osciloscope = new Osciloscope();
La clase SerialCommunication
lee el puerto serial del Arduino, DataParser
separa los valores de cada canal y Osciloscope
los grafica.
Otra sección importante es la conexión de la señal readyRead de QSerialPort con la función displayReadedData(), donde los datos se separan, se grafican y se colocan en la interfaz.
connect(serialCom->serial, SIGNAL(readyRead()),
this, SLOT(displayReadedData()) );
La interfaz luce así
Con el dial puedes controlar el zoom horizontal de la amplitud de la señal.
Y aquí puedes verlo en funcionamiento en una de las primeras pruebas.
Si te interesan más detalles del código, deja un mensaje.
¡Hasta la autonomía, siempre!