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

المقدمة

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

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

arduino-simulator-tinkercad

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

المقدمة

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

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

اردوينو(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);
}
}

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

في هذا المشروع سنتعلم كيفية استخدام حساس الموجات الفوق صوتية (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 عبر الأوامر الصوتية

في هذا المشروع، سنقوم بتعلم كيفية تحويل الصوت إلى أوامر تحكم . وتعرف على وحدة التعرف على الصوت (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

استخدام DS3231 RTC Module مع الاردوينو

سنتعلم في هذا المشروع كيفية إستخدام وحدة RTC “Real Time Clock” وعرض التوقيت على شاشة LCD

استخدام-ds3231-rtc-module-مع-الاردوينو

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

arduino uno r3

Arduino Uno

 

rtc ds3231

RTC – Real Time Clock

 

https://geeksvalley.com/tutorial/rtc-arduino/

2×16 LCD

Push Buttons

potentiometer 10k

Potentiometer 10K Ω

10k Ohm Resistors

Full size breadboard 830

Breadboard

Breadboard Jumper Wire 65 pcs

Wires

الشاشة LCD

تعمل الشاشة في أحد الحالات التالية:

1- ان تستقبل امر من الارودوينو و تقوم بتنفيذة مثلا: امر مسح الشاشة و امر التهيئة 

lcd.begin(16,2);
lcd.clear();

2- ان تستقبل معلومات من الاردوينو و تقوم بعرضها مثلا : كتابة جملة معينة  

lcd.print("Hello");

استخدام-ds3231-rtc-module-مع-الاردوينو

DS3231 RTC Module

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

استخدام-ds3231-rtc-module-مع-الاردوينو

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

استخدام-ds3231-rtc-module-مع-الاردوينو

 

توصيل الدارة

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

استخدام-ds3231-rtc-module-مع-الاردوينو

توصيل وحدة DS3231 RTC :

RTC Module Arduino
5v Vcc
GND Gnd
SCL A5
SDA A4

تقوم وحدة الRTC بحساب التوقيت و ارساله الى الاردوينو ليتم عرضه على شاشة LCD ، المفاتيح تستخدم لضبط التوقيت في البداية فقط.

استخدام-ds3231-rtc-module-مع-الاردوينو

البرمجة

قم بتنزيل الكود البرمجي كاملا من خلال الرابط هنـا .

شرح البرمجة :

حتى نتمكن من التواصل مع RTC Module بإستخدام الاردوينو، نحتاج الى إستخدام بروتوكول I2C و الذي تتيحه لنا المكتبة Wire.h.

لذلك في البداية نقوم بإدراج مكتبة Wire.h ، التي تحتوى على الدوال اللازمة للتواصل بين الاردوينو و الـ RTC Module .كما نقوم أيضا بإدراج مكتبة LiquidCrystal.h التي تحتوي على الدوال الخاصة بشاشة الـ LCD، و التي سيتم إستخدمها لعرض التوقيت.

#include <Wire.h>
#include <LiquidCrystal.h>

نقوم بعد ذلك بتسمية منافذ الأردوينو التي تم استخدامها في المشروع والخاصة بالـ Push Buttons.

#define ENTER A2
#define UP A1
#define DOWN A0

بعد ذلك قمنا بتحديد عنوان خاص بالRTC Module و هو 0x68 و قمنا بتعيين متغير له :

#define DS3231_I2C_ADDRESS 0x68

أي وحدة تستخدم برتوكول I2C تمتلك عنوان معين ليتمكن الاردوينو من التواصل معه. لان هذا البرتوكول يستطيع التواصل مع عدد كبير من الاجهزة على نفس الـProtocol Bus . لذلك كل واحد منها يمتلك عنوان معين ليستطيع الاردوينو التواصل معه بدون التأثير على باقي الاجهزة الموصلة على الـ Busـ

يتم الحصول على العنوان الخاص بكل موديول من الـ Datasheet  الخاصه به :

استخدام-ds3231-rtc-module-مع-الاردوينو

 

 ثم نقوم بإنشاء المتغير الخاص بشاشة الـ LCD، وتحديد الـ Pins الموصوله بينها وبين الأردوينو :

LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

 ونقوم بالإعلان عن المتغيرات المستخدمه في تخزين حالة الـ Push Buttons، والتي ستيم استخدامها في البداية لضبط التوقيت فقط. كما نقوم أيضا بالإعلان عن متغيرات لحفظ التوقيت الذي سنقوم بضبطه مثل متغير SetM و SetH .

//variables for button states
int enterState = 0, enterStateLast = 0, upState = 0, upStateLast = 0, downState = 0, downStateLast = 0;

//variables for ui
boolean blinkOn = true; //visibility of ':' between hour and minutes
boolean setVisible = false; //visibility of the set time ui

//variables for new time
int setM = 0; //users new minute value
int setH = 0; //users new hour value

هذا الموديول يتعامل مع الارقام على النظامBinary Coded Decimal . أي اننا سوف نحتاج لضبط التوقيت في البداية إلى تحويل الأعداد من الصورة Decimal إلى الصورة BCD . لذلك نستخدم الدالة ()decToBcd.

//convert normal decimal numbers to binary coded decimals
byte decToBcd(byte val)
{
  return ( (val / 10 * 16) + (val % 10) );
}

 وعند قراءة قيمة الوقت من الموديول نستخدم الدالة ()bcdToDec ، لتحويل التوقيت المرسل من الموديول من الصورة BCD الى الصورة Decimal ، لنتمكن من عرضها على الشاشة LCD.

//convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
  return ( (val / 16 * 10) + (val % 16) );
}

 

 الدالة ()setup نقوم بضبط الإعدادات اللازمة للمشروع، مثل تشغيل بروتوكول I2C و شاشةالـ  LCD و قمنا بضبط المفاتيح المستخدمة في ضبط التوقيت كمدخل.

//code that runs once at setup
void setup() {
  //start wire and lcd
  Wire.begin();
  lcd.begin(16,2); //(col, rows)

  //intialize buttons as inputs
  pinMode(ENTER, INPUT);
  pinMode(UP, INPUT);
  pinMode(DOWN, INPUT);
}

 الدالة ()loop نقوم بقراءة الـPush Buttons لضبط التوقيت في البداية ثم نقوم بالتواصل مع الـRTC Module و نعرض التوقيت على الشاشة LCD .

void loop() {
  checkButtons();
  printTime();
}

الدالة ()setRTCTime تقوم بإنشاء اتصال بين الأردوينو و الـ RTC Module بإستخدام مكتبة Wire.  وظيفة هذه الداله هو ضبط التوقيت الذي سنقوم بإدخاله بإستخدام الـ Push Buttons. في البداية تقوم بإرسال عنوان الجهاز الذي تريد الإتصال معه وفي حالتنا هنا هو RTC Module و الذي قمنا بتحديده في بداية الكود.

//set the time and date to the RTC
void setRTCTime(byte second, byte minute, byte hour, byte dayOfWeek, byte
                dayOfMonth, byte month, byte year)
{
  // sets time and date data to DS3231
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set next input to start at the seconds register
  Wire.write(decToBcd(second)); // set seconds
  Wire.write(decToBcd(minute)); // set minutes
  Wire.write(decToBcd(hour)); // set hours
  Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday)
  Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
  Wire.write(decToBcd(month)); // set month
  Wire.write(decToBcd(year)); // set year (0 to 99)
  Wire.endTransmission();
}

بعد ذلك تقوم بإرسال التوقيت الذي تم ضبطه مع ملاحظة أننا عندما نرسل البيانات الى الموديول نقوم بتحويلها الى الصورة BCD .

 Wire.write(decToBcd(minute));

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

الدالة ()readRTCTime تقوم بعمل إتصال مع الموديول و قراءة معلومات التوقيت منه.  وسيتم استخدامها في الحصول على التوقيت وعرضه.
وهي تعمل بنفس اسلوب الداله السابقة مع اختلاف اننا نقوم بقراءة البيانات من الموديول بدلا من إرسالها له، مع ملاحظة اننا في هذه الحاله نقوم بتحويلها من الصورة BCD الى الصورة Decimal .

//read the time and date from the RTC
void readRTCTime(byte *second, byte *minute, byte *hour, byte *dayOfWeek,
                 byte *dayOfMonth, byte *month, byte *year)
{
  Wire.beginTransmission(DS3231_I2C_ADDRESS);
  Wire.write(0); // set DS3231 register pointer to 00h
  Wire.endTransmission();
  Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
  // request seven bytes of data from DS3231 starting from register 00h
  *second = bcdToDec(Wire.read() & 0x7f);
  *minute = bcdToDec(Wire.read());
  *hour = bcdToDec(Wire.read() & 0x3f);
  *dayOfWeek = bcdToDec(Wire.read());
  *dayOfMonth = bcdToDec(Wire.read());
  *month = bcdToDec(Wire.read());
  *year = bcdToDec(Wire.read());
}

الدالة ()printTime تقوم بقراءة قيمة التوقيت من الموديول بإستخدام الدالة ()readRTCTime ثم تقوم بعرضها على الشاشة LCD مع اضافة كود مهمته عرض التوقيت على الصورة  HH : MM .

//reads the RTC time and prints it to the top of the LCD
void printTime()
{
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  
  //retrieve time
  readRTCTime(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
  
  //print to lcd top
  lcd.setCursor(0,0);
  lcd.print(" Current: ");
  if (hour<10)
  {
    lcd.print("0");
  }
  lcd.print(hour, DEC);
  if (blinkOn == true)
  {
    lcd.print(" ");
    blinkOn = false;
  }
  else if (blinkOn == false)
  {
    lcd.print(":");
    blinkOn = true;
  }
  if (minute<10)
  {
    lcd.print("0");
  }
  lcd.print(minute, DEC);
  
  delay(100);
}

الجزء السابق مختص بقراءة قيمة التوقيت الذي يقوم الRTC Module بحسابه بدون تدخل من المستخدم . الجزء التالي من الكود مختص بقراءة حالة المفاتيح لضبط التوقيت و ارسالة الى الRTC Module.

الدالة ()checkButtons تقوم بقراءة حالة المفاتيح و التي تعمل كالتالي :

المفتاح المسمى UP يقوم بزيادة عداد الدقائق والساعات.
المفتاح المسمى DOWN يقوم بإنقاص عداد الدقائق والساعات.
المفتاح المسمى ENTER يقوم بتطبيق التوقيت الذي تم ضبطه وإرساله الى الـRTC Module.

//checks if buttons are pressed and responds accordingly
void checkButtons()
{
  //check enter
  enterState = digitalRead(ENTER);
  if (enterState != enterStateLast)
  {
    if (enterState == HIGH)
    {
      if (setVisible == true)
      {
        byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
        readRTCTime(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
        
        setRTCTime(0, setM, setH, dayOfWeek, dayOfMonth, month, year);
      }
      else if (setVisible == false)
      {
        showSet();
      }
    }
  }
  enterStateLast = enterState;
  
  //check up
  upState = digitalRead(UP);
  if (upState != upStateLast)
  {
    if (upState == HIGH)
    {
      if (setVisible == true)
      {
        addMin();
        printSetTime();
      }
      else if (setVisible == false)
      {
        showSet();
      }
    }
  }
  upStateLast = upState;

  //check down
  downState = digitalRead(DOWN);
  if (downState != downStateLast)
  {
    if (downState == HIGH)
    {
      if (setVisible == true)
      {
        subMin();
        printSetTime();
      }
      else if (setVisible == false)
      {
        showSet();
      }
    }
  }
  downStateLast = downState;
}

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

//displays the new time interface in the bottom of the LCD
void showSet ()
{
  //update new time variables to current RTC values
  byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
  readRTCTime(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
    setH = hour, DEC;
    setM = minute, DEC;

  //pints to the LCD
  lcd.setCursor(0,1);
  lcd.print("   New: ");
  printSetTime();
  setVisible = true;
}

الدالة ()printSetTime تستخدمها الدالة ()showSet لعرض التوقيت الذي تم ضبطه في اسفل الشاشة LCD .

//prints the new time values on the bottom of the LCD
void printSetTime()
{
 lcd.setCursor(8,1); 
 if (setH<10)
  {
    lcd.print("0");
  }
  lcd.print(setH);
  lcd.print(":");
  if (setM<10)
  {
    lcd.print("0");
  }
  lcd.print(setM); 
}

الدوال التالية مستخدمة في الجزء السابق من الكود وهي تقوم بضبط المتغيرات التي تستخدم في تخزين قيم الدقائق والساعه التي يقوم المستخدم بضبطها بواسطة الPush Buttons .

الدالة ()addMin تقوم بزياد عداد الدقائق عند الضغط على المفتاح UP و تقوم أيضا بعمل اختبار لعداد الدقائق فإذا وصل العدد الى 59 تقوم بزيادة عداد الساعات .

//adds a minute to new time
void addMin()
{
  if (setM < 59)
  {
    setM++;
  }
  else if (setM == 59)
  {
    setM = 0;
    addHr();
  }
}

الدالة ()subMin تعمل عكس عمل الدالة السابقة وهي تقوم بإنقاص عداد الدقائق عند الضغط على المفتاح DOWN وتقوم أيضا باختبار عداد الدقائق فإذا وصل الى 0 تقوم بإنقاص عداد الساعات.

//subtracts a minute from new time
void subMin()
{
  if (setM > 0)
  {
    setM--;
  }
  else if (setM == 0)
  {
    setM = 59;
    subHr();
  }
}

الدالة ()addHr نقوم بإستدعائها الدالة ()addMin عندما يصل عداد الدقائق الى 59 لتقوم بزيادة عداد الساعات بمقدار واحد.

//adds an hour to new time
void addHr ()
{
  if (setH < 23)
  {
    setH++;
  }
  else if (setH == 23)
  {
    setH = 0;
  }
}

الدالة ()subHr تقوم باستدعائها الدالة ()subMin عندما يصل عداد الدقائق الى 0 لتقوم بإنقاص عداد الساعات بمقدار واحد.

//subtracts an hour from new time
void subHr ()
{
  if (setH > 0)
  {
    setH--;
  }
  else if (setH == 0)
  {
    setH = 23;
  }
}

التحكم في مصفوفة ليدات (LED Matrix)

في هذا المشروع سنتعلم كيف يمكننا التحكم في تشغيل مصفوفة الليدات LED Matrix بإستخدام الاردوينو ولتقليل عدد المخارج المستخدمة سيتم إستخدام الدارة المتكاملة MAX7219 .

 

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

 

arduino uno r3

Arduino Uno

 

8×8 LED Matrix

 

MAX7219

Potentiometer 10K Ω

Full size breadboard 830

Breadboard

Breadboard Jumper Wire 65 pcs

Wires

مصفوفة الليدات LED Matrix :

هي عبارة عن 64 LEDs موصلين معا على شكل مصفوفة مكونة من صفوف وأعمدة.

كل عامود يتصل بالـ cathode الخاص بمجموعة الـ LEDs لهذا العامود، وكل صف يتصل بالـ Anode الخاص بمجموعة الـ LEDs لهذا الصف.

كي نقوم بتشغيل LED معين، نضع الجهد الموجب على الصف الموجود به الـ LED والجهد السالب على العامود الموجود به. كما هو موضح بالصورة التالية: عند وضع جهد موجب على الصف A وأرضي على العامود 2 تعمل الـ LED رقم L2 وهكذا.

التحكم فى مصفوفة ليدات Led Matrix باستخدام الاردوينو

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

التحكم فى مصفوفة ليدات Led Matrix باستخدام الاردوينو

كي نتمكن من توصيل مصفوفة الليد (LED Matrix)، نحتاج إلى معرفة اين مواقع الصفوف والأعمدة، انظر الصورة أدناه :

التحكم فى مصفوفة ليدات Led Matrix باستخدام الاردوينو

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

التحكم فى مصفوفة ليدات Led Matrix باستخدام الاردوينو

MAX7219 :

هي عبارة عن دارة متكاملة تقوم بتشغيل الـ LED Matrix. وما يميزها هو أن دخلها عبارة عن إشارة تسلسلية بمعنى أننا إذا قمنا بتوصيل الـ LED Matrix مباشرة إلى الأردوينو نحتاج إلى 16 منفذ، أما بإستخدام هذه الشريحة فإننا بحاجة إلى 3 منافذ فقط.

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

توصيلها :

توصيلها مع الاردوينو و الLED Matrix من الداتا شيت

led-matrix

الجدول التالي يوضح كيفية التوصيل مع الأردوينو :

MAX7219 التوصيل
اى رجل تسمى SEG مع الصفوف
اى رجل تسمى DIG مع الاعمدة
24 Not Connected
19 Vcc
18 10K Ohm to VCC
13 Arduino Pin 9
12 Arduino Pin 10
9 Ground
4 Ground
1 Arduino Pin 8

الجدول أعلاه يوضح أن الأرجل ذو الإسم SEG توصل مع الصفوف، والأرجل ذو الإسم DIG يتم توصيلها مع الأعمدة. على سبيل المثال: SEG A يتم توصيلها مع الصف الأول و SEG B يتم توصيلها مع الصف الثاني، وهكذا. اما DIG0 يتم توصيلها مع العامود الأول، و DIG1 يتم توصيلها مع العامود الثاني وهكذا.. .

لاحظ:

بداية الاعمدة فى الدارة MAX7219 من DIG 0. لذلك اول عمود يتصل مع DIG 0 و ليس DIG 1

توصيل الدارة

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

التحكم فى مصفوفة ليدات Led Matrix باستخدام الاردوينو

 

يقوم البرنامج بقراءة قيمة مقاومة متغيرة و على حسب القيمة يقوم برسم شكل وجه مبتسم على الLED Matrix

التحكم فى مصفوفة ليدات Led Matrix باستخدام الاردوينو

البرمجة :

يعتمد الكود على بعض المكتبات التى نحتاج الى اضافتها الى Arduino IDE يمكنك تحميلها من هنا

#include <binary.h>
#include <Sprite.h>
#include <Matrix.h>

#define POT 0 

Matrix Leds = Matrix(8, 9, 10);

Sprite smile[3] = {
  Sprite(8, 8,  B01000000, B01000110, B01000110, B01000000, B01000000, B01000110, B01000110, B01000000),
  Sprite(8, 8,  B00100000, B01000110, B01000110, B01000000, B01000000, B01000110, B01000110, B00100000),
  Sprite(8, 8,  B00010000, B00100110, B01000110, B01000000, B01000000, B01000110, B00100110, B00010000)
};

void setup() {
  Leds.clear(); // turn off all diodes
}

void loop() {
  
  int pot = analogRead(POT); 
  pot = min(2, map(potData, 0, 1023, 0, 3)); // which smile
  
  // display face
  Leds.write(0, 0, smile[pot]);

}

شرح الكود :

في البداية نقوم بإدراج المكتبات التي نحتاجها في المشروع. وهم عبارة عن مكتبات للتعامل مع الـ LED Matrix والدارة MAX7219 :

#include <binary.h>
#include <Sprite.h>
#include <Matrix.h>

نقوم بتسمية منافذ الأردوينو التي سنقوم بتوصيلها مع المقاومة المتغيرة :

#define POT 0

ثم نقوم بإنشاء متغير مسؤول عن الـ LED Matrix .  و يتم تعريف الارجل التي يتم توصيلها بين الاردوينو و الدارة MAX7219 في هذا المتغير

Matrix Leds = Matrix(8, 9, 10);

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

Sprite smile[3] = {
  Sprite(8, 8,  B01000000, B01000110, B01000110, B01000000, B01000000, B01000110, B01000110, B01000000),
  Sprite(8, 8,  B00100000, B01000110, B01000110, B01000000, B01000000, B01000110, B01000110, B00100000),
  Sprite(8, 8,  B00010000, B00100110, B01000110, B01000000, B01000000, B01000110, B00100110, B00010000)
};

فى الدالة ()setup نقوم بوضع الاعدادات اللازمة للمشروع مثل اطفاء جميع الليدات فى البداية .

void setup() {
  Leds.clear(); // turn off all diodes
}

و في الدالة ()loop نقوم بقراءة قيمة المقاومة المتغيرة و بإستخدام الدالة map نقوم بتحويل القيمة التي تمت قرائتها وتتراوح بين 0 الى 1023 الى رقم بين 0 الى 2
ونستخدم الرقم الناتج في تشغيل وعرض الشكل المقابل له في مصفوفة الأشكال على الـ LED Matrix

void loop() {
  
  int pot = analogRead(POT); 
  pot = min(2, map(potData, 0, 1023, 0, 3)); // which smile
  
  // display face
  Leds.write(0, 0, smile[pot]);

}

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

 في هذا المشروع سنقوم بتعلم كيفية استخدام الأردوينو لقياس مستوى الصوت، وسيتم توضيح مستوى الصوت على مجموعة من الـ 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);
}

جهاز تنبيه لوصول رسائل بريد إلكتروني

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

Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

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

arduino uno r3

Arduino Uno

serial port bluetooth module hc-06

Bluetooth Module HC-06

blue led 5mm

LED

220 Ω resistor

220 Ohm Resistor

 

Full size breadboard 830

Breadboard

Breadboard Jumper Wire 65 pcs

Wires

 

توصيل الدارة

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

Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

قمنا بتوصيل وحدة البلوتوث كما هو موضح بالجدول أدناه :
موديول البلوتوث الاردوينو
VCC 5 v
GND GND
TXD RX (Pin 0)
RXD TX (Pin 1)

 

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

Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

 

البرامج المطلوبة

نحتاج لتجهيز بعض البرمجيات كي يعمل منبة الرسائل الإلكترونية بشكل صحيح :

  1. Microsoft Outlook
  2. Script File “.vbs”
  3. Bluetooth
  4. Putty

Script File

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

مثلا بدلا من أن نضغط بأنفسنا مفتاح Enter يمكننا

x.sendkeys "{ENTER}"

عند تنفيذه سيحاكي مفتاح Enter كما لو ان أحد ما ضغط عليه.

 

لانشاء هذا الملف

  1. نفتح ملف جديد باستخدام برنامج Notepad
  2. اضف السطور التالية للملف، لاحظ الحروف يجب ان تكون بنفس الشكل
    set ghost = wscript.CreateObject("WScript.Shell")
    ghost.sendkeys "a"
    
  3. قم بحفظ الملف عن طريق :
    save as –> Email.vbs
    و تأكد من إختيار  Save as Type : All Files

Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

 

Microsoft Outlook

هنا سنقوم بضبط بعض الإعدادات بحيث عندما يصلنا رسالة إلكترونية جديدة يقوم برنامج Outlook بتنفيذ ملف الScript الذي قمنا بتجهيزه.

  1. قم بفتح برنامج Outlook بعدها قم بالضغط على ‘Rules’ ثم إختار ‘Manage Rules & Alerts’

    Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

  2. بعد ذلك إضغط على ‘New Rule’ تحت التوبيب في أعلى يسار النافذة

    Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

  3. اضغط ‘Apply on messages I receive’، بعد ذلك اضغط ‘Next’

    Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

  4. اختار ‘Where my name is in the To Box’، ثم اضغط ‘Next’

    Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

  5. اختار ‘Start application’،بعد ذلك في اسفل النافذة اضغط على كلمة ‘application’

    Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

  6. في النافذة الجديدة، قم بتغيير نوع الملفات من ‘(Executable Files (EXE’، الى ‘All files’.بعد ذلك اذهب الى المكان الموجود به الملف الذي قمنا بإنشاءة سابقا وقم باختياره، ‘Email.vbs’، ثم اضغط ‘Open’

    Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

  7. قم بإعطاء اسم ما لهذا الـRule ثم قم بالضغط على  ‘Finish’

    Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

  8. اضغط على  ‘Apply’ ثم  ‘Ok’

    Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

Bluetooth

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

  1. قم بالتوجه الى Control Panel
  2. اضغط على ‘Hardware and Sound’
  3. تأكد من توصيل موديول البلوتوث بالاردوينو ثم قم بتوصيل الاردوينو الى جهاز الحاسوب ستلاحظ ان ضوء الليد على موديول البلوتوث يضىء و يطفىء بسرعه
  4. ثم بعد ذلك اختار ‘Add a Device’
  5. انتظر حتى يظهر موديول البلوتوث على الحاسوب بإسم ‘HC-06’ ثم اضغط عليه
  6. ستظهر نافذة أخرى تطلب ادخال كود مرور، قم بكتابة ‘1234’
  7. انتظر حتى تظهر رساله انه تم الاتصال

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

في أعلى النافذة ستجد ‘COM Ports’ قم بالضغط عليها و ستجد رقم فيOutgoing port تذكره جيدا

Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

ملاحظة

لمزيد من المعلومات عن وحدة البلوتوث وكيفية ضبطة، يرجى الإطلاع على مشروع نظام التحكم في الإضاءة عبر البلوتوث.

 

Putty

  1. أولا، قم بتنزيله من الرابط هنـا   Putty
  2. نقوم بفتحه من Putty.exe
  3. نختار Serial
  4. نقوم بتغير COM1 الى رقم الـPort الذي حصلنا عليه من خطوة البلوتوث
  5. نضغط Open
  6. سنرى شاشة سوداء مما يعنى اننا نجحنا في عمل إتصال مع البلوتوث

 

البرمجة :

#define LED 13

int state;

void setup() {
 Serial.begin(9600);
 pinMode(LED, OUTPUT);
}

void loop() {
 if (Serial.available() > 0) {
  state = Serial.read();
 }

 if (state == 'a') {
  digitalWrite(LED, HIGH);
 }
}

شرح الكود :

في البداية نقوم بتسمية منافذ الأردوينو التي تم استخدامها في المشروع. اي منفذ 13 الذي تم توصيل الـ LED عليه. ثم نقوم بالإعلان عن المتغيرات التي سنحتاج إليها.

متغير state لتسجيل القيمة المستقبلية عن طريق البلوتوث في حالة وصول رسالة جديدة.

#define LED 13

int state;

في الدالة ()setup، نقوم بضبط الإعدادات اللازمة. وهي تشغيل بروتوكول Serial المستخدم بواسطة البلوتوث، وتعيين الـ LED كمخرج.

void setup() {
 Serial.begin(9600);
 pinMode(LED, OUTPUT);
}

في الدالة ()loop، نقوم بقراءة الـ Serial وفي حال وصول قيمة جديدة، نقوم بمقارنتها بقيمة ‘a’ التي تم ضبطها سابقا في ملف الـ Script. إذا كانت القيمة التي تم إستقبالها هي ‘a’ أي انه تم وصول رسالة جديدة.

void loop() {
 if (Serial.available() > 0) {
  state = Serial.read();
 }

 if (state == 'a') {
  digitalWrite(LED, HIGH);
 }
}

ملخص للدارة كاملة

قمنا بضبط برنامج Outlook بحيث عندما تصل رسالة جديدة يقوم بفتح ملف الscript الذي قمنا بتسميته Email.vbs و الذي يحتوى على بعض الأوامر التي يقوم الحاسوب بتنفيذها .

يقوم بكتابة حرف ‘a’  على شاشة برنامج Putty الذي يقوم بإرسالها إلى البلوتوث فيستقبلها الأردوينو ويقوم الحاسوب بتشغيل الـ LED

ملاحظة هامة

لكي يعمل كل شيء بشكل صحيح يحب أن يكون برنامج Outlook مفتوح و ايضا برنامج Putty مفتوح و يكون التركيز على الشاشة السوداء لبرنامج Putty
اي أن يكون كما في الصورة

Email Alert: استخدام الاردوينو مع البلوتوث لعمل تنبية لرسائل البريد

استخدام شاشة 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);
}

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

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

في هذا المشروع سنتعلم كيفية استخدام الأردوينو لعمل ساعة رقمية. سنقوم بعرض الوقت على شاشة الـ LCD والتحكم في ضبط الوقت من خلال مفاتيح الـ Push Buttons.

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

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

arduino uno r3

Arduino Uno

16×2 LCD

10K Ohms Resistors

Push Buttons

220 Ω resistor

220 Ohm Resistor

Full size breadboard 830

Breadboard

Breadboard Jumper Wire 65 pcs

Wires

الشاشة LCD

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

استقبال الأوامر من الأردوينو وتنفيذها، مثلا : أمر تهيئة ومسح الشاشة :

lcd.begin(16,2);
lcd.clear();

استقبال المعلومات من الأردوينو وعرضها، مثلا : كتابة جملة معينة :

lcd.print("Hello");

 

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

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

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

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

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

 

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

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

#include <LiquidCrystal.h>

#define HOUR_BUTTON 6
#define MINUTE_BUTTON 7
#define TICK_LENGTH 60000
#define CHECK_TIME 250


LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

unsigned int minute = 0;
unsigned int hour = 01;
unsigned long previousMillis = 0;
unsigned char am = 1;

void setup() {
  // put your setup code here, to run once:
  lcd.begin(16, 2);
  pinMode(HOUR_BUTTON, INPUT);
  pinMode(MINUTE_BUTTON, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  checkTick();
  lcd.setCursor(0,0);
  lcd.print("Time:");
  lcd.setCursor(6,1);
  lcd.print(hour);
  lcd.print(":");
  lcd.print(minute);
  if(am == 1) lcd.print(" AM");
  else lcd.print(" PM");
}

void checkTick() {
   unsigned long currentMillis = millis();
   
   if ((unsigned long)(currentMillis - previousMillis) >= TICK_LENGTH) {
      tick();
      previousMillis = currentMillis;
   }
   if((unsigned long)(currentMillis - previousMillis) >= CHECK_TIME){
      readHourButton();
      readMinuteButton();
   }
}

void tick() {
  minuteUp();
}

  
void readHourButton() {
  if (digitalRead(HOUR_BUTTON) == HIGH) {
    delay(200);
    hourUp();
  }
}

void readMinuteButton() {
  if (digitalRead(MINUTE_BUTTON) == HIGH) {
    delay(200);
    minuteUp();
  }
}

void hourUp() {
  hour = hour + 1;
  if (hour > 12) {
    lcd.clear();
    hour = 1;
    if(am == 1) am = 0;
    else am = 1;
  }
}

void minuteUp() {
  minute = minute + 1;
  if (minute > 59) {
    minute = 0;
    hourUp();
  }
}

شرح الكود :

في البداية نقوم بإضافة مكتبة شاشة الـ LCD وتسمية منافذ الأردوينو المستخدمة مع مفاتيح الضبط :

#include <LiquidCrystal.h>

#define HOUR_BUTTON 6
#define MINUTE_BUTTON 7

ثم نقوم نقوم بالإعلان عن بعض الثوابت التي سيتم استخدامها لاحقا :

#define TICK_LENGTH 60000
#define CHECK_TIME 250

بعد ذلك نقوم بالإعلان عن بعض المتغيرات. يستخدم المتغير minute لحفظ الدقائق. والمتغير hour لحفظ الساعات. والمتغير am لتسجيل ما إذا كان التوقيت صباحا أم مساءاً. وسيتم تسجيل الوقت الخاص بأخر مرة تم حساب التوقيت في المتغير previousMillis .

unsigned int minute = 0;
unsigned int hour = 01;
unsigned long previousMillis = 0;
unsigned char am = 1;

في الدالة ()setup قمنا بضبط الاعدادات اللازمة للمشروع مثل ضبط ارجل الاردوينو كمخارج للمفاتيح وتشغيل الشاشة LCD :

void setup() {
  // put your setup code here, to run once:
  lcd.begin(16, 2);
  pinMode(HOUR_BUTTON, INPUT);
  pinMode(MINUTE_BUTTON, INPUT);
}

في الدلة ()loop، نقوم بحساب عدد الثوان التي مرت، فإذا كانت 60 ثانية نقوم بزيادة عدد الدقائق في المتغير minute. كما نقوم بإختبار الدقائق التي مرت، فعند مرور 60 دقيقة نقوم بزيادة عدد الساعات في المتغير hour. ثم نقوم بالنهاية بعرض الدقائق والساعات على شاشة الـ LCD.

أيضا اثناء الإختبار نقوم بإختبار المفاتيح إذا ما تم الضغط عليها ام لا عبر استدعاء الدالة ()checkTick . فإذا تم الضغط على مفتاح الدقائق نقوم بزيادة عدد الدقائق في المتغير minute، ويعمل بنفس الطريقة عند الضغط على مفتاح الساعات.

void loop() {
  // put your main code here, to run repeatedly:
  checkTick();
  lcd.setCursor(0,0);
  lcd.print("Time:");
  lcd.setCursor(6,1);
  lcd.print(hour);
  lcd.print(":");
  lcd.print(minute);
  if(am == 1) lcd.print(" AM");
  else lcd.print(" PM");
}

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

الدالة ()checkTick ، في كل مرة يتم استدعائها تقوم بحفظ الوقت الذي مضى منذ عمل Reset للأردوينو. ثم تتم مقارنة الوقت الحالي بقيمة مسبقة فإذا اصبح الفارق بين التوقيتين يتعدى الـ TICK_LENGTH الذي تم تحديده في أول البرنامج، فهذا يعني انه قد مرة دقيقة فنقوم بزيادتها بإستخدام الدالة ()tick. ثم نقوم بتسجيل هذا التوقيت لإستخدامه مرة ارخى لمعرفة هل مر دقيقة أخرى ام لا.

 unsigned long currentMillis = millis();
   
   if ((unsigned long)(currentMillis - previousMillis) >= TICK_LENGTH) {
      tick();
      previousMillis = currentMillis;
   }

ايضا يتم التحقق ما إذا تعدى الفارق في التوقيت قيمة CHECK_TIME ، فعندها نقوم بقراءة المفاتيح المستخدمة في تعديل التوقيت.

   if((unsigned long)(currentMillis - previousMillis) >= CHECK_TIME){
      readHourButton();
      readMinuteButton();
   }

الدالة ()tick، تقوم بإستدعاء الدالة ()minuteUp، والتي بدورها تقوم بزيادة عداد الدقائق.

void tick() {
  minuteUp();
}

 

الدالة ()readHourtButton تقوم بقراءة المفتاح الخاص بتعديل خانة الساعات فإذا تم الضغط عليه تقوم بتشغيل الدالة ()hourUp التي تقوم بزيادة عداد الساعات.

void readHourButton() {
  if (digitalRead(HOUR_BUTTON) == HIGH) {
    delay(200);
    hourUp();
  }
}

الدالة readMinuteButton() تقوم بقراءة المفتاح الخاص بتعديل خانة الدقائق فإذا تم الضغط عليه تقوم بإستدعاء دالة ()minuteUp التي تقوم بزيادة عداد الدقائق.

void readMinuteButton() {
  if (digitalRead(MINUTE_BUTTON) == HIGH) {
    delay(200);
    minuteUp();
  }
}

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

void hourUp() {
  hour = hour + 1;
  if (hour > 12) {
    lcd.clear();
    hour = 1;
    if(am == 1) am = 0;
    else am = 1;
  }
}

الداله minuteUp() في كل مرة يتم إستدعائها تقوم بزيادة عداد الدقائق بمقدار واحد وعمل اختبار اذا وصل عداد الدقائق الى 60 تقوم بتصفير عداد الدقائق و زيادة عداد الساعات بمقدار واحد .

void minuteUp() {
  minute = minute + 1;
  if (minute > 59) {
    minute = 0;
    hourUp();
  }
}