La clase Serial
Las operaciones para utilizar las comunicaciones serie en Processing están definidas en la clase Serial
.La primera operación a realizar para utilizarlas en un programa (sketch) será incorporarla al código con import processing.serial.*;
.
La clase Serial
tiene cinco constructores diferentes en función de los parámetros que se indiquen. El único parámetro obligatorio es el objeto padre (parent) que suele corresponder con el programa principal (digamos, la ventana del programa) de la clase PApplet
. Como normalmente el padre será el programa que se está escribiendo (el sketch actual), el valor de este primer parámetro obligatorio será this
.
Los otros cinco parámetros que pueden pasarse al constructor son ① la velocidad, ② el nombre del puerto serie ③ la paridad usada en el protocolo, ④ los bits de datos y ⑤ los bits de parada. Los parámetros que con más frecuencia se pasan, además del objeto padre, obligatorio, son el nombre del puerto y la velocidad.
La velocidad de las comunicaciones serie es un número entero (int
) que toma por defecto el valor 9600 si es que este parámetro no se pasa al constructor.
Puertos serie disponibles. El método list.
El nombre del puerto tiene la forma que determina el sistema, de esta forma, por ejemplo en distribuciones Linux será algo como /dev/ttyS4 /dev/ttyACM3 o /dev/ttyUSB1 (dependiendo del tipo de puerto), mientras que en Windows será algo como COM12. Salvo que se asocie físicamente un puerto a un dispositivo, normalmente no se sabrá desde el programa qué puerto utilizar. Una forma habitual de seleccionar el puerto es obtener una lista de los disponibles, mostrarla al usuario y permitirle elegir el que desea utilizar. El método Serial.list()
devuelve una vector de cadenas de texto (String
) con los nombres de los puertos disponibles en el sistema.
1 2 3 4 5 6 7 8 9 |
// Mostrar los puertos serie disponibles en el sistema import processing.serial.*; void setup() { noLoop(); // No iterar (no llama a draw periódicamente) println(Serial.list()); } |
El puerto usado por defecto por la librería Serial
es el primero de los devueltos por el método list
(seguramente COM1 en Windows o /dev/ttyS0 en GNU/Linux). Salvo en contextos muy acotados en los que se conoce estrictamente el hardware con el que se trabaja (como un sistema en modo quiosco) no suele omitirse y se indica expresamente el puerto destino.
En la captura de pantalla de arriba se muestra la salida de un sistema GNU/Linux que cuenta con cuatro puertos serie RS-232 (ttyS0 a ttyS3) y cinco adaptadores de dos tipos (ttyACM0 a ttyACM1 y ttyUSB0 a ttyUSB2).
Para poder acceder a los puertos serie, el usuario debe pertenecer al grupo al que el sistema los asigne, normalmente tty o dialout. En la captura de pantalla de la imagen de arriba puede verse que los puertos serie listados con ls /dev/tty[ASU]* -la
pertenecen al grupo dialout que tiene permisos de acceso de lectura y escritura sobre ellos.
Parámetros del protocolo serie
La paridad de las comunicaciones serie se expresa en Processing como un carácter (char
) que puede tomar los valores: ① N
(none) para no detectar la paridad, ② E
(even) para indicar que el bit de paridad es par, ③ O
(odd) para indicar que el bit de paridad es impar, ④ M
(mark) para hacer siempre cero el bit de paridad y ⑤ S
(space) para hacer siempre uno el bit de paridad. El valor por defecto, si no se pasa al constructor como parámetro, es N
(sin paridad).
El número de bits de datos, que es ocho por defecto, indica el número de bits que componen la carga neta de datos (llamado carácter o a veces palabra) que se transmiten en cada unidad básica de la trama. El parámetro que indica el número de bits de datos se expresa como un número entero (int
).
Por último, el quinto posible parámetro indica la duración de la marca final, expresado como bits de parada (stop-bits), que se indica como un número representado en coma flotante (float
) que puede tomar los valores 1.0
(el valor por defecto si no se pasa el parámetro al constructor), 1.5
, o 2.0
.
Constructores de la clase Serial
En la siguiente lista se muestran las diferentes combinaciones de los parámetros que se pueden pasar al constructor de la clase Serial
:
Serial(padre)
Serial(padre,puerto)
Serial(padre,velocidad)
Serial(padre,puerto,velocidad)
Serial(padre,puerto,velocidad,paridad,bits_datos,bits_parada)
Finalizar las comunicaciones serie. El método stop.
Para liberar el puerto serie, asignado al instanciar Serial
, y que otras aplicaciones del sistema puedan utilizarlo, se finalizan las comunicaciones con el método stop
, que no recibe parámetros.
1 2 3 4 5 6 7 8 9 10 |
import processing.serial.*; Serial serie; void setup() { noLoop(); // No iterar serie=new Serial(this,"/dev/ttyUSB0",9600); // Usar un puerto USB con un adaptador UART serie.stop(); // Detiene las comunicaciones serie y libera el puerto ttyUSB0 para otros usos } |
Enviar datos por el puerto serie. El método write.
Para enviar datos, la clase Serial
de Processing incorpora el método write
con el que se pueden transmitir ① cadenas de texto (String
), ② bytes o ③ vectores de bytes (byte[]
). Es interesante recordar que byte
en Processing (en Java) representa un número entero comprendido entre -128 y 127 y que, por defecto, las cadenas utilizan la codificación UTF-16.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import processing.serial.*; Serial serie; String texto="Ohm"; void setup() { noLoop(); serie=new Serial(this,"/dev/ttyUSB0",9600); // Usar un puerto USB con un adaptador UART serie.write(texto); // Envía el texto "Ohm" serie.write(10); // Envía un fin de línea \n que corresponde con el ASCII 10 serie.write(200); // Envía el valor -56 ¡Es un byte, va de -128 a 127! (200-256=-56) serie.stop(); // Detiene las comunicaciones serie y libera el puerto ttyUSB0 para otros usos } |
Leer datos desde el puerto serie
Para que el programa pueda realizar otras tareas mientras se reciben datos por el puerto serie, lo habitual es almacenar en un buffer los datos que llegan y leerlos cuando corresponda. Aunque normalmente no es muy eficiente, se puede detener la aplicación para cargar todos los datos que haya disponibles; sin embargo, lo más habitual será ir leyendo la información según llega, ya sea en cada iteración de draw
, cuando estén disponibles cierta cantidad o se haya recibido un código especial.
Cantidad de datos disponibles en el buffer. El método available
Para saber si han llegado datos al buffer serie, el método available
devuelve el número de bytes que ya se han almacenado en este buffer. En cualquier caso, las operaciones de lectura pueden devolver un valor especial (como -1
o null
) cuando se intenta cargar un dato del buffer serie cuando está vacío.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import processing.serial.*; Serial serie; void setup() { frameRate(1); // Llama a draw una vez por segundo serie=new Serial(this,"/dev/ttyUSB0",9600); } void draw() { print("Hay "); print(serie.available()); println(" bytes en el buffer serie"); } |
Cargar un byte cada vez. El método read
Los principales métodos de la clase Serial
que sirven para leer la información recibida por un puerto serie son los de «tipo read
» que se diferencian entre ellos, principalmente, por el tipo de dato en el que entregan la información recibida.
read
se utiliza para entregar los bytes recibidos por el puerto serie como un valor entre 0 y 255. Como el tipo de datos byte
de Processing representa el rango entre -128 y 127 y no entre 0 y 255, es necesario utilizar un int
para poder representar el rango devuelto por read
. Si se intenta leer con read
y el buffer serie está vacío, devuelve el valor -1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import processing.serial.*; Serial serie; void setup() { frameRate(10); // Llama a draw 10 veces por segundo serie=new Serial(this,"/dev/ttyUSB0",9600); } void draw() { if(serie.available()>0) { println(serie.read()); } } |
Leer caracteres desde el puerto serie. El método readChar
El método readChar
es similar a read
pero devuelve un valor en formato char
en lugar de un int
. Como internamente, los char
en Processing (en Java) se almacenan con dos bytes, el valor elegido para devolver cuando se realiza la lectura con readChar
de un buffer serie vacío es 0xFFFF
o -1
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import processing.serial.*; Serial serie; void setup() { frameRate(10); serie=new Serial(this,"/dev/ttyUSB0",9600); } void draw() { if(serie.available()>0) { print(serie.readChar()); } } |
Cargar una cadena de texto. Los métodos readString y readStringUntil.
El método readString
devuelve un objeto String
formado a partir de todos los datos que haya disponibles en el buffer serie en el momento de consultarlo.
El método readString
crea la cadena de texto suponiendo que los bytes recibidos por el puerto serie están en el formato ASCII así que este método de lectura no podrá utilizarse para otras codificaciones.
Si se trata de leer el buffer serie con readString
cuando está vacío, el valor devuelto es null
.
El método readStringUntil
añade a readString
la capacidad de devolver la información cargada en el buffer serie partiéndola por un carácter (código) especial que se pasa como parámetro. Esta forma de leer la información recibida permite distinguir tanto separadores como terminadores que ayudan a interpretar la información recibida.
El método readStringUntil
devuelve null
cuando en el buffer serie no se encuentra el código especificado en el argumento que se le pasa (un byte).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import processing.serial.*; Serial serie; String mensaje; void setup() { frameRate(5); serie=new Serial(this,"/dev/ttyUSB0",9600); } void draw() { if(serie.available()>1) { mensaje=serie.readStringUntil(9); // Lee los datos del buffer hasta encontrar un tabulador if(mensaje!=null) // Si la respuesta no es null ya ha llegado el tabulador y el mensaje está completo { println("Mensaje recibido: "+mensaje); // Mostrar el mensaje si ha llegado completo } } } |
En el siguiente código para Arduino envía tres mensajes por el puerto serie. Los dos primeros terminan en un tabulador, por lo que aparecerán en la consola de Processing, mientras que el tercero, aunque se enviará por el puerto serie no se leerá con readStringUntil(9)
ya que no termina en tabulador (con código ASCII 9).
1 2 3 4 5 6 7 8 9 10 11 12 |
void setup() { Serial.begin(9600); while(!Serial); Serial.print("Primer mensaje\t"); Serial.print("Segundo mensaje\t"); Serial.print("Tercer mensaje"); // Este mensaje no llega porque no termina en tabulador } void loop() { } |
Leer bloques de datos. Los métodos readBytes y readBytesUntil.
Los métodos vistos anteriormente se utilizan para leer datos con formatos específicos, para leer bloques de datos en bruto o con un formato que no esté previsto en Processing se utilizan los métodos readBytes
y readBytesUntil
El método readBytes
trata de leer los datos disponibles en el buffer serie. Si no se pasa ningún parámetro al método readBytes
se leen todos los datos disponibles y se devuelven en un vector (byte[]
). Si se pasa como parámetro un número entero, se leen como máximo, el número de bytes que este número indica y se devuelven también como un vector.
Existe una tercera forma de utilizar readBytes
, más eficiente, que toma como argumento un vector de bytes en el que se cargará el contenido del buffer serie. Esta forma de utilizar readBytes
devuelve un número entero (int
) que representa la cantidad de bytes que se han leído.
El método readBytesUntil
funciona de manera similar pero incluye un primer parámetro que representa el valor del byte que, si se encuentra en el buffer, indicará el final de la lectura. En este método no tiene sentido el parámetro que determina el máximo número de bytes que se leerán ya que la cantidad estará determinada por el código especial.
Para probar el funcionamiento del método readBytes
supongamos el siguiente código para Arduino que envía un texto por el puerto serie.
1 2 3 4 5 6 7 8 9 10 |
void setup() { Serial.begin(9600); while(!Serial); Serial.println("En Viena hay diez muchachas, un hombro donde solloza la muerte y un bosque de palomas disecadas. Hay un fragmento de la mañana en el museo de la escarcha. Hay un salón con mil ventanas."); } void loop() { } |
El siguiente programa de ejemplo para Processing lee el texto desde el puerto serie en bloques de 32 bytes (TOTAL_BYTES). Para verificar que funciona lo muestra por la consola como caracteres forzando el tipo de los bytes recibidos a char
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
import processing.serial.*; Serial serie; byte bloque_datos[]; static byte TOTAL_BYTES=32; void setup() { frameRate(10); serie=new Serial(this,"/dev/ttyUSB0",9600); bloque_datos=new byte[TOTAL_BYTES]; } void draw() { if(serie.available()>0) { bloque_datos=serie.readBytes(TOTAL_BYTES); if(bloque_datos!=null) { for(byte numero_byte=0;numero_byte<bloque_datos.length;numero_byte++) { print((char)bloque_datos[numero_byte]); } } } } |
En la siguiente captura de pantalla puede verse como se muestran en la consola de Processing los datos que se han ido cargando en bloques de (como máximo) 32 bytes (TOTAL_BYTES) cada vez. Pero hay un problema del que ya se ha hablado: Arduino ha ido enviando los versos de Federico García Lorca del ejemplo codificados como texto en formato UTF-8, que no es el que utiliza Processing (Java), que prefiere UTF-16 así que los que no corresponden al rango del ASCII imprimible se interpretan incorrectamente.
Para resolver este inconveniente se pueden cargar los juegos de caracteres (charset) y definir un nuevo objeto String
forzando que se represente con la codificación UTF-8 como se muestra en el siguiente código de ejemplo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
import processing.serial.*; import static java.nio.charset.StandardCharsets.*; Serial serie; byte bloque_datos[]; static byte TOTAL_BYTES=32; void setup() { frameRate(10); serie=new Serial(this,"/dev/ttyUSB0",9600); bloque_datos=new byte[TOTAL_BYTES]; } void draw() { if(serie.available()>0) { bloque_datos=serie.readBytes(TOTAL_BYTES); if(bloque_datos!=null) { print(new String(bloque_datos,UTF_8)); } } } |
Leer los últimos datos recibidos. Los métodos last y lastChar.
Mientras que el resto de los métodos de lectura (los «tipo read
») van cargando la información del buffer serie en el mismo orden que ha llegado (FIFO), con estos dos métodos se lee el último byte que ha llegado al buffer serie. El método last
devuelve el valor del último byte como un int
y lastChar
devuelve el valor como un char
.
Gestión del buffer serie
Aunque los métodos que se han visto hasta ahora son perfectamente funcionales no representan siempre la mejor forma de explotar el acceso al puerto serie. Para cargar los datos, necesitan consultar periódicamente el estado del buffer serie y leer los datos disponibles en una parte del código que se repita. Una forma generalmente más eficiente consiste en leer los datos solamente cuando se tiene constancia de que están disponibles.
Leer el puerto serie cuando se reciban datos. El evento Serial.
Para realizar los accesos al buffer serie cuando se reciban los datos, se puede explotar el evento Serial gestionándolo por medio de la definición del método serialEvent
. Este método utiliza como argumento el puerto serie que lo lanza.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import processing.serial.*; Serial serie; void setup() { serie=new Serial(this,"/dev/ttyUSB0",9600); } void draw() { } void serialEvent(Serial comunicaciones) { print(comunicaciones.readChar()); } |
Dimensionar el buffer serie. El método buffer.
Si se conoce la cantidad de bytes que forman un bloque de datos útiles se puede optimizar aún más este estilo de lectura del buffer serie por medio de serialEvent
. El método buffer
permite establecer el número de bytes que se almacenarán en el buffer antes de lanzar un evento Serial. El método espera como parámetro un entero que representa la cantidad de bytes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import processing.serial.*; Serial serie; void setup() { //noLoop(); serie=new Serial(this,"/dev/ttyUSB0",9600); serie.buffer(32); // Esperar a recibir 32 bytes antes de lanzar el evento Serial } void draw() { } void serialEvent(Serial comunicaciones) { while(comunicaciones.available()>0) { print(comunicaciones.readChar()); } } |
Llenar el buffer hasta recibir un valor. El método bufferUntil.
En lugar de configurar la llamada al método serialEvent
por una cantidad de datos en el buffer, con el método bufferUntil
se puede configurar que se almacenen datos hasta que llegue un valor especial y entonces lanzar el evento Serial. El parámetro que se pasa a este método es un int
que representa el valor que produce la llamada a serialEvent
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
import processing.serial.*; Serial serie; void setup() { serie=new Serial(this,"/dev/ttyUSB0",9600); serie.bufferUntil(9); // Espera a recibir un tabulador (ASCII 9) antes de llamar a serialEvent } void draw() { } void serialEvent(Serial comunicaciones) { println(comunicaciones.readString()); // Imprime en una línea diferente cada valor separado originalmente por tabuladores } |
Borrar los datos almacenados en el buffer. El método clear.
Con el método clear
se pueden borrar los datos que estén actualmente en el buffer. Este método puede utilizarse, por ejemplo, para empezar una nueva sesión de recepción de datos ignorando los que resten de la anterior.
Aplicación Processing típica para lectura de datos por el puerto serie
Para terminar, conviene recapitular las operaciones del objeto Serial
de Processing que se utilizan más habitualmente, recorriendo un ejemplo típico de recepción de datos por el puerto serie para dibujar con ellos un gráfico, en este caso de áreas apiladas.
Importar la librería Serial
1 |
import processing.serial.*; |
Determinar el protocolo de datos (separadores)
1 2 |
static final String SEPARADOR="\t"; // Los datos de cada sensor se separan con un tabulador static final char TERMINADOR=10; // Cada grupo de datos se termina con un código ASCII 10 → Nueva línea → \n |
Determinar el objeto de la clase Serial
1 |
Serial conexion_sensores; |
Instanciar el objeto de la clase Serial configurando el puerto serie usado
1 |
conexion_sensores=new Serial(this,"/dev/ttyUSB1",9600); |
Configurar el buffer del puerto serie
1 |
conexion_sensores.bufferUntil(TERMINADOR); |
Implementar un manejador para el evento Serial
1 |
void serialEvent(Serial serie) |
Leer el buffer serie
1 |
String[] texto_valor=serie.readString().split(SEPARADOR); |
Acondicionar los datos recibidos
1 2 3 4 5 |
float[] valor=new float[texto_valor.length]; for(int numero_valor=0;numero_valor<texto_valor.length;numero_valor++) { valor[numero_valor]=parseFloat(texto_valor[numero_valor]); } |
Finalizar las comunicaciones serie
1 2 |
conexion_sensores.clear(); conexion_sensores.stop(); |
En el código de ejemplo de más abajo se ilustra este resumen con una aplicación funcional (aunque muy sencilla) que genera un gráfico de áreas con los valores que se van recibiendo por el puerto serie, algo similar a lo que muestra la siguiente animación.
Para no perderse en el resto del programa y centrar la atención en las comunicaciones serie con Processing, están resaltadas las líneas de código que corresponden con las operaciones anteriores.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
import processing.serial.*; static final byte ROJO=0,VERDE=1,AZUL=2,OPACIDAD=3; static final int CANTIDAD_SENSORES=3; static final int CANTIDAD_VALORES=20; static final String NOMBRE_FONDO="fondo.png"; static final int[][] COLOR_LINEA={{0x44,0x88,0xCC,0xFF},{0xFF,0xAA,0x00,0xFF},{0xCC,0x44,0xAA,0xFF}}; static final int[][] COLOR_AREA={{0x44,0x88,0xCC,0x88},{0xFF,0xAA,0x00,0x88},{0xCC,0x44,0xAA,0x88}}; static final int[] COLOR_FONDO={0xFF,0xFF,0XFF}; static final float GROSOR_LINEA=2.0; static final float DIAMETRO_MARCA=8.0; static final float VALOR_MINIMO=0.0; // Valor mínimo de la suma de todos los componentes static final float VALOR_MAXIMO=100.0; // Valor máximo de la suma de valores static final String SEPARADOR="\t"; // Los datos de cada sensor se separan con un tabulador static final char TERMINADOR=10; // Cada grupo de datos se termina con un código ASCII 10 → Nueva línea → \n Serial conexion_sensores; float[][] valor_sensor=new float[CANTIDAD_SENSORES][CANTIDAD_VALORES]; float coeficiente_valor; float[] vertical_area=new float[CANTIDAD_VALORES]; float[] vertical_marca=new float[CANTIDAD_VALORES]; PImage fondo; void setup() { size(792,396,P2D); // El tamaño de la ventana no se puede establecer con variables en setup (usar settings) surface.setResizable(false); surface.setTitle("consumo relativo comparado"); noLoop(); smooth(4); conexion_sensores=new Serial(this,"/dev/ttyUSB1",9600); conexion_sensores.bufferUntil(TERMINADOR); for(int numero_sensor=0;numero_sensor<CANTIDAD_SENSORES;numero_sensor++) { for(int numero_valor=0;numero_valor<CANTIDAD_VALORES;numero_valor++) { valor_sensor[numero_sensor][numero_valor]=0.0; } } fondo=loadImage(NOMBRE_FONDO); coeficiente_valor=height/(VALOR_MAXIMO-VALOR_MINIMO); //strokeCap(ROUND); // El modo del final de líneas por defecto es redondeado //ellipseMode(CENTER); // Por defecto el modo de elipse es desde el centro if(DIAMETRO_MARCA>GROSOR_LINEA) // Si la marca no es visible hay que configurar el tipo de esquina { strokeJoin(ROUND); // El modo de esquina por defecto es en ángulo } } void draw() { for(int numero_valor=0;numero_valor<valor_sensor[0].length;numero_valor++) { vertical_area[numero_valor]=height; for(int numero_sensor=0;numero_sensor<valor_sensor.length;numero_sensor++) { vertical_area[numero_valor]-=(valor_sensor[numero_sensor][numero_valor]-VALOR_MINIMO)*coeficiente_valor; } vertical_marca[numero_valor]=vertical_area[numero_valor]; } if(fondo==null) { background(COLOR_FONDO[ROJO],COLOR_FONDO[VERDE],COLOR_FONDO[AZUL]); } else { image(fondo,0,0); } strokeWeight(GROSOR_LINEA); for(int numero_sensor=0;numero_sensor<valor_sensor.length;numero_sensor++) { stroke ( COLOR_LINEA[numero_sensor][ROJO], COLOR_LINEA[numero_sensor][VERDE], COLOR_LINEA[numero_sensor][AZUL], COLOR_LINEA[numero_sensor][OPACIDAD] ); fill ( COLOR_AREA[numero_sensor][ROJO], COLOR_AREA[numero_sensor][VERDE], COLOR_AREA[numero_sensor][AZUL], COLOR_AREA[numero_sensor][OPACIDAD] ); beginShape(); for(int numero_valor=valor_sensor[numero_sensor].length-1;numero_valor>=0;numero_valor--) { vertex(numero_valor*width/(valor_sensor[numero_sensor].length-1),vertical_area[numero_valor]); } for(int numero_valor=0;numero_valor<valor_sensor[numero_sensor].length;numero_valor++) { vertical_area[numero_valor]+=(valor_sensor[numero_sensor][numero_valor]-VALOR_MINIMO)*coeficiente_valor; vertex(numero_valor*width/(valor_sensor[numero_sensor].length-1),vertical_area[numero_valor]); } endShape(CLOSE); if(DIAMETRO_MARCA>0) { noStroke(); fill ( COLOR_LINEA[numero_sensor][ROJO], COLOR_LINEA[numero_sensor][VERDE], COLOR_LINEA[numero_sensor][AZUL], COLOR_LINEA[numero_sensor][OPACIDAD] ); for(int numero_valor=0;numero_valor<valor_sensor[numero_sensor].length;numero_valor++) { ellipse ( numero_valor*width/(valor_sensor[numero_sensor].length-1), vertical_marca[numero_valor], DIAMETRO_MARCA, DIAMETRO_MARCA ); vertical_marca[numero_valor]=vertical_area[numero_valor]; } } } } void stop() // Al terminar un Applet. No hay garantía de que se ejecute y, como estas operaciones se realizan al terminar, en realidad no son necesarias y solo se incluyen para recordar el uso de clear y stop { conexion_sensores.clear(); // Solo para ilustrar la posibilidad de borrar los datos que queden en el buffer conexion_sensores.stop(); // Solo para ilustrar la posibilidad de terminar las comunicaciones serie y liberar el puerto que se está usando } void serialEvent(Serial serie) { String[] texto_valor=serie.readString().split(SEPARADOR); float[] valor=new float[texto_valor.length]; for(int numero_valor=0;numero_valor<texto_valor.length;numero_valor++) { valor[numero_valor]=parseFloat(texto_valor[numero_valor]); } nuevo_valor(valor_sensor,valor); redraw(); } void nuevo_valor(float[][] valor_sensor, float[] valor) { for(int numero_sensor=0;numero_sensor<valor_sensor.length;numero_sensor++) { for(int numero_valor=1;numero_valor<valor_sensor[0].length;numero_valor++) { valor_sensor[numero_sensor][numero_valor-1]=valor_sensor[numero_sensor][numero_valor]; } valor_sensor[numero_sensor][valor_sensor[0].length-1]=valor[numero_sensor]; } } |
Guillermo J. Gil aitken
Una consulta y en el Modo Android para Processing? la libreria serial no me funciona del todo y no se si cambiar y ejecutarlo desde una raspberry pi para evitar el error…
Víctor Ventura
Hola, Guillermo.
¿A qué errores te refieres? A lo mejor con más información alguien por aquí podría comentar alguna cosa. Tampoco me queda muy claro si la Raspberry Pi la usarías con Android, porque con un SBC resolverías los problemas, o con otro sistema operativo (¿Una distribución Linux?) para evitar usar Processing for Android 🙁
En cualquier caso, mucha suerte con tu proyecto y gracias por visitar polaridad.es
Eduardo Lagos A
Gracias por la excelente información
Víctor Ventura
Gracias a ti por leerme 🙂
Jesus Aguilar
Excelente aportación :D, me ayudó mucho en mi proyecto, mil gracias
Sebastian
Hola, muy buena aportación, aunque me quedan mis dudas ya que apenas son pocos días que voy usando processing, espero que aun puedas responder. Recibo datos por puerto serial de un sensor giroscopio, el ultimo ejemplo me parece que guarda de manea perfecta los datos que recibo para luego con un cubo en processing moverlo, ahora aplicando el ultimo ejemplo no se como visualizar estos datos para ver si se han guardado bien, podrías ayudarme? desde ya gracias y éxitos.
Sebastian
En el comentario anterior quiero hacer referencia a la funcion void serialEvent(Serial serie), en que momento se usa?
carlos ugidos altadill
Hola Víctor he visto que en muchos sitios como la fundación “Processing” el código lo imprimen siempre en la consola. La pregunta es, ¿si yo quiero, imprimir un ejemplo de los que nos enseñas en este tutorial en una ventana dentro del Sketch como lo envió a una “size”
Un saludo y mil gracias.
Carlos