الاتصالات التسلسلية مع المعالجة
فئة المسلسل
عمليات استخدام الاتصالات التسلسلية في اﻟﻤﻌﺎﻟﺠﺔ يتم تعريفها في الفصل Serial
أول عملية يتم إجراؤها لاستخدامها في برنامج (رسم تخطيطي) سيتم دمجه في الكود باستخدام import processing.serial.*;
.
فصل Serial
لديها خمسة منشئين مختلفين اعتمادًا على المعلمات المشار إليها. المعلمة الوحيدة المطلوبة هي الكائن الأصلي (أصل) والذي يتوافق عادةً مع البرنامج الرئيسي (على سبيل المثال، نافذة البرنامج) للفئة PApplet
. كما هو معتاد، سيكون الوالد هو البرنامج الذي تتم كتابته (ملف رسم تخطيطي الحالي)، ستكون قيمة هذه المعلمة الإلزامية الأولى this
.
المعلمات الخمس الأخرى التي يمكن تمريرها إلى المنشئ هي ① السرعة، ② اسم المنفذ التسلسلي ③ التكافؤ المستخدمة في البروتوكول، ④ بتات البيانات و ⑤ بتات التوقف. المعلمات التي يتم تمريرها بشكل متكرر، بالإضافة إلى الكائن الأصلي المطلوب، هي اسم المنفذ وسرعته.
La سرعة الاتصالات التسلسلية هو عدد صحيح (int
) أن الافتراضي إلى القيمة 9600 إذا لم يتم تمرير هذه المعلمة إلى المنشئ.
المنافذ التسلسلية المتاحة. طريقة القائمة
El اسم المنفذ له الشكل الذي يحدده النظام، بهذه الطريقة، على سبيل المثال في توزيعات لينكس سيكون شيء من هذا القبيل / ديف / 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 على ويندوز أو / ديف / ttyS0 en جنو / لينكس). باستثناء السياقات المحدودة جدًا التي تكون فيها الأجهزة التي يتم العمل معها معروفة تمامًا (مثل النظام في وضع الكشك)، لا يتم حذفها عادةً ويتم الإشارة إلى منفذ الوجهة صراحةً.
توضح لقطة الشاشة أعلاه إخراج النظام جنو / لينكس الذي يحتوي على أربعة منافذ تسلسلية RS-232 (ttyS0 a ttyS3) وخمسة محولات من نوعين (ttyACM0 a ttyACM1 y ttyUSB0 a ttyUSB2).
ومن أجل الوصول إلى المنافذ التسلسلية، يجب أن ينتمي المستخدم إلى المجموعة التي يعينه لها النظام، عادةً الكتابة البعيدة o الإتصال. في لقطة الشاشة للصورة أعلاه، يمكنك أن ترى أن المنافذ التسلسلية مدرجة مع ls /dev/tty[ASU]* -la
بيرتينسين آل جروبو الإتصال التي لديها أذونات الوصول للقراءة والكتابة عليها.
معلمات البروتوكول التسلسلي
La التكافؤ من الاتصالات التسلسلية يتم التعبير عنه في اﻟﻤﻌﺎﻟﺠﺔ كحرف (char
) والتي يمكن أن تأخذ القيم: ① N
(لا شيء) لعدم الكشف عن التكافؤ، ② E
(حتى) للإشارة إلى أن بت التكافؤ هو حتى، ③ O
(غريب) للإشارة إلى أن بت التكافؤ أمر غريب، ④ M
(علامة) لجعل دائما بت التكافؤ و⑤ S
(الفضاء) لجعل واحدة دائما بت التكافؤ. القيمة الافتراضية، إذا لم يتم تمريرها إلى المنشئ كمعلمة، هي N
(بدون التكافؤ).
عدد بتات البيانات، وهو ثمانية بشكل افتراضي، يشير إلى عدد البتات التي تشكل حمولة البيانات الصافية (تسمى حرفًا أو أحيانًا كلمة) التي يتم إرسالها في كل وحدة أساسية من الإطار. يتم التعبير عن المعلمة التي تشير إلى عدد بتات البيانات كعدد صحيح (int
).
وأخيرًا، تشير المعلمة الخامسة المحتملة إلى مدة العلامة النهائية، معبرًا عنها بـ نقطة التوقف (نقطة التوقف) ، والذي يشار إليه كرقم ممثل في النقطة العائمة (float
) والتي يمكن أن تأخذ القيم 1.0
(القيمة الافتراضية إذا لم يتم تمرير المعلمة إلى المنشئ)، 1.5
أو 2.0
.
بناة الطبقة التسلسلية
تعرض القائمة التالية مجموعات مختلفة من المعلمات التي يمكن تمريرها إلى مُنشئ الفئة 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
}
|
إرسال البيانات من خلال المنفذ التسلسلي. طريقة الكتابة
لإرسال البيانات، والطبقة 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”);
}
|
تحميل بايت واحد في كل مرة. طريقة القراءة
الطرق الرئيسية للفئة 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 اﻟﻤﻌﺎﻟﺠﺔ (في جافا) يتم تخزينها ببايتين، وهي القيمة التي تم اختيارها للرجوع إليها عند القراءة 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
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));
}
}
}
|
قراءة أحدث البيانات الواردة. الأساليب الأخيرة والأخيرة.
بينما بقية طرق القراءة (النوع " read
») يقومون بتحميل معلومات العازلة السلسلة بنفس الترتيب الذي وصلت به (FIFO)، مع هاتين الطريقتين البايت الأخير الذي وصل إلى العازلة مسلسل. طريقة last
ترجع قيمة البايت الأخير كـ a int
y lastChar
ترجع القيمة كـ a char
.
إدارة المخزن المؤقت التسلسلي
على الرغم من أن الأساليب التي تم مشاهدتها حتى الآن تعمل بشكل مثالي، إلا أنها لا تمثل دائمًا أفضل طريقة لاستغلال الوصول إلى المنفذ التسلسلي. لتحميل البيانات، يجب عليهم التحقق بشكل دوري من حالة الملف العازلة سلسلة وقراءة البيانات المتوفرة في جزء متكرر من التعليمات البرمجية. الطريقة الأكثر فعالية بشكل عام هي قراءة البيانات فقط عندما تعلم أنها متاحة.
قراءة المنفذ التسلسلي عند تلقي البيانات. حدث المسلسل.
للوصول إلى العازلة 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
يمكنك تكوين تخزين البيانات حتى تصل قيمة خاصة ثم رفع الحدث التسلسلي. المعلمة التي تم تمريرها إلى هذه الطريقة هي أ 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
|
تحديد كائن الفئة التسلسلية
1
|
Serial conexion_sensores;
|
قم بإنشاء مثيل لكائن الفئة التسلسلية عن طريق تعيين المنفذ التسلسلي المستخدم
1
|
conexion_sensores=new Serial(this,“/dev/ttyUSB1”,9600);
|
تكوين المخزن المؤقت للمنفذ التسلسلي
1
|
conexion_sensores.bufferUntil(TERMINADOR);
|
تنفيذ معالج للحدث التسلسلي
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];
}
}
|
أكتب تعليق