프로세싱을 통한 직렬 통신
직렬 클래스
직렬 통신을 사용하는 작업 처리 클래스에 정의되어 있습니다. Serial
.프로그램에서 사용하기 위해 수행하는 첫 번째 작업(스케치)는 이를 코드에 통합하는 것입니다. import processing.serial.*;
.
수업 Serial
표시된 매개변수에 따라 5개의 서로 다른 생성자가 있습니다. 유일한 필수 매개변수는 상위 객체(부모의) 이는 일반적으로 클래스의 기본 프로그램(예: 프로그램 창)에 해당합니다. PApplet
. 일반적으로 부모는 작성되는 프로그램이 됩니다( 스케치 현재), 이 첫 번째 필수 매개변수의 값은 다음과 같습니다. this
.
생성자에 전달할 수 있는 다른 5개 매개 변수는 ① 속도, ② 직렬 포트 이름 ③ 직렬 포트 이름입니다. 동등 프로토콜에 사용되는 ④ 데이터 비트와 ⑤ 정지 비트입니다. 필수 상위 개체 외에 가장 자주 전달되는 매개 변수는 포트 이름과 속도입니다.
La 직렬 통신 속도 정수(int
)이 기본값은 9600입니다. 이 매개변수가 생성자에 전달되지 않은 경우.
직렬 포트를 사용할 수 있습니다. 목록 방법
El 포트 이름 예를 들어 다음과 같이 시스템에 의해 결정된 형식을 갖습니다. Linux 배포 그것은 다음과 같을 것이다 /dev/ttyS4 / dev / ttyACM3 o /dev/ttyUSB1 (포트 유형에 따라) Windows에서는 다음과 같습니다. COM12. 포트가 장치와 물리적으로 연결되어 있지 않으면 프로그램은 일반적으로 어떤 포트를 사용할지 알 수 없습니다. 포트를 선택하는 일반적인 방법은 사용 가능한 포트 목록을 가져와 사용자에게 보여주고 사용하려는 포트를 선택하도록 허용하는 것입니다. 방법 Serial.list()
텍스트 문자열의 벡터를 반환합니다(String
)을 시스템에서 사용 가능한 포트 이름으로 바꿉니다.
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());
}
|
라이브러리에서 기본적으로 사용하는 포트 Serial
메소드에 의해 반환된 것 중 첫 번째입니다. list
(확실히 COM1 Windows 또는 /dev/ttyS0 en GNU / 리눅스). 작업 중인 하드웨어가 엄격하게 알려진 매우 제한된 상황(예: 키오스크 모드의 시스템)을 제외하고 일반적으로 생략되지 않으며 대상 포트가 명시적으로 표시됩니다.
위 스크린샷은 시스템의 출력을 보여줍니다. GNU / 리눅스 4개의 직렬 포트가 있는 RS-232 (ttyS0 a ttyS3) 및 두 가지 유형의 어댑터 5개(ttyACM0 a ttyACM1 y ttyUSB0 a ttyUSB2).
직렬 포트에 액세스하려면 사용자는 일반적으로 시스템이 할당하는 그룹에 속해야 합니다. 청각 장애 o 다이얼 아웃. 위 이미지의 스크린샷에서 직렬 포트가 다음과 같이 나열된 것을 볼 수 있습니다. ls /dev/tty[ASU]* -la
pertenenecen 알 그룹 다이얼 아웃 읽기 및 쓰기 액세스 권한이 있습니다.
직렬 프로토콜 매개변수
La 동등 직렬 통신의 로 표현된다 처리 캐릭터로 (char
) 다음 값을 사용할 수 있습니다. ① N
(없음) 감지하지 못하도록 동등, ② E
(조차)을 나타냅니다. 패리티 비트 짝수이다, ③ O
(이상한)을 나타냅니다. 패리티 비트 이상하다, ④ M
(표) 항상 패리티 비트 그리고 ⑤ S
(공간) 항상 하나를 패리티 비트. 매개변수로 생성자에 전달되지 않은 경우 기본값은 다음과 같습니다. N
(죄 동등).
의 수 데이터 비트는 기본적으로 8개이며 프레임의 각 기본 단위로 전송되는 순 데이터 페이로드(문자 또는 때로는 단어라고도 함)를 구성하는 비트 수를 나타냅니다. 데이터 비트 수를 나타내는 매개변수는 정수(int
).
마지막으로 다섯 번째 가능한 매개변수는 최종 마크의 지속 시간을 나타내며 다음과 같이 표현됩니다. 정지 비트 (정지 비트)는 다음과 같은 숫자로 표시됩니다. 부동 소수점 (float
) 값을 취할 수 있습니다. 1.0
(매개변수가 생성자에 전달되지 않은 경우 기본값) 1.5
또는 2.0
.
Serial 클래스의 생성자
다음 목록은 클래스 생성자에 전달될 수 있는 다양한 매개변수 조합을 보여줍니다. Serial
:
Serial(padre)
Serial(padre,puerto)
Serial(padre,velocidad)
Serial(padre,puerto,velocidad)
Serial(padre,puerto,velocidad,paridad,bits_datos,bits_parada)
직렬 통신을 종료합니다. 중지 방법입니다.
인스턴스화 시 할당된 직렬 포트를 해제하려면 Serial
, 다른 시스템 애플리케이션이 이를 사용할 수 있는 경우 다음 메소드로 통신이 종료됩니다. stop
, 매개변수를 받지 않습니다.
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
}
|
직렬 포트를 통해 데이터를 보냅니다. write.method
데이터를 보내려면 클래스 Serial
de 처리 방식을 통합하고 있다 write
① 텍스트 문자열을 전송할 수 있는 것(String
), ② 바이트 또는 ③ 바이트 벡터(byte[]
). 그것을 기억하는 것은 흥미 롭습니다. byte
en 처리 (시 자바)는 -128에서 127 사이의 정수를 나타내며 기본적으로 문자열은 인코딩을 사용합니다. 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
}
|
직렬 포트에서 데이터 읽기
직렬 포트를 통해 데이터가 수신되는 동안 프로그램이 다른 작업을 수행할 수 있도록 일반적으로 버퍼 도착하는 데이터를 읽고 적절할 때 읽습니다. 일반적으로 그다지 효율적이지는 않지만 애플리케이션을 중지하여 사용 가능한 모든 데이터를 로드할 수 있습니다. 그러나 가장 일반적인 방법은 각 반복에서 정보가 도착하는 대로 읽는 것입니다. draw
, 특정 수량을 사용할 수 있거나 특별 코드를 받은 경우.
버퍼에서 사용 가능한 데이터의 양입니다. 사용 가능한 방법
데이터가 도착했는지 확인하려면 버퍼 시리즈, 방법 available
이 파일에 이미 저장되어 있는 바이트 수를 반환합니다. 버퍼. 두 경우 모두 읽기 작업은 특수 값(예: -1
o null
)에서 데이터를 로드하려고 할 때 버퍼 비어있을 때 시리즈.
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”);
}
|
한 번에 1바이트씩 로드합니다. 읽기 방법
클래스의 주요 메소드 Serial
직렬 포트에서 수신한 정보를 읽는 역할을 하는 것은 "유형"입니다. read
» 주로 수신된 정보를 전달하는 데이터 유형에 따라 다릅니다.
read
직렬 포트에서 수신한 바이트를 0에서 255 사이의 값으로 전달하는 데 사용됩니다. 데이터 유형으로 byte
de 처리 는 -128에서 127 사이의 범위를 나타내며 0에서 255 사이는 아닙니다. int
반환된 범위를 나타내기 위해 read
. 함께 읽으려고 하면 read
과 버퍼 문자열이 비어 있으면 값을 반환합니다. -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());
}
}
|
직렬 포트에서 문자를 읽습니다. readChar 메소드
방법 readChar
~와 비슷하다 read
하지만 형식으로 값을 반환합니다. char
대신 int
. 내부적으로는 char
en 처리 (시 자바)는 2바이트로 저장되며, 로 읽을 때 반환하도록 선택된 값입니다. readChar
로 버퍼 빈 시리즈는 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());
}
}
|
텍스트 문자열을 로드합니다. readString 및 readStringUntil 메소드.
방법 readString
객체를 반환합니다 String
에서 사용 가능한 모든 데이터로 구성됩니다. 버퍼 상담 당시 시리즈입니다.
방법 readString
직렬 포트에서 수신한 바이트가 다음 형식이라고 가정하여 텍스트 문자열을 생성합니다. ASCII 따라서 이 읽기 방법은 다른 인코딩에 사용할 수 없습니다.
독서에 관한 것이라면 버퍼 시리즈 readString
비어 있으면 반환 값은 다음과 같습니다. null
.
방법 readStringUntil
에 추가하다 readString
로드된 정보를 반환하는 기능 버퍼 시리즈는 매개변수로 전달되는 특수 문자(코드)로 분할됩니다. 수신된 정보를 읽는 이러한 방식을 통해 수신된 정보를 해석하는 데 도움이 되는 구분 기호와 종결 기호를 모두 구별할 수 있습니다.
방법 readStringUntil
다시 가져오다 null
언제 버퍼 시리즈는 전달된 인수(1바이트)에 지정된 코드를 찾지 않습니다.
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
}
}
}
|
다음 코드에서는 아두 이노 직렬 포트를 통해 세 개의 메시지를 보냅니다. 처음 두 개는 탭으로 끝나므로 콘솔에 표시됩니다. 처리, 세 번째는 직렬 포트를 통해 전송되지만 다음으로는 읽을 수 없습니다. readStringUntil(9)
탭으로 끝나지 않기 때문에 (코드 포함) 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()
{
}
|
데이터 블록을 읽습니다. readBytes 및 readBytesUntil 메소드.
위에 표시된 메서드는 특정 형식의 데이터를 읽거나 원시 데이터 블록을 읽거나 에서 제공되지 않는 형식을 읽는 데 사용됩니다. 처리 방법이 사용됩니다 readBytes
y readBytesUntil
방법 readBytes
에서 사용 가능한 데이터를 읽으십시오. 버퍼 시리즈. 메소드에 매개변수가 전달되지 않은 경우 readBytes
사용 가능한 모든 데이터를 읽고 벡터로 반환합니다(byte[]
). 정수가 매개변수로 전달되면 이 숫자가 나타내는 최대 바이트 수를 읽어서 벡터로 반환합니다.
세 번째로 사용하는 방법이 있습니다 readBytes
, 더 효율적이며, 이는 내용이 포함된 바이트 벡터를 인수로 사용합니다. 버퍼 시리즈. 이 사용방법은 readBytes
정수를 반환합니다(int
) 읽은 바이트 수를 나타냅니다.
방법 readBytesUntil
비슷한 방식으로 작동하지만 바이트 값을 나타내는 첫 번째 매개변수가 포함되어 있습니다. 버퍼, 읽기가 끝났음을 나타냅니다. 이 방법에서는 읽을 최대 바이트 수를 결정하는 매개변수가 의미가 없습니다. 그 양은 특수 코드에 의해 결정되기 때문입니다.
메소드의 작동을 테스트하려면 readBytes
다음 코드를 가정해 보겠습니다. 아두 이노 직렬 포트를 통해 텍스트를 보냅니다.
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()
{
}
|
다음 예제 프로그램은 처리 32바이트 블록으로 직렬 포트에서 텍스트를 읽습니다(TOTAL_BYTES). 작동하는지 확인하기 위해 콘솔을 통해 문자로 표시하고 수신된 바이트 유형을 강제로 표시합니다. 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]);
}
}
}
}
|
다음 스크린샷에서는 콘솔에 어떻게 표시되는지 확인할 수 있습니다. 처리 (최대) 32바이트 블록에 로드된 데이터(TOTAL_BYTES) 매번. 그러나 이미 언급된 문제가 있습니다. 아두 이노 의 구절을 보내왔습니다 페데리코 가르시아 로르카 형식의 텍스트로 인코딩된 예 UTF-8, 이는 사용된 것이 아닙니다. 처리 (자바), 당신은 무엇을 선호합니까 UTF-16 그래서 그 직급에 해당하지 않는 사람들은 ASCII 인쇄 가능이 잘못 해석되었습니다.
이 문제를 해결하기 위해 문자 세트를 로드할 수 있습니다(문자셋) 새 개체를 정의합니다. String
인코딩으로 표현되도록 강제 UTF-8 다음 예제 코드와 같이.
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));
}
}
}
|
수신된 최신 데이터를 읽습니다. last 및 lastChar 메서드.
나머지 읽기 방법("유형 read
») 그들은 다음의 정보를 로드합니다. 버퍼 도착한 순서대로 시리즈(FIFO), 이 두 가지 방법을 사용하면 버퍼 시리즈. 방법 last
마지막 바이트의 값을 다음과 같이 반환합니다. int
y lastChar
값을 다음과 같이 반환합니다. char
.
직렬 버퍼 관리
지금까지 살펴본 방법은 완벽하게 작동하지만 항상 직렬 포트에 대한 액세스를 이용하는 최선의 방법을 나타내는 것은 아닙니다. 데이터를 로드하려면 주기적으로 상태를 확인해야 합니다. 버퍼 시리즈를 만들고 코드의 반복 부분에서 사용 가능한 데이터를 읽습니다. 일반적으로 더 효율적인 방법은 데이터가 사용 가능하다는 것을 알고 있는 경우에만 데이터를 읽는 것입니다.
데이터가 수신되면 직렬 포트를 읽으십시오. 시리얼 이벤트.
액세스하려면 버퍼 serial 데이터가 수신되면 메소드 정의를 통해 이를 관리하여 Serial 이벤트를 활용할 수 있습니다. serialEvent
. 이 메서드는 인수로 실행하는 직렬 포트를 사용합니다.
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());
}
|
직렬 버퍼의 크기를 조정합니다. 버퍼 방식.
유용한 데이터 블록을 구성하는 바이트 수를 알고 있으면 이 데이터 읽기 스타일을 더욱 최적화할 수 있습니다. 버퍼 시리즈를 통해 serialEvent
. 방법 buffer
저장할 바이트 수를 설정할 수 있습니다. 버퍼 직렬 이벤트를 시작하기 전에. 이 메서드는 바이트 수를 나타내는 정수를 매개변수로 기대합니다.
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());
}
}
|
값이 수신될 때까지 버퍼를 채웁니다. bufferUntil 메소드.
메소드 호출을 설정하는 대신 serialEvent
대량의 데이터에 대해 버퍼, 방법으로 bufferUntil
특별한 값이 도착할 때까지 데이터를 저장한 다음 Serial 이벤트를 발생시키도록 구성할 수 있습니다. 이 메소드에 전달된 매개변수는 int
이는 호출에 의해 생성된 값을 나타냅니다. 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
}
|
버퍼에 저장된 데이터를 삭제합니다. 명확한 방법.
방법으로 clear
현재 있는 데이터를 삭제할 수 있습니다. 버퍼. 이 방법은 예를 들어 이전 세션에 남아 있는 데이터를 무시하고 새로운 데이터 수신 세션을 시작하는 데 사용할 수 있습니다.
직렬 포트를 통해 데이터를 읽는 일반적인 처리 애플리케이션
마지막으로 객체의 동작을 요약하는 것이 편리합니다. Serial
de 처리 좀 더 일반적으로 사용되는 직렬 포트를 통한 데이터 수신의 일반적인 예를 살펴보고 이를 이용해 그래프를 그려보겠습니다. 이 경우에는 쌓인 영역입니다.
직렬 라이브러리 가져오기
1
|
import processing.serial.*;
|
데이터 프로토콜(구분자) 결정
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
|
Serial 클래스의 객체 결정
1
|
Serial conexion_sensores;
|
사용된 직렬 포트를 설정하여 직렬 클래스 객체를 인스턴스화합니다.
1
|
conexion_sensores=new Serial(this,“/dev/ttyUSB1”,9600);
|
직렬 포트 버퍼 구성
1
|
conexion_sensores.bufferUntil(TERMINADOR);
|
Serial 이벤트에 대한 핸들러 구현
1
|
void serialEvent(Serial serie)
|
직렬 버퍼 읽기
1
|
String[] texto_valor=serie.readString().split(SEPARADOR);
|
수신된 데이터 조건
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]);
}
|
직렬 통신 종료
1
2
|
conexion_sensores.clear();
conexion_sensores.stop();
|
아래 예제 코드는 직렬 포트를 통해 수신된 값으로 영역 그래프를 생성하는 기능적(매우 간단하지만) 애플리케이션을 사용하여 이 요약을 보여줍니다. 이는 다음 애니메이션에서 보여주는 것과 유사합니다.
프로그램의 나머지 부분에서 길을 잃지 않고 직렬 통신에 주의를 집중하기 위해 처리, 이전 작업에 해당하는 코드 줄이 강조 표시됩니다.
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];
}
}
|
코멘트 남기기