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

مقدمة

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

تخطي الحواجز

المواد والأدوات

arduino-game-by-lcd

1× اردوينو اونو

arduino-game-by-lcd

1× سلك الاردوينو

arduino-game-by-lcd

1× لوحة تجارب – حجم كبير

تخطي الحواجز

 شاشة كرستالية (LCD 2×16 )

تخطي الحواجز

1× مقاومة متغيرة

تخطي الحواجز

 حزمة أسلاك توصيل (ذكر- ذكر)

تخطي الحواجز

1× مقاومة 220 Ω

تخطي الحواجز

مفتاح تحكم

اردوينو

1× 40 رأس دبوس

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

لمعرفة المزيد حول الشاشة الكرستالية يمكنك الرجوع للدرس التحكم بالشاشة الكرستالية LCD

لابد من تلحيم المنافذ مع الشاشة الكرستالية، للمزيد حول اللحام يمكنك الرجوع للدرس تعلم كيفية التلحيم – تلحيم القطع باللوحة الإلكترونية

تخطي الحواجز

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

ارفع كود لعبة تخطي الحواجز إلى لوحة الاردوينو.

#include <LiquidCrystal.h>
#define PIN_BUTTON 2
#define PIN_AUTOPLAY 1
#define PIN_READWRITE 10
#define PIN_CONTRAST 12

#define SPRITE_RUN1 1
#define SPRITE_RUN2 2
#define SPRITE_JUMP 3
#define SPRITE_JUMP_UPPER '.' // Use the '.' character for the head
#define SPRITE_JUMP_LOWER 4
#define SPRITE_TERRAIN_EMPTY ' ' // User the ' ' character
#define SPRITE_TERRAIN_SOLID 5
#define SPRITE_TERRAIN_SOLID_RIGHT 6
#define SPRITE_TERRAIN_SOLID_LEFT 7

#define HERO_HORIZONTAL_POSITION 1 // Horizontal position of hero on screen
#define TERRAIN_WIDTH 16
#define TERRAIN_EMPTY 0
#define TERRAIN_LOWER_BLOCK 1
#define TERRAIN_UPPER_BLOCK 2

#define HERO_POSITION_OFF 0 // Hero is invisible
#define HERO_POSITION_RUN_LOWER_1 1 // Hero is running on lower row (pose 1)
#define HERO_POSITION_RUN_LOWER_2 2 // (pose 2)

#define HERO_POSITION_JUMP_1 3 // Starting a jump
#define HERO_POSITION_JUMP_2 4 // Half-way up
#define HERO_POSITION_JUMP_3 5 // Jump is on upper row
#define HERO_POSITION_JUMP_4 6 // Jump is on upper row
#define HERO_POSITION_JUMP_5 7 // Jump is on upper row
#define HERO_POSITION_JUMP_6 8 // Jump is on upper row
#define HERO_POSITION_JUMP_7 9 // Half-way down
#define HERO_POSITION_JUMP_8 10 // About to land

#define HERO_POSITION_RUN_UPPER_1 11 // Hero is running on upper row (pose 1)
#define HERO_POSITION_RUN_UPPER_2 12 // (pose 2)

LiquidCrystal lcd(11, 9, 6, 5, 4, 3);
static char terrainUpper[TERRAIN_WIDTH + 1];
static char terrainLower[TERRAIN_WIDTH + 1];
static bool buttonPushed = false;
void initializeGraphics(){
static byte graphics[] = {
// Run position 1
B01100,
B01100,
B00000,
B01110,
B11100,
B01100,
B11010,
B10011,
// Run position 2
B01100,
B01100,
B00000,
B01100,
B01100,
B01100,
B01100,
B01110,
// Jump
B01100,
B01100,
B00000,
B11110,
B01101,
B11111,
B10000,
B00000,
// Jump lower
B11110,
B01101,
B11111,
B10000,
B00000,
B00000,
B00000,
B00000,
// Ground
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
// Ground right
B00011,
B00011,
B00011,
B00011,
B00011,
B00011,
B00011,
B00011,
// Ground left
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
};
int i;
// Skip using character 0, this allows lcd.print() to be used to
// quickly draw multiple characters
for (i = 0; i < 7; ++i) {
lcd.createChar(i + 1, &graphics[i * 8]);
} for (i = 0; i < TERRAIN_WIDTH; ++i) {
terrainUpper[i] = SPRITE_TERRAIN_EMPTY;
terrainLower[i] = SPRITE_TERRAIN_EMPTY;
} } // Slide the terrain to the left in half-character increments
// void advanceTerrain(char* terrain, byte newTerrain){ for (int i = 0; i < TERRAIN_WIDTH; ++i) { char current = terrain[i]; char next = (i == TERRAIN_WIDTH-1) ? newTerrain : terrain[i+1]; switch (current){ case SPRITE_TERRAIN_EMPTY: terrain[i] = (next == SPRITE_TERRAIN_SOLID) ? SPRITE_TERRAIN_SOLID_RIGHT : SPRITE_TERRAIN_EMPTY; break; case SPRITE_TERRAIN_SOLID: terrain[i] = (next == SPRITE_TERRAIN_EMPTY) ? SPRITE_TERRAIN_SOLID_LEFT : SPRITE_TERRAIN_SOLID; break; case SPRITE_TERRAIN_SOLID_RIGHT: terrain[i] = SPRITE_TERRAIN_SOLID; break; case SPRITE_TERRAIN_SOLID_LEFT: terrain[i] = SPRITE_TERRAIN_EMPTY; break; } } } bool drawHero(byte position, char* terrainUpper, char* terrainLower, unsigned int score) { bool collide = false; char upperSave = terrainUpper[HERO_HORIZONTAL_POSITION]; char lowerSave = terrainLower[HERO_HORIZONTAL_POSITION]; byte upper, lower; switch (position) { case HERO_POSITION_OFF: upper = lower = SPRITE_TERRAIN_EMPTY; break; case HERO_POSITION_RUN_LOWER_1: upper = SPRITE_TERRAIN_EMPTY; lower = SPRITE_RUN1; break; case HERO_POSITION_RUN_LOWER_2: upper = SPRITE_TERRAIN_EMPTY; lower = SPRITE_RUN2; break; case HERO_POSITION_JUMP_1: case HERO_POSITION_JUMP_8: upper = SPRITE_TERRAIN_EMPTY; lower = SPRITE_JUMP; break; case HERO_POSITION_JUMP_2: case HERO_POSITION_JUMP_7: upper = SPRITE_JUMP_UPPER; lower = SPRITE_JUMP_LOWER; break; case HERO_POSITION_JUMP_3: case HERO_POSITION_JUMP_4: case HERO_POSITION_JUMP_5: case HERO_POSITION_JUMP_6: upper = SPRITE_JUMP; lower = SPRITE_TERRAIN_EMPTY; break; case HERO_POSITION_RUN_UPPER_1: upper = SPRITE_RUN1; lower = SPRITE_TERRAIN_EMPTY; break; case HERO_POSITION_RUN_UPPER_2: upper = SPRITE_RUN2; lower = SPRITE_TERRAIN_EMPTY; break; } if (upper != ' ') { terrainUpper[HERO_HORIZONTAL_POSITION] = upper; collide = (upperSave == SPRITE_TERRAIN_EMPTY) ? false : true; } if (lower != ' ') { terrainLower[HERO_HORIZONTAL_POSITION] = lower; collide |= (lowerSave == SPRITE_TERRAIN_EMPTY) ? false : true; } byte digits = (score > 9999) ? 5 : (score > 999) ? 4 : (score > 99) ? 3 : (score > 9) ? 2 : 1; // Draw the scene terrainUpper[TERRAIN_WIDTH] = '\0'; terrainLower[TERRAIN_WIDTH] = '\0'; char temp = terrainUpper[16-digits]; terrainUpper[16-digits] = '\0'; lcd.setCursor(0,0); lcd.print(terrainUpper); terrainUpper[16-digits] = temp; lcd.setCursor(0,1); lcd.print(terrainLower); lcd.setCursor(16 - digits,0); lcd.print(score); terrainUpper[HERO_HORIZONTAL_POSITION] = upperSave; terrainLower[HERO_HORIZONTAL_POSITION] = lowerSave; return collide; } // Handle the button push as an interrupt void buttonPush() { buttonPushed = true; } void setup(){ pinMode(PIN_READWRITE, OUTPUT); digitalWrite(PIN_READWRITE, LOW); pinMode(PIN_CONTRAST, OUTPUT); digitalWrite(PIN_CONTRAST, LOW); pinMode(PIN_BUTTON, INPUT);
digitalWrite(PIN_BUTTON, HIGH);
pinMode(PIN_AUTOPLAY, OUTPUT);
digitalWrite(PIN_AUTOPLAY, HIGH); // Digital pin 2 maps to interrupt 0
attachInterrupt(0/*PIN_BUTTON*/, buttonPush, FALLING); initializeGraphics(); lcd.begin(16, 2);
} void loop(){ static byte heroPos = HERO_POSITION_RUN_LOWER_1;
static byte newTerrainType = TERRAIN_EMPTY;
static byte newTerrainDuration = 1;
static bool playing = false;
static bool blink = false;
static unsigned int distance = 0; if (!playing) { drawHero((blink) ? HERO_POSITION_OFF : heroPos, terrainUpper, terrainLower, distance >> 3);
if (blink) {
lcd.setCursor(0,0);
lcd.print("Press Start"); } delay(250); blink = !blink;
if (buttonPushed) {
initializeGraphics();
heroPos = HERO_POSITION_RUN_LOWER_1;
playing = true;
buttonPushed = false;
distance = 0; } return; } // Shift the terrain to the left advanceTerrain(terrainLower, newTerrainType == TERRAIN_LOWER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
advanceTerrain(terrainUpper, newTerrainType == TERRAIN_UPPER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY); // Make new terrain to enter on the right
if (--newTerrainDuration == 0) { if (newTerrainType == TERRAIN_EMPTY) {
newTerrainType = (random(3) == 0) ? TERRAIN_UPPER_BLOCK : TERRAIN_LOWER_BLOCK;
newTerrainDuration = 2 + random(10);
} else { newTerrainType = TERRAIN_EMPTY;
newTerrainDuration = 10 + random(10); } } if (buttonPushed) { if (heroPos <= HERO_POSITION_RUN_LOWER_2) heroPos = HERO_POSITION_JUMP_1;
buttonPushed = false;
} if (drawHero(heroPos, terrainUpper, terrainLower, distance >> 3)) { playing = false; // The hero collided with something. Too bad.
} else { if (heroPos == HERO_POSITION_RUN_LOWER_2 || heroPos == HERO_POSITION_JUMP_8) {
heroPos = HERO_POSITION_RUN_LOWER_1;
} else if ((heroPos >= HERO_POSITION_JUMP_3 && heroPos <= HERO_POSITION_JUMP_5) && terrainLower[HERO_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY) {
heroPos = HERO_POSITION_RUN_UPPER_1; } else if (heroPos >= HERO_POSITION_RUN_UPPER_1 && terrainLower[HERO_HORIZONTAL_POSITION] == SPRITE_TERRAIN_EMPTY) {
heroPos = HERO_POSITION_JUMP_5; } else if (heroPos == HERO_POSITION_RUN_UPPER_2) {

heroPos = HERO_POSITION_RUN_UPPER_1;
} else { ++heroPos; } ++distance; digitalWrite(PIN_AUTOPLAY, terrainLower[HERO_HORIZONTAL_POSITION + 2] == SPRITE_TERRAIN_EMPTY ? HIGH : LOW); }
delay(100); }

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

هذا السطر يستدعي مكتبة الشاشة الكرستالية.

نستطيع تحميلها بتتبع المسار التالي:

Sketch > Include libraries > Manage libraries

ثم نكتب بخانة البحث Liquid crystal by Arduino

ثم نضغط على Install.

#include <LiquidCrystal.h>

هذا السطر يوضح منفذ الاردوينو الذي ستستخدمه لربط مفتاح التحكم في هذا المشروع.

#define PIN_BUTTON 2

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

#define SPRITE_RUN1 1
#define SPRITE_RUN2 2
#define SPRITE_JUMP 3
#define SPRITE_JUMP_UPPER '.' // Use the '.' character for the head
#define SPRITE_JUMP_LOWER 4
#define SPRITE_TERRAIN_EMPTY ' ' // User the ' ' character
#define SPRITE_TERRAIN_SOLID 5
#define SPRITE_TERRAIN_SOLID_RIGHT 6
#define SPRITE_TERRAIN_SOLID_LEFT 7

هذه الأسطر تعرّف المتغيرات التي سنستخدمها لرسم الشخصية وتحديد مسارها.

#define HERO_HORIZONTAL_POSITION 1 // Horizontal position of hero on screen
#define TERRAIN_WIDTH 16
#define TERRAIN_EMPTY 0
#define TERRAIN_LOWER_BLOCK 1
#define TERRAIN_UPPER_BLOCK 2

#define HERO_POSITION_OFF 0 // Hero is invisible
#define HERO_POSITION_RUN_LOWER_1 1 // Hero is running on lower row (pose 1)
#define HERO_POSITION_RUN_LOWER_2 2 // (pose 2)

#define HERO_POSITION_JUMP_1 3 // Starting a jump
#define HERO_POSITION_JUMP_2 4 // Half-way up
#define HERO_POSITION_JUMP_3 5 // Jump is on upper row
#define HERO_POSITION_JUMP_4 6 // Jump is on upper row
#define HERO_POSITION_JUMP_5 7 // Jump is on upper row
#define HERO_POSITION_JUMP_6 8 // Jump is on upper row
#define HERO_POSITION_JUMP_7 9 // Half-way down
#define HERO_POSITION_JUMP_8 10 // About to land

#define HERO_POSITION_RUN_UPPER_1 11 // Hero is running on upper row (pose 1)
#define HERO_POSITION_RUN_UPPER_2 12 // (pose 2)

بعد ذلك عرّفنا المتغيرات الخاصة بالشاشة الكرستالية.

LiquidCrystal lcd(11, 9, 6, 5, 4, 3);

في دالة ()void initializeGraphics يتم توضيح الرسومات المستخدمة لرسم العقبات في كل اتجاه.

void initializeGraphics(){
static byte graphics[] = {
// Run position 1
B01100,
B01100,
B00000,
B01110,
B11100,
B01100,
B11010,
B10011,
// Run position 2
B01100,
B01100,
B00000,
B01100,
B01100,
B01100,
B01100,
B01110,
// Jump
B01100,
B01100,
B00000,
B11110,
B01101,
B11111,
B10000,
B00000,
// Jump lower
B11110,
B01101,
B11111,
B10000,
B00000,
B00000,
B00000,
B00000,
// Ground
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
// Ground right
B00011,
B00011,
B00011,
B00011,
B00011,
B00011,
B00011,
B00011,
// Ground left
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
B11000,
};

هذه الأسطر تجعل العقبات تظهر بشكل مستمر طوال اللعبة.

int i;
// Skip using character 0, this allows lcd.print() to be used to
// quickly draw multiple characters
for (i = 0; i < 7; ++i) {
lcd.createChar(i + 1, &graphics[i * 8]);
}
for (i = 0; i < TERRAIN_WIDTH; ++i) {
terrainUpper[i] = SPRITE_TERRAIN_EMPTY;
terrainLower[i] = SPRITE_TERRAIN_EMPTY;
}
}

هنا يتم التحكم بحجم العقبات فحجمها ليس ثابتًا في اللعبة.

void advanceTerrain(char* terrain, byte newTerrain){
for (int i = 0; i < TERRAIN_WIDTH; ++i) {
char current = terrain[i];
char next = (i == TERRAIN_WIDTH-1) ? newTerrain : terrain[i+1];
switch (current){
case SPRITE_TERRAIN_EMPTY:
terrain[i] = (next == SPRITE_TERRAIN_SOLID) ? SPRITE_TERRAIN_SOLID_RIGHT : SPRITE_TERRAIN_EMPTY;
break;
case SPRITE_TERRAIN_SOLID:
terrain[i] = (next == SPRITE_TERRAIN_EMPTY) ? SPRITE_TERRAIN_SOLID_LEFT : SPRITE_TERRAIN_SOLID;
break;
case SPRITE_TERRAIN_SOLID_RIGHT:
terrain[i] = SPRITE_TERRAIN_SOLID;
break;
case SPRITE_TERRAIN_SOLID_LEFT:
terrain[i] = SPRITE_TERRAIN_EMPTY;
break;
}
}
}

هذه الأسطر توضح طريقة رسم الشخصية الرئيسية وطريقة تحركها للأعلى أو للأسفل.

bool drawHero(byte position, char* terrainUpper, char* terrainLower, unsigned int score) {
bool collide = false;
char upperSave = terrainUpper[HERO_HORIZONTAL_POSITION];
char lowerSave = terrainLower[HERO_HORIZONTAL_POSITION];
byte upper, lower;
switch (position) {
case HERO_POSITION_OFF:
upper = lower = SPRITE_TERRAIN_EMPTY;
break;
case HERO_POSITION_RUN_LOWER_1:
upper = SPRITE_TERRAIN_EMPTY;
lower = SPRITE_RUN1;
break;
case HERO_POSITION_RUN_LOWER_2:
upper = SPRITE_TERRAIN_EMPTY;
lower = SPRITE_RUN2;
break;
case HERO_POSITION_JUMP_1:
case HERO_POSITION_JUMP_8:
upper = SPRITE_TERRAIN_EMPTY;
lower = SPRITE_JUMP;
break;
case HERO_POSITION_JUMP_2:
case HERO_POSITION_JUMP_7:
upper = SPRITE_JUMP_UPPER;
lower = SPRITE_JUMP_LOWER;

break;
case HERO_POSITION_JUMP_3:
case HERO_POSITION_JUMP_4:
case HERO_POSITION_JUMP_5:
case HERO_POSITION_JUMP_6:
upper = SPRITE_JUMP;
lower = SPRITE_TERRAIN_EMPTY;
break;
case HERO_POSITION_RUN_UPPER_1:
upper = SPRITE_RUN1;
lower = SPRITE_TERRAIN_EMPTY;
break;
case HERO_POSITION_RUN_UPPER_2:
upper = SPRITE_RUN2;
lower = SPRITE_TERRAIN_EMPTY;
break;
}

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

سيكون هناك عدّاد (نقاط) يعمل بشكل تصاعدي على الشاشة.

// Draw the scene
terrainUpper[TERRAIN_WIDTH] = '\0';
terrainLower[TERRAIN_WIDTH] = '\0';
char temp = terrainUpper[16-digits];
terrainUpper[16-digits] = '\0';
lcd.setCursor(0,0);
lcd.print(terrainUpper);
terrainUpper[16-digits] = temp;
lcd.setCursor(0,1);
lcd.print(terrainLower);
lcd.setCursor(16 - digits,0);
lcd.print(score);
terrainUpper[HERO_HORIZONTAL_POSITION] = upperSave;
terrainLower[HERO_HORIZONTAL_POSITION] = lowerSave;
return collide;
}

في حالة الضغط على الزر سيتم تحريك الشخصية للأعلى لتفادي العقبات.

// Handle the button push as an interrupt
void buttonPush() {
buttonPushed = true;
}

في الدالة ()void setup تتم تهيئة العقبات والشخصية على الشاشة.

وربط الزر مع حركة الشخصية.

void setup(){
pinMode(PIN_READWRITE, OUTPUT);
digitalWrite(PIN_READWRITE, LOW);
pinMode(PIN_CONTRAST, OUTPUT);
digitalWrite(PIN_CONTRAST, LOW);
pinMode(PIN_BUTTON, INPUT);
digitalWrite(PIN_BUTTON, HIGH);
pinMode(PIN_AUTOPLAY, OUTPUT);
digitalWrite(PIN_AUTOPLAY, HIGH);
// Digital pin 2 maps to interrupt 0
attachInterrupt(0/*PIN_BUTTON*/, buttonPush, FALLING);
initializeGraphics();
lcd.begin(16, 2);
}

في الدالة ()void loop يتم عرض العقبات على الشاشة وتحديد مدة زمنية لكل عقبة بعدها تختفي وتظهر في مكان آخر بحجم آخر.

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

وسيكون هناك عدّاد (نقاط) يعمل بشكل تصاعدي على يمين الشاشة إذا حدث تلامس بين الشخصية والعقبات سيتوقف العدّاد وتنتهي اللعبة.

يمكنك البدء من جديد من خلال الضغط على زر التحكم.

void loop(){
static byte heroPos = HERO_POSITION_RUN_LOWER_1;
static byte newTerrainType = TERRAIN_EMPTY;
static byte newTerrainDuration = 1;
static bool playing = false;
static bool blink = false;
static unsigned int distance = 0;
if (!playing) {
drawHero((blink) ? HERO_POSITION_OFF : heroPos, terrainUpper, terrainLower, distance >> 3);
if (blink) {
lcd.setCursor(0,0);
lcd.print("Press Start");
}
delay(250);
blink = !blink;
if (buttonPushed) {
initializeGraphics();
heroPos = HERO_POSITION_RUN_LOWER_1;
playing = true;
buttonPushed = false;
distance = 0;
}
return;
}
// Shift the terrain to the left
advanceTerrain(terrainLower, newTerrainType == TERRAIN_LOWER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
advanceTerrain(terrainUpper, newTerrainType == TERRAIN_UPPER_BLOCK ? SPRITE_TERRAIN_SOLID : SPRITE_TERRAIN_EMPTY);
// Make new terrain to enter on the right
if (--newTerrainDuration == 0) {
if (newTerrainType == TERRAIN_EMPTY) {
newTerrainType = (random(3) == 0) ? TERRAIN_UPPER_BLOCK : TERRAIN_LOWER_BLOCK;
newTerrainDuration = 2 + random(10);
} else {
newTerrainType = TERRAIN_EMPTY;
newTerrainDuration = 10 + random(10);
}
}
if (buttonPushed) {
if (heroPos <= HERO_POSITION_RUN_LOWER_2) heroPos = HERO_POSITION_JUMP_1;
buttonPushed = false;
}
if (drawHero(heroPos, terrainUpper, terrainLower, distance >> 3)) {
playing = false; // The hero collided with something. Too bad.
} else {
if (heroPos == HERO_POSITION_RUN_LOWER_2 || heroPos == HERO_POSITION_JUMP_8) {
heroPos = HERO_POSITION_RUN_LOWER_1;
} else if ((heroPos >= HERO_POSITION_JUMP_3 && heroPos <= HERO_POSITION_JUMP_5) && terrainLower[HERO_HORIZONTAL_POSITION] != SPRITE_TERRAIN_EMPTY) {
heroPos = HERO_POSITION_RUN_UPPER_1;
} else if (heroPos >= HERO_POSITION_RUN_UPPER_1 && terrainLower[HERO_HORIZONTAL_POSITION] == SPRITE_TERRAIN_EMPTY) {
heroPos = HERO_POSITION_JUMP_5;

} else if (heroPos == HERO_POSITION_RUN_UPPER_2) {

heroPos = HERO_POSITION_RUN_UPPER_1;
} else {

++heroPos;

}

++distance;
digitalWrite(PIN_AUTOPLAY, terrainLower[HERO_HORIZONTAL_POSITION + 2] == SPRITE_TERRAIN_EMPTY ? HIGH : LOW);
}

delay(100);
}




التحكم بالحاسوب بحركة اليد بدلًا من الفأرة باستخدام الاردوينو

مقدمة

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

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

arduino-python-gesture-control

المواد والأدوات

التحكم بالحاسوب بحركات اليد

اردوينو اونو

التحكم بالحاسوب بحركات اليد

1× سلك الاردوينو

التحكم بالحاسوب بحركات اليد

حزمة أسلاك توصيل (ذكر – أنثى)

التحكم بالحاسوب بحركات اليد

حساس المسافة (HC-SR04)

التحكم بالحاسوب بحركات اليد

1× شريط لاصق ذو وجهين

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

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

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

arduino-python-gesture-control

تثبيت القطع على جهاز الحاسوب

قم بقص الشريط اللاصق لثلاث قطع صغيرة وثبتها على جهاز الحاسوب كما هو واضح بالصورة:

التحكم بالحاسوب بحركات اليد

ثبت الدائرة الكهربائية كما هو موضح:

التحكم بالحاسوب بحركات اليد

الكود البرمجي للتحقق من قراءة المدخلات

في البداية سنقوم برفع كود برمجي لمحاكاة مشروع التحكم بالحاسوب بحركات اليد بدلًا من الفأرة والتحقق من أن الحساس يعطي قراءات صحيحة، ويتم طباعة الأوامر على شاشة الاتصال التسلسلي

ارفع الكود التالي إلى لوحة الاردوينو عن طريق (IDE) وافتح شاشة الاتصال التسلسلي.

const int trigger1 = 2; //Trigger pin of 1st Sesnor
const int echo1 = 3; //Echo pin of 1st Sesnor
const int trigger2 = 4; //Trigger pin of 2nd Sesnor
const int echo2 = 5;//Echo pin of 2nd Sesnor long time_taken;
int dist,distL,distR; void setup() { Serial.begin(9600);
pinMode(trigger1, OUTPUT);  pinMode(echo1, INPUT); 
pinMode(trigger2, OUTPUT); 
pinMode(echo2, INPUT); 
} /*###Function to calculate distance###*/ void calculate_distance(int trigger, int echo) { digitalWrite(trigger, LOW); delayMicroseconds(2);
digitalWrite(trigger, HIGH);
delayMicroseconds(10);
digitalWrite(trigger, LOW); time_taken = pulseIn(echo, HIGH);
dist= time_taken*0.034/2;
if (dist>50)
dist = 50; } void loop() { //infinite loopy calculate_distance(trigger1,echo1); distL =dist; //get distance of left sensor
calculate_distance(trigger2,echo2);
distR =dist; //get distance of right sensor //Uncomment for debudding
/*Serial.print("L=");
Serial.println(distL); Serial.print("R="); Serial.println(distR);
*/ //Pause Modes -Hold
if ((distL >40 && distR>40) && (distL <50 && distR<50)) //Detect both hands
{Serial.println("Play/Pause"); delay (500);} calculate_distance(trigger1,echo1);
distL =dist;
calculate_distance(trigger2,echo2);
distR =dist; if ((distL >40 && distL<50) && (distR ==50)) //Detect Left Hand {Serial.println("Rewind"); delay (500);} if ((distR >40 && distR<50) && (distL ==50)) //Detect Right Hand {Serial.println("Forward"); delay (500);} //Control Modes
//Lock Left - Control Mode if (distL>=13 && distL<=17) {
  delay(100); //Hand Hold Time   calculate_distance(trigger1,echo1);
distL =dist;   if (distL>=13 && distL<=17) {     Serial.println("Left Locked");     while(distL<=40)     {
      calculate_distance(trigger1,echo1);
      distL =dist;
      if (distL<10) //Hand pushed in        {Serial.println ("Vup"); delay (300);}       if (distL>20) //Hand pulled out
      {Serial.println ("Vdown"); delay (300);} }
} } //Lock Right - Control Mode if (distR>=13 && distR<=17) {   delay(100); //Hand Hold Time   calculate_distance(trigger2,echo2);   distR =dist;
  if (distR>=13 && distR<=17)
{     Serial.println("Right Locked");
  while(distR<=40)
{       calculate_distance(trigger2,echo2);       distR =dist;       if (distR<10) //Right hand pushed in       {Serial.println ("Rewind"); delay (300);}       if (distR>20) //Right hand pulled out
      {Serial.println ("Forward"); delay (300);} }
}
} delay(200); }

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

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

المخرج للحساس الأول (trigger1) والمدخل للحساس الأول (echo1).

المخرج للحساس الثاني (trigger2) والمدخل للحساس الثاني (echo2).

const int trigger1 = 2; //Trigger pin of 1st Sesnor
const int echo1 = 3; //Echo pin of 1st Sesnor
const int trigger2 = 4; //Trigger pin of 2nd Sesnor
const int echo2 = 5;//Echo pin of 2nd Sesnor long time_taken;
int dist,distL,distR; void setup() { Serial.begin(9600); 
pinMode(trigger1, OUTPUT);  pinMode(echo1, INPUT); 
pinMode(trigger2, OUTPUT); 
pinMode(echo2, INPUT); 
}.

عند تشغيل أي مقطع فيديو على مشغل الفيديو (VLC) أو اليوتيوب سيتم حساب المسافة بين كلا حساسي الموجات فوق الصوتية واليدين في كل مرة من خلال الدالة ()calculate_distance، وسيكون هناك عدة اجراءات بناء على تلك القراءات.

/*###Function to calculate distance###*/
void calculate_distance(int trigger, int echo)
{
digitalWrite(trigger, LOW);
delayMicroseconds(2);
digitalWrite(trigger, HIGH);
delayMicroseconds(10);
digitalWrite(trigger, LOW); time_taken = pulseIn(echo, HIGH);
dist= time_taken*0.034/2;
if (dist>50)
dist = 50; }

داخل دالة ()Loop، يتم التحقق من قيمة المسافة وعلى أساسها يتم الاختيار من بين الاجراءات.

نستخدم المتغير distL ويشير إلى مقدار المسافة بين حساس الموجات الصوتية و اليد الموجود باليسار

نستخدم المتغير distR ويشير إلى مقدار المسافة بين حساس الموجات الصوتية و اليد الموجود باليمين

calculate_distance(trigger1,echo1);
distL =dist; //get distance of left sensor

calculate_distance(trigger2,echo2);
distR =dist; //get distance of right sensor

بناء على المسافة (عند وضع كلتا اليدين على الحساسين في نفس الوقت وكانت المسافة أكبر من 40 وأصغر من 50) سيتوقف الفيديو وعند الرغبة بتشغيل الفيديو مرة أخرى يمكنك إرجاع كلتا يديك لكلا الحساسين.

if ((distL >40 && distR>40) && (distL <50 && distR<50)) //Detect both hands
{Serial.println("Play/Pause"); delay (500);}

بناء على المسافة (المسافة بين الحساس الأيسر واليد اليسرى أكبر من 40 وأقل من 50 والمسافة بين الحساس الأيمن واليد اليمنى تساوي 50)  سيتم ترجيع الفيديو للخلف.

بناء على المسافة (المسافة بين الحساس الأيمن واليد اليمنى أكبر من 40 وأقل من 50 والمسافة بين الحساس الأيسر واليد اليسرى تساوي 50)  سيتم تقديم الفيديو للامام.

if ((distL >40 && distL<50) && (distR ==50)) //Detect Left Hand
{Serial.println("Rewind"); delay (500);}

if ((distR >40 && distR<50) && (distL ==50)) //Detect Right Hand
{Serial.println("Forward"); delay (500);}

إذا كانت المسافة (المسافة بين الحساس الأيسر ويدك اليسرى أقل من 10) سيزيد حجم الصوت.

إذا كانت المسافة (المسافة بين الحساس الأيسر ويدك اليسرى أكبر من 20)  سينقص حجم الصوت.

//Lock Left - Control Mode
if (distL>=13 && distL<=17)
{
  delay(100); //Hand Hold Time
  calculate_distance(trigger1,echo1);
  distL =dist;
  if (distL>=13 && distL<=17)
  {
    Serial.println("Left Locked");
    while(distL<=40)
    {
      calculate_distance(trigger1,echo1);
      distL =dist;
      if (distL<10) //Hand pushed in 
      {Serial.println ("Vup"); delay (300);}
      if (distL>20) //Hand pulled out
      {Serial.println ("Vdown"); delay (300);}
    }
  }
}

إذا كانت المسافة (المسافة بين الحساس الأيمن ويدك اليمنى أقل من 10) سيتم ترجيع الفيديو للخلف.

إذا كانت المسافة (المسافة بين الحساس الأيمن ويدك اليمنى أكبر من 20)  سيتم تقديم الفيديو للأمام.

//Lock Right - Control Mode
if (distR>=13 && distR<=17)
{
  delay(100); //Hand Hold Time
  calculate_distance(trigger2,echo2);
 distR =dist;
if (distR>=13 && distR<=17)
{
Serial.println("Right Locked");
    while(distR<=40)
{
calculate_distance(trigger2,echo2);
 distR =dist;
 if (distR<10) //Right hand pushed in
 {Serial.println ("Rewind"); delay (300);}
 if (distR>20) //Right hand pulled out
 {Serial.println ("Forward"); delay (300);}
 }
}
}
delay(200);
}

تنصيب (Python IDLE) و (Pyserial)

1- تنصيب (Python IDLE)

تثبيت البايثون على نظام ويندوز سوا كان نظام تشغيل 32 بت أو 64 بت هو نفسه.

 انقر على الرابط التالي Python 3.9.2 وثبت البرنامج على جهاز الحاسوب.

حمّل نسخة 32 بت دائمًا مهما كان نظام التشغيل.

التحكم بالحاسوب

افتح ملف exe الذي تم تنزيله واتبع التعليمات لا تقم بتغيير المسار الذي يتم فيه تثبيت Python.

سيكون المسار C:\Program Files (x86)\Python39-32 بشكل افتراضي اتركه كما هو عليه.

التحكم بالحاسوب بحركات اليد

2- ضبط إعدادات لوحة الأوامر

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

علينا اضافة مترجم اوامر البايثون إلى امتدادات الأوامر – PATH environment variable.
عليك الذهاب للوحة التحكم بويندوز واتباع المسار التالي:
Control Panel\System and Security\System
اختر من القائمة Advanced system setting:

التحكم بالحاسوب بحركات اليد

انقر على Environment variable:

التحكم بالحاسوب بحركات اليد

انقر على Path من قائمة System Variables.

arduino-python-gesture-control

انقر على New وأضف المسار التالي (أو المسار الذي قمت بتحميل البرنامج عليه):
C:\Program Files (x86)\Python39-32

 انقر على New مرة أخرى وضع مسار مجلد Scripts:

C:\Program Files (x86)\Python39-32\Scripts

التحكم بالحاسوب

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

3- تنصيب (Pyserial)

بعد ذلك علينا تنصيب Pyserial انقر على الرابط التالي وحمله على حاسوبك Pyserial 3.5 

التحكم بالحاسوب عن طريق التلويح

بعد تحميله فك الضغط عنه وانسخ المسار الذي يتواجد فيه ملف Pyserial.

انقر بالسهم اليمين على Command Prompt واختر من القائمة Run as administrator.

arduino-python-gesture-control

اكتب الأمر التالي وضع المسار الذي قمت بتحميل Pyserial فيه:

cd (Pyserial المسار لبرنامج)

حمل setup.py.

python setup.py install
4- تنصيب مكتبة (Pyautogui)

تركيب وحدة Pyautogui للويندوز:

تأكد من أن جهاز الحاسوب متصل بالإنترنت واتبع الخطوات أدناه:

الخطوة 1: افتح صفحة الأوامر بويندوز Windows prompt وقم بتغيير المسار إلى المجلد الذي قمت بتثبيت به البايثون.

cd C:\Program Files (x86)\Python39-32

الخطوة 2: داخل مسار بايثون، سنقوم بترقية Pip وهي أداة في البايثون تساعدنا على تثبيت وحدات البايثون بسهولة استخدم الأمر التالي لترقيتها.

pip install –-upgrade pip

 الخطوة 3: ثبت وحدة Pyautogui.

pip install pyautogui

  الكود البرمجي (بايثون)

افتح برنامج IDLE (Python 3.9 32-bit) من قائمة File اختر New File والصق الكود البرمجي التالي.

التحكم بالحاسوب بحركات اليد

ارفع الكود البرمجي للوحة الاردوينو من قائمة Run انقر على Run module.

import serial #Serial imported for Serial communication
import time #Required to use delay functions
import pyautogui
ArduinoSerial = serial.Serial('com18',9600) #Create Serial port object called arduinoSerialData
time.sleep(2) #wait for 2 seconds for the communication to get established
while 1:
    incoming = str (ArduinoSerial.readline()) #read the serial data and print it as line
    print (incoming)
    if 'Play/Pause' in incoming:
        pyautogui.typewrite(['space'], 0.2)
    if 'Rewind' in incoming:
        pyautogui.hotkey('ctrl', 'left')  
    if 'Forward' in incoming:
        pyautogui.hotkey('ctrl', 'right') 
    if 'Vup' in incoming:         pyautogui.hotkey('ctrl', 'down')     if 'Vdown' in incoming:       pyautogui.hotkey('ctrl', 'up')
  incoming = "";

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

افتح صفحة جديدة في IDLE Python وقم باستدعاء المكتبات المطلوبة pyautogui, serial python و time.

import serial #Serial imported for Serial communication
import time #Required to use delay functions
import pyautogui

حدد المنفذ COM المستخدم في الاتصال مع الاردوينو.
(هذا سطر قابل للتغيير بناء على نوع المنفذ الذي ستستخدمه في المشروع يمكنك تعيينه عن طريق برنامج اردوينو IDE من قائمة Port).

ArduinoSerial = serial.Serial('com18',9600) #Create Serial port object called arduinoSerialData 
time.sleep(2) #wait for 2 seconds for the communication to get established

 في هذا السطر تتم قراءة المسافة بين اليدين والحساس من المنفذ التسلسلي.

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

الاختصارات المستخدمة في هذا الدرس من مشغل VLC:

Space :ايقاف/تشغيل الملف

 Ctrl +  السهم يمين أو يسار: التقديم أو الترجيع دقيقة

Ctrl + السهم للأعلى والأسفل: رفع وخفض مستوي الصوت

while 1:
    incoming = str (ArduinoSerial.readline()) #read the serial data and print it as line
    print incoming
    
    if 'Play/Pause' in incoming:
        pyautogui.typewrite(['space'], 0.2)

    if 'Rewind' in incoming:
        pyautogui.hotkey('ctrl', 'left')  

    if 'Forward' in incoming:
        pyautogui.hotkey('ctrl', 'right') 

    if 'Vup' in incoming:
        pyautogui.hotkey('ctrl', 'down')
        
    if 'Vdown' in incoming:
        pyautogui.hotkey('ctrl', 'up')

ستلاحظ كقدرتك على التحكم بالحاسوب بحركات اليد بدلًا من الفأرة باستخدام الاردوينو يمكنك اختبار صحة الخطوات والاجراءات من خلال الصفحة المنبثقة بعد النقر على Run modul ستظهر  صفحة IDLE Shell.

التحكم بالحاسوب بحركات اليد

لا تنسى إيقاف مصدر الطاقة بعد الانتهاء من استخدام النظام.

 




تسجيل الدخول لنظام ويندوز باستخدام الاردوينو و (RFID)

 مقدمة

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

arduino-unlock-computer-with-rfid

المواد والأدوات

الدخول لنظام ويندوز RFID

1× اردوينو اونو

الدخول لنظام ويندوز RFID

1× سلك الاردوينو

arduino-unlock-computer-with-rfid

حزمة أسلاك توصيل (ذكر – أنثى)

اردوينو

 أسلاك توصيل (ذكر-ذكر)

security-access-using-rfid-reader

قارئ البطاقة (RFID mfrc522)

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

وصل الدائرة كما هو موضح بالشكل:

arduino-unlock-computer-with-rfid

الدخول لنظام ويندوز RFID

لمعرفة المزيد حول بطاقة RFID يمكنك الرجوع للدرس RFID

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

في البداية عليك تثبيت المكتبة الخاصة بقارئ البطاقات RFID انقر على الرابط التالي RFID-master وحملها على جهازك.

1- قراءة رمز البطاقة

حتى تتمكن من تسجيل الدخول لنظام ويندوز باستخدام الاردوينو و (RFID) ارفع الكود البرمجي التالي على لوحة الاردوينو.

#include <SPI.h>
#include <MFRC522.h> // RFID library
#define SS_PIN 10 //RX slave select
#define RST_PIN 9
int gled = 7; // optional
int rled = 4;// optional
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance.
String card_ID=""; //
String password="" ; // Change It To Your Windows / fb / any Account's Password String rfid="";// UID (unique Id Code Of Your Rfid Tag) void setup() { Serial.begin(9600); // Initialize serial communications with the PC SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card
pinMode(gled,OUTPUT);
pinMode(rled,OUTPUT);
} void loop() { //look for new card if ( ! mfrc522.PICC_IsNewCardPresent()) { return; }
if ( ! mfrc522.PICC_ReadCardSerial()) { return;//if read card serial(0) returns 1, the uid struct contians the ID of the read card. }
for (byte i = 0; i < mfrc522.uid.size; i++) { card_ID += mfrc522.uid.uidByte[i]; }
// Serial.println(card_ID); Serial.print(card_ID); delay(1000); card_ID=""; }

افتح شاشة الاتصال التسلسلي وضع البطاقة على قارئ البطاقات.

انسخ رمز البطاقة واحفظه في ملف ستحتاج إليه لاحقًا.

الدخول لنظام ويندوز RFID

2- رفع الكود البرمجي لنظام

قبل رفع الكود البرمجي إلى لوحة الاردوينو هناك أمور مهمة عليك العمل بها، اقرأ شرح الكود البرمجي.

uint8_t buf[8] = { 0 };
#include <SPI.h>
#include <MFRC522.h> // RFID library
#define SS_PIN 10 //RX slave select
#define RST_PIN 9
int gled = 7; // optional
int rled = 4;// optional
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance.
String card_ID=""; //
String password="" ; // Change It To Your Windows / fb / any Account's Password
String rfid="";// UID (unique Id Code Of Your Rfid Tag) void setup() { Serial.begin(9600); // Initialize serial communications with the PC
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card
pinMode(gled,OUTPUT);
pinMode(rled,OUTPUT);
}
void loop() { //look for new card if ( ! mfrc522.PICC_IsNewCardPresent()) { return; } if ( ! mfrc522.PICC_ReadCardSerial()) {
return;//if read card serial(0) returns 1, the uid struct contains the ID of the read card.
} for (byte i = 0; i < mfrc522.uid.size; i++) {
card_ID += mfrc522.uid.uidByte[i];
} // Serial.println(card_ID);
if(card_ID==rfid){digitalWrite(gled,HIGH);
typeLiteralString(password);
pressKey("enter"); releaseKey("enter");
digitalWrite(gled,LOW); delay(200);digitalWrite(gled,HIGH); delay(200);digitalWrite(gled,LOW);
} if(card_ID!=password){
digitalWrite(rled,HIGH); digitalWrite(rled,LOW); delay(200);digitalWrite(rled,HIGH); delay(200);digitalWrite(rled,LOW); } else{ goto cont;}
delay(1000);
cont:
delay(1000);
card_ID=""; } boolean isModifier(int keycode) {
boolean result = false;
if (keycode >= 224 && keycode <= 231) { // if the keycode is a modifier key
result = true;
} return result; } void pressModifier(String keyname) {
pressModifier(getKeycode(keyname));
} void pressModifier(int keycode) {
int modifiermask = 0;
if (isModifier(keycode)) { // if the keycode represents a modifier key modifiermask = getModifierMask(keycode);
buf[0] = buf[0] | modifiermask;
Serial.write(buf, 8); // Send key report
} } void releaseModifier(String keyname) {
releaseModifier(getKeycode(keyname));
} void releaseModifier(int keycode) {
int modifiermask = 0;
if (isModifier(keycode)) { // if the keycode represents a modifier key
modifiermask = getModifierMask(keycode);
buf[0] = buf[0] & (~modifiermask);
Serial.write(buf, 8); // Send key report
} } void releaseAllModifiers() {
buf[0] = B00000000;
Serial.write(buf, 8); // Send key report
} void pressKey(String keyname) {
pressKey(getKeycode(keyname));
} void pressKey(int keycode) { // TODO: cycle the 6 key spots in the report buffer instead of just using buf[2] each time. buf[2] = keycode;
Serial.write(buf, 8); // Send key report
} void releaseKey(String keyname) {
releaseKey(getKeycode(keyname));
} void releaseKey(int keycode) {
// find the keycode in the report buffer, then set it to zero.
int i=0;
for (i=2; i<8; i++) {
if (buf[i] == keycode) {
buf[i] = 0;
} } Serial.write(buf, 8); // Send key report
} void releaseAllKeys() {
int i=0;
for (i=2; i<8; i++) {
buf[i] = 0;
} Serial.write(buf, 8); // Send key report
} void pressSequenceOfKeys(const char * keySequence[], int numberOfKeys) {
// This function can be good for pressing a few keys while holding a modifier down for example.
int i = 0; for (i=0; i<numberOfKeys; i++) {
pressKey(keySequence[i]);
releaseKey(keySequence[i]);
} } void typeLiteralString(String string) {
char charArray[string.length()+1];
string.toCharArray(charArray, string.length()+1);
typeLiteralString(charArray, string.length());
} void typeLiteralString(char string[], int stringLength) { // stringLength is the length of the printable string without considering the null byte.
// This function will type the given string exactly as given, automatically pressing left_shift where necessary for capitals and symbols. // just in case: releaseAllKeys(); releaseAllModifiers(); boolean charNeedsShift = false; boolean shiftIsPressed = false; int i=0;
for (i=0; i<stringLength; i++) {
charNeedsShift = characterNeedsShift(string[i]);
if (charNeedsShift && !shiftIsPressed) {
pressModifier("left_shift");
shiftIsPressed = true;
} else if (!charNeedsShift && shiftIsPressed) {
releaseModifier("left_shift");
shiftIsPressed = false;
} pressKey(String(string[i])); // without converting the char in string[i] to a String, arduino would prefer the pressKey(int) function instead of the pressKey(String) function, casting the char to a keycode (int) instead of a keyname (String).
releaseKey(String(string[i])); // same as previous comment, but with releaseKey(). } releaseAllModifiers(); } boolean characterNeedsShift(char character) { int needsModifier = false; if ( // look up an ascii table and this will make sense.
(character >= 33 && character <= 38)
|| (character >= 40 && character <= 43)
|| (character == 58)
|| (character == 60)
|| (character >= 62 && character <= 90)
|| (character >= 94 && character <= 95)
|| (character >= 123 && character <= 126)
) { needsModifier = true;
} return needsModifier; } int getKeycode(String keyname) { String key = String(keyname); // Use a copy so that we don't mutate the user's String. Not sure if this is needed, but just in case. TODO: find out.
key.toLowerCase(); int keycode = 0; // keycode of zero means nothing pressed. // non-modifier keys
if (key == "a") { keycode = 4; }
else if (key == "b") { keycode = 5; }
else if (key == "c") { keycode = 6; }
else if (key == "d") { keycode = 7; }
else if (key == "e") { keycode = 8; }
else if (key == "f") { keycode = 9; }
else if (key == "g") { keycode = 10; }
else if (key == "h") { keycode = 11; }
else if (key == "i") { keycode = 12; }
else if (key == "j") { keycode = 13; }
else if (key == "k") { keycode = 14; }
else if (key == "l") { keycode = 15; }
else if (key == "m") { keycode = 16; }
else if (key == "n") { keycode = 17; } else if (key == "o") { keycode = 18; } else if (key == "p") { keycode = 19; }
else if (key == "q") { keycode = 20; }
else if (key == "r") { keycode = 21; }
else if (key == "s") { keycode = 22; }
else if (key == "t") { keycode = 23; }
else if (key == "u") { keycode = 24; }
else if (key == "v") { keycode = 25; } else if (key == "w") { keycode = 26; }
else if (key == "x") { keycode = 27; }
else if (key == "y") { keycode = 28; }
else if (key == "z") { keycode = 29; } else if (key == "1" || key == "!") { keycode = 30; }
else if (key == "2" || key == "@") { keycode = 31; }
else if (key == "3" || key == "#") { keycode = 32; } else if (key == "4" || key == "$") { keycode = 33; }
else if (key == "5" || key == "%") { keycode = 34; }
else if (key == "6" || key == "^") { keycode = 35; }
else if (key == "7" || key == "&") { keycode = 36; }
else if (key == "8" || key == "*") { keycode = 37; } else if (key == "9" || key == "(") { keycode = 38; } else if (key == "0" || key == ")") { keycode = 39; }
else if (key == "enter" || key == "return") { keycode = 40; }
else if (key == "escape" || key == "") { keycode = 41; }
else if (key == "backspace" || key == "") { keycode = 42; }
else if (key == "tab" || key == " ") { keycode = 43; }
else if (key == "space" || key == " ") { keycode = 44; } else if (key == "-" || key == "_") { keycode = 45; }
else if (key == "=" || key == "+") { keycode = 46; }
else if (key == "[" || key == "{") { keycode = 47; }
else if (key == "]" || key == "}") { keycode = 48; }
else if (key == "\\" || key == "|") { keycode = 49; } else if (key == ";" || key == ":") { keycode = 51; }
else if (key == "'" || key == "\"") { keycode = 52; } else if (key == "`" || key == "~") { keycode = 53; }
else if (key == "," || key == "<") { keycode = 54; }
else if (key == "." || key == ">") { keycode = 55; }
else if (key == "/" || key == "?") { keycode = 56; } // TODO: Fix these keycodes. V
else if (key == "capslock") { keycode = 58; } else if (key == "f1") { keycode = 59; }
else if (key == "f2") { keycode = 60; }
else if (key == "f3") { keycode = 61; }
else if (key == "f4") { keycode = 62; }
else if (key == "f5") { keycode = 63; }
else if (key == "f6") { keycode = 64; }
else if (key == "f7") { keycode = 65; }
else if (key == "f8") { keycode = 66; }
else if (key == "f9") { keycode = 67; }
else if (key == "f10") { keycode = 68; } else if (key == "f11") { keycode = 69; }
else if (key == "f12") { keycode = 70; } else if (key == "print_screen") { keycode = 70; }
else if (key == "scroll_lock") { keycode = 71; }
else if (key == "pause") { keycode = 72; } else if (key == "insert") { keycode = 73; }
else if (key == "home") { keycode = 74; } else if (key == "page_up") { keycode = 75; }
else if (key == "delete") { keycode = 76; }
else if (key == "end") { keycode = 77; }
else if (key == "page_down") { keycode = 78; } else if (key == "right_arrow") { keycode = 79; }
else if (key == "left_arrow") { keycode = 80; }
else if (key == "down_arrow") { keycode = 81; }
else if (key == "up_arrow") { keycode = 82; } else if (key == "numlock" || key == "clear") { keycode = 83; } //TODO: keypad and miscellaneous keys if you want them. // modifier keys. else if (key == "left_control") { keycode = 224; }
else if (key == "left_shift") { keycode = 225; }
else if (key == "left_alt") { keycode = 226; }
else if (key == "left_gui") { keycode = 227; } else if (key == "right_control") { keycode = 228; }
else if (key == "right_shift") { keycode = 229; }
else if (key == "right_alt") { keycode = 230; }
else if (key == "right_gui") { keycode = 231; } return keycode; }
int getModifierMask(String keyname) { return getModifierMask(getKeycode(keyname));
}
int getModifierMask(int keycode) { // return value of 0 means key is not a modifier.
int modifiermask = 0; // NOTE: these are not the usage keycodes like for other keys, but rather the bit masks.
if (keycode == 224) { modifiermask = B00000001; } // left ctrl
else if (keycode == 225) { modifiermask = B00000010; } // left shift
else if (keycode == 226) { modifiermask = B00000100; } // left alt
else if (keycode == 227) { modifiermask = B00001000; } // left gui
else if (keycode == 228) { modifiermask = B00010000; } // right ctrl
else if (keycode == 229) { modifiermask = B00100000; } // right shift
else if (keycode == 230) { modifiermask = B01000000; } // right alt
else if (keycode == 231) { modifiermask = B10000000; } // right gui return modifiermask;
}

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

في هذا السطر نستدعي مكتبة قارئ البطاقة RFID ومكتبة الاتصال المتزامن.

#include <SPI.h>
#include <MFRC522.h> // RFID library

هذه الأسطر توضح منافذ الاردوينو التي ستستخدمها للربط في هذا المشروع.

#define SS_PIN 10 //RX slave select
#define RST_PIN 9

بعد ذلك أعلنا عن المتغيرات اللازمة مثل المتغيرات الخاصة بقارئ البطاقة RFID.

int gled = 7; // optional
int rled = 4;// optional
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance.

هذه الأسطر حساسة ومهمة.

السطر الأول: اتركه كما هو عليه فارغ.

السطر الثاني: اكتب كلمة المرور التي تستخدمها عند تسجيل دخولك لنظام ويندوز (كن منتبهًا لحالة الأحرف).

السطر الثالث: اكتب رقم ID للبطاقة.

String card_ID=""; //
String password="12345986" ; // Change It To Your Windows / fb / any Account's Password
String rfid="215177152200";// UID (unique Id Code Of Your Rfid Tag)

في الدالة ()setup يتم انشاء جسر تواصل تسلسلي بين الحاسوب وقارئ البطاقة وتهيئة قارئ البطاقة لتسجيل القيم.

void setup() {
Serial.begin(9600); // Initialize serial communications with the PC
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card
pinMode(gled,OUTPUT);
pinMode(rled,OUTPUT);

في الدالة ()loop يستبدل الرمز السابق للبطاقة بالرمز الجديد (رمز تسجيل الدخول لنظام ويندوز).

 تتم قراءة البيانات من قارئ البطاقة ويتم ارسالها إلى الاردوينو لمعالجة البيانات المدخلة.

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

وسيعطي الصلاحية فقط لحامل البطاقة التي تحمل كلمة المرور لنظام ويندوز.

void loop() {}

يمكنك الآن رفع الكود البرمجي على لوحة الاردوينو بعد تعيين رمز البطاقة وكلمة المرور لنظام ويندوز.

وضع (DFU)

هو اختصار لكلمة (Device Firmware Update).

عند وضع الاردوينو بوضع DFU فلا يمكنك استخدام المنفذ التسلسلي USB مرة أخرى مع برنامج Arduino IDE.
وهنا يمكنك عمل ترقية باستخدام برامج أخرى مثل Flip.

بعد رفع الكود البرمجي لنظام تسجيل الدخول لنظام ويندوز باستخدام الاردوينو و RFID.

وصل سلك من GND إلى الرأس المشار إليه بالسهم لمدة ثانية أو ثانيتين ستبدأ لوحة الاردوينو بالوميض أزل السلك بعد انتهاء الوميض.

الدخول لنظام ويندوز RFID

عند الذهاب للمسار التالي:

Control panel > Hardware and Sound > Device Manager

ستلاحظ وجود جهاز جديد بهذا الاسم.

arduino-unlock-computer-with-rfid

يمكنك فصل سلك USB الموصل مع لوحة الاردوينو وإعادته مرة أخرى.

سيبدأ النظام بالعمل مباشرة يمكنك اختباره واختبار صحة خطواتك.

لا تنسَ فصل مصدر الطاقة بعد الانتهاء من استخدام النظام.

 




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

مقدمة

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

شفرة مورس

المواد والأدوات

الاردوينو يستخدم في حساس الغاز/ الدخان

1× اردوينو اونو

arduino-smoke-gas-sensor

1× سلك الاردوينو

arduino-smoke-gas-sensor

لوحة تجارب – حجم كبير

lcd

 شاشة كرستالية (LCD 2×16 )

شفرة مورس

1× مقاومة متغيرة

arduino-smoke-gas-sensor

 حزمة أسلاك توصيل (ذكر- ذكر)

arduino-unlock-computer-with-rfid

حزمة أسلاك توصيل (ذكر – أنثى)

arduino-smoke-gas-sensor

3× مقاومة 220 Ω

شفرة مورس

أزرار

شفرة

1× 40 رأس دبوس

شفرة مورس

هي نوع من الشفرات التي تستخدم في إرسال المعلومات التلغرافية باستخدام العناصر المتتابعة المكونة من النقطة والشرطة.

وتتم ترجمة شفرة مورس إلى حروف وأرقام ورموز أخرى.

arduino-morse-code-translator

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

لمعرفة المزيد حول الشاشة الكرستالية يمكنك الرجوع للدرس التحكم بالشاشة الكرستالية LCD

لابد من تلحيم المنافذ مع الشاشة الكرستالية، للمزيد حول اللحام يمكنك الرجوع للدرس تعلم كيفية التلحيم – تلحيم القطع باللوحة الإلكترونية

شفرة مورس

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

#include <LiquidCrystal.h>
#define BUTTON1PIN 6
#define BUTTON2PIN 8
#define BUTTON3PIN 7
#define DISPLAY_NUMOFCOLUMNS 16
int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
int Button1State = 0;
int Button1LastState = 0;
int Button2State = 0;
int Button2LastState = 0;
int Button3State = 0;
int Button3LastState = 0;
String tonesBuffer;
String text;
String expectedText;
String symbolsAlphabet[][2] =
{
{ ".-","A" },
{ "-...","B" },
{ "-.-.","C" },
{ "-..","D" },
{ ".","E" },
{ "..-.","F" },
{ "--.","G" },
{ "....","H" },
{ "..","I" },
{ ".---","J" },
{ "-.-","K" },
{ ".-..","L" },
{ "--","M" },
{ "-.","N" },
{ "---","O" },
{ ".--.","P" },
{ "--.-","Q" },
{ ".-.","R" },
{ "...","S" },
{ "-","T" },
{ "..-","U" },
{ "...-","V" },
{ ".--","W" },
{ "-..-","X" },
{ "-.--","Y" },
{ "--..","Z" },
{ ".----","1" },
{ "..---","2" },
{ "...--","3" },
{ "....-","4" },
{ ".....","5" },
{ "-....","6" },
{ "--...","7" },
{ "---..","8" },
{ "----.","9" },
{ "-----","0"}
};
char getToneFromButtonStates()
{
if (!Button1State && Button1LastState)
return '-';
if (!Button2State && Button2LastState)
return '.';
if (!Button3State && Button3LastState)
return ' ';
return (char)0;
}
char getSymbolFromBuffer()
{
if (tonesBuffer == "")
return ' ';
for (int i = 0; i < sizeof symbolsAlphabet / sizeof symbolsAlphabet[0]; i++)
if (tonesBuffer == symbolsAlphabet[i][0])
return symbolsAlphabet[i][1][0];
return (char)0; }
 void extractActionFromTonesBuffer() {
 if (tonesBuffer == "......")
 text.remove(text.length() - 1, 1);
 if (tonesBuffer == "------") text = ""; } 
void setup()
 { lcd.clear();
 lcd.begin(16,2);
 lcd.print("Morse");
 lcd.setCursor(0, 1); 
lcd.print("Code");
 pinMode(BUTTON1PIN, INPUT); 
pinMode(BUTTON2PIN, INPUT); 
pinMode(BUTTON3PIN, INPUT); }
 void loop() { 
Button1State = digitalRead(BUTTON1PIN);
 Button2State = digitalRead(BUTTON2PIN);
 Button3State = digitalRead(BUTTON3PIN);
 char tone = getToneFromButtonStates(); 
if (tone != (char)0) 
{ if (tone == ' ') 
{ char symbol = getSymbolFromBuffer(); 
if (symbol != (char)0) { text += symbol; 
if (text.length() > DISPLAY_NUMOFCOLUMNS) {
 text = (String)symbol; }
 } else { extractActionFromTonesBuffer(); } tonesBuffer = "";
 } else {
 tonesBuffer += tone;
 if (tonesBuffer.length() > DISPLAY_NUMOFCOLUMNS) 
{ tonesBuffer = (String)tone; 
} 
} lcd.clear();
 lcd.print(text);
 lcd.setCursor(0, 1); 
lcd.print(tonesBuffer); }
 Button1LastState = Button1State; 
Button2LastState = Button2State; 
Button3LastState = Button3State; }

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

هذا السطر يستدعي مكتبة الشاشة الكرستالية.

نستطيع تحميلها بتتبع المسار التالي:

Sketch > Include libraries > Manage libraries

ثم نكتب بخانة البحث Liquid crystal by Arduino

ثم نضغط على Install.

#include <LiquidCrystal.h>

هذه الأسطر توضح منافذ الاردوينو التي ستستخدمها للربط في هذا المشروع.

#define BUTTON1PIN 6
#define BUTTON2PIN 8
#define BUTTON3PIN 7
#define DISPLAY_NUMOFCOLUMNS 16

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

int Button1State = 0;
int Button1LastState = 0;
int Button2State = 0;
int Button2LastState = 0;
int Button3State = 0;
int Button3LastState = 0;

أيضًا أنشئنا متغيرات لتخزين التسلسل الحالي للضغطات والرمز التابع لكل ضغطة، التسلسل الحالي إما نقطة أو شرطة.

ترجمة الرموز في شفرة مورس ستكون إما رقم أو حرف أو رموز خاصة.

String tonesBuffer;
String text;
String expectedText;

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

String symbolsAlphabet[][2] =
{
{ ".-","A" },
{ "-...","B" },
{ "-.-.","C" },
{ "-..","D" },
{ ".","E" },
{ "..-.","F" },
{ "--.","G" },
{ "....","H" },
{ "..","I" },
{ ".---","J" },
{ "-.-","K" },
{ ".-..","L" },
{ "--","M" },
{ "-.","N" },
{ "---","O" },
{ ".--.","P" },
{ "--.-","Q" },
{ ".-.","R" },
{ "...","S" },
{ "-","T" },
{ "..-","U" },
{ "...-","V" },
{ ".--","W" },
{ "-..-","X" },
{ "-.--","Y" },
{ "--..","Z" },
{ ".----","1" },
{ "..---","2" },
{ "...--","3" },
{ "....-","4" },
{ ".....","5" },
{ "-....","6" },
{ "--...","7" },
{ "---..","8" },
{ "----.","9" },
{ "-----","0"}
};

في دالة ()setup، التي ستبدأ عند بدء التشغيل، تتم تهيئة الشاشة وتشغيل الإضاءة الخلفية وطباعة النص “Morse code” هنا علينا تعريف المنافذ إلى مدخلات  للاتصال الرقمي باستخدام الأزرار.

void setup() {
clear.lcd();
lcd.begin(16,2);
lcd.print("Morse");
lcd.setCursor(0, 1);
lcd.print("Code");
pinMode(BUTTON1PIN, INPUT);
pinMode(BUTTON2PIN, INPUT);
pinMode(BUTTON3PIN, INPUT);
}

تبدأ دالة ()loop بقراءة حالات الأزرار واستدعاء دالة ()getToneFromButtonStates.

void loop() {
Button1State = digitalRead(BUTTON1PIN);
Button2State = digitalRead(BUTTON2PIN);
Button3State = digitalRead(BUTTON3PIN);
char tone = getToneFromButtonStates();

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

إذا كانت تحمل قيم (ضغط المستخدم على أي زر)، يأتي تحليل آخر، يتفرع البرنامج إلى قسم “تم الضغط على النقطة” وقسم ” “تم الضغط على الشرطة”.

if (tone != (char)0) {
        if (tone == ' ')
        {
        }
        else
       {
        }
        lcd.clear();
        lcd.print(text);
        lcd.setCursor(0, 1);
        lcd.print(tonesBuffer);
}
Button1LastState = Button1State;
Button2LastState = Button2State;
Button3LastState = Button3State;

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

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

إذا لم يكن المخزن المؤقت فارغًا، يتم البحث عن ترجمة الرموز المدخلة ويتم إرجاع ترجمة الرمز الذي تم العثور عليه.

إذا كان هناك رمز ولم يتم العثور على ترجمة الرمز، فسيتم إرجاع قيمة فارغة.

char getSymbolFromBuffer()
{
if (tonesBuffer == "")
return ' ';
for (int i = 0; i < sizeof symbolsAlphabet / sizeof symbolsAlphabet[0]; i++)
if (tonesBuffer == symbolsAlphabet[0])
return symbolsAlphabet[1][0];
return (char)0;
}

في هذه الدالة سيكون هناك رموز خاصة في شفرة مورس لها تفسير معين.

عند إدخال المستخدم 6 نقط متتابعة فذلك يعني أن المستخدم يريد حذف آخر حرف تمت كتابته.

عند إدخال المستخدم 6 شرطات متتابعة فذلك يعني أن المستخدم يريد حذف كامل النص المكتوب.

void extractActionFromTonesBuffer()
{
if (tonesBuffer == "......")
text.remove(text.length() - 1, 1);
if (tonesBuffer == "------")
text = "";
}

شفرة مورس

شفرة مورس

 

 

 




نظام مراقبة نقاوة الجو باستخدام الاردوينو و حساس الغبار

المقدمة

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

حساس الغبار

 

المواد والأدوات المطلوبة

اردوينو

1 X اردوينو

سلك اردوينو

1 X سلك الأردوينو

حساس الغبار

1 X حساس الغبار

لوحة تجارب حجم متوسط

1X لوحة تجارب

red led

1 X  الثنائي المشع للضوء لون أحمر

green-led-5mm

1 X  الثنائي المشع للضوء لون أخضر

مقاومة 220

2X مقاومة 220

مكثف

1X مكثف (16v,220uf) متوفر مع الحساس

مقاومة 150 اوم

1X مقاومة (150 اوم ) متوفره مع الحساس

اسلاك توصيل

مجموعة أسلاك توصيل (ذكر/ ذكر )

 

حساس الغبار

يتكون حساس الغبار من صمام ثنائي ضوئي. يبعث الضوء ومستكشف ضوء (Photodiode) لتحديد انعكاسات الضوءـ ويتم وضعهما بشكل قطري بالقرب من مدخل الهواء.

حساس الغبار

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

 

يحتوي حساس الغبار على 6 منافذ وظيفتها كالتالي

حساس الغبار

 

الجدول التالي يوضح وضيفة كل منفذ موجود بحساس الغبار

المنفذ وظيفة منفذ حساس الغبار
V-LED لتزويد الصمام الثنائي الضوئي بالطاقة
LED GND الأرضي للصمام الثنائي الضوئي
LED للتحكم بتشغيل الضوء
S-GND المنفذ الأرضي للحساس
Vo ينتج نبضات جهد تناظرية وفقًا لتركيز جزيئات الغبار في الهواء.
VCC لتزويد الحساس بالطاقة مقدارها 5 فولت
GND GUN

 

 

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

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

 

البرمجة

#include <Wire.h> 
#include <Adafruit_GFX.h> 
#include <Adafruit_SSD1306.h> 
#define OLED_RESET 4 
Adafruit_SSD1306 display(OLED_RESET);
#if (SSD1306_LCDHEIGHT != 64) 
#error("Height incorrect, please fix Adafruit_SSD1306.h!"); 
#endif 
const int LED1 = 8;
const int LED2 = 10; 
int measurePin = 0; 
int ledPower = 2; 
unsigned int samplingTime = 280;
unsigned int deltaTime = 40;
unsigned int sleepTime = 9680;
float voMeasured = 0; 
float calcVoltage = 0; 
float dustDensity = 0; 
void setup(){ 
  pinMode(ledPower,OUTPUT); 
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C); 
  display.display(); // show splashscreen 
  delay(2000); 
  display.clearDisplay(); // clears the screen and buffer 
  pinMode(LED1,OUTPUT);
  pinMode(LED2,OUTPUT);
  }
  void loop(){
    digitalWrite(ledPower,LOW);// power on the LED 
    delayMicroseconds(samplingTime); 
    voMeasured = analogRead(measurePin); // read the dust value 
    delayMicroseconds(deltaTime); 
    digitalWrite(ledPower,HIGH); // turn the LED off 
    delayMicroseconds(sleepTime); 
    calcVoltage = voMeasured * (5.0 / 1024.0); 
    dustDensity = 0.17 * calcVoltage - 0.1; 
    display.clearDisplay(); 
    display.setTextSize(2); 
    display.setTextColor(WHITE); 
    display.setCursor(0,0); 
    display.println("airquality="); 
    display.print(dustDensity); 
    display.display(); 
    delay(100); 
     if ( dustDensity < 0)
  {
    dustDensity = 0.00;
  }
    if(dustDensity < 0.12) { 
digitalWrite(LED1, HIGH); 
digitalWrite(LED2, LOW); } 
if(dustDensity >0.12 ) {
        digitalWrite(LED1, LOW);
        digitalWrite(LED2, HIGH);} 
        delay(100); 
  }

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

نبدأ بتحميل المكتبات شرح طريقة تحميل المكتبات من خلال الرابط

ابحث عن مكتبتي (Adafruit_GFX) و (Adafruit_SSD1306) عن طريق النقر على sketch < include library < manage libraries ثم حمل المكتبات

من داخل ملف المكتبات على جهاز انقر على مكتبة (Adafruit_SSD1306) ثم على ملف (Adafruit_SSD1306.h) و قم بالتعديل التالي :

ازالة رمز التعليق () //عن سطر

#define SSD1306_128_64

اضافة رمز التعليق (//) إلى السطر

//#define SSD1306_128_32

بالبداية نقوم باستدعاء المكتبات المطلوبة

#include <Wire.h> 
#include <Adafruit_GFX.h> 
#include <Adafruit_SSD1306.h> 
#define OLED_RESET 4 
Adafruit_SSD1306 display(OLED_RESET); 
#if (SSD1306_LCDHEIGHT != 64) 
#error("Height incorrect, please fix Adafruit_SSD1306.h!"); 
#endif 

نقوم بتعريف منافذ الثنائي المشع للضوء

const int LED1 = 8; 
const int LED2 = 10;

نقوم بتعريف المنافذ التي تم توصيلها مع حساس الغبار وهي  A0 و  المنفذ الرقمي 2

و حدد قيمة ابتدائية تساوي 0 للمتغيرات التالية (samplingTimeو deltaTime  و  int sleepTime)

int measurePin = 0; 
int ledPower = 2; 

int samplingTime = 280;
int deltaTime = 40;
int sleepTime = 9680;

نحدد قيمة ابتدائية لكل من (voMeasured  و calcVoltage  و dustDensity )

float voMeasured = 0;
float calcVoltage = 0;
float dustDensity = 0;

في دالة void setup نعرف نعرف منافذ الدخل و الخرج و تهيئة شاشة Oled

void setup(){
  pinMode(ledPower,OUTPUT);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C); 
  display.display(); 
  delay(2000);
  display.clearDisplay();   
  pinMode(LED1,OUTPUT);
  pinMode(LED2,OUTPUT);

}

في دالة void loop نشغل و نطفئ الثنائي المشع للضوء الموجود في حساس الغبار و نقرأ البيانات من المنفذ التناظري A0

void loop(){
  digitalWrite(ledPower,LOW); // power on the LED
  delayMicroseconds(samplingTime);

  voMeasured = analogRead(measurePin); // read the dust value

  delayMicroseconds(deltaTime);
  digitalWrite(ledPower,HIGH); // turn the LED off
  delayMicroseconds(sleepTime);

نقوم بعمل عملية حسابية لحساب تركيز ذرات الغبار بالجو بالميكروجرام (واحد على مليون من الجرام) لكل متر مكعب من الهواء أو ميكروغرام / م 3.

calcVoltage = voMeasured * (5.0 / 1024.0);

  dustDensity = 0.17* calcVoltage - 0.1;

بعد حساب تركيز الغبار نقوم بطباعة القيمة على الشاشة

 display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(0,0);
    display.println("airquality=");
    display.print(dustDensity);
    display.display();
    delay(2000);

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

 if ( dustDensity < 0)
{
dustDensity = 0.00;
}

اذا كان تركيز الغبارأصغر من  (0.12 ) نعطي أمر بتشغيل الثنائي المشع للضوء الأول

if(dustDensity <= 0.12) {
 digitalWrite(LED1, HIGH); 
digitalWrite(LED2, LOW); }

اذا كان تركيز الغبار أكبر من قيمة (0.12) نعطي أمر بتشغيل الثنائي المشع للضوء الثاني

if(dustDensity > 0.12) {   
digitalWrite(LED1, LOW);
   digitalWrite(LED2, HIGH);}
  delay(1000);
}



قياس درجة حرارة الماء باستخدام الاردوينو

مقدمة

DS18B20 درجة الحرارة المقاوم للماء

في هذا الدرس نتعرف على كيفية استخدام مستشعر درجة الحرارة المقاوم للماء وإظهار القراءة على شاشة العرض (OLED Display). حيث يمكننا مستشعر درجة الحرارة من قياس درجة الحرارة من (-55 ℃ إلى 125 ℃) بدقة ± 5. كما يمكن استخدامه في قياس درجة حرارة الهواء أو السوائل.

المواد والأدوات

ds18b20-digital-temperature-arduino

1 X اردوينو

OLED شاشة

1 X شاشة عرض (OLED Display)

DS18B20 مستشعر الحرارة درجة الحرارة المقاوم للماء

1 X حساس حرارة ( DS18B20)

 

ds18b20-digital-temperature-arduino

1X لوحة تجارب

1 X مقاومة مقدار 4.7k

ds18b20-digital-temperature-arduino

مجموعة أسلاك ذكر/ ذكر

شاشة عرض (OLED Display)

التوصيل

شاشة عرض (OLED Display) اختصارا لـ (organic light emitting diode) متوفرة بحجم 128 × 64 وهي شاشة عرض رسومية نقطية بسيطة. تحتوي على 128 عمودًا و64 صفًا مما يجعله يعرض إجمالي 128 × 64 = 8192 بكسل. تحتوي الشاشة على أربعة دبابيس فقط ويتواصل مع Arduino باستخدام بروتوكول اتصال I2C.

توصيل الشاشة مع الاردوينو حسب الجدول التالي

الاردوينو OLED Display
5v VCC
GND GND
A4 SLC
A5 SDA

عمل مسح لمعرفة عنوان i2c

بعد توصيل الشاشة مع الاردوينو، نقوم برفع الكود البرمجي التالي عليها لمعرفة عنوان i2c

#include 

void setup()
{
    Wire.begin();

    Serial.begin(9600);
    while (!Serial); // Leonardo: wait for serial monitor
    Serial.println("\nI2C Scanner");
}

void loop()
{
    byte error, address;
    int nDevices;

    Serial.println("Scanning...");

    nDevices = 0;
    for (address = 1; address < 127; address++) {
  
        Wire.beginTransmission(address);
        error = Wire.endTransmission();

        if (error == 0) {
            Serial.print("I2C device found at address 0x");
            if (address < 16)
                Serial.print("0");
            Serial.print(address, HEX);
            Serial.println(" !");

            nDevices++;
        }
        else if (error == 4) {
            Serial.print("Unknown error at address 0x");
            if (address < 16)
                Serial.print("0");
            Serial.println(address, HEX);
        }
    }
    if (nDevices == 0)
        Serial.println("No I2C devices found\n");
    else
        Serial.println("done\n");

    delay(5000); // wait 5 seconds for next scan
}

 و من ثم نقوم بإظهار شاشة (serial Monitor) ومعرفة عنوان i2c

OLED شاشة

البرمجة:

للتحكم في شاشة OLED ، تحتاج إلى مكتبة  adafruit_SSD1306.h مكتبة  adafruit_GFX.h. لتثبيت هذه المكتبات نقوم بالخطوات التالية:
1. افتح Arduino IDE وانتقل إلى Sketch> Include Library> Manage Libraries. يجب أن يفتح مدير المكتبة
. 2. اكتب “SSD1306” في مربع البحث وقم بتثبيت مكتبة SSD1306 من Adafruit.

OLED شاشة

3. بعد تثبيت مكتبة SSD1306 من Adafruit ، نكتب  “GFX” في مربع البحث ونقوم بتثبيت المكتبة.

ds18b20-digital-temperature-arduino

نفتح  ملف Adafruit_SSD1306.h في محرر نصي. من خلال القسم الذي يعرض SSD1306. نقوم بإلغاء التعليق #define SSD1306_128_64 بحيث يظهر الكود في هذا القسم كما يلي:

ds18b20-digital-temperature-arduino

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

ds18b20-digital-temperature-arduino

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

نقوم بتحميل مكتبة  DallasTemperature بالبحث عن المكتبات عن طريق Tools > Manage Libraries لبرمجة DS18B20 مستشعر درجة الحرارة المقاوم للماء

 درجة حرارة الماء

و مكتبة OneWire Arduino library

 درجة حرارة الماء

ثم ستقوم برفع الكود البرمجي التالي :

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS 2
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

void setup() {
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64)
display.display(); // show splashscreen
delay(2000);
display.clearDisplay(); // clears the screen and buffer
Serial.begin(9600);
sensors.begin();
}
void loop() {
// Send the command for all devices on the bus to perform a temperature conversion:
sensors.requestTemperatures();

// Fetch the temperature in degrees Celsius for device index:
float tempC = sensors.getTempCByIndex(0); // the index 0 refers to the first device
// Fetch the temperature in degrees Fahrenheit for device index:
float tempF = sensors.getTempFByIndex(0);

// Print the temperature in Celsius in the Serial Monitor:
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0,0);

display.println("Temp: ");
display.print(tempC);
display.print((char)247); // degree symbol
display.print("C");
display.display();
delay(2000);
// routine for displaying text for temp/hum readout

}

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

استدعاء المكتبات التي ستساعدنا في برمجة شاشة OLED Display و حساس الحرارة

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>
#include <OneWire.h>
#include <DallasTemperature.h>

نقوم بتعريف المنفذ الذي سيتم توصيل and حساس الحرارة معه

#define ONE_WIRE_BUS 2

بعد ذلك ، نقوم بإنشاء كائن
من أجل التواصل مع مستشعر DS18B20 ، نحتاج إلى إنشاء كائن من مكتبة DallasTemperature

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);

نكتب بروتوكول إعادة الضبط

#define OLED_RESET 4 
Adafruit_SSD1306 display(OLED_RESET);
#if (SSD1306_LCDHEIGHT != 64)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif

في دالة void setup()نحتاج إلى تهيئة حساس الحرارة و تهيئة العرض ومسحه لشاشة OLED Display تأكد من كتابة عنوان  I2Cلشاشة العرض

void setup()   {                
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C); 
  display.display(); // show splashscreen
  delay(2000);
  display.clearDisplay();   // clears the screen and buffer
  Serial.begin(9600);
  sensors.begin();  
}

نعطي الأمر استشعار  درجة الحرارة

void loop() {
  sensors.requestTemperatures();

  float tempC = sensors.getTempCByIndex(0); // the index 0 refers to the first device

نعطي الأمر بطباعة درجة الحرارة بالدرجة المئوية على شاشة OLED Display

  // Print the temperature in Celsius in the Serial Monitor:
 display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.setCursor(0,0);

  display.println("Temp: ");
  display.print(tempC);
  display.print((char)247); // degree symbol 
  display.print("C");
    display.display();
    delay(2000);    
  }



نظام انذار الحرائق باستخدام حساس الغاز مع الاردوينو

مقدمة

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

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

حساس الغاز الاردوينو

المواد والأدوات

الاردوينو يستخدم في حساس الغاز/ الدخان

اردوينو اونو

arduino-smoke-gas-sensor

1× سلك الاردوينو

arduino-smoke-gas-sensor

لوحة تجارب – حجم كبير

الشاشة تستخدم في مع الاردوينو وحساس الغاز/ الدخان

 1× شاشة كرستالية

حساس الغاز/ الدخان الاردوينو

حساس الغاز

arduino-smoke-gas-sensor

1× مقاومة متغيرة

arduino-smoke-gas-sensor

1× مصدر صوت

arduino-smoke-gas-sensor

 حزمة أسلاك توصيل (ذكر- ذكر)

arduino-smoke-gas-sensor

1× مقاومة 220 Ω

مهاراتك في الرياضيات

1× 40 رأس دبوس

حساس الغاز

هو حساس يكشف عن تسرب الغاز في المكان.

يُمكن لحساس الغاز الكشف عن الغازات القابلة للاشتعال مثل البوتان، البروبان، الميثان والهيدروجين.

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

gas-smoke-sensor-gif

له أربعة مداخل موضحة في الشكل التالي:

arduino-smoke-gas-sensor

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

لمعرفة المزيد حول الشاشة الكرستالية يمكنك الرجوع للدرس التحكم بالشاشة الكرستالية LCD

لابد من تلحيم المنافذ مع الشاشة الكرستالية، للمزيد حول اللحام يمكنك الرجوع للدرس تعلم كيفية التلحيم – تلحيم القطع باللوحة الإلكترونية

arduino-smoke-gas-sensor

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

الكود البرمجي لنظام انذار الحرائق باستخدام حساس الغاز.
#include <LiquidCrystal.h>
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
#define buzzer 8
#define Gas A0
void setup() {
Serial.begin(9600);
lcd.begin(16,2);
pinMode(buzzer,OUTPUT);
lcd.setCursor(0, 0);
lcd.print("Calibrating");
for(int i = 0; i <10; i++) {
if (i==4)
{
lcd.setCursor(0, 1);
lcd.print("."); }
else lcd.print(".");
delay(500);
}
lcd.setCursor(5,1);
lcd.print("done");
delay(1000);
lcd.clear() ;
lcd.setCursor(1,0);
lcd.print("SENSOR ACTIVE");
delay(1500);
lcd.clear(); }
void loop() {
int gasSensor = analogRead(Gas);
if (gasSensor > 300) {
digitalWrite(buzzer,HIGH);
lcd.setCursor(0, 0);
lcd.print("Value: ");
lcd.print(gasSensor);
Serial.print(gasSensor);
Serial.print("\t");
lcd.setCursor(0,1);
Serial.println("Gas is Detected"); 
lcd.print("Gas is Detected"); 
delay(300);
lcd.clear(); }
else if (gasSensor < 300) {
digitalWrite(buzzer,LOW);
lcd.setCursor(0, 0);
lcd.print("Value: ");
lcd.print(gasSensor);
Serial.print(gasSensor);
Serial.print("\t");
lcd.setCursor(0,1);
Serial.println("Gas not Detected"); 
lcd.print("Gas not Detected"); 
delay(300); }}

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

هذا السطر يطلب من الأردوينو استعمال مكتبة الشاشة الكرستالية (حتى يستطيع الاردوينو استيعاب الأوامر الخاصة بالشاشة).

نستطيع تحميلها بتتبع المسار التالي:

Sketch > Include libraries > Manage libraries

ثم نكتب بخانة البحث Liquid crystal by Arduino

ثم نضغط على Install.

#include <LiquidCrystal.h>

هذه الأسطر توضح منافذ الاردوينو التي ستستخدمها للربط في هذا المشروع:

#define buzzer 8
#define Gas A0

بعد ذلك أعلنا عن المتغيرات اللازمة مثل المتغيرات الخاصة بالشاشة الكرستالية والتي تستخدمها المكتبات أيضًا:

const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

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

void setup()
 { 
 Serial.begin(9600);
 lcd.begin(16,2); 
 pinMode(buzzer,OUTPUT); 
 lcd.setCursor(0, 0); 
 lcd.print("Calibrating"); 
 for(int i = 0; i <10; i++) 
 { if (i==4) { 
 lcd.setCursor(0, 1); 
 lcd.print("."); } 
 else lcd.print("."); 
 delay(500); } lcd.setCursor(5,1); 
 lcd.print("done"); 
 delay(1000); 
 lcd.clear();  
 lcd.setCursor(1,0); 
 lcd.print("SENSOR ACTIVE"); 
 delay(1500); 
 lcd.clear(); 
}

تبدأ عملية المعايرة واختبار جاهزية حساس الغاز.

 معايرة حساس الغاز/ الدخان المربوط مع الاردوينو

ستظهر رسالة عند انتهاء عملية المعايرة (done).

arduino-smoke-gas-sensor

تليها رسالة تبين جاهزية الحساس لاختبار المكان.

 جاهزية حساس الغاز/ الدخان المربوط مع الاردوينو

في الدالة ()loop يقوم الحساس بحساب نسبة الغاز الموجود في الهواء.

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

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

void loop() 
{ 
int gasSensor = analogRead(Gas); 
if (gasSensor > 300) { 
digitalWrite(buzzer,HIGH); 
lcd.setCursor(0, 0); lcd.print("Value: "); 
lcd.print(gasSensor); 
Serial.print(gasSensor); 
Serial.print("\t"); 
lcd.setCursor(0,1); 
Serial.println("Gas is Detected"); 
lcd.print("Gas is Detected"); 
delay(300); lcd.clear(); } 
else if (gasSensor < 300) { 
digitalWrite(buzzer,LOW); 
lcd.setCursor(0, 0); 
lcd.print("Value: "); 
lcd.print(gasSensor); 
Serial.print(gasSensor); 
Serial.print("\t"); 
lcd.setCursor(0,1); 
Serial.println("Gas not Detected"); 
lcd.print("Gas not Detected"); 
delay(300); }
}

هنا نسبة الغاز في المكان أكثر من 300.

 بدء عمل حساس الغاز/ الدخان المربوط مع الاردوينو اكتشاف الغاز

هنا نسبة الغاز  في المكان أقل من 300.

 بدء عمل حساس الغاز/ الدخان المربوط مع الاردوينو

يصبح حساس الغاز بعد فترة من استخدامه ساخنًا علينا الحذر من لمسه.



نظام الأمان و الإنذار باستخدام الليزر و الاردوينو

المقدمة

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

الليزر

المواد والأدوات

اردوينو

 1 X اردوينو

سلك اردوينو

1 X سلك الأردوينو

1 X لوحة تجارب

laser-security-arduino

1 X مصدر صوت

مستقبل الليزر

1 X مستقبل أشعة الليزر

مرسل الليزر

1 X مرسل أشعة الليزر

أسلاك ذكر / ذكر

مجموعة أسلاك توصيل ( ذكر/ ذكر)

أسلاك توصيل

مجموعة أسلاك توصيل ( أنثى/ ذكر)

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

قم بتوصيل القطع مع الأردوينو كما هو موضح بالشكل التالي

توصيل مستقبل و مرسل الليزر مع الاردوينو

 

جدول توصيل منافذ مستقبل أشعة الليزرمع الاردوينو
الاردوينو مستقبل أشعة ليزر
GND GND
2 OUT
5 V VCC
جدول توصيل مرسل أشعة الليزر مع الأردوينو
الاردوينو مرسل أشعة ليزر
5v S
GNS

 

وحدة إرسال الليزر :

laser-security-arduino

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

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

البرمجة

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

#define DETECT 2
#define ACTION 13
void setup() {
pinMode(DETECT, INPUT); //define detect input pin 
pinMode(ACTION, OUTPUT);//define ACTION output pin 
} 
void loop() { 
int detected = digitalRead(DETECT);
if( detected == HIGH) 
{ digitalWrite(ACTION,LOW); }
else{ digitalWrite(ACTION,HIGH); }
delay(200); 
}

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

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

حيث سيكون المنفذ رقم 2 موصل مع مستقبل أشعة ليزر

والمنفذ رقم 13 يكون موصل مع مصدر الصوت

#define DETECT 2
#define ACTION 13

في دالية setup(): نقوم بتعريف المنافذ سواء كانت منافذ ادخال أو منافذ اخراج

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

و يعد منفذ الاخراج هو المنفذ المتصل مع مصدر الصوت

void setup() {
pinMode(DETECT, INPUT); //define detect input pin 
pinMode(ACTION, OUTPUT);//define ACTION output pin 
}

 

في دالة loop() نقوم بجعل البرنامج يعطي اشارة لتشغيل مصدر الصوت اذا اخترق احد أشعة الليزر

يقوم البرنامج بقراءة منفذ الادخال، فاذا كانت القراءة (LOW)  يتم اعطاء اشارة للمنفذ رقم 13 (منفذ مصدر الصوت ) ليعملو العكس حيث في حال أعطى منفذ الادخال اشارة (HIGH) لن يعمل مصدر الصوت

ثم يتم اضافة زمن انتظار و هو ثانيتين

void loop() { 
int detected = digitalRead(DETECT);
if( detected == HIGH) 
{ digitalWrite(ACTION,LOW); }
else{ digitalWrite(ACTION,HIGH); }
delay(200); 
}



التحكم بالإضاءة باستخدام مستقبل الأشعة تحت الحمراء(IRreceiver)

المقدمة

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

irreceiver مستقبل الأشعة تحت الحمراء

المواد والأدوات

اردوينو

X1 اردوينو أونو 

سلك اردوينو

X1 سلك الأردوينو

rgbcontrol

 X1مستقبل الإشارات تحت الحمراء(IR receiver)

led-5mm-red

X3 الثنائي المشع للضوء 

مقاومة 220

X3 مقاومة 220أوم

لوحة تجارب

X1 لوحة تجارب 

أسلاك ذكر / ذكر

مجموعة أسلاك توصيل (ذكر/ذكر)

 

مستقبل الأشعة تحت الحمراء(IRreceiver )

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

الأشعة تحت الحمراء
الأشعة تحت الحمراء هي شكل من أشكال الضوء مشابه للضوء الذي نراه من حولنا. الفرق الوحيد بين ضوء الأشعة تحت الحمراء والضوء المرئي هو التردد وطول الموجة. تقع الأشعة تحت الحمراء خارج نطاق الضوء المرئي ، لذلك لا يمكن للبشر رؤيتها:

مستقبل الأشعة تحت الحمراء 

irreceiver مستقبل الأشعة تحت الحمراء

فك اشارات ريموت التحكم باستخدام الأردوينو 

Label Value
CH- 0x00FFA25D
CH 0x00FF629D
CH+ 0x00FFE21D
|<< 0x00FF22DD
>>| 0x00FF02FD
>|| 0x00FFC23D
+ 0x00FFE01F
0x00FFA857
EQ 0x00FF906F
0 0x00FF6897
100+ 0x00FF9867
200+ 0x00FFB04F
1 0x00FF30CF
2 0x00FF18E7
3 0x00FF7A85
4 0x00FF10EF
5 0x00FF38C7
6 0x00FF5AA5
7 0x00FF42BD
8 0x00FF4AB5
9 0x00FF52AD

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

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

توصيل الدائرة لـ irreceiver مستقبل الأشعة تحت الحمراء

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

#include <IRremote.h>
int LED1= 3;
int LED2= 4;
int LED3= 5;

int RECV_PIN = 2;
IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {  
   Serial.begin(9600);
   irrecv.enableIRIn(); // Start the receiver
  
    /***Pin mode declaration***/
    pinMode(LED1, OUTPUT);
    pinMode(LED2, OUTPUT);
    pinMode(LED3, OUTPUT);
    
  }
  
  void loop() {
    if (irrecv.decode(&results)) {
    
    
    switch (results.value)
    {
      case 0xFFA25D:
      digitalWrite(red, HIGH);
      delay(300);
      digitalWrite(red, LOW);
      break;
      case 0xFF629D:
      digitalWrite (green, HIGH);
      delay(300);
      digitalWrite(green, LOW); 
      break;
      case 0xFFE21D:
      digitalWrite (blue, HIGH);
      delay(300);
      digitalWrite(blue, LOW); 
       break;
}
      
   irrecv.resume();

      
    }
 }

 

شرح الكود :

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

int red = 3;
int green = 9;
int blue = 10;
#include <IRremote.h>
int RECV_PIN = 11

تعريف كائن  (object) من النوع IRrecv  ،  وتعريف متغيرات لحفظ قيم سطوع إضاءة الثلاث ألوان الخاصة بالRGB (أحمر،أخضر،أزرق).

IRrecv irrecv(RECV_PIN);
decode_results results;
int redBrightness = 0;
int greenBrightness = 0;
int blueBrightness = 0;

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

void setup() {  
   Serial.begin(9600);
   irrecv.enableIRIn(); 
  
 
    pinMode(red, OUTPUT);
    pinMode(green, OUTPUT);
    pinMode(blue, OUTPUT);
    
  }

إنشاء دالة ()setcolor والتي تقوم بحساب شدة الإضاءة الخاصة بكل لون في RGB اعتمادا على نوعه إما مصعد مشترك (Common anode) أو مهبط مشترك (Common Cathode)، ثم ارسال القيم عبر المنافذ الخاصة بالـ RGB ليتم إضاءة اللون المطلوب .

 void setcolor (int redx, int greenx, int bluex){
    Serial.print("Led");
    int red1= 255 - redx ;
    int green1=255 - greenx;
    int blue1=255 - bluex;
    analogWrite(red, red1);
    analogWrite(green, green1);
    analogWrite(blue, blue1);
  
  }  

في الدالة loop() نقوم بقراءة القيمة التي يستقبلها IR receiver عند الضغط على أزرار الريموت و تحديد اللون الذي سيظهر على RGB اعتمادا على القيمة المقروءة .

void loop() {
    if (irrecv.decode(&results)) {
    
    
    switch (results.value)
    {
      case 0xE318261B:
        setcolor (255, 0, 0);
        break;
      case 0x511DBB:
        setcolor (0, 0, 255);
        break;
      case 0xFFE21D:
        setcolor (0, 255, 0);
        break;
      }
   irrecv.resume();

      
    }
 }



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

المقدمة

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

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

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
  • انقر على زر تشغيل المحاكاة، ستلاحظ بأن الضوء يومض مرارًا وتكرارًا.

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




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

المقدمة

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

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

اردوينو(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 الخاص ببرنامج الاردوينو مفتوح