Donde está el puerto serie de Arduino?
Como sabemos de la lección “Acerca de la plataforma”, las placas Arduino tienen un convertidor USB-TTL que permite que el microcontrolador se comunique con la computadora a través de la interfaz serie, Serial, en el modo de texto “consola”. Se crea un puerto COM virtual en la computadora, al cual puede conectarse usando los programas de terminal de puerto y recibir y enviar datos de texto. El firmware se carga a través del mismo puerto. El soporte en serie está integrado en el microcontrolador en el nivel de «hardware», y el convertidor USB-TTL está conectado a estos pines del microcontrolador. En la placa Arduino Nano, esto es, por cierto, los pines D0 y D1.
Es por eso que los pines D0 y D1 en las placas Arduino Nano / Arduino Uno no pueden ser ocupados por sensores o conectados a la alimentación / tierra en el momento de la carga del programa: el microcontrolador no podrá recibir datos y recibirá un error de arranque.
Puede conectarse a los mismos pines utilizando placas de “programador” separadas, por ejemplo, en chips CP2102 o el mismo CH340 para descargar el firmware o simplemente comunicarse con la placa.
El propio Arduino IDE también tiene una «consola» incorporada: un monitor de puerto, un botón con un icono de lupa en la esquina superior derecha del programa. Al hacer clic en este botón, abriremos el propio monitor del puerto, en el que habrá configuraciones:
Todo está bastante claro enviar, el desplazamiento automático, la marca de tiempo y el botón » borrar salida «, entonces consideraremos el Nueva línea y la velocidad con más detalle:
- Nueva línea: hay varias opciones para elegir, entenderás un poco más adelante a qué afectan. Es mejor no poner fin a la línea, ya que esto evitará errores incomprensibles en las primeras etapas del conocimiento de Arduino.
- Sin final de línea: no hay caracteres adicionales al final de los caracteres ingresados después de presionar el botón enviar.
- NL – carácter de avance de línea al final de los datos enviados.
- CR – carácter de retorno de carro al final de los datos enviados.
- NL + CR – ambos
- Velocidad: aquí se nos ofrece una lista completa de velocidades para elegir. La comunicación vía Serial puede realizarse a diferentes velocidades, medidas en baudios ( baudios ), y si las velocidades de recepción y envío no coinciden, los datos se recibirán incorrectamente. La velocidad predeterminada es 9600 , así que la dejaremos.
- Limpiar Salida: aquí todo está claro, borra la salida.
Objeto serial.
Comencemos a conocer una de las cosas más útiles de un desarrollador de Arduino: Serial. Serial es un objeto de la clase Stream, que le permite simplemente recibir / enviar datos a través de un puerto serie, y hereda un montón de características y trucos interesantes de la clase Stream, veámoslos todos a la vez y luego sigamos adelante. a ejemplos específicos.
Serial.begin ( velocidad ) |
Inicia la comunicación a través de serie a la velocidad ( velocidad en baudios, bits por segundo). Se puede configurar cualquier velocidad, pero hay varias «estándar». Lista de velocidades en baudios para el monitor de puerto Arduino IDE: 300 1200 2400 4800 9600 es el más utilizado, se puede llamar estándarTTL 19200 38400 57600 115200 también es común 230400 250.000 500.000 1,000,000 2,000,000 |
Serial.end () |
Detiene la comunicación serie. Para UNO / NANO (ATmega328), esto permite liberar los pines 0 y 1 para fines normales (lectura / escritura). |
Serial.available() |
Devuelve el número de bytes almacenados en el búfer (el tamaño del búfer es de 64 bytes ) y están disponible para lectura. |
Serial.availableForWrite () |
Devuelve el número de bytes que se pueden escribir en el búfer del puerto serie sin bloquear la función de escritura. |
Serial.write ( val ), Serial.write ( buf , len ) |
Envía un valor numérico o una cadena al puerto val, o envía len bytes desde buf . ¡Importante! Envía datos en bytes (ver tabla ASCII ), es decir, al enviar 88 recibirás la letra X: Serial.write (88); imprimirá la letra X. |
Serial.print ( val ), Serial.print ( val , format ) |
Envía el valor val al puerto: un número o una cadena. A diferencia de la anterior, genera exactamente caracteres, es decir enviando 88 obtienes 88: Serial.print (88); generará 88. Además, el método print / println tiene varias configuraciones para diferentes datos, lo que lo convierte en una herramienta de depuración muy acertada: |
Serial.print(78); // imprime 78 Serial.print(1.23456); // 1,23 (2 caracteres por defecto) Serial.print('N'); // imprime N Serial.print("Hello world."); // Hello world. // puedes hacer una salida formateada con estilo Serial.print("i have " + String(50) + " apples"); // imprime la línea tengo 50 manzanas // en lugar de números, puedes introducir variables byte appls = 50; Serial.print("i have " + String(appls) + " apples"); // imprimirá lo mismo
Serial.println (), Serial.println ( val ), Serial.println ( val , format ) |
Completo análogo de print (), pero traduce automáticamente la cadena después de la salida. También permite ser llamado sin argumentos (con corchetes vacíos) solo para saltos de línea. |
Serial.flush () |
Esperando el final de la transferencia de datos. |
Serial.peek () |
Devuelve el byte actual al inicio del búfer sin eliminarlo del búfer. Al llamar a Serial.read (), se leerá el mismo byte, pero ya se eliminará del búfer. |
Serial.read () |
Lee y devuelve un byte como código de carácter de la tabla ASCII. Si necesita devolver un dígito, haga Serial.read () – ‘0’; |
Serial.setTimeout ( time ) |
Establece el tiempo de espera (milisegundos) para esperar las siguientes funciones. El valor predeterminado es 1000 ms (1 segundo) |
Serial.find ( destino ), Serial.find ( destino , longitud ) |
Lee datos del búfer y busca el conjunto de caracteres de destino (tipo char), opcionalmente puede especificar la longitud. Devuelve verdadero si encuentra los caracteres especificados. |
// busca la palabra hola char target[] = "hello"; void setup() { Serial.begin(9600); } void loop() { if (Serial.available() > 0) { if (Serial.find(target)) Serial.println("found"); } }
Serial.findUntil(target, terminal) |
Lee datos del búfer y busca el juego de caracteres de destino (tipo char) o el terminal de cadena terminal. Espera el final de la transmisión por tiempo de espera, o completa la recepción después de leer el terminal. |
Serial.readBytes ( búfer , length) |
Lee los datos del puerto y los carga en el búfer (char array [] o byte []), también se indica el número de bytes a leer – length (para no desbordar el búfer). |
Serial.readBytesUntil ( carácter , búfer , longitud ) |
Lee datos del puerto y los carga en el búfer (char array [] o byte []), también se indica el número de bytes a leer – longitud (para no desbordar el búfer) y el carácter terminal. El final de la recepción en el búfer ocurre cuando se alcanza la cantidad de longitud especificada, cuando se recibe el carácter terminal (no entra en el búfer) o por tiempo de espera. |
Serial.readString () |
Lee el búfer del puerto y forma una cadena a partir de los datos, que devuelve. |
Serial.readStringUntil ( terminator) |
Lee el búfer del puerto y forma una cadena a partir de los datos, que devuelve. Sale en el tiempo de espera o al recibir un carácter de terminación (carácter char) |
Serial.parseInt (), Serial.parseInt ( skipChar ) |
Lee un valor entero del búfer del puerto y lo devuelve (tipo long). También puede especificar por separado el skipChar que se omitirá. |
Serial.parseFloat () |
Lee un valor de coma flotante del búfer del puerto y lo devuelve. |
Trazador Gráfico.
Además del monitor del puerto serie, el IDE de Arduino tiene un trazador, un trazador gráfico en tiempo real basado en datos del puerto serie. Basta con enviar el valor usando el comando serial.println ( valor ) y abra el trazador a través de una conexión en serie, por ejemplo, trace el valor del pin analógico A0:
void setup() { Serial.begin(9600); } void loop() { Serial.println(analogRead(0)); delay(10); }
El trazador admite varias líneas de gráficos al mismo tiempo, para mostrarlos debe seguir el siguiente protocolo de envío de datos: valor1,valor2,valor3,valor4,valorN,salto de línea, es decir, los valores se emiten en una línea, uno después el otro en orden, separado por un espacio o coma, y un final de lineal debe estar al final de las cadenas. Imprimamos algunas variables aleatorias:
void setup() { Serial.begin(9600); } byte val1, val2, val3; uint32_t timer; void loop() { // cada 300 ms if (millis() - timer >= 300) { timer = millis(); val1 = random(100); val2 = random(100); val3 = random(100); } // salida cada 10 ms Serial.print(val1); Serial.print(' '); Serial.print(val2); Serial.print(' '); Serial.print(val3); Serial.println(' '); delay(10); }
Los valores se emiten cada 10 milisegundos y los valores se actualizan cada 300 milisegundos. Obtenemos el siguiente gráfico:
Leyendas en los gráficos
Desde la versión 1.8.10, el IDE de Arduino ha agregado la capacidad de firmar gráficos, para esto, antes de mostrar, debe enviar los nombres en el formulario título 1, título 2, título n con un salto de línea, y luego simplemente genere los datos:
Usando los pines.
Como escribí anteriormente, el hardware Serial tiene pines dedicados en las patas del microcontrolador, para Nano / Uno / Mini estos son pines D0 y D1. ¿Puedes trabajar con estos pines como pines digitales normales? Con puerto Serial deshabilitado si puede, habilitado no. Después de llamar serial.begin() los pines dejan de funcionar como pines digitales en modo manual, pero después de llamar Serial.end () ¡Puedes usarlos de nuevo!
Enviar y analizar.
Considere el ejemplo más clásico para todos los lenguajes de programación: ¡Hola mundo!
El envío de datos al puerto no debería causar dificultades y preguntas, porque todo es claro / obvio, y un poco más arriba en la descripción del método print, consideramos todas las opciones de salida. Enviar a un puerto le permite averiguar el valor de una variable en el lugar correcto en el programa, este proceso se llama depuración. Cuando el código no funciona como debería, comenzamos a ver dónde se toman los valores de las variables y mostramos el texto de diferentes lugares en el programa para observar la corrección (orden) de su trabajo. Recordemos los ciclos y matrices de la lección y enviemos una matriz al puerto:
void setup() { Serial.begin(9600); // puerto abierto para comunicación byte arr[] = {0, 50, 68, 85, 15, 214, 63, 254}; for (byte i = 0; i < 8; i++) { Serial.print(arr[i]); Serial.print(" "); } Serial.println(); } void loop() { }
Salida: 0 50 68 85 15214 63 254 – ¡elementos de matriz separados por espacios!
Surgen problemas al intentar recibir datos en el puerto. El punto es que el método read() lee un carácter, incluso si envía un número largo; el programa recibirá un dígito a la vez y tendrá que componer manualmente un número a partir de los dígitos. El problema se agrava por el hecho de que read() lee exactamente el carácter, es decir, un código del carácter en la tabla ASCII.
Veamos este ejemplo, en el que los datos recibidos se re-envían al puerto (el llamado eco):
void setup() { Serial.begin(9600); } void loop() { if (Serial.available()) { Serial.println(Serial.read()); // saldrá al puerto 49 si se envía "1" // mostrará 80 si envía un "P" } }
Entonces, ¿cómo acepta exactamente un número? Hay un truco: restar el código de dígito 0 del código de carácter recibido, o el propio 0 como carácter: ‘0’
void setup() { Serial.begin(9600); } void loop() { if (Serial.available()) { Serial.println(Serial.read() - '0'); // saldrá al puerto 1 si se envía 1 // saldrá al puerto 7 si se envía 7 } }
Si obtiene dígitos adicionales al analizar, desactive el avance de línea al enviar. En el menú inferior del monitor de puerto, seleccione «Sin fin de línea» en el selector. Vea el comienzo de la lección.
También, para aceptar números individuales, tenemos un método listo para usar: parseInt () / parseFloat ()– para números enteros y racionales, respectivamente. El proceso de recibir y descifrar datos se llama análisis. Llevemos el número 1234 al puerto usando un método de análisis ya hecho.
void setup() { Serial.begin(9600); Serial.println("Hello World!"); } void loop() { if (Serial.available()) { // ¿hay algo en la entrada? int buff = Serial.parseInt(); // tomar en variable buff if (buff == 1234) { // si se recibió 1234 Serial.println("OK"); // exito } else { Serial.println("error"); //error } } }
Entonces, usamos la construcción if (Serial.available()) {} para sondear el puerto solo si llega algo. Al enviar el número 1234 al puerto, recibiremos una respuesta OK, enviando cualquier otro – error. También notará que después de enviar, la placa tarda un segundo en responder. Este segundo está oculto dentro del método parseInt, el programa espera un segundo después de recibir los datos para que todos los datos tengan tiempo de llegar. Un segundo es mucho, fue suficiente esperar, digamos, 50 milisegundos. Esto se puede hacer usando el método setTimeout ().
void setup() { Serial.begin(9600); Serial.setTimeout(50);// establecer tiempo de espera Serial.println("Hello World!"); // enviar } void loop() { if (Serial.available()) { // ¿hay algo en la entrada? int buff = Serial.parseInt(); // tomar en variable buf// si se recibió 1234 // если приняли 1234 Serial.println("OK"); // exito } else { Serial.println("error"); // error } } }
Ahora, después de enviar el dígito, el programa esperará solo 50 ms y le responderá de inmediato.
Caracteres de control.
Existen los llamados caracteres de control que le permiten formatear la salida. Hay alrededor de una docena de ellos, pero estos son los más útiles.
- \ n – nueva línea
- \ r – retorno de carro
- \ v – pestaña vertical
- \ t – tabulación horizontal
- \ » – doble comillas
- \ ‘ – apóstrofe
- \\ – barra invertida
- \ 0 – carácter nulo
- \? – signo de interrogación
¿Cómo utilizarlo? Solo escribelo en la salida. Por ejemplo la combinación\ r \ n moverá a nueva línea y devolverá el cursor a la posición izquierda:
Serial.print("Hello, World!\r\nArduino Forever"); // saldrá // ¡Hola Mundo! // Arduino para siempre // (¡en diferentes líneas!)
Así es como funciona la función. println (), solo agrega a la salida \ r \ n después imprimir.
Los caracteres de tabulación harán que sea fácil enviar datos para su posterior inserción en Excel u otras tablas. Por ejemplo, mostramos varias potencias de dos en forma de tabla usando el carácter de tabulación \t:
for (byte i = 0; i < 16; i++) { // i a 16 Serial.print(i); Serial.print("\t"); Serial.println(round(pow(2, i))); // 2elevado a i }