التخاطب مع الحاسب

يضيف ربط الدارات الإلكترونية بأجهزة الحاسب الشخصي إمكانيات كبيرة إليها كتسهيل التخاطب مع الدارة باستخدام واجهات البرامج الحاسوبية أو أخذ القراءات من الدارة و تخزينها لمعالجتها أو عرض تغيراتها كمنحنيات بيانية أو الاستفادة من قوة معالجة الحواسب الشخصية في خوارزميات الذكاء الصنعي و حتى الربط بالشبكات الحاسوبية .

سنشرح في هذه المقالة كيفية ربط دارة SYRDUINO (و جميع الدارات المتوافقة مع Arduino بصورة عامة) بالحاسب الشخصي لتبادل البيانات ( كالأوامر و قراءات المداخل) بينهما , و ذلك من حيث برمجة دارة SYRDUINO و كذلك كيفية بناء واجهة رسومية حاسوبية للتخاطب مع الدارة باستخدام بيئة البرمجة Microsoft Visual Studio و كمثال على ذلك سنقوم ببناء مشروع بسيط للتحكم ببعض الأجهزة المنزلية .

في الحقيقة توجد عدة منافذ لوصل الطرفيات أو الدارات الإلكترونية بالحواسب الشخصية و المحمولة منها مثلاً : المنفذ التسلسلي Serial و التفرعي LPT و قد أصبحا منفذيين قديمين , و المنفذ التسلسلي العام USB بنسخه الثلاثة, و المنفذ Fire-wire , و حتى منفذ الشبكة الحاسوبية RJ45 , و طبعاً الخيار الأكثر سهولة لمستخدم الحاسب الشخصي و المحمول و كذلك لدارة SYRDUINO هو المنفذ التسلسلي العام USB و ذلك لعدّة أسباب منها سهولة التعامل و عدم الحاجة إلى تعريف Driver خاص و إمكانية منفذ USB لتغذية الدارة و لأن دارات Arduino و الدارات المتوافقة معها يتم برمجتها من نفس المنفذ .

السؤال الذي يخطر على البال في البداية هو أي نوع من الأجهزة ستكون دارتنا (SYRDUINO)؟ لأنه من المعروف أنه توجد عدة فئات للأجهزة التي يمكن وصلها بمنفذ USB كأجهزة التخاطب مع المستخدم (كالفأرة و لوحة المفاتيح) و تسمى Human Interface Devices , و كذلك أجهزة التصوير (كالكاميرات و أجهزة الماسح الضوئي) , و أجهزة الاتصال كالموديمات السلكية و اللاسلكية , و حتى أن بعض تلك الأجهزة تظهر كبوابات تسلسلية أو تفرعية .

الجواب هو أن معظم الدارات المتوافقة مع Arduino تظهر لدى وصلها بمنفذ USB كبوابة تسلسلية يمكن معرفة رقمها (COM1 , COM2 , COM30 …..) من بيئة البرمجة Arduino IDE أو حتى من برنامج إدارة الأجهزة Device Manager في الحواسب العاملة بنظام Windows .

إن ما سبق يعني أن العمل البرمجي اللازم من جهة دارة SYRDUINO و الحاسب سيكون كأن الوصل بينهما يتم عن طريق بوابة تسلسلية عادية (مع تجاهل تام للمنفذ التسلسلي العام USB) .

يتم إرسال البيانات في البوابة التسلسلية على شكل بتات bits متتالية من خلال خطين , فكل طرف من طرفي الوصلة التسلسلية يجب أن يتضمن قطبين tx لإرسال البيانات و rx لاستقبال البيانات و طبعا يجب أن يوصل طرف الإرسال من كل منهما بطرف الاستقبال من الآخر (هذا الوصل موجود ضمن دارة SYRDUINO) و تكون البيانات على شكل كتل من سبعة أو ثمانية أو تسعة بتات يرسل قبلها و بعدها بتات أخرى ليعرف الطرف المستقبل بداية و نهاية الكتلة التي تسمى بإطار البيانات Frame و كذلك للتحقق من الأخطاء و يكون الإطار الناتج على الشكل التالي :


و بالتالي يجب أن يتفق المرسل و المستقبل التسلسليان على ما يلي :

- معدل الإرسال baud rate : طبعا توجد معدلات إرسال قياسية 4800 و 9600 و 19200 و 115200 بت في الثانية bps مثلاً .

- عدد بتات البيانات 7 أو 8 أو 9 بتات .

- نوع بت التحقق من الأخطاء parity الذي يتم حسابه من بتات البيانات بطريقتين : زوجي even أو فردي odd .

- عدد بتات التوقف .

- التحكم بالإرسال flow control : و هي طريقة كل طرف في الوصلة التسلسلية مدى جاهزية الطرف الآخر لاستقبال البيانات و هنا توجد ثلاثة خيارات : إما أن يتم ذلك باستخدام خطوط إضافية CTS و RTS و يسمى تحكم عتادي الإرسال hardware flow control , أو بصورة برمجية software flow control بإرسال إطارات معروفة مسبقاً , أو أن لا يتم استخدام التحكم بالإرسال البتة .

إن إرسال و استقبال البيانات التسلسلية من جهة دارة SYRDUINO يتم من خلال ودحدة مدمجة في متحكم الدارة تسمى USART هذه الوحدة تتيح مرونة كبيرة و خيارات واسعة للتعامل مع البيانات التسلسلية و ذلك ينطوي على بعض التعقيد , و لكن لغة Arduino C تبسط الأمر كثيراً و في نفس الوقت تقيد عملية تبادل البيانات كما يلي :

- عدد بتات البيانات : 8 .

- لا يمكن استخدام بت التحقق من الأخطاء parity .

- بت توقف واحد فقط .

- لا يوجد تحكم بالإرسال .

و يبقى الخيار الوحيد المتاح تقريباً هو معدل الإرسال , كما يجب ملاحظة أن استخدام الوصلة التسلسلية سيشغل القطبين الرقميين رقم 1 و 2 من دارة SYRDUINO .

برمجياً لتفعيل الوصلة التسلسلية يجب استدعاء التابع begin لمرة واحدة في البرنامج و يتم ذلك عادة في تابع الاعداد setup كما يلي :

 

Serial.begin(9600);

و لإرسال البيانات على شكل نصوص قابلة للقراءة يمكن استخدام التابعين print أوprintln كما يلي :

print(“hello world”);
x=12.33; print(x);

 

الفارق بين التابعين هو أن println يرسل بالإضافة إلى البيانات المطلوبة أمر سطر جديد بحيث تظهر البيانات التالية على سطر جديد عند الاستقبال .و المثال التالي سيوضح الموضوع :


مبدئياً سنستخدم النافذة Serial monitor ضمن البيئة Arduino IDE لاستقبال البيانات التي ترسلها الدارة كما هو مبين في الشكل , و كما هو واضح تطبع البيانات التالية لأمر print على نفس السطر في حين تطبع البيانات التالية لأمر println على سطر جديد .

يمكن استخدام معامل ثاني لتحويل الأرقام من نظام عد لآخر أو لتقريب الأرقام ذات الفاصلة مثلاً:

Serial.println(3.141592٫2)----------> 3.14

 

Serial.print(78, BIN)----------> 1001110

 

Serial.print(78, OCT)----------> 116

 

Serial.print(78, DEC)----------> 78

 

Serial.print(78, HEX)----------> 4E

 

أما لإرسال البيانات على شكل بايتات يمكن أن نستخدم التابع write كما يلي :

 

Serial.write(55);

لكن عند استخدام هذا التابع مع نافذة Serial monitor ستظهر على النافذة الأحرف المقابلة للبيانات وفق نظام الترميز ASCII و المثال التالي يبين الفرق بين الحالتين :


لاستقبال البيانات يمكن استخدام التابع read كما يلي :

x=Serial.read();

يعيد هذا التابع آخر بايت تم استقباله أو يعيد القيمة -1 في حال عدم استقبال أي شيء و لذلك يجب الانتظار حتى وصول بايت جديد باستخدام التابع available وهو تابع يعيد القيمة المنطقية true في حال استقبال بايت واحد على الأقل إلى البوابة التسلسلية كما يلي :

 

if (Serial.available()) x=Serial.read();

و المثال التالي يقوم بتشغيل ثنائي مضيء عند استقبال الحرف a و يقوم بإطفائه عند استقبال الحرف b :


#define led 2
char c;
void setup()
{
  Serial.begin(9600);
  pinMode(led,OUTPUT);
}
void loop()
{
  if (Serial.available())
  {
    c=Serial.read();
    switch(c)
    {
    case 'a':
      digitalWrite(led,HIGH);
      Serial.println("The LED is ON!");
      break;
    case 'b':
      digitalWrite(led,LOW);
      Serial.println("The LED is OFF!");
      break;
    }
  }
}

 

الخطوة التالية و المهمة هي تصميم برنامج حاسوبي خاص بنا للتخاطب مع الدارة :

لتصميم برنامجنا الحاسوبي سنستخدم بيئة البرمجة Microsoft Visual Studio 2010 و سنكتب برنامجنا باللغة #C , يجب أن يقوم البرنامج بقراءة قيمة حساس حرارة (موصول إلى المدخل التمثيلي رقم 1 من دارة SYRDUINO NG) كما سيؤمن المتحكم بمخرجين رقميين سنفترض أن أحدهما يقود أجهزة إنارة (رقم 2) و الآخر يقود مروحة تهوية (رقم 3) و ذلك وفق الخطوات التالية :

بعد تشغيل بيئة البرمجة Microsoft Visual Studio 2010 نختار New من القائمة File ثم Project :


من قائمة Installed Templates نختار #Visual C ثم Windows و من القائمة التي على يمينها نختار Windows Form Application و من ثم تقوم بإدخال اسم لمشروعنا الجديد في الخانة Name الموجودة في أسفل النافدة :


تظهر بعد ذلك نافذة فارغة كما هو في الشكل التالي :


من صندوق الأدوات Toolbox (الموجود في يسار نافذة بيئة البرمجة) نختار عناصر واجهة البرنامج و نقوم بترتيبها كما هو مبين في الشكل التالي و هذه العناصر هي :

  • عنصر من نوع Label .
  • عنصر من نوع TextBox .
  • أربعة أزرار button .
  • عنصر واحد من نوع SerialPort و هو العنصر الذي سيؤمن التواصل مع الدارة .
  • عنصر واحد من نوع Timer سنستخدمه لتحديث قيمة درجة الحرارة الظاهرة على واجهة البرنامج بصورة دورية .

 

نقوم بضبط خصائص كل عنصر في واجهة البرنامج باختياره (بالنقر عليه) و من ثم بتعديل خواصه من صندوق الأدوات Properties الموجود أسفل يسار نافذة بيئة البرمجة و عموماً سنغير قيمة الخاصة Text للأزرار و العلامات Labes بحيث يكون لها أسماء مفهومة كما هو مبين في الشكل كما يجب أن نعدل قياس الأزرار لكي تظهر أسماؤها الجديدة , من المهم جداَ ضبط الخاصة PortName لعنصر البوابة التسلسلية SerialPort على رقم البوابة التسلسلية لدارتنا مثلاً COM50 (نفس رقم البوابة الذي نستخدمه لبيئة Arduino IDE )و لا مانع من التحقق من قيم باقي خواص هذا العنصر كما يلي :

Baud rate : 9600 .

Handshaking : none .

Data bits : 8 .

Parity : none .

StopBits : 1 .

أخيراً تغير قيمة خاصة عنصر المؤقت Timer1 وهي الخاصة Enabled إلى القيمة true لتفعيل العنصر في بداية البرنامج .


بقي الآن الموضوع الأهم وهو برمجة عناصر الواجهة و ذلك باستخدام توابع الأحداث Events التابعة لكل منها , وبما أن النشاط الوحيد لبرنامجنا هو التخاطب مع البوابة التسلسلية فلابد في البداية من فتح هذه البوابة قبل التخاطب معها باستخدام التابع open و سنستخدم لذلك تابع حدث تحميل نافذة البرنامج Form Load و لتفعيل هذا التابع ننقر نقرتين على عنصر نافذة المشروع (يفترض أن يكون اسمه Form1)ليتم فتح نافذة البرمجة و فيها تابع تحميل واجهة البرنامج و سندخل الأسطر التالية :

try

{

serialPort1.Open();

}

catch

{

MessageBox.Show("تعذر فتح البوابة التسلسلية");

}

تحاول الأسطر السابقة (من خلال تعليمتي try وcatch) فتح البوابة التسلسلية و تظهر رسالة خطأ في حال فشلها في ذلك , طبعاً كان يمكن الاكتفاء بسطر واحد وهو :

serialPort1.Open();

لكن الطريقة السابقة تجعل البرنامج أكثر مقاومة للأخطاء كأن لا تكون الدارة موصولة إلى الحاسب.

 

يجب أن يقوم كل مفتاح في واجهة البرنامج بإرسال أمر إلى الدارة و سنرمز لكل أمر بحرف كما يلي:

لتشغيل الإنارة نرسل الحرف A .

لإطفاء الإنارة نرسل الحرف a .

لتشغيل المروحة نرسل الحرف B .

لإيقاف المروحة نرسل الحرف b .

يمكن إرسال البيانات إلى البوابة التسلسلية (بعد فتحها) باستخدام التابع write .

و لذلك سنستخدم تابع حدث النقر click على كل زر في الواجهة , مثلاً ننقر مرتين على الزر button1 و ندخل السطرين التاليين :

if (serialPort1.IsOpen)

serialPort1.Write("A");

أيضاً هنا قمنا بالتحقق من كون البوابة مفتوحة(بفحص القيمة IsOpen للبوابة)قيل إرسال الأمر لضمان عدم توقف البرنامج في حال عدم كون البوابة مفتوحة.

نكرر ما سبق لبقية أزرار الواجهة مع تغيير حرف الأمر لتكون توابع أحداث النقر عليها كما يلي :

private void button1_Click(object sender, EventArgs e)

{

if (serialPort1.IsOpen)

serialPort1.Write("A");

}

 

private void button2_Click(object sender, EventArgs e)

{

if (serialPort1.IsOpen)

serialPort1.Write("a");

}

 

private void button3_Click(object sender, EventArgs e)

{

if (serialPort1.IsOpen)

serialPort1.Write("B");

}

 

private void button4_Click(object sender, EventArgs e)

{

if (serialPort1.IsOpen)

serialPort1.Write("b");

}

لاستقبال البيانات من الواجهة سنستخدم حدث وصول بيانات DataReceived الخاص بعنصر البوابة التسلسلية الذي ستدخل فيه السطر التالي :

heat = serialPort1.ReadByte();

هنا استخدمنا التابع ReadByte لقراءة القيمة الواصلة إلى البوابة و تحزينها في المتحول heat الذي يجب أن نعرفه في بداية فئة class نافذة البرنامج كما يلي :

public partial class Form1 : Form

{

int heat;

ننقر نقرتين على عنصر المؤقت Timer1 لننتقل إلى تابعه الذي ينفذ كل 100mS (يمكن تغيير هذا الزمن بتغيير قيمة خاصته Duration) و ندخل فيه السطر التالي الذي يقوم بإظهار قيمة المتحول heat على مربع النص في الواجهة :

 

textBox1.Text = Convert.ToString(heat);

يمكننا تشغيل الواجهة بالضغط على رز start debugging في بيئة البرمجة أو باستخدام المفتاح F5 و في حال عدم وجود أخطاء في إدخال البرنامج ستظهر الواجهة كما هو مبين في الشكل التالي :


الآن يجب توصيل دارتنا و من ثم برمجتها كما يلي :


#define light  2
#define fan    3
int i;
char c;
void setup()
{
  Serial.begin(9600);
  analogReference(INTERNAL);
  pinMode(light,OUTPUT);
  pinMode(fan,OUTPUT);
}
void loop()
{
  i=analogRead(1);
  i=map(i,0,1023,0,255);
  Serial.write(i);
  delay(200);
  if (Serial.available())
  {
    c=Serial.read();
    switch(c)
    {
    case 'A':
      digitalWrite(light,HIGH);
      break;
    case 'a':
      digitalWrite(light,LOW);
      break;
    case 'B':
      digitalWrite(fan,HIGH);
      break;
    case 'b':
      digitalWrite(fan,LOW);
      break;
    }
  }
}
ملاحظة : يمكن استخدام الثنائي المضيئ الموجود على دارة SYRDUINO والموصول على البوابة الرقمية رقم 13 (حاول أن تعدل البرنامج لتستخدمه!).

أخيراً لا بد من أن ننوه على وجود توابع أخرى لإرسال و استقبال البيانات في عنصر البوابة التسلسلية في بيئة البرمجة Microsoft Visual Studio 2010 , كما أن الحالة التي عالجناها هنا هي حالة بسيطة (قراءة قيمة واحدة من الدارة) و لكن توسيع هذا المثال ليس بالأمر الصعب فأطلق العنان لأفكارك !.

ملاحظة: توجد مكتبة برمجية خاصة بالدارات المتوافقة مع دارات Arduino خاصة بالتخاطب مع الحواسب وهي مكتبة Firmata و توجد لها مكتبات متوافقة معها خاصة بالبيئة Microsoft Visual Studio تقوم بقراءة قيم مداخل الدارة التحكم بمخارجها .

أضف تعليق

كود امني
تحديث