أداة فتح الأبواب و ضغط الأزرار

مقدمة

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

grabberpusher

نظام محاكاة الأردوينو

المقدمة

تعد المعامل الافتراضية أحد الأساليب الحديثة في مجال التعليم والتدريب لمساهمتها في تنمية المهارات العلمية والتطبيقية بطريقة اكثر فاعلية واختصار الوقت والجهد والتكلفة.

في هذا الدرس ستتعرف على برنامج محاكاة الأردوينو الذي يمكنك من تنفيذ العديد من التجارب في مجال الالكترونيات.

arduino-simulator-tinkercad

ماهو نظام محاكاة الأردوينو

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

arduino-simulator-tinkercad

يتميز البرنامج بالعديد من المميزات منها:

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

بالإضافة إلى العديد من المميزات الأخرى التي ستتمكن من اكتشافها بالممارسة.

واجهة نظام محاكاة الأردوينو

ابدأ بإنشاء حساب في موقع Tinkercad ، او تسجيل الدخول بحساب قوقل (Google) الخاص بك، ثم انقر على زر دوائر (Circuits)، ثم انشاء دائرة جديدة بالنقر على زر (Create new Circuit).

arduino-simulator-tinkercad
الشكل (1): واجهة موقع تينكركاد (Tinkercad)

 ستظهر واجهة البرنامج التي تتكون من مجموعة من المكونات أهمها كما يظهر في الشكل 2 .

arduino-simulator-tinkercad
الشكل (2): واجهة نظام محاكاة الأردوينو
  1. مكتبة عناصر الدوائر الالكترونية: تحتوي على لوحات الأردوينو، ومقاومات، والحساسات، والمحركات، ولوحات التجارب، وأسلاك التوصيل … إلخ.
  2. منصة التجارب: مساحة سحب عناصر الدوائر الإلكترونية لإنشاء محاكاة للتجارب المطلوبة.
  3. نافذة التحكم في مواصفات القطع الإلكترونية المدرجة على منصة التجارب.
  4. نافذة كتابة الأوامر البرمجية
  5. تشغيل وإيقاف المحاكاة
  6. تدوير العناصر الالكترونية
  7. حذف العناصر الالكترونية
  8. التراجع عن إجراء ما: للتراجع عن أحد الإجراءات
  9. إعادة إجراء: لإعادة شيء ما تراجعت عنه
  10. حفظ الملف (تصديره)
  11. مشاركة الملف

كيف يمكن استخدام النظام

  • افتح موقع تينكركاد (Tinkercad)، ثم أنشئ دائرة جديدة.
  • ادرج العناصر الإلكترونية لتجربة الوميض.
  • وصل القطب السالب(المهبط) للثنائي المشع للضوء إلى منفذ GND ، والقطب الموجب (المصعد) للثنائي المشع للضوء إلى المنفذ الرقمي 13 مع استخدام مقاومة 220 أوم.
arduino-simulator-tinkercad
  • ارسم المخطط الانسيابي للمهمة التي ترغب بتنفيذها
arduino-simulator-tinkercad
  • اكتب تسلسل الأوامر البرمجي
arduino-simulator-tinkercad
  • انقر على زر تشغيل المحاكاة، ستلاحظ بأن الضوء يومض مرارًا وتكرارًا.

ملاحظة: إذا ظهرت نتيجة غير ذلك، تحقق من توصيل الدائرة، وتحقق من تسلسل وكتابة الأوامر البرمجية.

قناع حماية الوجه

المقدمة

تعد معدات الوقاية الشخصية الطبية من أبرز الوسائل التي تستعمل لمنع انتقال العدوى لمقدمي الرعاية الصحية

سنتعرف في هذا الدرس على واقي الوجه.

face-shield-protection

الأدوات

face-shield-protection

طابعة ثلاثية الأبعاد

face-shield-protection

خيوط فلمنت   Filament

face-shield-protection

PET plastic sheet

face-shield-protection face-shield-protection

 

قاطع ليزر أو مقص يدوي

 

قناع حماية الوجه

هو عبارة عن قناع بلاستيكي يستخدم لحماية الوجه والعينين للوقاية من احتمالات تعرض مقدمي الرعاية الصحية لرذاذ الدم أو سوائل الجسم المتطايرة من المريض.

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

أجزاء القناع

توضح الصورة التالية الأجزاء الرئيسية لقناع حماية الوجه

face-shield-protection

تصميم قناع حماية الوجه

بدأ فريق من المصنعين تصميم النموذج الأول (RC1) ثم تم تطويره للنموذج الثاني (RC2)، ويعد النموذجان ملائمين للاستخدام في قناع حماية الوجه. إلا أن التصميم الثاني يوفر حماية أفضل ويمنح راحة أكثر عند الإرتداء.

face-shield-protection

 التحديثات التي تم إجراؤها على التصميم الثاني (RC2)

التحديث RC1 RC2
  •  زيادة سمك سطح الجزء الداخلي للتصميم : من 1.5 مليمترإلى 2 مليمتر
  • زيادة سمك سطح الجزء الخارجي للتصميم : من 2 مليمتر إلى 2.5 مليمتر
face-shield-protection face-shield-protection
  • تخفيف ضغط عقال الرأس
face-shield-protection face-shield-protection
  • ابعاد الواقي عن الوجه ليتناسب مع أجهزة التنفس والكمامات والنظارات الواقية
face-shield-protection face-shield-protection
  • يحتوي عقال الرأس في RC2 على امتداد من الجانبين
face-shield-protection face-shield-protection

اللوح الشفاف

يمكن استخدام أي نوع من البلاستيك المرن والشفاف ويكون قابل للقطع بالليزر – السمك الموصى به من 0.5 مليمتر إلى 1 مليمتر-. في حال عدم توفر قاطع الليزر لديك، يمكنك  قطع البلاستيك يدويًا بالمقص.

face-shield-protection
 يمكنك تنزيل تصميم واقي حماية الوجه من خلال النقر على الرابط.

التجميع

تجهيز اللوح الشفاف-في حال عدم توفر قاطع ليزر- :

  • اثني الجانب الأطول من اللوح البلاستيكي بلطف إلى النصف.
  • ضع علامة على مركز اللوح الشفاف باستخدام المحدد

face-shield-protection

  • طابق النقطة التي تم تحديدها في الخطوة السابقة مع منتصف عقال الرأس
  • ضع علامات عند النتوءات البارزة من عقال الرأس على اللوح البلاستيكي باستخدام المحدد، ليتم تحديد المسافات الفاصلة بين ثقوب النتوءات على اللوح – يفضل أن تكون المسافة المحددة على اللوح أكبر قليلا من المسافة الفعلية على عقال الرأس-
face-shield-protection
  • استخدم مثقاب الورق لثقب اللوح البلاستيكي
face-shield-protection

تجميع الجزء العلوي لعقال الرأس:

  • ثبت اللوح البلاستيكي على عقال الرأس كما هو موضح بالصور التالية
face-shield-protection face-shield-protection

تجميع الجزء السفلي لقناع الوجه:

  • ثبت الجزء السفلي لقناع الوجه باستخدام الغراء
face-shield-protection

الاحتياطات المتبعة للتعقيم:

  1. لا تترك الجزء المطبوع لساعات طويلة على سطح الطابعة لتجنب تلوث الجزء المطبوع.
  2. تقيد بإرتداء قفازات جديدة والكمامة الطبية قبل إزالة المطبوعات ثم ضعها مباشرة في كيس قابل للإغلاق.
  3.  لا تزل غلاف اللوح الشفاف قبل قصه بالليزر أو المقص للحد من احتمال حدوث أي تلوث.
  4. تقيد بإرتداء القفازات والكمامة الطبية عند تصنيع اللوح البلاستيكي  باستخدام قاطع الليزر او المقص، وتقيد بإرتداء قفازات جديده لكل لوح تقوم بإنتاجه.
  5.  احفظ الأجزاء على الفور في كيس قابل للاغلاق.
  6. وضح لمن تقوم بالطباعة لهم ، عن بيئة التصنيع لديك
  7. اترك واقيات الوجه بالتخزين لمدة 2-3 أيام قبل توزيعها، معظم المصادرتوضح أن مدة بقاء الفيروس على البلاستيك 2-3 أيام
  8. لا تقم بتخزين الكمية بالكامل في مكان واحد.

ملاحظة: يتم تسخين البلاستيك إلى درجة حرارة عالية أثناء الطباعة يتنج عن هذا تعقم الجزء المطبوع

من هنا يمكنك الاطلاع على مصدر الدرس

استخدام المعقم بدون لمس

المقدمة

في الدرس السابق تعلمنا تصنيع معقم بالمنزل في هذا الدرس تعلم كيف، يمكننا استخدام حساس المسافة للتعقيم بدون مسك العبوة،

القطع المطلوبة

اردوينو(Arduino)

us-sanitizer-without-touch

A-B USB cable

us-sanitizer-without-touch

حساس مسافة (ultrasonic sensor)

us-sanitizer-without-touch

(TowerPro MG946R Servo )محرك سيرفو

us-sanitizer-without-touch

لوح تجارب صغير (Small size breadboard)

us-sanitizer-without-touch

(Jumper Wires Female/male )اسلاك توصيل أنثى/ذكر

us-sanitizer-without-touch

(Jumper Wires Male/Male) اسلاك توصيل ذكر/ذكر

توصيل الدائرة :

قم بتوصيل الدارة كما هو موضح بالصورة التالية :

servo_sanitizer

الكود البرمجي :

#include <Servo.h>
Servo myservo; 
const int trigPin = 10;
const int echoPin = 11;
long duration;
int distance; 
void setup() {
  myservo.attach(9);  
pinMode(trigPin, OUTPUT); 
pinMode(echoPin, INPUT); 
}

void loop() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance= duration*0.034/2;

if (distance < 5)
{
myservo.write(110);
}
else
{
myservo.write(35);
}}

شرح الكود البرمجي :

في البداية قمنا بادراج مكتبة السيرفو ثم قمنا بتسمية منافذ الأردوينو المستخدمة في المشروع و تعريف المتغيرات

#include <Servo.h>
Servo myservo; 
const int trigPin = 10;
const int echoPin = 11;
long duration;
int distance; 

في دالة setup تعريف المنفذ الذي سيتحكم بمحرك السيرفو ،

و تعريف منافذ حساس  المسافة

myservo.attach(9);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);

يقوم الكود بتشغيل الحساس و حساب المسافة

void loop() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance= duration*0.034/2;

فقط اذا كانت المسافة أقل من 5سم يتحرك السيرفو بمقدار 110درجة

if (distance < 5)
{
myservo.write(110);
}
else
{
myservo.write(35);
}
}

اصنع بنفسك معقم يدين

مقدمة

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

 

 

 

الأدوات

 

 

how-to-make-handsanitizer

 

تركيز أعلى من 70% (Isopopyl alcohol)كحول

 

how-to-make-handsanitizer

 

جل الصبار

how-to-make-handsanitizer

زيوت عطرية(اختياري)

 

الخطوات:

أضف 1/3  كوب من جل الصبار للمساعدة في الحفاظ على يديك ناعمة ولتخفيف قوة تركيز الكحول. إلى 3/2 كوب من الكحول الذي يعتبر العنصر النشط في وصفة مطهر اليدين،حيث يحتاج إلى أن يحتوي على 60٪ على الأقل من المعقم ليكون فعالًا

 

how-to-make-handsanitizer

 إضافة بضع قطرات من الزيت العطري الزيوت العطرية إذا كنت تحب رائحة معينة مثل الليمون أو اللافندر

how-to-make-handsanitizer

اخلط جميع المكونات جيدا

how-to-make-handsanitizer

باستخدام القمع ، اسكب معقم يدك بعناية في الزجاجة التي تختارها ، ثم ثبّت الجزء العلوي من الزجاجة بإحكام ، وابدأ في استخدامها

how-to-make-handsanitizer

 

نظام مسح الفضاء المحيط

في هذا المشروع سنتعلم كيفية استخدام حساس الموجات الفوق صوتية (Ultrasonic) مع الأردوينو في عمل مسح للفضاء المحيط

المكونات المطلوبة

 

arduino uno r3

الأردوينو (Arduino Uno)

حساس المسافة (Ultrasonic Sensor)

محرك سيرفو (Servo Motor)

Full size breadboard 830

 لوحة تجارب (Breadboard)

Breadboard Jumper Wire 65 pcs

أسلاك توصيل (Wires)

محرك السيرفو (Servo Motor)

هو عبارة عن جهاز يقوم بتحويل الاشارة الكهربائية إلى حركة ميكانيكية. يختلف هذا المحرك عن محركات التيار المستمر (DC) في ان حركته غير مستمرة.

Servo Motor: استخدام مقاومة متغيرة للتحكم فى حركة محرك سيرفو

اي عند دخول إشارة كهربائية إلى محرك السيرفو بقيمة معينة ينتج عن ذلك حركة بزاوية معينة ثم يتوقف المحرك عن الحركة ويظل محتفظ بالزاوية التي وصل إليها. على عكس محركات التيار المستمر فإنها تستمر في الدوران بمجرد إمدادها بالتيار الكهربائي.

مداخل محرك السيرفة :

Servo Motor: استخدام مقاومة متغيرة للتحكم فى حركة محرك سيرفو

كيفية عمله

يعمل محرك السيرفو بوضع اشارة كهربائية PWM بزمن محدد . تقوم مكتبة السيرفو في الاردوينو بالاهتمام بهذا الامر عنك، فحسب هذه الإشارة يتحرك السيرفو بزاوية معينة من 0 إلى 180 درجة.

   لمعلومات اكتر راجع درس محرك السيرفو Servo Motor

 

حساس الموجات فوق الصوتية

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

استخدام-حساس-الموجات-فوق-الصوتية-مع-ال

توصيل الدارة

قم بتوصيل الدارة كما هو موضح بالصورة التالية :

Room Map-Making Using Ultrasonic With Arduino

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

Room Map-Making Using Ultrasonic With Arduino

قم توصيل محرك السيرفو كما هو موضح بالجدول :

الطرف التوصيل
الاحمر Vcc / 5 V
البرتقالى Arduino Pin 9
البنى Ground

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

Room Map-Making Using Ultrasonic With Arduino

 

التوصيل كاملا :

 

Room Map-Making Using Ultrasonic With Arduino

البرمجة :

سنقوم بكتابة برنامج يقوم بإرسال إشارة دوران محرك السيرفو بزاوية معينة وحساب المسافة الموجودة امام المستشعر عند تلك الزاوية. ثم يتم ارسال هذه النتائج إلى المنفذ التسلسلي.

وبدلا من قراءة هذه النتائج على الشاشة التسلسلية (Serial Monitor) نقوم بتشغيل برنامج الماتلاب. نقوم بالماتلاب بعمل كود خاص به ليتم تشغيله لإجراء بعض الحسابات ومن ثم إعطاء مخطط بسيط لخريطة المكان.

برمجة الأردوينو :

#include <Servo.h>
#include <NewPing.h>

#define TRIGGER_PIN  12 
#define ECHO_PIN     11  
#define MAX_DISTANCE 200 

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); 
Servo myservo;  

int pos = 0;   
int it = 10;

void setup() {
  myservo.attach(9); 
  Serial.begin(9600);
  delay(3000);
}

void loop() {
  int i = 0;
  int t = 0;
  int a = 0;

  for (i = 0; i < 180; i ++)
  {
    unsigned int uS = sonar.ping();
    myservo.write(i);
    delay(20);
    for (t = 0; t < it; t++)
    {
      uS = sonar.ping();
      a = uS/US_ROUNDTRIP_CM + a;
      delay(30);
    }
    
    a = a / (it-1);
    t = 0;

    Serial.println(a); 
    a = 0;
  }

}

شرح الشفرة البرمجية (Code):

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

#include <Servo.h>
#include <NewPing.h>

#define TRIGGER_PIN  12 
#define ECHO_PIN     11  
#define MAX_DISTANCE 200

بعد ذلك اعلنا عن المتغيرات اللازمة مثل المتغيرات الخاصة بالسيرفو و حساس الموجات فوق الصوتية والتي تستخدمها المكتبات ايضا. اعلنا عن متغير pos المستخدم في تسجيل موضع السيرفو و المتغير it المستخدم كعداد.

NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE); 
Servo myservo;  

int pos = 0;   
int it = 10;

في الدالة ()setup قمنا بوضع الاعدادات اللازمة مثل تشغيل السيرفو و السيريال ثم الانتظار 3 ثوان حتى نضمن ان المحرك اصبح في وضعه الصحيح :

void setup() {
  myservo.attach(9); 
  Serial.begin(9600);
  delay(3000);
}

في الدالة ()loop نقوم بتحريك السيرفو حركة واحدة واخذ 10 قراءات للحساس، لحساب المتوسط لها، ثم كتابتها على المنفذ التسلسلي الذي لاحقا سنقوم باستقبال النتائج من خلاله على برنامج الماتلاب.

ثم بعد ذلك نقوم بتحريك السيرفو درجة اخرى و ننفذ كل ماسبق مرة اخرى و هكذا حتى يكتمل دوران السيرفو 180 درجة .

void loop() {
  int i = 0;
  int t = 0;
  int a = 0;

  for (i = 0; i < 180; i ++)
  {
    unsigned int uS = sonar.ping();
    myservo.write(i);
    delay(20);
    for (t = 0; t < it; t++)
    {
      uS = sonar.ping();
      a = uS/US_ROUNDTRIP_CM + a;
      delay(30);
    }
    
    a = a / (it-1);
    t = 0;

    Serial.println(a); 
    a = 0;
  }

}

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

بعد رفع كود الأردوينو نقوم بتشغيل برنامج الماتلاب عم طريق كتابة الكود الخاص بالماتلاب في ملف file.m . اي عند كتابة كود الماتلاب في ملف نصي تأكد من أن الإمتداد له m. أو قم بإنشاء ملف script من داخل اماتلاب وضع الكود به.

الشفرة البرمجية للماتلاب

theta = 0:(pi/180):pi;
s = serial('/dev/ttyS1011');
s.BaudRate=9600
fopen(s)
i = 0;

inc = 1;

while i<180
   A = fgets(s);
   num(i+1) = str2num(A);
   i = i+1;
end
fclose(s)

j = 1

while j<181
    tab(j,1) = (j-1)*inc
    tab(j,2) = num(j)
    tab(j,3) = num(j)*cosd((j-1)*inc)
    tab(j,4) = num(j)*sind((j-1)*inc)
    j = j+1
end
%figure
%polar(theta,num)

plot(tab(:,3),tab(:,4))

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

Room Map-Making Using Ultrasonic With Arduino

ملاحظة :

التغيير الوحيد الذي ستقوم بة في كود الماتلاب هو تغير اسم منفذ السيريال الذي يستخدمه الاردوينو.

Room Map-Making Using Ultrasonic With Arduino

فتقوم بتغييرة الى COM0 او COM1 او ايا كان اسم المنفذ الذي يستخدمة الاردوينو

Room Map-Making Using Ultrasonic With Arduino

و بعد ان تنتهي من كل شيء تقوم بضغط Run في برنامج الماتلاب و تنتظر إلى ان ينتهي السيرفو من عمل المشوار كامل ثم تظهر النتائج على الشاشة

ملاحظة : عند تشغيل كود الماتلاب تاكد ان لا يكون الSerial Monitor الخاص ببرنامج الاردوينو مفتوح

التحكم بالـ LED من خلال Node-RED

خلال هذا المشروع، ستتعرف على كيفية استخدام الـ Node-RED للتواصل مع منافذ GPIO الخاصة بالـ Raspberry Pi.
سنقوم بإنشاء تدفق Node-RED للتحكم في LED.

getting-started-with-node-red

القطع المطلوبة :

الأدوات التي تحتاجها لهذا المشروع :

5mm Red LED

العدد :1

220 Ω resistor

مقاومة 220 اوم

العدد :1

لوح تجارب صغير

Raspberry Pi 3 Model B

Female-Male Jumper Wires

 اسلاك توصيل أنثى/ذكر (Jumper Wires Female/male)

5V 2A Switching Power Supply MicroUSB Cable

Power Supply

Samsung 8Gb Class 6 Microsd Memory Card 2

Samsung 8Gb Class 10 Microsd Memory Card

تصميم لوحة التجارب:

قم بتوصيل الـ LED إلى منفذ GPIO 17 على الـ Raspberry Pi من خلال اتباع الشكل التالي :

getting-started-with-node-red

عادة ما يكون الطرف الموجب للـ LEDأطول، وهذا الطرف موصل إلى الـ GPIO 17 على الجانب الأيسر من اللوح.

البدء مع الـ Node-RED

ابدأ من الـ Raspberry Pi ، قم بالنقر على أيقونة Raspberry (القائمة الرئيسية) ، ثم قائمة البرمجة Programming لفتح Node-RED

getting-started-with-node-red

يجب أن تشاهد نافذة تعرض معلومات حول بدء تشغيل Node-RED

getting-started-with-node-red

انتقل الآن إلى قائمة الإنترنت وافتح متصفح الويب Chromium .

getting-started-with-node-red

في المتصفح عند شريط العنوان في الأعلى قم بكتابة localhost:1880 ، ثم اضغط على Enter .  سيعرض هذا واجهة الـ Node-RED

getting-started-with-node-red

لا يحتاج جهاز الراسبيري باي إلى الاتصال بالانترنت لاستخدام Node-RED: localhost هو العنوان الذي يستخدمه الراسبيري باي للإشارة إلى نفسه و :1880 يعني ان يستخدم المنفذ 1880.

إنشاء التدفق

أولا الاتصال بمنفذ GPIO :

البرامج في الـ Node-RED تسمى تدفقات. يمكنك إنشاء العديد من التدفقات كما تريد، ويمكن تشغيلها كلها في نفس الوقت.
البلوك الملونه على الجانب الأيسر من الواجهة هي العقد. قم بالتمرير إلى اسفل القائمة وسترى بعض العقد المسمى بـ Raspberry Pi.

getting-started-with-node-red

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

getting-started-with-node-red

انقر نقرا مزدوجا على العقدة وسيظهر مربع يتيح لك إعداد وتهيئة هذه العقدة.

أولا نقوم بتحديد منفذ الـ GPIO الذي تم استخدامه GPIO17، وتحديد القيمة الابتدائية للمنفذ low . يمكنك تغير اسم العقد هنا تم تسميتها بإسم Green LED . عند الإنتهاء ، انقر فوق تم Done.

getting-started-with-node-red

ثانيا التحكم بمخرجات المنفذ :

الآن انتقل مرة أخرى إلى قائمة العقد. لتشغيل وإيقاف الـ LED، نحن بحاجة إلى عقدة إدخال. في Node-RED، يمكننا إدخال الرسائل في التدفقلتؤدي بعد ذلك إلى حدوث أشياء نتيجة لذلك. قم بسحب عقدة الإدخال inject node للتدفق.

getting-started-with-node-red

انقر نقراً مزدوجاً فوق عقدة الإدخال inject node . استخدم القائمة المنسدلة بجانب Payload لتغير نوع البيانات إلى string . قم بكتابة القيمة 1 في مربع Payload – ستمثل هذه الرسالة قيمة 1 والتي سيتم اخراجها على المنفذ ليتم تشغيل الـ LED.
يمكنك تغير اسم العقدة إلى ON.

 

getting-started-with-node-red

كرر الخطوات السابقة لإنشاء عقدة إدخال أخرى، ولكن سيتم إخراج قيمة 0 كرسالة محمولة، وتسمية هذه العقدة OFF.

Create two inject nodes

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

getting-started-with-node-red

نشر التدفق

تم الانتهاء من التدفق، حتى نتمكن من نشره. انقر على زر Deploy الأحمر الكبير أعلى يمين الشاشة. ستظهر رسالة في الأعلى تقول تم نشرها بنجاح “Successfully deployed”

getting-started-with-node-red

انقر الآن على المربع الأزرق على يسار العقدة on لإدخال الرسالة 1. تتلقى عقدة Green LED الرسالة وينبغي أن يضيء الـ LED. يجب أن تكون قادراً على إيقاف تشغيل الـ LED بالنقر على عقدة OFF.

getting-started-with-node-red

التحكم بالـ LED عبر الأوامر الصوتية

في هذا المشروع، سنقوم بتعلم كيفية تحويل الصوت إلى أوامر تحكم . وتعرف على وحدة التعرف على الصوت (Voice Recognition)، وكيفية تشغيل/ إيقاف الـ LED  باستخدام الأوامر الصوتية. هناك عدة طرق أخرى لتنفيذ التعرف على الصوت في مشروعك من خلال هاتف  android إلى   Alexa أو  Raspberry pi أو اي طريقة أخرى.

arduino-voice-recognition

القطع المطلوبة

الأدوات التي سيتم استخدامها لهذا المشروع :

arduino-voice-recognition

Voice Recognition Module v3

arduino uno r3

Arduino Uno R3

arduino-voice-recognition

Pin Male Headers

 اسلاك توصيل أنثى/ذكر (Jumper Wires Female/male)

وحدة التعرف على الصوت (Voice Recognition ) :

وحدة Voice Recognition هي عبارة عن لوحة مدمجة سهله التحكم، تستخدم للتعرف على الصوت/ الكلام للتحكم في المشاريع المختلفة.

arduino-voice-recognition

لدى وحدة V3 القدرة على تخزين ما يصل إلى 80 أمرا صوتياً لكل منها مدة  1500 milliseconds  (اي ما يقارب كلمة أو كلمتين لكل امر). يتم تخزينها  في مجموعة واحدة كبيرة مثل المكتبة. ولكن خلال عملها لا تستطيع التمييز بين الـ 80 صوتا في نفس اللحظة، لذلك يتم استيراد/استدعاء سبعة أوامر صوتية في المكتبة إلى أداة التعرف(Recognizer) . وهذا يعني أن الحد الأقصى للأوامر الصوتية هي سبع أوامر فعالة في نفس الوقت.

arduino-voice-recognition

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

كما انها تحتاج إلى تهيئة وتسجيل الأصوات أولاً قبل اعطاء الأوامر الصوتية للتحكم بالمشاريع.

تصميم لوح التجارب :

هناك طريقتان لإستخدام والتحكم بهذه الوحدة، أولا باستخدام  UART أو من خلال دبابيس GPIO  المدمجة. سيتم في هذا المشروع استخدام UART.

قم بتوصيل الدارة كما هو موضح بالصورة التالية :

arduino-voice-recognition
الاردوينو وحدة Voice Recognition
5v 5v
2 Tx
3 Rx
GND GND

 

تهيئة وحدة التعرف على الصوت (Voice Recognition )

ليتم  برمجة وحدة Voice Recognition ، والتحكم بالمختلف المشاريع عبر الأوامر الصوتية، قم بإتباع التالي :

– أولا قم بتثبيت Arduino IDE  على جهازك، يمكنك الاطلاع على درس تنصيب الأردوينو على جهازك .
– ثم قم بتنزيل مكتبة VoiceRecognitionV3  (قم بتنزيل ملف zip أو استخدم git clone https://github.com/elechouse/VoiceRecognitionV3.git command )
في حالة استخدام ملف zip ، قم باستخراج VoiceRecognitionV3.zip  إلى اردوينو Sketch\libraries ، وإذا استخدمت   gitclonecommand  قم بنسخ VoiceRecognitionV3  إلى ArduinoSketch\libraries .

  تحتاج الوحدة إلى تهئية وتخزين الأوامر الصوتية ثم استدعائها، قم بإتباع الخطوات التالية :

 1. فتح كود  File -> Examples -> VoiceRecognitionV3 -> vr_sample_train) vr_sample_train).
2. اختيار لوحة التحكم الصحيحة (Tool -> Board, UNO recommended ) ، ثم اختيار المنفذ الصحيح.
3. رفع الكود إلى لوحة الأردوينو.
4. بعد ذلك يتم فتح الشاشة التسلسلية (Serial Monitor) . واختيار الـ Baund rate = 115200  ، وتحديد الإرسال مع Newline أو Both NL & CR

 arduino-voice-recognition
الأوامر الخاصة بوحدة الـ Voice Recognition :

يتم التحقق من إعدادات وحدة الـ Voice Recognition من خلال الأمر settings ثم الضغط على ارسال send او على مفتاح Enter  .

arduino-voice-recognition

يتم تسجيل وتخزين الأوامر الصوتية على وحدة Voice Recognition بإسم معين، من خلال ادخال الأمر sigtrain 0 On ثم الضغط على ارسال send او على مفتاح Enter  :

arduino-voice-recognition

بعد ادخال الأمر السابق والضغط على ارسال send، عند طباعة “Speak now” على الشاشة التسلسلية (Serial Monitor)  ، انت بحاجة إلى نطق أمر بصوتك ( يمكنك قول اي كلمة ، يوصى باستخدام كلمات ذات صلة بالمشروع ، هنا ستكون كلمة الأمر الصوتي هو “ON” )، عند طباعة “Speak again”  على الشاشة التسلسلية (Serial Monitor) ، تحتاج إلى إعادة نطق الأمر الصوتي مرة اخرى.

إذا تمت مطابقة هذين الصوتين، سيتم طباعة “Success” على الشاشة التسلسلية (Serial Monitor), ويتم تخزين الأمر الصوتي في سجل  0 “record 0” ,  وإذا لم يتم التطابق، فكرر نطق الكلام حتى تنجح العملية.

arduino-voice-recognition

بعد ذلك نقوم بإرسال الأمر sigtrain1Off للتخزين على السجل 1 ( record 1)  مع اسم “Off” . قم بنطق كلماتك التي ترغب باستخدامها لإيقاف الـ LED .

arduino-voice-recognition

عند ارسال الأمر load 0 1 يتم استدعاء/تحميل الأوامر الصوتيه للسجل 0 و 1. كما ذكرنا مسبقا يمكنك استدعاء سبعة أصوات في نفس اللحظة.

arduino-voice-recognition

الآن يمكنك نطق الكلمات التي تم تسجيلها واختبار ما إذا تم التعرف على الصوت. يمكنك أن ترى التالي على الشاشة التسلسلية :

arduino-voice-recognition

 

برمجة الأردوينو

هنا نقوم بعرض مثال بسيط يوضح كيفية التحكم بالـ LED المدمج على لوحة الـ Arduino (الـ LED الموصل على المنفذ الرقمي 13) من خلال الأوامر الصوتية. في هذا المثال تحتاج إلى تهيئة وحدة التعرف على الاصوات أولاً (تم عرض الطريقة في الخطوة السابقة) . سيتم استخدام الأوامر التالية :

– تخزين امر صوتي لإستخدامه في تشغيل الـ LED من خلال الأمر sigtrain 0 on .
– تخزين امر صوتي لإستخدامه في إيقاف الـ LED من خلال الأمر sigtrain 1 off .

الآن نقوم بفتح الملف vr_sample_control_led  (File -> Examples -> VoiceRecognitionV3 -> vr_sample_control_led) ، والذي يحتوي على كود أدناه للتحكم بالـ LED .

#include 
#include "VoiceRecognitionV3.h"

VR myVR(2,3);    // 2:RX 3:TX, you can choose your favourite pins.

uint8_t records[7]; // save record
uint8_t buf[64];

int led = 13;

#define onRecord    (0)
#define offRecord   (1) 

void printSignature(uint8_t *buf, int len)
{
  int i;
  for(i=0; i<len; i++){ 
    if(buf>0x19 && buf<0x7F){ 
      Serial.write(buf); 
     } 
    else{ 
      Serial.print("["); 
      Serial.print(buf, HEX); 
      Serial.print("]"); 
    } 
  } 
} 
/** @brief Print signature, if the character is invisible, print hexible value instead. @param buf -->  VR module return value when voice is recognized.
             buf[0]  -->  Group mode(FF: None Group, 0x8n: User, 0x0n:System
             buf[1]  -->  number of record which is recognized. 
             buf[2]  -->  Recognizer index(position) value of the recognized record.
             buf[3]  -->  Signature length
             buf[4]~buf[n] --> Signature
*/
void printVR(uint8_t *buf)
{
  Serial.println("VR Index\tGroup\tRecordNum\tSignature");

  Serial.print(buf[2], DEC);
  Serial.print("\t\t");

  if(buf[0] == 0xFF){
    Serial.print("NONE");
  }
  else if(buf[0]&0x80){
    Serial.print("UG ");
    Serial.print(buf[0]&(~0x80), DEC);
  }
  else{
    Serial.print("SG ");
    Serial.print(buf[0], DEC);
  }
  Serial.print("\t");

  Serial.print(buf[1], DEC);
  Serial.print("\t\t");
  if(buf[3]>0){
    printSignature(buf+4, buf[3]);
  }
  else{
    Serial.print("NONE");
  }
  Serial.println("\r\n");
}

void setup()
{
  /** initialize */
  myVR.begin(9600);
  
  Serial.begin(115200);
  Serial.println("Elechouse Voice Recognition V3 Module\r\nControl LED sample");
  
  pinMode(led, OUTPUT);
    
  if(myVR.clear() == 0){
    Serial.println("Recognizer cleared.");
  }else{
    Serial.println("Not find VoiceRecognitionModule.");
    Serial.println("Please check connection and restart Arduino.");
    while(1);
  }
  
  if(myVR.load((uint8_t)onRecord) >= 0){
    Serial.println("onRecord loaded");
  }
  
  if(myVR.load((uint8_t)offRecord) >= 0){
    Serial.println("offRecord loaded");
  }
}

void loop()
{
  int ret;
  ret = myVR.recognize(buf, 50);
  if(ret>0){
    switch(buf[1]){
      case onRecord:
        /** turn on LED */
        digitalWrite(led, HIGH);
        break;
      case offRecord:
        /** turn off LED*/
        digitalWrite(led, LOW);
        break;
      default:
        Serial.println("Record function undefined");
        break;
    }
    /** voice recognized */
    printVR(buf);
  }
}

شرح الـ Code  :

اولا يتم انشاء متغير led يحتوي على المنفذ 13 الخاص بالـ led المدمج بلوحة الاردوينو.

int led = 13;

  ثم يتم تعريف اثنين من السجلات الأول  onrecord ( لتشغلي الـ   LED ) و  offrecord(لإيقاف تشغيل الـ LED ) .  قم بتغير قيمة السجلات حسب العنوان/الرقم الذي تم تخزين أوامر التشغيل والايقاف عليها. في هذا المشروع تم استخدام السجل رقم 0 و 1.

#define onRecord (0) 
#define offRecord (1) 

 في الـ ()setup ،نقوم بتهيئة وحدة  Voice Recognition ، والشاشة التسلسلية .

myVR.begin(9600); 
Serial.begin(115200); 
Serial.println("Elechouse Voice Recognition V3 Module\r\nControl LED sample");

تهيئة المنفذ الموصل بالـ LED كمخرج .

pinMode(led, OUTPUT);

التحقق ما إذا تم توصيل وحدة التعرف على الصوت بشكل صحيح إلى الأردوينو.

if(myVR.clear() == 0){ 
Serial.println("Recognizer cleared."); 
}
else{ 
Serial.println("Not find VoiceRecognitionModule."); 
Serial.println("Please check connection and restart Arduino."); 
while(1); 
}

استيراد/نسخ الأمر الصوتي الموجود في السجل 0 (onRecord) إلى recognizer .

if(myVR.load((uint8_t)onRecord) >= 0){ 
Serial.println("onRecord loaded"); 
}

استيراد/نسخ الأمر الصوتي الموجود في السجل 1 (offRecord) إلى recognizer .

if(myVR.load((uint8_t)offRecord) >= 0){ 
Serial.println("offRecord loaded");
}

في ()loop، نقوم بمحاولة استقبال الصوت من الميكروفون مع تحديد قيمة مهلة الانتظار timeout

int ret; 
ret = myVR.recognize(buf, 50);

يحتوي buf[1] على رقم السجل الذي تم مطابقته مع السجلات التي تم نسخها على recognizer. لذالك نقوم بمقارنته من السجلات التي تم انشائها onRecord و offRecord، واعتمادا على النتيجة نقوم بالتحكم بالـ LED.

switch(buf[1]){ 
case onRecord: 
/** turn on LED */ 
digitalWrite(led, HIGH); 
break; 
case offRecord: 
/** turn off LED*/ 
digitalWrite(led, LOW); 
break; 
default: 
Serial.println("Record function undefined"); 
break; 
}

وأخيراً، يتم استدعاء دالة printVR و دالة printSignature ليتم طباعة النتيجة والسجلات التي تم التطابق معها.

عند رفع الكود على الأردوينو والبدء بالتحدث سيتم طباعة التالي على الشاشة التسلسلية  :

arduino-voice-recognition

قياس مستوى الصوت باستخدام الاردوينو

 في هذا المشروع سنقوم بتعلم كيفية استخدام الأردوينو لقياس مستوى الصوت، وسيتم توضيح مستوى الصوت على مجموعة من الـ LEDs.

المكونات المطلوبة

arduino uno r3

Arduino Uno

Electret Microphone

10K Ohm Potentiometer

LM386

Capacitor 10uF 50V

100nF capacitor

Red LED

 

220 Ω resistor

220 Ohm Resistor

10K Ohm Resistor

Full size breadboard 830

Breadboard

Breadboard Jumper Wire 65 pcs

Wires

 

LM386 :

هو عبارة عن دارة متكاملة تحتوي على مكبر عمليات (Operational Amplifier) ،يقوم بتكبير الاشارة الصوتية القادمة اليه من المايكروفون

نقوم بقراءة هذه الإشارة الصوتية بعد تكبيرها عن طريق الأردوينو، الذي بدوره يقوم بتشغيل الـ LEDs حسب شدة الإشارة الصوتية.

توصيل الدارة

قم بتوصيل الدارة كما هو موضح بالصورة التالية :

قياس مستوى الصوت باستخدام الاردوينو

سنقوم ببرمجة الأردوينو بحيث يقوم بقراءة الإشارة الصوتية التي يلتقطها المايكروفون بعد تكبيرها من خلال LM386، ثم يوضح شدة الصوت على مجموعة من الـ LEDs.

قياس مستوى الصوت باستخدام الاردوينو

 

الكود البرمجي

#define LED1 3
#define LED2 4
#define LED3 5
#define LED4 6
#define LED5 7
#define LED6 8

int value;

void setup()
{
  //set pins attached to LEDs as outputs
  pinMode(LED1,OUTPUT);
  pinMode(LED2,OUTPUT);
  pinMode(LED3,OUTPUT);
  pinMode(LED4,OUTPUT);
  pinMode(LED5,OUTPUT);
  pinMode(LED6,OUTPUT);
}

void loop()
{
  value = analogRead(5); 
  
  //glow the LEDs depending on the ammount of sound detected by the electret  
  if (value > 455 && value <555)
  {//glow first LED
    Clear(4);
    Glow(4);
  } 
  else if (value > 378 && value < 624)
  {//glow 2nd LED
    Clear(5);
    Glow(5);
  }
  else if (value > 311 && value < 693)
  {//glow 3rd LED
    Clear(6);
    Glow(6);
  }
  else if (value > 244 && value < 762)
  {//glow 4th LED
    Clear(7);
    Glow(7);
  }
  else if (value > 177 && value < 831)
  {//glow 5th LED
    Clear(8);
    Glow(8);
  }
  else if (value < 177 || value > 831)
  {//glow 6th LED
    Clear(9);
    Glow(9);
  }
  
}

void Glow(int initial)//function to glow LEDs
{
  for(int i=3;i<initial;i++)	digitalWrite(i,HIGH);
}

void Clear(int initial)//function to clear LEDs
{
  for(int i=initial;i<9;i++)	digitalWrite(i,LOW);
}

شرح الكود :

في البداية نقوم بتسمية منافذ الأردوينو المستخدمة في المشروع. ونقوم بالإعلان عن المتغير value الذي سيستخدم في تخزين قيمة قراءة الإشارة الصوتية :

#define LED1 3
#define LED2 4
#define LED3 5
#define LED4 6
#define LED5 7
#define LED6 8

int value;

في الدلة ()setup ، نقوم بضبط الإعدادات اللازمة مثل ضبط المنافذ الموصله مع الـ LEDs كمخرج :

void setup()
{
  //set pins attached to LEDs as outputs
  pinMode(LED1,OUTPUT);
  pinMode(LED2,OUTPUT);
  pinMode(LED3,OUTPUT);
  pinMode(LED4,OUTPUT);
  pinMode(LED5,OUTPUT);
  pinMode(LED6,OUTPUT);
}

في الدالة ()loop ، نقوم بقراءة الإشارة الصوتية الناتجة عن المايكروفون بعد تكبيرها بواسطة LM386 :

value = analogRead(5);

بعد ذلك نقوم بإختبار القيمة المقاسه وإعتمادا على هذه القيمة نقوم بإضاءة الـ LEDs بإستخدام الدالتين Clear وGlow للتعبر عن مدى ارتفاع أو انخفاض شدة الإشارة الصوتية :

  if (value > 455 && value <555)
  {//glow first LED
    Clear(4);
    Glow(4);
  } 
  else if (value > 378 && value < 624)
  {//glow 2nd LED
    Clear(5);
    Glow(5);
  }
  else if (value > 311 && value < 693)
  {//glow 3rd LED
    Clear(6);
    Glow(6);
  }
  else if (value > 244 && value < 762)
  {//glow 4th LED
    Clear(7);
    Glow(7);
  }
  else if (value > 177 && value < 831)
  {//glow 5th LED
    Clear(8);
    Glow(8);
  }
  else if (value < 177 || value > 831)
  {//glow 6th LED
    Clear(9);
    Glow(9);
  }

نستخدم الدالة Glow لإضاءة الـ LEDs تدريجيا حسب قيمة شدة الصوت :

void Glow(int initial)//function to glow LEDs
{
  for(int i=3;i<initial;i++)	digitalWrite(i,HIGH);
}

نستخدم الدالة Clear لإطفاء الـ LEDs تدريجيا حسب قيمة شدة الصوت :

void Clear(int initial)//function to clear LEDs
{
  for(int i=initial;i<9;i++)	digitalWrite(i,LOW);
}

استخدام شاشة Nokia 5110 مع الاردوينو

في هذا المشروع سنتعلم كيفية استخدام شاشة Nokia 5110 مع الأردوينو لعرض صور ثابتة ومتحركة، وأيضا عرض الكلمات التي يتم ارسالها عن طريق الحاسوب.

استخدام شاشة جرافيك مع الاردوينو (Nokia 5110)

 

المكونات المطلوبة

arduino uno r3

Arduino Uno

nokia screen 5110

Nokia Screen 5110

220 Ω resistor

220 Ohm Resistor

 

Full size breadboard 830

Breadboard

Breadboard Jumper Wire 65 pcs

Wires

Nokia Screen 5110

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

nokia screen 5110

كما يمكننها التحكم بها على مستوى الـ Pixels، اي انها تعطي مرونة عالية للكتابة أو الرسم عليها.

توصيلها مع الأردوينو :

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

توصيل الدارة

نقوم بتوصيل الشاشة بالاردوينو باستخدام برتوكول الـ SPI ، ثم نقوم بإرسال الاوامر والبيانات اللازمة لعرض الصور بالشكل الذي نريده عن طريق الاردوينو.

قم بتوصيل الدارة كما هو موضح بالصورة التالية :

استخدام شاشة جرافيك مع الاردوينو (Nokia 5110)

يعمل هذا البروتوكول على ارجل محددة فى الاردوينو لذلك لا يمكننا تغيير المنافذ المستخدمة لذلك  تم توصيل الشاشة مع الأردوينو كما هو موضح بالجدول :

الطرف (بداية من اليسار) التوصيل
1 Arduino Pin 6
2 Arduino Pin 7
3 Arduino Pin 5
4  Arduino Pin 11
5  Arduino Pin 13
6  3.3v
7  Arduino Pin 4
8  GND

سنقوم بعمل برنامج يقوم في البداية بتشغيل الصور الثابتة والمتحركة، ثم يتوقف على صورة معينة منتظرا ادخال المستخدم لرسالة ما من خلال الشاشة التسلسلية (Serial Monitor)، ليقوم بعرضها على الشاشة.

استخدام شاشة جرافيك مع الاردوينو (Nokia 5110)

البرمجة

في البداية، نقوم بإدراج المكتبة الخاصة بالـ SPI حتى نتمكن من استخدامه :

#include <SPI.h>

ثم نقوم بتسمية بعض الثوابت المستخدمة في إرسال الأوامر إلى الشاشة :

#define LCD_COMMAND  0 
#define LCD_DATA     1
#define LCD_WIDTH   84 
#define LCD_HEIGHT  48 
#define WHITE       0 
#define BLACK       1 

بعد ذلك نقوم بتمسية منافذ الأردوينو المستخدمة في توصيل الشاشة :

const int scePin = 7;   // SCE - Chip select, pin 3 on LCD.
const int rstPin = 6;   // RST - Reset, pin 4 on LCD.
const int dcPin = 5;    // DC - Data/Command, pin 5 on LCD.
const int sdinPin = 11;  // DN(MOSI) - Serial data, pin 6 on LCD.
const int sclkPin = 13;  // SCLK - Serial clock, pin 7 on LCD.
const int blPin = 9;    // LED - Backlight LED, pin 8 on LCD.

بعد ذلك قمنا بعمل مصفوفة تحتوي على قيم الحروف والرموز والأرقام الممثلة بصيغة الـ ASCII ، وهي طريقة قياسية لتمثيل الحروف والأقام وبعض الرموز. فعند عرض الحرف a فإن القيمة المكافئة له تساوي 0x16 وهكذا.

static const byte ASCII[][5] = {
   {0x00, 0x00, 0x00, 0x00, 0x00} // 0x20  
  ,{0x00, 0x00, 0x5f, 0x00, 0x00} // 0x21 !
  ,{0x00, 0x07, 0x00, 0x07, 0x00} // 0x22 "
  ,{0x14, 0x7f, 0x14, 0x7f, 0x14} // 0x23 #
  ,{0x24, 0x2a, 0x7f, 0x2a, 0x12} // 0x24 $
  ,{0x23, 0x13, 0x08, 0x64, 0x62} // 0x25 %
  ,{0x36, 0x49, 0x55, 0x22, 0x50} // 0x26 &
  ,{0x00, 0x05, 0x03, 0x00, 0x00} // 0x27 '
  ,{0x00, 0x1c, 0x22, 0x41, 0x00} // 0x28 (
  ,{0x00, 0x41, 0x22, 0x1c, 0x00} // 0x29 )
  ,{0x14, 0x08, 0x3e, 0x08, 0x14} // 0x2a *
  ,{0x08, 0x08, 0x3e, 0x08, 0x08} // 0x2b +
  ,{0x00, 0x50, 0x30, 0x00, 0x00} // 0x2c ,
  ,{0x08, 0x08, 0x08, 0x08, 0x08} // 0x2d -
  ,{0x00, 0x60, 0x60, 0x00, 0x00} // 0x2e .
  ,{0x20, 0x10, 0x08, 0x04, 0x02} // 0x2f /
  ,{0x3e, 0x51, 0x49, 0x45, 0x3e} // 0x30 0
  ,{0x00, 0x42, 0x7f, 0x40, 0x00} // 0x31 1
  ,{0x42, 0x61, 0x51, 0x49, 0x46} // 0x32 2
  ,{0x21, 0x41, 0x45, 0x4b, 0x31} // 0x33 3
  ,{0x18, 0x14, 0x12, 0x7f, 0x10} // 0x34 4
  ,{0x27, 0x45, 0x45, 0x45, 0x39} // 0x35 5
  ,{0x3c, 0x4a, 0x49, 0x49, 0x30} // 0x36 6
  ,{0x01, 0x71, 0x09, 0x05, 0x03} // 0x37 7
  ,{0x36, 0x49, 0x49, 0x49, 0x36} // 0x38 8
  ,{0x06, 0x49, 0x49, 0x29, 0x1e} // 0x39 9
  ,{0x00, 0x36, 0x36, 0x00, 0x00} // 0x3a :
  ,{0x00, 0x56, 0x36, 0x00, 0x00} // 0x3b ;
  ,{0x08, 0x14, 0x22, 0x41, 0x00} // 0x3c 
  ,{0x02, 0x01, 0x51, 0x09, 0x06} // 0x3f ?
  ,{0x32, 0x49, 0x79, 0x41, 0x3e} // 0x40 @
  ,{0x7e, 0x11, 0x11, 0x11, 0x7e} // 0x41 A
  ,{0x7f, 0x49, 0x49, 0x49, 0x36} // 0x42 B
  ,{0x3e, 0x41, 0x41, 0x41, 0x22} // 0x43 C
  ,{0x7f, 0x41, 0x41, 0x22, 0x1c} // 0x44 D
  ,{0x7f, 0x49, 0x49, 0x49, 0x41} // 0x45 E
  ,{0x7f, 0x09, 0x09, 0x09, 0x01} // 0x46 F
  ,{0x3e, 0x41, 0x49, 0x49, 0x7a} // 0x47 G
  ,{0x7f, 0x08, 0x08, 0x08, 0x7f} // 0x48 H
  ,{0x00, 0x41, 0x7f, 0x41, 0x00} // 0x49 I
  ,{0x20, 0x40, 0x41, 0x3f, 0x01} // 0x4a J
  ,{0x7f, 0x08, 0x14, 0x22, 0x41} // 0x4b K
  ,{0x7f, 0x40, 0x40, 0x40, 0x40} // 0x4c L
  ,{0x7f, 0x02, 0x0c, 0x02, 0x7f} // 0x4d M
  ,{0x7f, 0x04, 0x08, 0x10, 0x7f} // 0x4e N
  ,{0x3e, 0x41, 0x41, 0x41, 0x3e} // 0x4f O
  ,{0x7f, 0x09, 0x09, 0x09, 0x06} // 0x50 P
  ,{0x3e, 0x41, 0x51, 0x21, 0x5e} // 0x51 Q
  ,{0x7f, 0x09, 0x19, 0x29, 0x46} // 0x52 R
  ,{0x46, 0x49, 0x49, 0x49, 0x31} // 0x53 S
  ,{0x01, 0x01, 0x7f, 0x01, 0x01} // 0x54 T
  ,{0x3f, 0x40, 0x40, 0x40, 0x3f} // 0x55 U
  ,{0x1f, 0x20, 0x40, 0x20, 0x1f} // 0x56 V
  ,{0x3f, 0x40, 0x38, 0x40, 0x3f} // 0x57 W
  ,{0x63, 0x14, 0x08, 0x14, 0x63} // 0x58 X
  ,{0x07, 0x08, 0x70, 0x08, 0x07} // 0x59 Y
  ,{0x61, 0x51, 0x49, 0x45, 0x43} // 0x5a Z
  ,{0x00, 0x7f, 0x41, 0x41, 0x00} // 0x5b [
  ,{0x02, 0x04, 0x08, 0x10, 0x20} // 0x5c \
  ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 0x5d ]
  ,{0x00, 0x41, 0x41, 0x7f, 0x00} // 0x5d ]
  ,{0x04, 0x02, 0x01, 0x02, 0x04} // 0x5e ^
  ,{0x40, 0x40, 0x40, 0x40, 0x40} // 0x5f _
  ,{0x00, 0x01, 0x02, 0x04, 0x00} // 0x60 `
  ,{0x20, 0x54, 0x54, 0x54, 0x78} // 0x61 a
  ,{0x7f, 0x48, 0x44, 0x44, 0x38} // 0x62 b
  ,{0x38, 0x44, 0x44, 0x44, 0x20} // 0x63 c
  ,{0x38, 0x44, 0x44, 0x48, 0x7f} // 0x64 d
  ,{0x38, 0x54, 0x54, 0x54, 0x18} // 0x65 e
  ,{0x08, 0x7e, 0x09, 0x01, 0x02} // 0x66 f
  ,{0x0c, 0x52, 0x52, 0x52, 0x3e} // 0x67 g
  ,{0x7f, 0x08, 0x04, 0x04, 0x78} // 0x68 h
  ,{0x00, 0x44, 0x7d, 0x40, 0x00} // 0x69 i
  ,{0x20, 0x40, 0x44, 0x3d, 0x00} // 0x6a j 
  ,{0x7f, 0x10, 0x28, 0x44, 0x00} // 0x6b k
  ,{0x00, 0x41, 0x7f, 0x40, 0x00} // 0x6c l
  ,{0x7c, 0x04, 0x18, 0x04, 0x78} // 0x6d m
  ,{0x7c, 0x08, 0x04, 0x04, 0x78} // 0x6e n
  ,{0x38, 0x44, 0x44, 0x44, 0x38} // 0x6f o
  ,{0x7c, 0x14, 0x14, 0x14, 0x08} // 0x70 p
  ,{0x08, 0x14, 0x14, 0x18, 0x7c} // 0x71 q
  ,{0x7c, 0x08, 0x04, 0x04, 0x08} // 0x72 r
  ,{0x48, 0x54, 0x54, 0x54, 0x20} // 0x73 s
  ,{0x04, 0x3f, 0x44, 0x40, 0x20} // 0x74 t
  ,{0x3c, 0x40, 0x40, 0x20, 0x7c} // 0x75 u
  ,{0x1c, 0x20, 0x40, 0x20, 0x1c} // 0x76 v
  ,{0x3c, 0x40, 0x30, 0x40, 0x3c} // 0x77 w
  ,{0x44, 0x28, 0x10, 0x28, 0x44} // 0x78 x
  ,{0x0c, 0x50, 0x50, 0x50, 0x3c} // 0x79 y
  ,{0x44, 0x64, 0x54, 0x4c, 0x44} // 0x7a z
  ,{0x00, 0x08, 0x36, 0x41, 0x00} // 0x7b {
  ,{0x00, 0x00, 0x7f, 0x00, 0x00} // 0x7c |
  ,{0x00, 0x41, 0x36, 0x08, 0x00} // 0x7d }
  ,{0x10, 0x08, 0x08, 0x10, 0x08} // 0x7e ~
  ,{0x78, 0x46, 0x41, 0x46, 0x78} // 0x7f DEL
};

بعد ذلك نقوم بالإعلان عن المصفوفة displayMap ، وهي تمثل الـ Pixels الموجودة في الشاشة. فعند وضع قيمة 0 في إحدى قيم المصفوفة، نقوم بذلك بإطفاء الـ Pixel المكافئة له على الشاشة. وعند وضع قيمة 255 نقوم بإضاءة الـ Pixel المكافئة :

byte displayMap[LCD_WIDTH * LCD_HEIGHT / 8] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0,
  0xF0, 0xF8, 0xFC, 0xFC, 0xFE, 0xFE, 0xFE, 0xFE, 0x1E, 0x0E, 0x02, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03,
  0x0F, 0x1F, 0x3F, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFC, 0xF8,
  0xF8, 0xF0, 0xF8, 0xFE, 0xFE, 0xFC, 0xF8, 0xE0, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0xF8, 0xFC, 0xFE, 0xFE, 0xFF, 0xFF, 0xF3, 0xE0, 0xE0, 0xC0, 
  0xC0, 0xC0, 0xE0, 0xE0, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x3E, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
  0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0x3F, 0x1F, 
  0x0F, 0x0F, 0x0F, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x03, 0x03,
  0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x3F, 0x1F, 0x0F, 0x07, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
};

نقوم بإنشاء مصفوفة xkcdSandwich التي يقوم الأردوينو بإرسالها إلى الشاشة فتقوم برسم صورة معينة سنراها عندما نكمل كتابة الكود ويتم رفعه إلى الأردوينو :

char xkcdSandwich[504] = {
0xFF, 0x8D, 0x9F, 0x13, 0x13, 0xF3, 0x01, 0x01, 0xF9, 0xF9, 0x01, 0x81, 0xF9, 0xF9, 0x01, 0xF1,
0xF9, 0x09, 0x09, 0xFF, 0xFF, 0xF1, 0xF9, 0x09, 0x09, 0xF9, 0xF1, 0x01, 0x01, 0x01, 0x01, 0x01,
0xF9, 0xF9, 0x09, 0xF9, 0x09, 0xF9, 0xF1, 0x01, 0xC1, 0xE9, 0x29, 0x29, 0xF9, 0xF1, 0x01, 0xFF,
0xFF, 0x71, 0xD9, 0x01, 0x01, 0xF1, 0xF9, 0x29, 0x29, 0xB9, 0xB1, 0x01, 0x01, 0x01, 0xF1, 0xF1,
0x11, 0xF1, 0xF1, 0xF1, 0xE1, 0x01, 0xE1, 0xF1, 0x51, 0x51, 0x71, 0x61, 0x01, 0x01, 0xC1, 0xF1,
0x31, 0x31, 0xF1, 0xFF, 0xFF, 0x00, 0x01, 0x01, 0x01, 0x01, 0x60, 0xE0, 0xA0, 0x01, 0x01, 0x81,
0xE1, 0x61, 0x60, 0xC0, 0x01, 0xE1, 0xE1, 0x21, 0x21, 0xE0, 0xC1, 0x01, 0xC1, 0xE1, 0x20, 0x20,
0xFC, 0xFC, 0xE0, 0xE0, 0xC1, 0xE1, 0xE0, 0xC1, 0xE0, 0xE1, 0x01, 0xFC, 0xFC, 0x21, 0x21, 0xE1,
0xC1, 0xE5, 0xE4, 0x01, 0xC1, 0xE0, 0x20, 0x21, 0x20, 0x00, 0x01, 0xFD, 0xFD, 0x21, 0x20, 0xE0,
0x00, 0x00, 0x01, 0x01, 0xC0, 0x61, 0x31, 0x31, 0x21, 0x20, 0xC0, 0x81, 0x01, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x01, 0x03, 0x02,
0x03, 0x01, 0x00, 0x01, 0x03, 0xF2, 0x1A, 0x0B, 0x08, 0x0B, 0x1B, 0x10, 0x60, 0xE3, 0x03, 0x00,
0x01, 0x03, 0x02, 0x02, 0x03, 0x03, 0x00, 0x03, 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x03,
0x03, 0x00, 0x00, 0x03, 0x03, 0x03, 0x03, 0x00, 0x01, 0x03, 0x02, 0x02, 0x03, 0x01, 0x00, 0x03,
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x3E, 0x63, 0x80, 0x80, 0x80, 0x80, 0x60, 0x3F, 0x07,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x01, 0x01, 0x02, 0x03, 0x3E, 0xE8, 0xF8, 0xF0, 0xD0, 0x90,
0x18, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x38, 0xFF,
0x0C, 0x38, 0xE0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33,
0x5F, 0x8F, 0x84, 0x05, 0x07, 0x06, 0x0C, 0x0E, 0x0E, 0x0C, 0x14, 0x34, 0x68, 0x88, 0xD8, 0x70,
0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x10, 0x10, 0x10, 0xF0, 0xE0, 0x00, 0xF0, 0xF0, 0x00, 0x80,
0x80, 0x00, 0x00, 0x80, 0x80, 0x80, 0x80, 0x00, 0x80, 0x80, 0x00, 0x80, 0x00, 0x00, 0x20, 0x38,
0x0E, 0x01, 0xC0, 0x3F, 0xE0, 0x00, 0x00, 0x03, 0x0E, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xFF, 0xFF, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xB6, 0xED, 0xC0, 0xC0,
0xC0, 0xE0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xE1, 0xE1, 0xC1,
0xEF, 0xBB, 0x83, 0x86, 0x88, 0xB0, 0x80, 0x80, 0x80, 0x8F, 0x90, 0x90, 0x90, 0x9F, 0x8F, 0x80,
0x9F, 0x9F, 0x87, 0x8D, 0x98, 0x80, 0x8C, 0x9E, 0x92, 0x92, 0x9F, 0xC0, 0xC7, 0xFF, 0xB8, 0x8F,
0x80, 0x90, 0x90, 0xC0, 0xF0, 0x8E, 0x81, 0x80, 0x81, 0x8F, 0xB8, 0xE0, 0x80, 0x80, 0x80, 0x80,
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0xFF, 
};

في الدالة ()setup، نقوم بوضع الإعدادات اللازمة للمشروع مثل تشغيل الشاشة وضبط إعداداتها اللازمة مثل الوضوح وإظهار بعض الصور المتحركة.
وايضا نقوم بتشغيل الشاشة التسلسلية (Serial Monitor) التي سنستخدمها في إرسال الأحرف والكلمات لعرضها على الشاشة لاحقا. حيث سنقوم بالككتابه على الشاشة بعد أن تنتهي من عرض الصور المتحركة.

void setup()
{
  Serial.begin(9600);

  lcdBegin(); // This will setup our pins, and initialize the LCD
  setContrast(55); // Pretty good value, play around with it

  updateDisplay(); // with displayMap untouched, SFE logo
  delay(2000);

  lcdFunTime(); // Runs a 30-second demo of graphics functions

  // Wait for serial to come in, then clear display and go to echo
  while (!Serial.available())
    ;
  clearDisplay(WHITE);
  updateDisplay();
}

في الدله ()loop، تقوم بإنتظار المستخدم إدخال الحروف أو الكلمات إلى الشاشة التسلسلية (Serial Monitor) ، ليقوم الأردوينو بإرسالها إلى الشاشة ليتم كتابتها. نقوم بعمل إختبار على ماتم ادخاله من قبل المستخدم. فمثلا إذا ادخل المستخدم الرمز ~ فإنه عبارة عن أمر لمسح الشاشة.

void loop()
{
  static int cursorX = 0;
  static int cursorY = 0;

  if (Serial.available())
  {
    char c = Serial.read();

    switch (c)
    {
    case '\n': // New line
      cursorY += 8;
      break;
    case '\r': // Return feed
      cursorX = 0;
      break;
    case '~': // Use ~ to clear the screen.
      clearDisplay(WHITE);
      updateDisplay();
      cursorX = 0; // reset the cursor
      cursorY = 0;
      break;
    default:
      setChar(c, cursorX, cursorY, BLACK);
      updateDisplay();
      cursorX += 6; // Increment cursor
      break;
    }
    // Manage cursor
    if (cursorX >= (LCD_WIDTH - 4)) 
    { // If the next char will be off screen...
      cursorX = 0; // ... reset x to 0...
      cursorY += 8; // ...and increment to next line.
      if (cursorY >= (LCD_HEIGHT - 7))
      { // If the next line takes us off screen...
        cursorY = 0; // ...go back to the top.
      }
    }
  }
}

باقي الدوال المستخدمة متقدمة بعض الشئ، يكفي فقط أن تعرف أنها تقوم بإرسال البيانات إلى الشاشة بشكل معين لتتمكن من عرض الصور أو الكلمات. وهي الدوال المستخدمة داخل الدالتين ()setup، و ()loop .

إذا كنت تريد تغيير ما يظهر على الشاشة، فلا حاجة إلى تغيير هذه الدوال، فقط قم بعمل التغير الذي تريده داخل الداليتين ()loop، و ()setup.

void lcdFunTime()
{
  clearDisplay(WHITE); 
  randomSeed(analogRead(A0));
  const int pixelCount = 100;
  for (int i=0; i<pixelCount; i++)
  {
    setPixel(random(0, LCD_WIDTH), random(0, LCD_HEIGHT));
    updateDisplay();
    delay(10);
  }
  setStr("full of stars", 0, LCD_HEIGHT-8, BLACK);
  updateDisplay();
  delay(1000);
  for (int i=0; i<5; i++)
  {
    invertDisplay(); 
    delay(200);
    invertDisplay();
    delay(200);
  }
  delay(2000);
  clearDisplay(WHITE); 
  int x0 = LCD_WIDTH/2;
  int y0 = LCD_HEIGHT/2;
  for (float i=0; i<2*PI; i+=PI/8)
  {
    const int lineLength = 24;
    int x1 = x0 + lineLength * sin(i);
    int y1 = y0 + lineLength * cos(i);
    setLine(x0, y0, x1, y1, BLACK);
    updateDisplay();
    delay(100);
  }
  for (int j=0; j<2; j++)
  {
    for (int i=255; i>=0; i-=5)
    {
      analogWrite(blPin, i);
      delay(20);
    }
    for (int i=0; i<256; i+=5)
    {
      analogWrite(blPin, i);
      delay(20);
    }
  }
  clearDisplay(WHITE); 
  for (int x=0; x<LCD_WIDTH; x+=8)
  { 
    setRect(0, 0, x, LCD_HEIGHT, 1, BLACK);
    updateDisplay();
    delay(10);
  }
  for (int x=0; x<LCD_WIDTH; x+=8)
  { 
    setRect(0, 0, x, LCD_HEIGHT, 1, WHITE);
    updateDisplay();
    delay(10);
  }
  for (int x=0; x<12; x++)
  { 
    setRect(0, 0, x, LCD_HEIGHT, 1, 1);
    setRect(11, 0, x+12, LCD_HEIGHT, 1, BLACK);
    setRect(23, 0, x+24, LCD_HEIGHT, 1, BLACK);
    setRect(35, 0, x+36, LCD_HEIGHT, 1, BLACK);
    setRect(47, 0, x+48, LCD_HEIGHT, 1, BLACK);
    setRect(59, 0, x+60, LCD_HEIGHT, 1, BLACK);
    setRect(71, 0, x+72, LCD_HEIGHT, 1, BLACK);
    updateDisplay();
    delay(10);
  }
  setRect(25, 10, 45, 30, 0, WHITE);
  setRect(35, 20, 55, 40, 0, WHITE);
  setLine(25, 10, 35, 20, WHITE);
  setLine(45, 30, 55, 40, WHITE);
  setLine(25, 30, 35, 40, WHITE);
  setLine(45, 10, 55, 20, WHITE);
  updateDisplay();
  delay(2000);
  clearDisplay(WHITE);
  for (int i=0; i<20; i++)
  {
    int x = random(0, LCD_WIDTH);
    int y = random(0, LCD_HEIGHT);
    setCircle(x, y, i, BLACK, 1);
    updateDisplay();
    delay(100);
  }
  delay(2000);
  setStr("Modern Art", 0, 10, WHITE);
  updateDisplay();
  delay(2000);
  setBitmap(xkcdSandwich);
  updateDisplay();
}

void setPixel(int x, int y)
{
  setPixel(x, y, BLACK); 
}

void clearPixel(int x, int y)
{
  setPixel(x, y, WHITE); 
}

void setPixel(int x, int y, boolean bw)
{
  if ((x >= 0) && (x < LCD_WIDTH) && (y >= 0) && (y < LCD_HEIGHT))
  {
    byte shift = y % 8;

    if (bw) 
      displayMap[x + (y/8)*LCD_WIDTH] |= 1<<shift;
    else   
      displayMap[x + (y/8)*LCD_WIDTH] &= ~(1<<shift);
  }
}

void setLine(int x0, int y0, int x1, int y1, boolean bw)
{
  int dy = y1 - y0; // Difference between y0 and y1
  int dx = x1 - x0; // Difference between x0 and x1
  int stepx, stepy;
  if (dy < 0)
  {
    dy = -dy;
    stepy = -1;
  }
  else
    stepy = 1;

  if (dx < 0)
  {
    dx = -dx;
    stepx = -1;
  }
  else
    stepx = 1;

  dy <<= 1; 
  dx <<= 1; 
  setPixel(x0, y0, bw); 
  if (dx > dy) 
  {
    int fraction = dy - (dx >> 1);
    while (x0 != x1)
    {
      if (fraction >= 0)
      {
        y0 += stepy;
        fraction -= dx;
      }
      x0 += stepx;
      fraction += dy;
      setPixel(x0, y0, bw);
    }
  }
  else
  {
    int fraction = dx - (dy >> 1);
    while (y0 != y1)
    {
      if (fraction >= 0)
      {
        x0 += stepx;
        fraction -= dy;
      }
      y0 += stepy;
      fraction += dx;
      setPixel(x0, y0, bw);
    }
  }
}

void setRect(int x0, int y0, int x1, int y1, boolean fill, boolean bw)
{
  if (fill == 1)
  {
    int xDiff;

    if(x0 > x1)
      xDiff = x0 - x1; 
    else
      xDiff = x1 - x0;

    while(xDiff > 0)
    {
      setLine(x0, y0, x0, y1, bw);

      if(x0 > x1)
        x0--;
      else
        x0++;

      xDiff--;
    }
  }
  else 
  {
    setLine(x0, y0, x1, y0, bw);
    setLine(x0, y1, x1, y1, bw);
    setLine(x0, y0, x0, y1, bw);
    setLine(x1, y0, x1, y1, bw);
  }
}

void setCircle (int x0, int y0, int radius, boolean bw, int lineThickness)
{
  for(int r = 0; r < lineThickness; r++)
  {
    int f = 1 - radius;
    int ddF_x = 0;
    int ddF_y = -2 * radius;
    int x = 0;
    int y = radius;

    setPixel(x0, y0 + radius, bw);
    setPixel(x0, y0 - radius, bw);
    setPixel(x0 + radius, y0, bw);
    setPixel(x0 - radius, y0, bw);

    while(x < y)
    {
      if(f >= 0)
      {
        y--;
        ddF_y += 2;
        f += ddF_y;
      }
      x++;
      ddF_x += 2;
      f += ddF_x + 1;

      setPixel(x0 + x, y0 + y, bw);
      setPixel(x0 - x, y0 + y, bw);
      setPixel(x0 + x, y0 - y, bw);
      setPixel(x0 - x, y0 - y, bw);
      setPixel(x0 + y, y0 + x, bw);
      setPixel(x0 - y, y0 + x, bw);
      setPixel(x0 + y, y0 - x, bw);
      setPixel(x0 - y, y0 - x, bw);
    }
    radius--;
  }
}

void setChar(char character, int x, int y, boolean bw)
{
  byte column; 
  for (int i=0; i<5; i++) 
  {
    column = ASCII[character - 0x20];
    for (int j=0; j<8; j++) 
    {
      if (column & (0x01 << j)) 
        setPixel(x+i, y+j, bw);
      else
        setPixel(x+i, y+j, !bw);
    }
  }
}

void setStr(char * dString, int x, int y, boolean bw)
{
  while (*dString != 0x00) 
  {
    setChar(*dString++, x, y, bw);
    x+=5;
    for (int i=y; i<y+8; i++)
    {
      setPixel(x, i, !bw);
    }
    x++;
    if (x > (LCD_WIDTH - 5)) 
    {
      x = 0;
      y += 8;
    }
  }
}

void setBitmap(char * bitArray)
{
  for (int i=0; i<(LCD_WIDTH * LCD_HEIGHT / 8); i++)
    displayMap = bitArray;
}

void clearDisplay(boolean bw)
{
  for (int i=0; i<(LCD_WIDTH * LCD_HEIGHT / 8); i++)
  {
    if (bw)
      displayMap = 0xFF;
    else
      displayMap = 0;
  }
}

void gotoXY(int x, int y)
{
  LCDWrite(0, 0x80 | x);  
  LCDWrite(0, 0x40 | y);  
}

void updateDisplay()
{
  gotoXY(0, 0);
  for (int i=0; i < (LCD_WIDTH * LCD_HEIGHT / 8); i++)
  {
    LCDWrite(LCD_DATA, displayMap);
  }
}

void setContrast(byte contrast)
{  
  LCDWrite(LCD_COMMAND, 0x21);
  LCDWrite(LCD_COMMAND, 0x80 | contrast); 
  LCDWrite(LCD_COMMAND, 0x20);
}

void invertDisplay()
{
  
  LCDWrite(LCD_COMMAND, 0x20);
  LCDWrite(LCD_COMMAND, 0x08 | 0x05); 
  LCDWrite(LCD_COMMAND, 0x20); 
  for (int i=0; i < (LCD_WIDTH * LCD_HEIGHT / 8); i++)
  {
    displayMap = ~displayMap & 0xFF;
  }
  updateDisplay();
}

void LCDWrite(byte data_or_command, byte data) 
{
  digitalWrite(dcPin, data_or_command); 
  digitalWrite(scePin, LOW);
  SPI.transfer(data); //shiftOut(sdinPin, sclkPin, MSBFIRST, data);
  digitalWrite(scePin, HIGH);
}

void lcdBegin(void) 
{
  pinMode(scePin, OUTPUT);
  pinMode(rstPin, OUTPUT);
  pinMode(dcPin, OUTPUT);
  pinMode(sdinPin, OUTPUT);
  pinMode(sclkPin, OUTPUT);
  pinMode(blPin, OUTPUT);
  analogWrite(blPin, 255);

  SPI.begin();
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST);
  digitalWrite(rstPin, LOW);
  digitalWrite(rstPin, HIGH);

  LCDWrite(LCD_COMMAND, 0x21); 
  LCDWrite(LCD_COMMAND, 0xB0); 
  LCDWrite(LCD_COMMAND, 0x04); 
  LCDWrite(LCD_COMMAND, 0x14);
  LCDWrite(LCD_COMMAND, 0x20); 
  LCDWrite(LCD_COMMAND, 0x0C);
}

 

استخدام الاردوينو في قياس الجهود الكهربائية

في هذا المشروع سنتعلم كيفية استخدام الاردوينو لقياس الجهود الكهربائية. يمكن استخدام هذه الفكرة لعمل Digital Voltameter أو جهاز لمعرفة حالة البطارية.

Voltameter: استخدام الاردوينو فى قياس الجهود الكهربية

 

المكونات المطلوبة

arduino uno r3

Arduino Uno

 

10K Ohm Resistor

 

1k Ohm Resistor

 

Full size breadboard 830

Breadboard

Breadboard Jumper Wire 65 pcs

Wires

شرح الدارة

تعتمد الدارة على مبدا تقسيم الجهد Voltage Divider في حساب الجهد المراد قياسه.

Servo Motor: استخدام مقاومة متغيرة للتحكم فى حركة محرك سيرفو

مثلا، لقياس الجهد على المقاومة R2 :

V(R2) = Vcc * R2 / (R1 + R2)

ملاحظة : مدى الجهود التى يمكن قياسها من 0 الى 5 فولت

توصيل الدارة :

قم بتوصيل الدارة كما هو مبين بالشكل التالي :

Voltameter: استخدام الاردوينو فى قياس الجهود الكهربية

Voltameter: استخدام الاردوينو فى قياس الجهود الكهربية

سيتم قياس جهد ما عن طريق توصيل طرفى القياس على الجهد المراد. لذلك، سنقوم بكتابه برنامج بحيث يتم تشغيل الشاشة التسلسلية Serial monitor لعرض الجهد المقاس عليها.

Voltameter: استخدام الاردوينو فى قياس الجهود الكهربية

 

الكود البرمجي

// Max Voltage ( reference voltage ) 
float vPow = 5.0; 
// first resistor 10K ohm 
float r1 = 10000; 
// second resistor 1k Ohm 
float r2 = 1000;

void setup() {
 Serial.begin(9600);
 Serial.println("--------------------");
 Serial.println("DC VOLTMETER");
 Serial.print("Maximum Voltage: ");
 Serial.print((int)(vPow / (r1 / (r1 + r2))));
 Serial.println("V");
 Serial.println("--------------------");
 Serial.println("");
 
 delay(2000);   //wait 2000 ms before starting
}

void loop() {
 float v = (analogRead(0) * vPow) / 1024.0;
 float v2 = v / (r1 / (r1 + r2));
 
 Serial.print("Voltage (Volts) = ");
 Serial.println(v2);
}

شرح الكود :

في البداية نقوم بالإعلان عن المتغيرات التي سيتم استخدامها في البرمجة. سيتم استخدام المتغير vPow لتسجيل قيمة الجهد المرجعي المستخدم في هذه الحالة 5 فولت. والمتغير r1 لتسجيل قيمة المقاومة الأولى المستخدمة في الـ Voltage Divider . والمتغير r2 لتسجيل قيمة المقاومة الثانية المستخدمة في voltage Divider.

// Max Voltage ( reference voltage )
float vPow = 5.0;
// first resistor 10K ohm
float r1 = 10000;
// second resistor 1k Ohm
float r2 = 1000;

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

void setup() {
 Serial.begin(9600);
 Serial.println("--------------------");
 Serial.println("DC VOLTMETER");
 Serial.print("Maximum Voltage: ");
 Serial.print((int)(vPow / (r1 / (r1 + r2))));
 Serial.println("V");
 Serial.println("--------------------");
 Serial.println("");
 
 delay(2000);   //wait 2000 ms before starting
}

في الدالة ()loop، نقوم بقراءة قيمة الجهد على المقاومة 10k ohm، ثم عن طريق استخدام قانون Voltage Divider نقوم بحساب الجهد الكلي وعرضة على الشاشة التسلسلية.

void loop() {
 float v = (analogRead(0) * vPow) / 1024.0;
 float v2 = v / (r1 / (r1 + r2));
 
 Serial.print("Voltage (Volts) = ");
 Serial.println(v2);
}

مشغل موسيقى بإستخدام الأردوينو

سنقوم في هذا المشروع بعمل مشغل موسيقى mp3 باستخدام الاردوينو و موديول DFPlayer Mini الذي يستخدم كارت ذاكرة يمكننا من وضع الموسيقى التي نريد تشغيلها .

Mp3 Player: مشغل اغانى باستخدام الاردينو

 

المكونات المطلوبة

arduino uno r3

Arduino Uno

 

 

Full size breadboard 830

Breadboard

Breadboard Jumper Wire 65 pcs

Wires

 

DFPlayer Mini MP3 Module :

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

Mp3 Player: مشغل اغانى باستخدام الاردينو

سنقوم بتحميل ملفات الصوتيات على بطاقة الذاكرة (SD Card) ، ثم نقوم بوضعه داخل الموديول واعتمادا على الكود البرمجي الذي يتم رفعه على الأردوينو سنتحكم بتشغيل هذه الملفات.

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

Mp3 Player: مشغل اغانى باستخدام الاردينو
ملاحظة : الملفات الصوتية ذات الإمتداد mp3 هي التي تعمل فقط مع هذا الموديول.

يجب ان نقوم باعادة تسمية الملفات الصوتية بالشكل التالى :

0001.mp3 – 0002.mp3 – ……….. – 0100.mp3

 

Mp3 Player: مشغل اغانى باستخدام الاردينو

توصيل الدارة :

قم بتوصيل الدارة كما هو موضح بالصورة التالية :

Mp3 Player: مشغل اغانى باستخدام الاردينو

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

Mp3 Player: مشغل اغانى باستخدام الاردينو

الكود البرمجي :

قم برفع الكود التالي إلى الأردوينو :

#include <DFPlayer_Mini_Mp3.h>
#include <SoftwareSerial.h>


void setup () {
 Serial.begin (9600);
 mp3_set_serial (Serial); //set Serial for DFPlayer-mini mp3 module 
 mp3_set_volume (10);
}

void loop () {        

 mp3_play (1); //play 0001.mp3
 delay (10000); //10 sec, time delay to allow 0001.mp3 to finish playing

 mp3_play (2);
 delay (5000);

 mp3_play (5);
 delay (5000);

 mp3_play (20); //play 0020.mp3
 delay (9000);
}

 

شرح الكود البرمجى

في البداية سنحتاج الى تحميل مكتبة الموديول من الرابط .
ثم نقوم باضافتة الى مكتبات الاردوينو :

#include <DFPlayer_Mini_Mp3.h>
#include <SoftwareSerial.h>

في الدالة ()setup، نقوم بتشغيل بروتوكول التسلسلي، الذي سيتم استخدامه لإرسال الأوامر بين الأردوينو وموديول الـ Mp3 .

void setup () {
 Serial.begin (9600);
 mp3_set_serial (Serial); //set Serial for DFPlayer-mini mp3 module 
 mp3_set_volume (10);
}

في الدالة ()loop، نقوم بإعطاء ترتيب الملفات الصوتية المراد تشغيلها. فمثلا، نبدأ بتشغيل الملف الصوتي  0001.mp3 وننتظر 10 ثوان قبل البدء في الملف التالي وهكذا.

void loop () {        

 mp3_play (1); //play 0001.mp3
 delay (10000); //10 sec, time delay to allow 0001.mp3 to finish playing

 mp3_play (2);
 delay (5000);

 mp3_play (5);
 delay (5000);

 mp3_play (20); //play 0020.mp3
 delay (9000);
}

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