نظام طبي لقياس نسبة الأكسجين في الدم ومعدل ضربات القلب

مقدمة

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

arduino-blood-oxygen-heart-rate-monitor-max30100

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

arduino-blood-oxygen-heart-rate-monitor-max30100

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

arduino-blood-oxygen-heart-rate-monitor-max30100

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

breadboard

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

arduino-blood-oxygen-heart-rate-monitor-max30100

3× مقاومة 4.7K Ωنظام طبي

مستشعر معدل نبضات القلب والأكسجين

arduino-blood-oxygen-heart-rate-monitor-max30100 اسلاك التوصيل لوح التجارب

arduino-blood-oxygen-heart-rate-monitor-max30100

1× شاشة (OLED)نظام طبي

موديول بلوتوث من النوع HC-06

arduino-blood-oxygen-heart-rate-monitor-max30100

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

arduino-blood-oxygen-heart-rate-monitor-max30100

1× هاتف بنظام اندرويد

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

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

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

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

#include <Wire.h>
#include "MAX30100_PulseOximeter.h"
#include "Wire.h"
#include "Adafruit_GFX.h"
#include "OakOLED.h"
#define REPORTING_PERIOD_MS 1000
OakOLED oled;
PulseOximeter pox;

uint32_t tsLastReport = 0;

const unsigned char bitmap [] PROGMEM=
{
0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x18, 0x00, 0x0f, 0xe0, 0x7f, 0x00, 0x3f, 0xf9, 0xff, 0xc0,
0x7f, 0xf9, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xf0,
0xff, 0xf7, 0xff, 0xf0, 0xff, 0xe7, 0xff, 0xf0, 0xff, 0xe7, 0xff, 0xf0, 0x7f, 0xdb, 0xff, 0xe0,
0x7f, 0x9b, 0xff, 0xe0, 0x00, 0x3b, 0xc0, 0x00, 0x3f, 0xf9, 0x9f, 0xc0, 0x3f, 0xfd, 0xbf, 0xc0,
0x1f, 0xfd, 0xbf, 0x80, 0x0f, 0xfd, 0x7f, 0x00, 0x07, 0xfe, 0x7e, 0x00, 0x03, 0xfe, 0xfc, 0x00,
0x01, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x00, 0x3f, 0xc0, 0x00,
0x00, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

void onBeatDetected()
{
Serial.println("Beat!");
oled.drawBitmap( 60, 20, bitmap, 28, 28, 1);
oled.display();
}

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

oled.begin();
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);

oled.println("Initializing pulse oximeter..");
oled.display();
Serial.print("Initializing pulse oximeter..");

if (!pox.begin()) {
Serial.println("FAILED");
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);
oled.println("FAILED");
oled.display();
for(;;);
} else {
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);
oled.println("SUCCESS");
oled.display();
Serial.println("SUCCESS");
}
pox.setOnBeatDetectedCallback(onBeatDetected);
}

void loop()
{
pox.update();

if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
Serial.print("Heart BPM:");
Serial.print(pox.getHeartRate());
Serial.print("-----");
Serial.print("Oxygen Percent:");
Serial.print(pox.getSpO2());
Serial.println("\n");
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0,16);
oled.println(pox.getHeartRate());

oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);
oled.println("Heart BPM");

oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 30);
oled.println("Spo2");

oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0,45);
oled.println(pox.getSpO2());
oled.display();
tsLastReport = millis();
}
}

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

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

عليك تحميل المكتبات التالية:

1. Arduino MAX30100 Library
2. OkaOLED Library
3. Adafruit GFX Library

وذلك بالرجوع للملف المضغوط، لمعرفة كيفية تنزيل المكتبات يمكنك الرجوع إلى الدرس التالي.

بعد تحميل الملف فك الضغط عنه سيكون هناك 3 مكتبات، أضف كل مكتبة على حدة لقائمة المكتبات.

#include <Wire.h>
#include "MAX30100_PulseOximeter.h"
#include "Wire.h"
#include "Adafruit_GFX.h"
#include "OakOLED.h"

هنا سيتم تعريف المتغير الخاص بالشاشة OLED.

OakOLED oled;

وهنا سيتم تعريف المتغير الخاص بمستشعر نبضات القلب والأكسجين.

PulseOximeter pox;

في هذه الأسطر يتم تهيئة مصفوفة تضم مجموعة من المتغيرات والتي تخص الشاشة OLED.

const unsigned char bitmap [] PROGMEM=
{
0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x18, 0x00, 0x0f, 0xe0, 0x7f, 0x00, 0x3f, 0xf9, 0xff, 0xc0,
0x7f, 0xf9, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xf0,
0xff, 0xf7, 0xff, 0xf0, 0xff, 0xe7, 0xff, 0xf0, 0xff, 0xe7, 0xff, 0xf0, 0x7f, 0xdb, 0xff, 0xe0,
0x7f, 0x9b, 0xff, 0xe0, 0x00, 0x3b, 0xc0, 0x00, 0x3f, 0xf9, 0x9f, 0xc0, 0x3f, 0xfd, 0xbf, 0xc0,
0x1f, 0xfd, 0xbf, 0x80, 0x0f, 0xfd, 0x7f, 0x00, 0x07, 0xfe, 0x7e, 0x00, 0x03, 0xfe, 0xfc, 0x00,
0x01, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x00, 0x3f, 0xc0, 0x00,
0x00, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

في الدالة ()onBeatDetected سيتم قراءة البيانات من مستشعر نبضات القلب والأكسجين وطباعتها على الشاشة OLED.

void onBeatDetected()
{
Serial.println("Beat!");
oled.drawBitmap( 60, 20, bitmap, 28, 28, 1);
oled.display();
}

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

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

oled.begin();
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);

oled.println("Initializing pulse oximeter..");
oled.display();
Serial.print("Initializing pulse oximeter..");

if (!pox.begin()) {
Serial.println("FAILED");
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);
oled.println("FAILED");
oled.display();
for(;;);
} else {
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);
oled.println("SUCCESS");
oled.display();
Serial.println("SUCCESS");
}
pox.setOnBeatDetectedCallback(onBeatDetected);
}

في الدالة ()loop سيتم قراءة معدل ضربات القلب ونسبة الأكسجين في الدم وطباعتها على الشاشة OLED وإرسال البيانات عن طريق البلوتوث إلى جهاز الاندرويد.

سيتم تحديث البيانات كل ثانية لكي يتم أخذ القراءات أولًا بأول.

void loop()
{
pox.update();

if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
Serial.print("Heart BPM:");
Serial.print(pox.getHeartRate());
Serial.print("-----");
Serial.print("Oxygen Percent:");
Serial.print(pox.getSpO2());
Serial.println("\n");
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0,16);
oled.println(pox.getHeartRate());

oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);
oled.println("Heart BPM");

oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 30);
oled.println("Spo2");

oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0,45);
oled.println(pox.getSpO2());
oled.display();
tsLastReport = millis();
}
}

برنامج MIT App Inventor

حمّل تصميم واجهة المستخدم والكود البرمجي من هنا.

من المتصفح افتح موقع MIT App Inventor.

أنشئ حساب على الموقع ثم انقر علي Create Apps.

arduino-blood-oxygen-heart-rate-monitor-max30100

من قائمة Project اختر Import projects (.aia) from my computer ثم انقر على الملف الذي حملته مسبقًا ويحتوي على واجهة المستخدم والكود البرمجي.

arduino-blood-oxygen-heart-rate-monitor-max30100

من قائمة Connect اختر Al companion

arduino-blood-oxygen-heart-rate-monitor-max30100

سيظهر كود ورمز مكون من 6 حروف.

حمّل تطبيق MIT App Inventor على جهازك الذكي.

يمكنك ادخال الرمز في المستطيل أو النقر على scan QR code ومسح الكود السابق.

ثم انقر على connect with code.
arduino-blood-oxygen-heart-rate-monitor-max30100

ستظهر هذه الواجهة انقر على Tap to connect.arduino-blood-oxygen-heart-rate-monitor-max30100

اختر موديول البلوتوث المستخدم هنا استخدمنا النوع HC-06.

نظام طبي

ستتم عملية الاقتران وستبدأ البيانات المقروءة من المستشعر بالظهور على الجهاز.نظام طبي

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

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

المشاكل والإصلاحات

إذا كان توصيلك صحيح والحساس لا يعمل، ببساطة قم بإزالة المقاومات ذات القيمة 1.8ohm ليتم استبدالها بمقاوامات ذات قيمة 4.7k ohm .

arduino-blood-oxygen-heart-rate-monitor-max30100

 

 

 

 




إنشاء خادم ويب باستخدام الاردوينو ووحدة ESP8266

مقدمة

في هذا الدرس سنتعلم كيفية إنشاء خادم ويب باستخدام الاردوينو ووحدة ESP8266.sending-arduino-data-to-webpage

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

 خادم الويب

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

 خادم الويب

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

sending-arduino-data-to-webpage

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

ذكر-ذكر

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

WiFi Module ESP8266

وحدة Wi-Fi

لوحة تجارب

1× لوحة تجارب حجم صغير

 

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

وصل الدائرة الكهربائية كما في الشكل المقابل:خادم الويب

تهيئة Arduino IDE

  • افتح ِArduino IDE، من قائمة File اختر Preference.

sending-arduino-data-to-webpage

  • حمّل الملف الخاص بوحدة ESP8266 ثم انقر على Ok.

https://arduino.esp8266.com/stable/package_esp8266com_index.json

sending-arduino-data-to-webpage

  • من قائمة Tools اختر Board ثم Board manger.

خادم الويب

  • اكتب في خانة البحث ESP8266 حمّل اللوحة التالية آخر إصدار.

خادم الويب

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

قبل رفع الكود البرمجي للوحة الاردوينو عليك قراءة شرح الكود البرمجي.

#include<SoftwareSerial.h>

SoftwareSerial client(2,3); //RX, TX

String webpage="";

int i=0,k=0;

String readString;
int x=0;
boolean No_IP=false;
String IP="";

char temp1='0';
String name="<p>Geeks valley</p>";   //22
String dat="<p>Data Received Successfully.....</p>";     //21


void check4IP(int t1)
{
int t2=millis();
while(t2+t1>millis())
 {
while(client.available()>0)
{
if(client.find("WIFI GOT IP"))
{
No_IP=true;
}
}

}
}

void get_ip()
{
 IP="";
char ch=0;
while(1)
{
client.println("AT+CIFSR");
while(client.available()>0)
{
if(client.find("STAIP,"))
{
delay(1000);
Serial.print("IP Address:");
while(client.available()>0)
 {
ch=client.read();
  if(ch=='+')
break;
 IP+=ch;
}
}
 if(ch=='+')
break;
 }
 if(ch=='+')
break;
delay(1000);
}
Serial.print(IP);
Serial.print("Port:");
Serial.println(80);
}

void connect_wifi(String cmd, int t)
{
 int temp=0,i=0;
 while(1)
{
Serial.println(cmd);
client.println(cmd);
while(client.available())
 {
if(client.find("OK"))
i=8;
}
delay(t);
  if(i>5)
break;
i++;
}
if(i==8)
Serial.println("OK");
else
Serial.println("Error");
}
void wifi_init()
{
connect_wifi("AT",100);
connect_wifi("AT+CWMODE=3",100);
connect_wifi("AT+CWQAP",100);  
connect_wifi("AT+RST",5000);
check4IP(5000);
 if(!No_IP)
 {
Serial.println("Connecting Wifi....");
 connect_wifi("AT+CWJAP=\"Network name\",\"Network password\"",7000);         //provide your WiFi username and password here
// connect_wifi("AT+CWJAP=\"vpn address\",\"wireless network\"",7000);
}
else
{
 }
Serial.println("Wifi Connected");
get_ip();
connect_wifi("AT+CIPMUX=1",100);
connect_wifi("AT+CIPSERVER=1,80",100);
}
void sendwebdata(String webPage)
{
 int ii=0;
while(1)
{
unsigned int l=webPage.length();
Serial.print("AT+CIPSEND=0,");
client.print("AT+CIPSEND=0,");
Serial.println(l+2);
client.println(l+2);
delay(100);
Serial.println(webPage);
client.println(webPage);
while(client.available())
{
 //Serial.print(Serial.read());
 if(client.find("OK"))
 {
ii=11;
break;
}
 }
 if(ii==11)
break;
delay(100);
  }
}
void setup()
{
Serial.begin(115200);
client.begin(115200);
wifi_init();


Serial.println("System Ready..");

}
void loop()

{
k=0;
Serial.println("Please Refresh your Page");
while(k<1000)
 {
 k++;
while(client.available())
{
 if(client.find("0,CONNECT"))
{
Serial.println("Start Printing");
 Send();
 Serial.println("Done Printing");
delay(1000);
}
 }
delay(1);
}

}
void Send()
{
webpage = "<h1>Welcome to Geeks valley</h1><body bgcolor=f0f0f0>";
sendwebdata(webpage);
webpage=name;
webpage+=dat;
sendwebdata(webpage);
delay(1000);
webpage = "<a href=\"https://geeksvalley.com/tutorials/";
webpage+="\">Click Here for tutorials</a>";
sendwebdata(webpage);
client.println("AT+CIPCLOSE=0");

}

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

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

#include<SoftwareSerial.h>

هذا السطر يوضح المنافذ المستخدمة لربط Rx وTx.

SoftwareSerial client(2,3); //RX, TX

المتغير IP يحمل عنوان الشبكة للوحة Wi-Fi.

String IP="";

المتغير name يحمل جملة Geeks valley بتنسيق لغة HTML.

والمتغير date يحمل الجملة Data Received Successfully…..

String name="<p>Geeks valley</p>";   //22
String dat="<p>Data Received Successfully.....</p>";     //21

في الدالة check4IP سيتم البحث عن عنوان الشبكة.

void check4IP(int t1)
{
int t2=millis();
while(t2+t1>millis())
 {
while(client.available()>0)
{
if(client.find("WIFI GOT IP"))
{
No_IP=true;
}
}

}
}

في الدالة get_ip سيتم طباعة عنوان الشبكة IP address.

void get_ip()
{
 IP="";
char ch=0;
while(1)
{
client.println("AT+CIFSR");
while(client.available()>0)
{
if(client.find("STAIP,"))
{
delay(1000);
Serial.print("IP Address:");
while(client.available()>0)
 {
ch=client.read();
  if(ch=='+')
break;
 IP+=ch;
}
}
 if(ch=='+')
break;
 }
 if(ch=='+')
break;
delay(1000);
}
Serial.print(IP);
Serial.print("Port:");
Serial.println(80);
}

في الدالة wifi-init سيتم البحث عن الشبكات الموجودة في النطاق، ثم سيتم اختيار الشبكة التي قمت بتعيينها في الكود البرمجي.

void wifi_init()
{
connect_wifi("AT",100);
connect_wifi("AT+CWMODE=3",100);
connect_wifi("AT+CWQAP",100);  
connect_wifi("AT+RST",5000);
check4IP(5000);
 if(!No_IP)
 {
Serial.println("Connecting Wifi....");
 connect_wifi("AT+CWJAP=\"Network name\",\"network password\"",7000);         //provide your WiFi username and password here
// connect_wifi("AT+CWJAP=\"vpn address\",\"wireless network\"",7000);
else
{
 }
Serial.println("Wifi Connected");
get_ip();
connect_wifi("AT+CIPMUX=1",100);
connect_wifi("AT+CIPSERVER=1,80",100);
}

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

 connect_wifi("AT+CWJAP=\"Network name\",\"Network password\"",7000);         //provide your WiFi username and password here

في الدالة setup سيتم تهيئة شاشة الاتصال التسلسلي لطباعة التحديثات الخاصة بوحدة Wi-Fi.

عليك تغيير سرعة نقل البيانات كما في الشكل.

خادم الويب

voidsetup()
{
Serial.begin(115200);
client.begin(115200);
wifi_init();


Serial.println("System Ready..");

}

في الدالة send سيتم انشاء خادم ويب.

ستظهر الجمل بهذا الشكل:

Welcome to Geeks valley

Geeks valley

Data Received Successfully…..

Click Here for tutorials

يمكن تحرير الجمل الموجودة:

void Send()
{
webpage = "<h1>Welcome to Geeks valley</h1><body bgcolor=f0f0f0>";
sendwebdata(webpage);
webpage=name;
webpage+=dat;
sendwebdata(webpage);
delay(1000);
webpage = "<a href=\"https://geeksvalley.com/tutorials/";
webpage+="\">Click Here for tutorials</a>";
sendwebdata(webpage);
client.println("AT+CIPCLOSE=0");

}

بعد رفع الكود البرمجي افتح شاشة الاتصال التسلسي وانسخ IP address عنوان الشبكة.

sending-arduino-data-to-webpage

وضعه في المتصفح الذي تفضله، ستظهر صفحة الويب كما في الشكل.

خادم الويب

المشاكل والإصلاحات

من أبرز المشاكل التي تظهر في وحدة Wi-Fi هي عدم الاستجابة لأوامر AT، إما بسبب خطأ بتوصيل الدائرة الكهربائية أو بسبب مشاكل في الكود البرمجي أو الوحدة تحتاج إلى تحديث Firmware.

سنتعلم في هذا القسم كيفية تحديث Firmware.

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

وصل الدائرة الكهربائية كما في الشكل المقابل:sending-arduino-data-to-webpage

2- تحميل ESP flasher و Firmware

حمّل Flasher و Firmware.

فك الضغط عن كلا الملفين.

3- تثبيت Firmware

افتح ESP8266 Flasher.

انقر على Bin اختر ملف Ai-Thinker_ESP8266 الذي قمت بتحميله مسبقًا.

واكتب رقم المنفذ التسلسلي الذي تستخدمه.sending-arduino-data-to-webpage

اضغط زر التحكم في الدائرة الكهربائية ثم انقر على زر تحميل Download.

sending-arduino-data-to-webpage

سيبدأ التحميل مباشرة، انتظر حتى ينتهي وتظهر هذه الرسالة.

خادم الويب

4- الاختبار

ملاحظة: من الدائرة الكهربائية افصل السلك المربوط مع GPI00.

سنستخدم AT commands لاختبار اللوحة.

AT Commands عبارة عن أوامر تسمح باتصال وحدة Wi-Fi مع لوحة الاردوينو.

افتح اردوينو IDE.

ارفع الكود البرمجي التالي للوحة الاردوينو (قبل رفع الكود البرمجي للوحة افصل سلك Rest و 3v ثم أعدهم بعد رفع الكود البرمجي.).

void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}

افتح شاشة الاتصال التسلسلي وغيّر سرعة النقل كما في الصورة.

خادم الويب

ابدأ بكتابة الأوامر التالية:

الأمر الأول: AT

يعتبر الأمر الأساسي الذي يختبر بدء تشغيل AT، إذا كان نظام AT يعمل بشكل صحيح ستظهر رسالة OK إذا لم يعمل ستظهر رسالة Error.خادم الويب

الأمر الثاني: AT+RST

هذا الأمر يعيد تشغيل وحدة ESP8266.

ستظهر رسالة ready في النهاية.

الأمر الثالث: AT+CWMODE=3

يستخدم هذا الأمر لضبط وضع WiFi للتشغيل.

ستظهر رسالة OK عند الانتهاء من الضبط.

خادم الويب

 الأمر الرابع: AT+CWLAP

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

الأمر الخامس: AT+CWJAP=”اسم الشبكة”,”كلمة المرور”

يستخدم هذا الأمر لربط وحدة Wi-Fi مع شبكة الانترنت.sending-arduino-data-to-webpage

الأمر السادس: AT+CIFSR

يوضع عنوان الشبكة IP Address.

sending-arduino-data-to-webpage

بعد اكتمال هذه الخطوات تصبح الوحدة جاهزة للاستخدام.

 




تحديد المواقع GPS باستخدام الاردوينو ورسائل SMS

مقدمة

في هذا الدرس سنصنع نظام تحديد المواقع GPS باستخدام الاردوينو ووحدة اتصال وارسال واستقبال الإشارة GPRS GSM Module ثم يتم ارسال بيانات الموقع على شكل رسالة نصية SMS.

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

sim808-gps-gsm-module-sending-gps-location-sms

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

number-conversion-by-arduino

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

الأنظمة العددية

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

 

12V adapter

1× محول تيار (12Volt adapter 2A)

تحديد المواقع GPS

وحدة اتصال وارسال واستقبال الإشارة (GPRS GSM Module)SIM

1× شريحة (SIM)

وحدة اتصال وارسال واستقبال الإشارة (GPRS GSM Module)

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

يمكن من خلالها إرسال رسائل SMS وإجراء المكالمات الهاتفية وغيرها من الأوامر.تحديد المواقع GPS

 

تتضمن الوحدة لاقط هوائي لاستخدامات تحديد الموقع GPS. GPS

كما تتضمن لاقط هوائي GSM لنقل إشارات الإتصال.
GSM

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

وصل الدائرة الكهربائية كما في الشكل المقابل.تحديد المواقع GPS

اضغط ضغطًا مطولًا على زر الطاقة حتى تضيء المصابيح الجانبية، هنا نتأكد بأن اللوحة تعمل بالشكل الصحيح.تحديد المواقع GPS

ادخل الشريحة SIM من المنفذ التالي.تحديد المواقع GPS

البرمجة

في البداية عليك تحميل مكتبة SoftwareSerial لمعرفة كيفية تنزيل المكتبات يمكنك الرجوع إلى الدرس التالي.

قبل رفع الكود البرمجي على لوحة الاردوينو عليك قراءة شرح الكود البرمجي.

#include <SoftwareSerial.h>
SoftwareSerial sim808(11,10);

char phone_no[] = "053366****"; // replace with your phone no.

String data[5];
#define DEBUG true
String state,timegps,latitude,longitude;
void setup() {
sim808.begin(9600);
Serial.begin(9600);
delay(50);
//sim808.print("AT+CSMP=17,167,0,0");  // set this parameter if empty SMS received
//delay(100);
sim808.print("AT+CMGF=1\r");
delay(400);
sendData("AT+CGNSPWR=1",1000,DEBUG);
delay(50);
sendData("AT+CGNSSEQ=RMC",1000,DEBUG);

delay(150);
}
void loop() 
{
sendTabData("AT+CGNSINF",1000,DEBUG);
if (state !=0) {
Serial.println("State  :"+state);
Serial.println("Time  :"+timegps);
Serial.println("Latitude  :"+latitude);
Serial.println("Longitude  :"+longitude);
sim808.print("AT+CMGS=\"");
sim808.print(phone_no);
sim808.println("\"");
delay(300);
sim808.print("http://maps.google.com/maps?q=loc:");
sim808.print(latitude);
sim808.print(",");
sim808.print (longitude);
delay(200);
sim808.println((char)26); // End AT command with a ^Z, ASCII code 26
delay(200);
sim808.println();
delay(20000);
sim808.flush();
} else {
Serial.println("GPS Initializing…");
}
}
void sendTabData(String command , const int timeout , boolean debug){
sim808.println(command);
long int time = millis();
int i = 0;
while((time+timeout) > millis()){
while(sim808.available()){
char c = sim808.read();
if (c != ',') {
data[i] +=c;delay(100);
} else {
i++;
}
if (i == 5) {
delay(100);
goto exitL;
}
}
}exitL:
if (debug) {
state = data[1];
timegps = data[2];
latitude = data[3];
longitude =data[4];
}
}
String sendData (String command , const int timeout ,boolean debug){
String response = "";
sim808.println(command);
long int time = millis();
int i = 0;
while ( (time+timeout ) > millis()){
while (sim808.available()){
char c = sim808.read();
response +=c;
}
}
if (debug) 
{Serial.print(response);}
return response;
}

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

نستدعي مكتبة SoftwareSerial.h التي تسمح بالاتصال التسلسلي مع المنافذ الرقمية في لوحة الاردوينو.

#include <SoftwareSerial.h>

هنا نكتب المنافذ التي استخدمناها لربط لوحة الاردوينو مع وحدة اتصال وارسال واستقبال الإشارة (GPRS GSM Module)

المنفذ الرقمي 10 مع TX

والمنفذ الرقمي 11 مع RX.

SoftwareSerial sim808(11,10);

اكتب رقم الهاتف الذي تريد إرسال له بيانات الموقع، والمكوَن من عشرة أرقام.

char phone_no[] = "053366****"; // replace with your phone no.

عندما تتم قراءة بيانات الموقع سيتم إرسالها مباشرة إلى الهاتف المحمول عن طريق رسالة SMS.

String data[5];
#define DEBUG true
String state,timegps,latitude,longitude;
void setup() {
sim808.begin(9600);
Serial.begin(9600);
delay(50);
//sim808.print("AT+CSMP=17,167,0,0");  // set this parameter if empty SMS received
//delay(100);
sim808.print("AT+CMGF=1\r");
delay(400);
sendData("AT+CGNSPWR=1",1000,DEBUG);
delay(50);
sendData("AT+CGNSSEQ=RMC",1000,DEBUG);

delay(150);
}

في الدالة loop سيتم توضيح بيانات الموقع والوقت والحالة على شاشة الاتصال التسلسلي.

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

void loop() 
{
sendTabData("AT+CGNSINF",1000,DEBUG);
if (state !=0) {
Serial.println("State  :"+state);
Serial.println("Time  :"+timegps);
Serial.println("Latitude  :"+latitude);
Serial.println("Longitude  :"+longitude);
sim808.print("AT+CMGS=\"");
sim808.print(phone_no);
sim808.println("\"");
delay(300);
sim808.print("http://maps.google.com/maps?q=loc:");
sim808.print(latitude);
sim808.print(",");
sim808.print (longitude);
delay(200);
sim808.println((char)26); // End AT command with a ^Z, ASCII code 26
delay(200);
sim808.println();
delay(20000);
sim808.flush();
} else {
Serial.println("GPS Initializing…");
}
}

بعد إكمال الخطوات المطلوبة يمكنك رفع الكود البرمجي الخاص بنظام تحديد المواقع GPS على لوحة الاردوينو.

ستظهر البيانات على شاشة الاتصال التسلسلي وستصلك رسالة تتضمن رابط الموقع على خرائط قوقل Google map، وسيتم تحديثه كل فترة وإعادة إرساله بشكل تلقائي.تحديد المواقع GPS




ساعة الكلمات باللغة العربية

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

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

اردوينو

X1 أردوينو

سلك اردوينو

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

محول طاقة

X1 محول طاقة (5فولت -2 أمبير)

X1 وحدة الوقت الفعلي

أسلاك توصيل

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

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

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

شريط اضاءة

X1 شريط ثنائي القطب يحتوي على (52 وحدة )

سلك لحام

X1 سلك لحام

كاوية لحام

X1 أداة لحام

بكرة سلك

X1 بكرة سلك

أكليريك أسود

X1 لوح أكليريك أٍسود (50*40*3)

لوح أكليريك

X2 لوح اكليريك أبيض نافذ للضوء(50*40*3)

لحام أكليرك

لحام أكليريك

قاطع الليزر

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

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

التصميم

يمكنك تحميل التصميم من خلال الرابط

أولا: تصميم الواجهة التي تحتوي على الكلمات باللغة العربية

-الجزء الأول من الساعة يحتوي على الوقت بالساعات من الواحدة حتى الثانية عشرة وبعدها الوقت بالدقائق و يفصلهما (و/ إلا ) ليتم قراءة الوقت بشكل صحيح

– يتم عرض التغير في الوقت كل 5 خمس دقائق

– عندما تكون الدقائق 25 ستكون الساعة (و النصف وإلا خمس دقائق)

-عندما تكون الدقائق 35 ستكون الساعة (و النصف وخمس دقائق)

الساعة 10:25

ثانيا: تصميم شبكة حتى تفصل بين الكلمات المتجاورة

ثالثا: يتم تصميم الصندوق الذي يجمع هذه المكونات

التصنيع

 قبل البدء بهذه الخطوة يجب أن التأكد من برمجة و ضبط وحدة الساعة بالانتقال إلى جزء البرمجة في هذا الدرس أو من خلال رابط درس استخدام DS3231 RTC Module مع الاردوينو ، وو كذلك التحقق من شريط الثنائي المشع للضوء
-توزيع الثنائي المشع للضوء كما هو موضح بالصورة

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

توزيع وحدات الثنائي المشع للضوء

-تثبيت لوحة الأكليريك التي تسمح بنفاذ الضوء ثم لوحة الكلمات على السطح

 

البرمجة

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

#include <RTClib.h>
#include <Wire.h>
RTC_DS3231 rtc;
char t[32];
int dtminutes;
int dthours;
int minutes;
int hours;
void setup()

{
  Serial.begin(9600);
  Wire.begin();
  rtc.begin();
  rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
  //rtc.adjust(DateTime(2019, 1, 21, 5, 0, 0));
}
void loop()
{ DateTime now = rtc.now();
  sprintf(t, "%02d:%02d:%02d %02d/%02d/%02d", now.hour(), now.minute(), now.second(), now.day(), now.month(), now.year());  
  Serial.print(F("Date/Time: "));
  Serial.println(t);
  delay(1000);
}

ثانيا : التحقق من التحكم بشريط الثنائي المشع للضوء من خلال رفع الشفرة البرمجية التالية 

#include <FastLED.h>

#define LED_PIN     5
#define NUM_LEDS    64
#define BRIGHTNESS  140
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

CRGB Colors [13]{CRGB::Gray,CRGB::Orange,CRGB::Maroon,CRGB::Red,CRGB::Cyan,CRGB::Lime,CRGB::Green,CRGB::Purple,
                     CRGB::Yellow,CRGB::White,CRGB::Blue};

void setup() {
    delay( 3000 ); // power-up safety delay
    FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
    FastLED.setBrightness(  BRIGHTNESS );
    
}

void loop()
{
for (int j=0;j<13;j++)  
  for (int i=0;i<65;i++){
    leds[i]=  Colors[j];
    FastLED.show();
    delay(50);
    leds[i]=CHSV( 0 , 0, 0);
    FastLED.show();
  }
}

ثالثا: بعد التحقق من ضبط الساعة و عمل شريط الثنائي المشع للضوء، نرفع الشفرة البرمجية من خلال الأمر التالي

#include <FastLED.h>                      
#include <RTClib.h>
#include <Wire.h>
#define LED_PIN     5
#define NUM_LEDS    65
#define BRIGHTNESS  200
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
RTC_DS3231 rtc;
int minutes;
int hours;
CRGB Colors[11]{CRGB::Chartreuse,CRGB::Orange,CRGB::BlueViolet,CRGB::Red,CRGB::Cyan,CRGB::Pink,
CRGB::Green,CRGB::Purple,CRGB::Yellow,CRGB::White,CRGB::Blue};
void setup()
{
  delay( 3000 );
  FastLED.clear() ;
  Serial.begin(9600);
  Wire.begin();
  rtc.begin();
  rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
  FastLED.addLeds<WS2812, LED_PIN, RGB>(leds, NUM_LEDS);
    FastLED.setBrightness(  BRIGHTNESS );
  //rtc.adjust(DateTime(2019, 1, 21, 5, 0, 0));
}
void huors()
{
if (hours==1){
     //28-29-30-31
      leds[28] = CRGB::Black;
      leds[29] = CRGB::Black;
      leds[30] = CRGB::Black;
      leds[31] = CRGB::Black;
      //7-8-9
      leds[7] = CRGB::Blue;
      leds[8] = CRGB::Blue;
      leds[9] = CRGB::Blue;
      FastLED.show();
      }
    if (hours==2){
      //7-8-9
      leds[7] = CRGB::Black;
      leds[8] = CRGB::Black;
      leds[9] = CRGB::Black;
      //18-19
      leds[18] = CRGB::Blue;
      leds[19] = CRGB::Blue;
     FastLED.show();
    }

    if (hours==3){
      //18-19
      leds[18] = CRGB::Black;
      leds[19] = CRGB::Black;
      //4-5-6
      leds[4] = CRGB::Blue;
      leds[5] = CRGB::Blue;
      leds[6] = CRGB::Blue;
      FastLED.show();
    }

    if (hours==4){
       //4-5-6
      leds[4] = CRGB::Black;
      leds[5] = CRGB::Black;
      leds[6] = CRGB::Black;
      //35-36
      leds[35] = CRGB::Blue;
      leds[36] = CRGB::Blue;  
      FastLED.show();
    }
      if (hours==5){//20-21
      //35-36
      leds[35] = CRGB::Black;
      leds[36] = CRGB::Black;
      //20-21
      leds[20] = CRGB::Blue;
      leds[21] = CRGB::Blue;
      FastLED.show();
      }
    if (hours==6){
      //20-21
      leds[20] = CRGB::Black;
      leds[21] = CRGB::Black;
      //32-33-34
      leds[32] = CRGB::Blue;
      leds[33] = CRGB::Blue;
      leds[34] = CRGB::Blue;
     FastLED.show();
    }
    if (hours==7){
      //32-33-34
      leds[32] = CRGB::Black;
      leds[33] = CRGB::Black;
      leds[34] = CRGB::Black;
      //24-25
      leds[24] = CRGB::Blue;
      leds[25] = CRGB::Blue;
      FastLED.show();
    }
    if (hours==8){
     //24-25
      leds[24] = CRGB::Black;
      leds[25] = CRGB::Black;
      //22-23
      leds[22] = CRGB::Blue;
      leds[23] = CRGB::Blue;
      FastLED.show();
    }
     if (hours==9){
      //22-23
      leds[22] = CRGB::Black;
      leds[23] = CRGB::Black;
      //26-27
      leds[26] = CRGB::Blue;
      leds[27] = CRGB::Blue;
      FastLED.show();
      }
    if (hours==10){
      leds[26] = CRGB::Black;
      leds[27] = CRGB::Black;
      //15-16-17
      leds[15] = CRGB::Blue;
      leds[16] = CRGB::Blue;
      leds[17] = CRGB::Blue;
     FastLED.show();
    }
    if (hours==11){
      //15-16-17
      leds[15] = CRGB::Black;
      leds[16] = CRGB::Black;
      leds[17] = CRGB::Black;
      //10-11-12-13-14
      leds[10] = CRGB::Blue;
      leds[11] = CRGB::Blue;
      leds[12] = CRGB::Blue;
      leds[13] = CRGB::Blue;
      leds[14] = CRGB::Blue;
      FastLED.show();
    }
    if (hours==12){
            //10-11-12-13-14
      leds[10] = CRGB::Black;
      leds[11] = CRGB::Black;
      leds[12] = CRGB::Black;
      leds[13] = CRGB::Black;
      leds[14] = CRGB::Black;
      //28-29-30-31
      leds[28] = CRGB::Blue;
      leds[29] = CRGB::Blue;
      leds[30] = CRGB::Blue;
      leds[31] = CRGB::Blue;
     FastLED.show();
    } }
    void Topast(){
      if (minutes){
      leds[26] = CRGB::Black;
      leds[27] = CRGB::Black;
      //15-16-17
      leds[15] = CRGB::Blue;
      leds[16] = CRGB::Blue;
      leds[17] = CRGB::Blue;
     FastLED.show();
    }
}

void mints(){
if (minutes==0){
      leds[43] = CRGB::Black;
      leds[51] = CRGB::Black;
      leds[52] = CRGB::Black;
}

if ((minutes==5)  or (minutes==55)){
      leds[43] = CRGB::Blue;
      leds[51] = CRGB::Blue;
      leds[52] = CRGB::Blue;
      leds[48] = CRGB::Black;
}
if ((minutes==10)  or (minutes==50)){
  leds[43] = CRGB::Black;
  leds[42] = CRGB::Black;
  leds[48] = CRGB::Blue;
  leds[51] = CRGB::Blue;
  leds[52] = CRGB::Blue;
}
if ((minutes==15)  or (minutes==45)){
  leds[42] = CRGB::Blue;
  leds[48] = CRGB::Black;
  leds[44] = CRGB::Black;
  leds[51] = CRGB::Black;
  leds[52] = CRGB::Black;
}
if ((minutes==20)  or (minutes==40)){
    leds[42] = CRGB::Black;
    leds[44] = CRGB::Blue;
    leds[51] = CRGB::Black;
    leds[52] = CRGB::Black; 
    leds[38] = CRGB::Black;
    leds[39] = CRGB::Black;
     leds[40] = CRGB::Black;
    leds[41] = CRGB::Black;
}
if (minutes==30) {
   
    leds[46] = CRGB::Black;
    leds[47] = CRGB::Black;
    leds[38] = CRGB::Black;
    leds[39] = CRGB::Black;
    leds[51] = CRGB::Black;
    leds[52] = CRGB::Black;
    leds[49] = CRGB::Blue;
    leds[50] = CRGB::Blue;
}
if (minutes==25) {
    leds[44] = CRGB::Black;
    leds[38] = CRGB::Blue;
    leds[39] = CRGB::Blue;
    leds[46] = CRGB::Blue;
    leds[47] = CRGB::Blue;
    leds[51] = CRGB::Blue;
    leds[52] = CRGB::Blue;
}

if (minutes==35) {
leds[38] = CRGB::Blue;
    leds[39] = CRGB::Blue;
    leds[49] = CRGB::Black;
    leds[50] = CRGB::Black;
    leds[40] = CRGB::Blue;
    leds[41] = CRGB::Blue;
    leds[51] = CRGB::Blue;
    leds[52] = CRGB::Blue;
}
}
void pastto(){
  if (minutes=35 and minutes>=5) {
  leds[37] = CRGB::Blue;
  leds[45] = CRGB::Black;}
  if (minutes<=55 and minutes>=40) {
  leds[45] = CRGB::Blue;
  leds[37] = CRGB::Black;}
if (minutes=0) {
  leds[45] = CRGB::Black; }
}
void loop()
{  
  leds[0] = CRGB::White;
  leds[1] = CRGB::White;
  leds[2] = CRGB::White;
  leds[3] = CRGB::White;
  DateTime now = rtc.now();
   minutes = ("%02d", now.minute() );
    minutes=(minutes/5)*5;
   hours = ("%02d", now.hour() );
 if (hours>12){
  hours=hours-12;
  if (minutes>35 and minutes<=55)
  hours=hours+1;}
  mints();
//Serial.print (hours);
//Serial.print (":");
//Serial.println (minutes);
delay(300);
huors();
pastto();
}

شرح البرمجة 

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

#include <FastLED.h>                     
#include <RTClib.h>
#include <Wire.h>

نعرف المتغيرات قيمة كل متغير ظاهرة حسب اسم المتغير

#define LED_PIN     5
#define NUM_LEDS    65
#define BRIGHTNESS  200
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
RTC_DS3231 rtc;
int minutes;
int hours;
CRGB Colors[11]{CRGB::Chartreuse,CRGB::Orange,CRGB::BlueViolet,CRGB::Red,CRGB::Cyan,CRGB::Pink,
CRGB::Green,CRGB::Purple,CRGB::Yellow,CRGB::White,CRGB::Blue};

في دالة (setup) نكتب أوامر التهيئة الرئيسية الخاصة بشريط الثنائي المشع للضوء و وحدة الوقت الفعلي

void setup()
{
delay( 3000 );
FastLED.clear() ;
Serial.begin(9600);
Wire.begin();
rtc.begin();
rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
FastLED.addLeds<WS2812, LED_PIN, RGB>(leds, NUM_LEDS);
  FastLED.setBrightness(  BRIGHTNESS );
//rtc.adjust(DateTime(2019, 1, 21, 5, 0, 0));
}

سنقوم بعمل ثلاث دوال واحدة تعمل على التحكم بثنائي المشع للضوء الخاص بالساعات
وواحدة للتحكم بثنائي المشع للضوء الخاص بالدقائق
وواحدة للتحكم بثنائي المشع للضوء الخاص بكل من ( إلا / و)

void huors()
{
if (hours==1){
   //28-29-30-31
    leds[28] = CRGB::Black;
    leds[29] = CRGB::Black;
    leds[30] = CRGB::Black;
    leds[31] = CRGB::Black;
    //7-8-9
    leds[7] = CRGB::Blue;
    leds[8] = CRGB::Blue;
    leds[9] = CRGB::Blue;
    FastLED.show();
    }
  if (hours==2){
    //7-8-9
    leds[7] = CRGB::Black;
    leds[8] = CRGB::Black;
    leds[9] = CRGB::Black;
    //18-19
    leds[18] = CRGB::Blue;
    leds[19] = CRGB::Blue;
   FastLED.show();
    }

  if (hours==3){
    //18-19
    leds[18] = CRGB::Black;
    leds[19] = CRGB::Black;
    //4-5-6
    leds[4] = CRGB::Blue;
    leds[5] = CRGB::Blue;
    leds[6] = CRGB::Blue;
    FastLED.show();
    }

  if (hours==4){
     //4-5-6
    leds[4] = CRGB::Black;
    leds[5] = CRGB::Black;
    leds[6] = CRGB::Black;
    //35-36
    leds[35] = CRGB::Blue;
    leds[36] = CRGB::Blue;  
    FastLED.show();
  }
    if (hours==5){//20-21
    //35-36
    leds[35] = CRGB::Black;
    leds[36] = CRGB::Black;
    //20-21
    leds[20] = CRGB::Blue;
    leds[21] = CRGB::Blue;
    FastLED.show();
    }
  if (hours==6){
    //20-21
    leds[20] = CRGB::Black;
    leds[21] = CRGB::Black;
    //32-33-34
    leds[32] = CRGB::Blue;
    leds[33] = CRGB::Blue;
    leds[34] = CRGB::Blue;
   FastLED.show();
  }
  if (hours==7){
    //32-33-34
    leds[32] = CRGB::Black;
    leds[33] = CRGB::Black;
    leds[34] = CRGB::Black;
    //24-25
    leds[24] = CRGB::Blue;
    leds[25] = CRGB::Blue;
    FastLED.show();
  }
  if (hours==8){
  //24-25
    leds[24] = CRGB::Black;
    leds[25] = CRGB::Black;
    //22-23
    leds[22] = CRGB::Blue;
    leds[23] = CRGB::Blue;
    FastLED.show();
  }
   if (hours==9){
    //22-23
    leds[22] = CRGB::Black;
    leds[23] = CRGB::Black;
    //26-27
    leds[26] = CRGB::Blue;
    leds[27] = CRGB::Blue;
    FastLED.show();
    }
  if (hours==10){
    leds[26] = CRGB::Black;
    leds[27] = CRGB::Black;
      //15-16-17
    leds[15] = CRGB::Blue;
    leds[16] = CRGB::Blue;
    leds[17] = CRGB::Blue;
   FastLED.show();
  }
  if (hours==11){
    //15-16-17
    leds[15] = CRGB::Black;
    leds[16] = CRGB::Black;
    leds[17] = CRGB::Black;
    //10-11-12-13-14
    leds[10] = CRGB::Blue;
    leds[11] = CRGB::Blue;
    leds[12] = CRGB::Blue;
    leds[13] = CRGB::Blue;
    leds[14] = CRGB::Blue;
    FastLED.show();
  }
  if (hours==12){
          //10-11-12-13-14
    leds[10] = CRGB::Black;
    leds[11] = CRGB::Black;
    leds[12] = CRGB::Black;
    leds[13] = CRGB::Black;
    leds[14] = CRGB::Black;
    //28-29-30-31
    leds[28] = CRGB::Blue;
    leds[29] = CRGB::Blue;
    leds[30] = CRGB::Blue;
    leds[31] = CRGB::Blue;
   FastLED.show();
  } }
  void Topast(){
    if (minutes){
    leds[26] = CRGB::Black;
    leds[27] = CRGB::Black;
    //15-16-17
    leds[15] = CRGB::Blue;
    leds[16] = CRGB::Blue;
    leds[17] = CRGB::Blue;
   FastLED.show();
  }
}

void mints(){
if (minutes==0){
    leds[43] = CRGB::Black;
    leds[51] = CRGB::Black;
    leds[52] = CRGB::Black;
}

if ((minutes==5)  or (minutes==55)){
    leds[43] = CRGB::Blue;
    leds[51] = CRGB::Blue;
    leds[52] = CRGB::Blue;
    leds[48] = CRGB::Black;
}
if ((minutes==10)  or (minutes==50)){
leds[43] = CRGB::Black;
leds[42] = CRGB::Black;
leds[48] = CRGB::Blue;
leds[51] = CRGB::Blue;
leds[52] = CRGB::Blue;
}
if ((minutes==15)  or (minutes==45)){
leds[42] = CRGB::Blue;
leds[48] = CRGB::Black;
leds[44] = CRGB::Black;
leds[51] = CRGB::Black;
leds[52] = CRGB::Black;
}
if ((minutes==20)  or (minutes==40)){
  leds[42] = CRGB::Black;
  leds[44] = CRGB::Blue;
  leds[51] = CRGB::Black;
  leds[52] = CRGB::Black; 
   leds[38] = CRGB::Black;
  leds[39] = CRGB::Black;
   leds[40] = CRGB::Black;
  leds[41] = CRGB::Black;
}
if (minutes==30) {
   leds[46] = CRGB::Black;
  leds[47] = CRGB::Black;
  leds[38] = CRGB::Black;
  leds[39] = CRGB::Black;
  leds[51] = CRGB::Black;
  leds[52] = CRGB::Black;
  leds[49] = CRGB::Blue;
  leds[50] = CRGB::Blue;
}
if (minutes==25) {
  leds[44] = CRGB::Black;
  leds[38] = CRGB::Blue;
  leds[39] = CRGB::Blue;
  leds[46] = CRGB::Blue;
  leds[47] = CRGB::Blue;
  leds[51] = CRGB::Blue;
  leds[52] = CRGB::Blue;
}

if (minutes==35) {
leds[38] = CRGB::Blue;
  leds[39] = CRGB::Blue;
  leds[49] = CRGB::Black;
  leds[50] = CRGB::Black;
  leds[40] = CRGB::Blue;
  leds[41] = CRGB::Blue;
  leds[51] = CRGB::Blue;
  leds[52] = CRGB::Blue;
}
}
void pastto(){
if (minutes=35 and minutes>=5) {
leds[37] = CRGB::Blue;
leds[45] = CRGB::Black;}
if (minutes<=55 and minutes>=40) {
leds[45] = CRGB::Blue;
leds[37] = CRGB::Black;}
if (minutes=0) {
leds[45] = CRGB::Black; }
}

في دالة (loop) سنعرف قيمة الوقت و نجري عمليات رياضية كالتالي
-تغير الدقائق لأحد مضاعفات الخمسة
-قراءة الساعة بصيغة اثنى عشر ساعة
-زيادة قيمة الساعة بواحد عندما تكون الدقائق أكبر من 35

استدعاء الدوال الثلاثة الخاصة بالتحكم بالثنائي المشع للضوء

void loop()
{ 
leds[0] = CRGB::White;
leds[1] = CRGB::White;
leds[2] = CRGB::White;
leds[3] = CRGB::White;
DateTime now = rtc.now();
 minutes = ("%02d", now.minute() );
  minutes=(minutes/5)*5;
 hours = ("%02d", now.hour() );
if (hours>12){
hours=hours-12;
if (minutes>35 and minutes<=55)
hours=hours+1;}
mints();
//Serial.print (hours);
//Serial.print (":");
//Serial.println (minutes);
delay(300);
huors();
pastto();
}

 

ساعة بالكلمات العربية




محول الأنظمة العددية باستخدام الاردوينو

مقدمة

في هذا الدرس سنتعلم طريقة برمجة محول الأنظمة العددية باستخدام الاردوينو ولوحة المفاتيح 4*4. بحيث يتمكن المستخدم من كتابة الرقم ثم تحويله إلى أي قيمة عددية آخرى وذلك بالنقر على الأحرف A و B و C و D.

الأنظمة العددية

الأنظمة العددية

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

1- النظام العشري: هو النظام المستخدم فى الحياة اليومية والأساس له 10
2- النظام الثنائي: هو النظام المستخدم في أجهزة الحاسب وهو يكون 0 أو 1 والأساس له 2
3- النظام الثماني: هو النظام يكون 0 1 2 3 4 5 6 7  والأساس له 8
4- النظام السادس عشر: هو النظام يكون 0 1 2 3 4 5 6 7 8 9 F E D C B A والأساس له 16

الأنظمة العددية

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

number-conversion-by-arduino

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

number-conversion-by-arduino

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

number-conversion-by-arduino

لوحة مفاتيح

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

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

الأنظمة العددية

1× 40 رأس دبوس

الأنظمة العددية

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

الأنظمة العددية

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

الأنظمة العددية

1× 2C / IIC Serial Interface Module

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

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

البرمجة

سنقوم برفع الكود البرمجي لمشروع محول الأنظمة العددية على لوحة الاردوينو باستخدام برنامج اردوينو IDE.

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display


int NumberToBeDisplayed = 0;
int TempNum = 0;
/*Rows of keypad connected to D6-D9 of Arduino*/
int r1=6;
int r2=7;
int r3=8;
int r4=9;
/*Colums of keypad connected to D10-D13 of Arduino*/
int c1=10;
int c2=11;
int c3=12;
int c4=13;
/*Declared four variable to read colums status*/
int col1;
int col2;
int col3;
int col4;
void setup()
{
/*Declared Row pins as OUTPUT*/
pinMode(r1,OUTPUT);
pinMode(r2,OUTPUT);
pinMode(r3,OUTPUT);
pinMode(r4,OUTPUT);
/*Declared Column pins as OUTPUT*/
pinMode(c1,INPUT);
pinMode(c2,INPUT);
pinMode(c3,INPUT);
pinMode(c4,INPUT);
digitalWrite(c1,HIGH);
digitalWrite(c2,HIGH);
digitalWrite(c3,HIGH);
digitalWrite(c4,HIGH);
pinMode(4,OUTPUT);
/*lcd brightness terminal connected*/
digitalWrite(4,0);
lcd.begin();
lcd.setCursor(0,0);
lcd.print("A:Oct B:Binary");
lcd.setCursor(0,1);
lcd.print("C:Hex D:Decimal");
lcd.setCursor(0,1);
delay(2000);
lcd.clear();
lcd.setCursor(0,1);
}
void loop()
{
digitalWrite(r1,LOW);
digitalWrite(r2,HIGH);
digitalWrite(r3,HIGH);
digitalWrite(r4,HIGH);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
lcd.print("1");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 1;
while(digitalRead(c1) == LOW);
}
else if(col2==LOW)
{
lcd.print("2");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 2;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
lcd.print("3");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 3;
while(digitalRead(c3) == LOW);
}
else if(col4==0)
{
lcd.clear();
lcd.print("—- OCTAL —–");
lcd.setCursor(0,1);
TempNum = ConvertDecimalToBase(NumberToBeDisplayed,8);
lcd.print(TempNum);
while(digitalRead(c4) == LOW);
}
digitalWrite(r1,HIGH);
digitalWrite(r2,LOW);
digitalWrite(r3,HIGH);
digitalWrite(r4,HIGH);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
lcd.print("4");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 4;
while(digitalRead(c1) == LOW);
}
else if(col2==LOW)
{
lcd.print("5");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 5;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
lcd.print("6");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 6;
while(digitalRead(c3) == LOW);
}
else if(col4==LOW)
{
delay(50);
lcd.clear();
lcd.print("—- BINARY —-");
lcd.setCursor(0,1);
for(int i=15 ; i>=0 ;i--)
{
lcd.print((NumberToBeDisplayed >> i & 1));
}
while(digitalRead(c4) == LOW);
}
digitalWrite(r1,HIGH);
digitalWrite(r2,HIGH);
digitalWrite(r3,LOW);
digitalWrite(r4,HIGH);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
lcd.print("7");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 7;
while(digitalRead(c1) == LOW);
}
else if(col2==LOW)
{
lcd.print("8");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 8;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
lcd.print("9");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 9;
while(digitalRead(c3) == LOW);
}
else if(col4==LOW)
{
delay(50);
lcd.clear();
lcd.print("- HEXADECIMAL –");
lcd.setCursor(0,1);
TempNum = NumberToBeDisplayed;
ConvertDecimalToHex(TempNum);
while(digitalRead(c4) == LOW);
}
digitalWrite(r1,HIGH);
digitalWrite(r2,HIGH);
digitalWrite(r3,HIGH);
digitalWrite(r4,LOW);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
delay(50);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("A:Oct B:Binary");
lcd.setCursor(0,1);
lcd.print(":Hex D:Decimal");
lcd.setCursor(0,1);
delay(2000);
lcd.clear();
NumberToBeDisplayed = 0;
while(digitalRead(c2) == LOW);
}
else if(col2==LOW)
{
lcd.print("0");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 0;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
delay(50);
lcd.clear();
lcd.print("—- OCTAL —–");
lcd.setCursor(0,1);
TempNum = ConvertDecimalToBase(NumberToBeDisplayed,8);
lcd.print(TempNum);
delay(1000);
lcd.clear();
lcd.print("—- BINARY —-");
lcd.setCursor(0,1);
for(int i=15 ; i>=0 ; i--)
{
lcd.print((NumberToBeDisplayed >> i & 1));
}
delay(1000);
lcd.clear();
lcd.print("- HEXADECIMAL –");
lcd.setCursor(0,1);
TempNum = NumberToBeDisplayed;
ConvertDecimalToHex(TempNum);
delay(1000);
lcd.clear();
lcd.print("— DECIMAL —-");
lcd.setCursor(0,1);
lcd.print(NumberToBeDisplayed);
delay(1000);
lcd.clear();
NumberToBeDisplayed = 0;
lcd.setCursor(0,0);
lcd.print("A:Oct B:Binary");
lcd.setCursor(0,1);
lcd.print("C:Hex D:Decimal");
lcd.setCursor(0,1);
delay(2000);
lcd.clear();
lcd.setCursor(0,1);
while(digitalRead(c3) == LOW);
}
else if(col4==LOW)
{
delay(50);
lcd.clear();
lcd.print("— DECIMAL —-");
lcd.setCursor(0,1);
lcd.print(NumberToBeDisplayed);
while(digitalRead(c4) == LOW);
}
}
int ConvertDecimalToBase(int n, int b)
{
int r=0, digitPos=1;
while (n)
{
r += (n%b)*digitPos;
n /= b;
digitPos *= 10;
}
return r;
}
void ConvertDecimalToHex(long int num)
{
long int rem[50],i=0,length=0;
while(num>0)
{
rem[i]=num%16;
num=num/16;
i++;
length++;
}
for(i=length-1 ; i>=0 ; i--)
{
switch(rem[i])
{
case 10:
lcd.print("A");
break;
case 11:
lcd.print("B");
break;
case 12:
lcd.print("C");
break;
case 13:
lcd.print("D");
break;
case 14:
lcd.print("E");
break;
case 15:
lcd.print("F");
break;
default :
lcd.print(rem[i]);
}
}
}

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

سنقوم في البداية باستدعاء مكتبة (LiquidCrystal_I2C.h) الخاصة بوحدة i2c والتي تحتوي على مجموعة أوامر برمجية نحتاجها في مشروع تحديد الاتجاهات.

ثم نضيفها للاردوينو IDE.

بتتبع المسار التالي:

Sketch > Include libraries > Add ZIP library 

ونضيف المجلد الذي قمنا بتحميله.

#include <LiquidCrystal_I2C.h>

نعرف عنوان وحدة i2c.

LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display

سيتم تعيين القيمة 0 للمتغير NumberToBeDisplayed.

int NumberToBeDisplayed = 0;

هنا يتم إيضاح طريقة ربط منافذ لوحة المفاتيح مع منافذ لوحة الاردوينو الرقمية.

/*Rows of keypad connected to D6-D9 of Arduino*/
int r1=6;
int r2=7;
int r3=8;
int r4=9;
/*Colums of keypad connected to D10-D13 of Arduino*/
int c1=10;
int c2=11;
int c3=12;
int c4=13;
/*Declared four variable to read colums status*/
int col1;
int col2;
int col3;
int col4;

 في الدالة Setup يتم تهيئة الشاشة الكرستالية استعدادًا لطباعة القيم عليها.

كما يتم تهيئة منافذ لوحة المفاتيح r1, r2,r3,r4, c1,c2,c3,c4.

void setup()
{
/*Declared Row pins as OUTPUT*/
pinMode(r1,OUTPUT);
pinMode(r2,OUTPUT);
pinMode(r3,OUTPUT);
pinMode(r4,OUTPUT);
/*Declared Column pins as OUTPUT*/
pinMode(c1,INPUT);
pinMode(c2,INPUT);
pinMode(c3,INPUT);
pinMode(c4,INPUT);
digitalWrite(c1,HIGH);
digitalWrite(c2,HIGH);
digitalWrite(c3,HIGH);
digitalWrite(c4,HIGH);
pinMode(4,OUTPUT);
/*lcd brightness terminal connected*/
digitalWrite(4,0);
lcd.begin();
lcd.setCursor(0,0);
lcd.print("A:Oct B:Binary");
lcd.setCursor(0,1);
lcd.print("C:Hex D:Decimal");
lcd.setCursor(0,1);
delay(2000);
lcd.clear();
lcd.setCursor(0,1);
}

في الدالة Loop سيتم برمجة الشاشة الكرستالية لتحتوي في البداية على واجهة تعريفية للمستخدم.

A: OCT (النظام الثماني)

B: Binary (النظام الثنائي)

C: Hex (النظام السادس عشر)

D: Dec (النظام العشري)

بعد ذلك ستظهر واجهة فارغة للمستخدم يمكن من خلالها كتابة أي رقم يريده وبعد ذلك يختار ما بين الأنظمة العددية (A,B, D و C)

ثم سيقوم النظام مباشرة بالتحويل حسب المطلوب.

لمسح الشاشة والبدء من جديد اضغظ على علامة النجمة *.

void loop()
{
digitalWrite(r1,LOW);
digitalWrite(r2,HIGH);
digitalWrite(r3,HIGH);
digitalWrite(r4,HIGH);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
lcd.print("1");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 1;
while(digitalRead(c1) == LOW);
}
else if(col2==LOW)
{
lcd.print("2");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 2;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
lcd.print("3");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 3;
while(digitalRead(c3) == LOW);
}
else if(col4==0)
{
lcd.clear();
lcd.print("—- OCTAL —–");
lcd.setCursor(0,1);
TempNum = ConvertDecimalToBase(NumberToBeDisplayed,8);
lcd.print(TempNum);
while(digitalRead(c4) == LOW);
}
digitalWrite(r1,HIGH);
digitalWrite(r2,LOW);
digitalWrite(r3,HIGH);
digitalWrite(r4,HIGH);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
lcd.print("4");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 4;
while(digitalRead(c1) == LOW);
}
else if(col2==LOW)
{
lcd.print("5");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 5;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
lcd.print("6");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 6;
while(digitalRead(c3) == LOW);
}
else if(col4==LOW)
{
delay(50);
lcd.clear();
lcd.print("—- BINARY —-");
lcd.setCursor(0,1);
for(int i=15 ; i>=0 ;i--)
{
lcd.print((NumberToBeDisplayed >> i & 1));
}
while(digitalRead(c4) == LOW);
}
digitalWrite(r1,HIGH);
digitalWrite(r2,HIGH);
digitalWrite(r3,LOW);
digitalWrite(r4,HIGH);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
lcd.print("7");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 7;
while(digitalRead(c1) == LOW);
}
else if(col2==LOW)
{
lcd.print("8");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 8;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
lcd.print("9");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 9;
while(digitalRead(c3) == LOW);
}
else if(col4==LOW)
{
delay(50);
lcd.clear();
lcd.print("- HEXADECIMAL –");
lcd.setCursor(0,1);
TempNum = NumberToBeDisplayed;
ConvertDecimalToHex(TempNum);
while(digitalRead(c4) == LOW);
}
digitalWrite(r1,HIGH);
digitalWrite(r2,HIGH);
digitalWrite(r3,HIGH);
digitalWrite(r4,LOW);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
delay(50);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("A:Oct B:Binary");
lcd.setCursor(0,1);
lcd.print(":Hex D:Decimal");
lcd.setCursor(0,1);
delay(2000);
lcd.clear();
NumberToBeDisplayed = 0;
while(digitalRead(c2) == LOW);
}
else if(col2==LOW)
{
lcd.print("0");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 0;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
delay(50);
lcd.clear();
lcd.print("—- OCTAL —–");
lcd.setCursor(0,1);
TempNum = ConvertDecimalToBase(NumberToBeDisplayed,8);
lcd.print(TempNum);
delay(1000);
lcd.clear();
lcd.print("—- BINARY —-");
lcd.setCursor(0,1);
for(int i=15 ; i>=0 ; i--)
{
lcd.print((NumberToBeDisplayed >> i & 1));
}
delay(1000);
lcd.clear();
lcd.print("- HEXADECIMAL –");
lcd.setCursor(0,1);
TempNum = NumberToBeDisplayed;
ConvertDecimalToHex(TempNum);
delay(1000);
lcd.clear();
lcd.print("— DECIMAL —-");
lcd.setCursor(0,1);
lcd.print(NumberToBeDisplayed);
delay(1000);
lcd.clear();
NumberToBeDisplayed = 0;
lcd.setCursor(0,0);
lcd.print("A:Oct B:Binary");
lcd.setCursor(0,1);
lcd.print("C:Hex D:Decimal");
lcd.setCursor(0,1);
delay(2000);
lcd.clear();
lcd.setCursor(0,1);
while(digitalRead(c3) == LOW);
}
else if(col4==LOW)
{
delay(50);
lcd.clear();
lcd.print("— DECIMAL —-");
lcd.setCursor(0,1);
lcd.print(NumberToBeDisplayed);
while(digitalRead(c4) == LOW);
}
}
int ConvertDecimalToBase(int n, int b)
{
int r=0, digitPos=1;
while (n)
{
r += (n%b)*digitPos;
n /= b;
digitPos *= 10;
}
return r;
}
void ConvertDecimalToHex(long int num)
{
long int rem[50],i=0,length=0;
while(num>0)
{
rem[i]=num%16;
num=num/16;
i++;
length++;
}
for(i=length-1 ; i>=0 ; i--)
{
switch(rem[i])
{
case 10:
lcd.print("A");
break;
case 11:
lcd.print("B");
break;
case 12:
lcd.print("C");
break;
case 13:
lcd.print("D");
break;
case 14:
lcd.print("E");
break;
case 15:
lcd.print("F");
break;
default :
lcd.print(rem[i]);
}
}
}

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

 




جهاز ألعاب فيديو محمول باستخدام الأردوينو

مقدمة

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

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

جهاز ألعاب فيديو محمول

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

جهاز ألعاب فيديو محمول

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

تحطيم الطوب

 شاشة كرستالية “2.8

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

قم بتثبيت الشاشة الكرستالية كما هو ظاهر بالصور عليك الانتباه للمنافذ يجب توصيلها في مكانها  الصحيح على لوحة الاردوينو.arduino-breakout-game

جهاز ألعاب فيديو محمول

 

البرمجة

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

  1. مكتبة Touchscreen 
  2. مكتبة  Adafruit-GFX-Library
  3. مكتبة MCUFRIEND_kbv
#include <Adafruit_GFX.h> // Core graphics library
#include <TouchScreen.h>
#include <MCUFRIEND_kbv.h>


#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
#define PRIMARY_COLOR 0x4A11
#define PRIMARY_LIGHT_COLOR 0x7A17
#define PRIMARY_DARK_COLOR 0x4016
#define PRIMARY_TEXT_COLOR 0x7FFF



MCUFRIEND_kbv tft;

#define LOWFLASH (defined(__AVR_ATmega328P__) && defined(MCUFRIEND_KBV_H_))


// Touch screen pressure threshold
#define MINPRESSURE 40
#define MAXPRESSURE 1000


// Touch screen calibration
const int16_t XP = 8, XM = A2, YP = A3, YM = 9; //240x320 ID=0x9341
const int16_t TS_LEFT = 122, TS_RT = 929, TS_TOP = 77, TS_BOT = 884;
const TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);


#define SCORE_SIZE 30
char scoreFormat[] = "%04d";
typedef struct gameSize_type {
int16_t x, y, width, height;
} gameSize_type;

gameSize_type gameSize;
uint16_t backgroundColor = BLACK;
int level;

const uint8_t BIT_MASK[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
uint8_t pointsForRow[] = {7, 7, 5, 5, 3, 3 , 1, 1};
#define GAMES_NUMBER 16
typedef struct game_type {
int ballsize;
int playerwidth;
int playerheight;
int exponent;
int top;
int rows;
int columns;
int brickGap;
int lives;
int wall[GAMES_NUMBER];
int initVelx;
int initVely;
} game_type;


game_type games[GAMES_NUMBER] =
// ballsize, playerwidth, playerheight, exponent, top, rows, columns, brickGap, lives, wall[8], initVelx, initVely
{
{ 10, 60, 8, 6, 40 , 8, 8, 3, 3, {0x18, 0x66, 0xFF, 0xDB, 0xFF, 0x7E, 0x24, 0x3C} , 28, -28},
{ 10, 50, 8, 6, 40 , 8, 8, 3, 3, {0xFF, 0x99, 0xFF, 0xE7, 0xBD, 0xDB, 0xE7, 0xFF} , 28, -28},
{ 10, 50, 8, 6, 40 , 8, 8, 3, 3, {0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55} , 28, -28},
{ 8, 50, 8, 6, 40 , 8, 8, 3, 3, {0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF} , 34, -34},
{ 10, 40, 8, 6, 40 , 8, 8, 3, 3, {0xFF, 0xAA, 0xAA, 0xFF, 0xFF, 0xAA, 0xAA, 0xFF} , 28, -28},
{ 10, 40, 8, 6, 40 , 8, 8, 3, 3, {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA} , 28, -28},
{ 12, 64, 8, 6, 60 , 4, 2, 3, 4, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 20, -20},
{ 12, 60, 8, 6, 60 , 5, 3, 3, 4, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 22, -22},
{ 10, 56, 8, 6, 30 , 6, 4, 3, 4, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 24, -24},
{ 10, 52, 8, 6, 30 , 7, 5, 3, 4, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 26, -26},
{ 8, 48, 8, 6, 30 , 8, 6, 3, 3, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 28, -28},
{ 8, 44, 8, 6, 30 , 8, 7, 3, 3, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 30, -30},
{ 8, 40, 8, 6, 30 , 8, 8, 3, 3, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 32, -32},
{ 8, 36, 8, 6, 40 , 8, 8, 3, 3, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 34, -34},
{ 8, 36, 8, 6, 40 , 8, 8, 3, 3, {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA} , 34, -34}
};

game_type* game;


typedef struct game_state_type {
uint16_t ballx;
uint16_t bally;
uint16_t ballxold;
uint16_t ballyold;
int velx;
int vely;
int playerx;
int playerxold;
int wallState[8];
int score;
int remainingLives;
int top;
int bottom;
int walltop;
int wallbottom ;
int brickheight;
int brickwidth;

};
game_state_type state;

//////////////////////////////////////////////////////////////
// ARDUINO SETUP
//////////////////////////////////////////////////////////////


void setup()
{
initTft(tft);
gameSize = {0, 0, tft.width(), tft.height()};
newGame(&games[0], &state, tft);
}
//////////////////////////////////////////////////////////////
// ARDUINO LOOP
//////////////////////////////////////////////////////////////

int selection = -1;

void loop(void)
{

selection = readUiSelection(game, &state, selection);


drawPlayer(game, &state);
// store old position to remove old pixels
state.playerxold = state.playerx;

// calculate new ball position x1 = x0 + vx * dt

// check max speed
if (abs( state.vely) > ((1 << game->exponent) - 1)) {
state.vely = ((1 << game->exponent) - 1) * ((state.vely > 0) - (state.vely < 0));
}
if (abs( state.velx) > ((1 << game->exponent) - 1)) {
state.velx = ((1 << game->exponent) - 1) * ((state.velx > 0) - (state.velx < 0));
}

state.ballx += state.velx;
state.bally += state.vely;

// check ball collisions and exit
checkBallCollisions(game, &state, state.ballx >> game->exponent, state.bally >> game->exponent);
checkBallExit(game, &state, state.ballx >> game->exponent, state.bally >> game->exponent);

// draw ball in new position
drawBall(state.ballx >> game->exponent, state.bally >> game->exponent, state.ballxold >> game->exponent, state.ballyold >> game->exponent, game->ballsize );

// store old position to remove old pixels
state.ballxold = state.ballx;
state.ballyold = state.bally;


// increment velocity
state.velx = (20 + (state.score >> 3 )) * ( (state.velx > 0) - (state.velx < 0));
state.vely = (20 + (state.score >> 3 )) * ( (state.vely > 0) - (state.vely < 0));

// if no bricks go to next level
if (noBricks(game, &state) && level < GAMES_NUMBER) {
level++;
newGame( &games[level], &state, tft);
} else if ( state.remainingLives <= 0) {
gameOverTouchToStart();
state.score = 0;
level = 0;
newGame(game, &state, tft);
}
}


void newGame(game_type* newGame, game_state_type * state, MCUFRIEND_kbv &tft) {
game = newGame;
setupState(game, state, tft);

clearDialog(gameSize);
updateLives(game->lives, state->remainingLives);
updateScore(state->score);

setupWall(game, state);


touchToStart();


clearDialog(gameSize);
updateLives(game->lives, state->remainingLives);
updateScore(state->score);
setupWall(game, state);

}

void setupStateSizes(game_type* game, game_state_type * state, MCUFRIEND_kbv &tft) {
state->bottom = tft.height() - 30;
state->brickwidth = tft.width() / game->columns;
state->brickheight = tft.height() / 24;
}

void setupState(game_type* game, game_state_type * state, MCUFRIEND_kbv &tft) {
setupStateSizes(game, state, tft);
for (int i = 0; i < game->rows ; i ++) {
state->wallState[i] = 0;
}
state->playerx = tft.width() / 2 - game->playerwidth / 2;
state->remainingLives = game->lives;
state->bally = state->bottom << game->exponent;
state->ballyold = state->bottom << game->exponent;
state->velx = game->initVelx;
state->vely = game->initVely;
}

void updateLives(int lives, int remainingLives) {

for (int i = 0; i < lives; i++) {
tft.fillCircle((1 + i) * 15, 15, 5, BLACK);
}

for (int i = 0; i < remainingLives; i++) {
tft.fillCircle((1 + i) * 15, 15, 5, YELLOW);
}
}
void setupWall(game_type * game, game_state_type * state) {


int colors[] = {RED, RED, BLUE, BLUE, YELLOW, YELLOW, GREEN, GREEN};
state->walltop = game->top + 40;
state->wallbottom = state->walltop + game->rows * state->brickheight;
for (int i = 0; i < game->rows; i++) {
for (int j = 0; j < game->columns; j++) {
if (isBrickIn(game->wall, j, i)) {
setBrick(state->wallState, j, i);
drawBrick(state, j, i, colors[i]);
}
}
}
}
void drawBrick(game_state_type * state, int xBrick, int yBrickRow, uint16_t backgroundColor) {
tft.fillRect((state->brickwidth * xBrick) + game->brickGap,
state->walltop + (state->brickheight * yBrickRow) + game->brickGap ,
state->brickwidth - game->brickGap * 2,
state->brickheight - game->brickGap * 2, backgroundColor);
}

boolean noBricks(game_type * game, game_state_type * state) {
for (int i = 0; i < game->rows ; i++) {
if (state->wallState[i]) return false;
}
return true;
}


void drawPlayer(game_type * game, game_state_type * state) {
// paint
tft.fillRect(state->playerx, state->bottom, game->playerwidth, game->playerheight, YELLOW);
if (state->playerx != state->playerxold) {
// remove old pixels
if (state->playerx < state->playerxold) {
tft.fillRect(state->playerx + game->playerwidth, state->bottom, abs(state->playerx - state->playerxold), game->playerheight, backgroundColor);
}
else {
tft.fillRect(state->playerxold, state->bottom, abs(state->playerx - state->playerxold), game->playerheight, backgroundColor);
}

}
}

void drawBall(int x, int y, int xold, int yold, int ballsize) {
// remove old pixels
//if (xold != x && yold != y) {
if (xold <= x && yold <= y) {
tft.fillRect(xold , yold, ballsize, y - yold, BLACK);
tft.fillRect(xold , yold, x - xold, ballsize, BLACK);
} else if (xold >= x && yold >= y) {
tft.fillRect(x + ballsize , yold, xold - x, ballsize, BLACK);
tft.fillRect(xold , y + ballsize, ballsize, yold - y, BLACK);
} else if (xold <= x && yold >= y) {
tft.fillRect(xold , yold, x - xold, ballsize, BLACK);
tft.fillRect(xold , y + ballsize, ballsize, yold - y, BLACK);
} else if (xold >= x && yold <= y) {
tft.fillRect(xold , yold, ballsize, y - yold, BLACK);
tft.fillRect(x + ballsize, yold, xold - x, ballsize, BLACK);
}
// paint new ball
tft.fillRect(x , y, ballsize, ballsize, YELLOW);
// }

}

void touchToStart() {
drawBoxedString(0, 200, " BREAKOUT", 3, YELLOW, BLACK);
drawBoxedString(0, 240, " TOUCH TO START", 2, RED, BLACK);
while (waitForTouch() < 0) {}
}

void gameOverTouchToStart() {
drawBoxedString(0, 180, " GAME OVER", 3, YELLOW, BLACK);
drawBoxedString(0, 220, " TOUCH TO START", 2, RED, BLACK);
while (waitForTouch() < 0) {}
}

void updateScore (int score) {
char buffer[5];
snprintf(buffer, sizeof(buffer), scoreFormat, score);
drawBoxedString(tft.width() - 50, 6, buffer, 2, YELLOW, PRIMARY_DARK_COLOR);
}

void checkBrickCollision(game_type* game, game_state_type * state, uint16_t x, uint16_t y) {
int x1 = x + game->ballsize;
int y1 = y + game->ballsize;
int collissions = 0;
collissions += checkCornerCollision(game, state, x, y);
collissions += checkCornerCollision(game, state, x1, y1);
collissions += checkCornerCollision(game, state, x, y1);
collissions += checkCornerCollision(game, state, x1, y);
if (collissions > 0 ) {
state->vely = (-1 * state->vely);
if ((((x % state->brickwidth) == 0) && ( state->velx < 0 ))
|| ((((x + game->ballsize) % state->brickwidth) == 0) && ( state->velx > 0 )) ) {
state->velx = (-1 * state->velx);
}
}

}
int checkCornerCollision(game_type * game, game_state_type * state, uint16_t x, uint16_t y) {
if ((y > state->walltop) && (y < state->wallbottom)) {
int yBrickRow = ( y - state->walltop) / state->brickheight;
int xBrickColumn = (x / state->brickwidth);
if (isBrickIn(state->wallState, xBrickColumn, yBrickRow) ) {
hitBrick(state, xBrickColumn, yBrickRow);
return 1;
}
}
return 0;
}
void hitBrick(game_state_type * state, int xBrick, int yBrickRow) {
state->score += pointsForRow[yBrickRow];
drawBrick(state, xBrick, yBrickRow, WHITE);
delay(16);
drawBrick(state, xBrick, yBrickRow, BLUE);
delay(8);
drawBrick(state, xBrick, yBrickRow, backgroundColor);
unsetBrick(state->wallState, xBrick, yBrickRow);
updateScore(state->score);
}

void checkBorderCollision(game_type * game, game_state_type * state, uint16_t x, uint16_t y) {
// check wall collision
if (x + game->ballsize >= tft.width()) {
state->velx = -abs(state->velx);

}
if (x <= 0 ) {
state->velx = abs(state->velx);
}
if (y <= SCORE_SIZE ) {
state->vely = abs(state->vely);
}
if (((y + game->ballsize) >= state->bottom)
&& ((y + game->ballsize) <= (state->bottom + game->playerheight))
&& (x >= state->playerx)
&& (x <= (state->playerx + game->playerwidth))) {
// change vel x near player borders
if (x > (state->playerx + game->playerwidth - 6)) {
state->velx = state->velx - 1;
} else if (x < state->playerx + 6) {
state->velx = state->velx + 1;
}
state->vely = -abs(state->vely) ;
}
}
void checkBallCollisions(game_type * game, game_state_type * state, uint16_t x, uint16_t y) {
checkBrickCollision(game, state, x, y);
checkBorderCollision(game, state, x, y);
}
void checkBallExit(game_type * game, game_state_type * state, uint16_t x, uint16_t y) {
if (((y + game->ballsize) >= tft.height())) {
state->remainingLives--;
updateLives(game->lives, state->remainingLives);
delay(500);
state->vely = -abs(state->vely) ;
}
}

void setBrick(int wall[], uint8_t x, uint8_t y) {
wall[y] = wall[y] | BIT_MASK[x];
}

void unsetBrick(int wall[], uint8_t x, uint8_t y) {
wall[y] = wall[y] & ~BIT_MASK[x];
}
boolean isBrickIn(int wall[], uint8_t x, uint8_t y) {
return wall[y] & BIT_MASK[x];
}
//////////////////////////////////////////////////////////////
// TFT SETUP
//////////////////////////////////////////////////////////////

void initTft(MCUFRIEND_kbv & tft) {
tft.reset();
uint16_t ID = tft.readID();
tft.begin(ID);
tft.setRotation(0);
}//////////////////////////////////////////////////////////////
// Screen Painting methods
//////////////////////////////////////////////////////////////

/**
Print a text in forecolor over a filled box with background color.
Rectangle size is calculated to include the whole text without margins

@param x horizontal coordinate in points left upper corner
@param y vertical coordinate in points left upper corner
@param fontsize font size of the text to print
@param foreColor forecolor of the text to print
@param backgroundColor color of the filled rect
@return void
*/
void drawBoxedString(const uint16_t x, const uint16_t y, const char* string, const uint16_t fontsize, const uint16_t foreColor, const uint16_t backgroundColor) {
tft.setTextSize(fontsize);
int16_t x1, y1;
uint16_t w, h;
tft.getTextBounds(string, x, y, &x1, &y1, &w, &h);
tft.fillRect(x, y, w, h, backgroundColor);
tft.setCursor(x, y);
tft.setTextColor(foreColor);
tft.print(string);
}/**
Clear the screen to the default backgrounds
@param void
@return void
*/
void clearDialog(gameSize_type gameSize) {
tft.fillRect(gameSize.x, gameSize.y, gameSize.width, gameSize.height, backgroundColor);
tft.fillRect(gameSize.x, gameSize.y, gameSize.width, SCORE_SIZE, PRIMARY_DARK_COLOR);
}

//////////////////////////////////////////////////////////////
// READ UI SELECTION
//////////////////////////////////////////////////////////////

/*
Checks if the user is selecting any of the visible enabled ui elements
The onTap callback of the selected element is called and it set as pressed

@param lastSelected the last selection
@return the new selection

*/
int readUiSelection(game_type * game, game_state_type * state, const int16_t lastSelected ) {
int16_t xpos, ypos; //screen coordinates
TSPoint tp = ts.getPoint(); //tp.x, tp.y are ADC values


// if sharing pins, you'll need to fix the directions of the touchscreen pins
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
// we have some minimum pressure we consider 'valid'
// pressure of 0 means no pressing!

if (tp.z > MINPRESSURE && tp.z < MAXPRESSURE) {
xpos = map(tp.x, TS_RT, TS_LEFT, 0, tft.width());
ypos = map(tp.y, TS_BOT, TS_TOP, 0, tft.height());
// are we in buttons area ?


if (xpos > tft.width() / 2) {
state->playerx += 2;
} else {
state->playerx -= 2;
}
if (state->playerx >= tft.width() - game->playerwidth) state->playerx = tft.width() - game->playerwidth;
if (state->playerx < 0) state->playerx = 0;
return 1;
}
#ifdef DEMO_MODE
state->playerx = (state->ballx >> game->exponent) - game->playerwidth / 2;
if (state->playerx >= tft.width() - game->playerwidth) state->playerx = tft.width() - game->playerwidth;
if (state->playerx < 0) state->playerx = 0;
#endif
return -1;
}
int waitForTouch() {
int16_t xpos, ypos; //screen coordinates
TSPoint tp = ts.getPoint(); //tp.x, tp.y are ADC values

// if sharing pins, you'll need to fix the directions of the touchscreen pins
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
// we have some minimum pressure we consider 'valid'
// pressure of 0 means no pressing!
if (tp.z > MINPRESSURE && tp.z < MAXPRESSURE) {
return 1;
}
return -1;
}

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

نستدعي المكتبات الضروية للشاشة الكرستالية مثل مكتبة Adafruit_GFX.h و TouchScreen.h و MCUFRIEND_kbv.h.

#include <Adafruit_GFX.h> // Core graphics library
#include <TouchScreen.h>
#include <MCUFRIEND_kbv.h>

نهيئ المتغيرات التي سنحتاجها في اللعبة مثل متغيرات الشاشة الكرستالية ومتغيرات لألوان الشاشة ومواقع اللاعب ومواقع اللبنات.

#define BLACK 0x0000
#define BLUE 0x001F
#define RED 0xF800
#define GREEN 0x07E0
#define CYAN 0x07FF
#define MAGENTA 0xF81F
#define YELLOW 0xFFE0
#define WHITE 0xFFFF
#define PRIMARY_COLOR 0x4A11
#define PRIMARY_LIGHT_COLOR 0x7A17
#define PRIMARY_DARK_COLOR 0x4016
#define PRIMARY_TEXT_COLOR 0x7FFF


MCUFRIEND_kbv tft;

#define LOWFLASH (defined(__AVR_ATmega328P__) && defined(MCUFRIEND_KBV_H_))


// Touch screen pressure threshold
#define MINPRESSURE 40
#define MAXPRESSURE 1000
// Touch screen calibration
const int16_t XP = 8, XM = A2, YP = A3, YM = 9; //240x320 ID=0x9341
const int16_t TS_LEFT = 122, TS_RT = 929, TS_TOP = 77, TS_BOT = 884;
const TouchScreen ts = TouchScreen(XP, YP, XM, YM, 300);

#define SCORE_SIZE 30
char scoreFormat[] = "%04d";
typedef struct gameSize_type {
int16_t x, y, width, height;
} gameSize_type;

gameSize_type gameSize;
uint16_t backgroundColor = BLACK;
int level;

const uint8_t BIT_MASK[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
uint8_t pointsForRow[] = {7, 7, 5, 5, 3, 3 , 1, 1};
#define GAMES_NUMBER 16
typedef struct game_type {
int ballsize;
int playerwidth;
int playerheight;
int exponent;
int top;
int rows;
int columns;
int brickGap;
int lives;
int wall[GAMES_NUMBER];
int initVelx;
int initVely;
} game_type;


game_type games[GAMES_NUMBER] =
// ballsize, playerwidth, playerheight, exponent, top, rows, columns, brickGap, lives, wall[8], initVelx, initVely
{
{ 10, 60, 8, 6, 40 , 8, 8, 3, 3, {0x18, 0x66, 0xFF, 0xDB, 0xFF, 0x7E, 0x24, 0x3C} , 28, -28},
{ 10, 50, 8, 6, 40 , 8, 8, 3, 3, {0xFF, 0x99, 0xFF, 0xE7, 0xBD, 0xDB, 0xE7, 0xFF} , 28, -28},
{ 10, 50, 8, 6, 40 , 8, 8, 3, 3, {0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55, 0xAA, 0x55} , 28, -28},
{ 8, 50, 8, 6, 40 , 8, 8, 3, 3, {0xFF, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xFF} , 34, -34},
{ 10, 40, 8, 6, 40 , 8, 8, 3, 3, {0xFF, 0xAA, 0xAA, 0xFF, 0xFF, 0xAA, 0xAA, 0xFF} , 28, -28},
{ 10, 40, 8, 6, 40 , 8, 8, 3, 3, {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA} , 28, -28},
{ 12, 64, 8, 6, 60 , 4, 2, 3, 4, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 20, -20},
{ 12, 60, 8, 6, 60 , 5, 3, 3, 4, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 22, -22},
{ 10, 56, 8, 6, 30 , 6, 4, 3, 4, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 24, -24},
{ 10, 52, 8, 6, 30 , 7, 5, 3, 4, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 26, -26},
{ 8, 48, 8, 6, 30 , 8, 6, 3, 3, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 28, -28},
{ 8, 44, 8, 6, 30 , 8, 7, 3, 3, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 30, -30},
{ 8, 40, 8, 6, 30 , 8, 8, 3, 3, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 32, -32},
{ 8, 36, 8, 6, 40 , 8, 8, 3, 3, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} , 34, -34},
{ 8, 36, 8, 6, 40 , 8, 8, 3, 3, {0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA} , 34, -34}
};

game_type* game;

typedef struct game_state_type {
uint16_t ballx;
uint16_t bally;
uint16_t ballxold;
uint16_t ballyold;
int velx;
int vely;
int playerx;
int playerxold;
int wallState[8];
int score;
int remainingLives;
int top;
int bottom;
int walltop;
int wallbottom ;
int brickheight;
int brickwidth;

};
game_state_type state;

في الدالة setup نعين القيمة الصفر للمتغيرات حتى يبدأ اللاعب باللعب حينها تتغير القيم.

void setup()
{
initTft(tft);
gameSize = {0, 0, tft.width(), tft.height()};
newGame(&games[0], &state, tft);
}

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

أيضًا يتم تهيئة واجهات اللعبة وتعيين الدوال المسؤولة عن حساب المحاولات وحساب عدد اللبنات المحطمة.

void loop(void)
{


selection = readUiSelection(game, &state, selection);

drawPlayer(game, &state);
// store old position to remove old pixels
state.playerxold = state.playerx;

// calculate new ball position x1 = x0 + vx * dt

// check max speed
if (abs( state.vely) > ((1 << game->exponent) - 1)) {
state.vely = ((1 << game->exponent) - 1) * ((state.vely > 0) - (state.vely < 0));
}
if (abs( state.velx) > ((1 << game->exponent) - 1)) {
state.velx = ((1 << game->exponent) - 1) * ((state.velx > 0) - (state.velx < 0));
}

state.ballx += state.velx;
state.bally += state.vely;


// check ball collisions and exit
checkBallCollisions(game, &state, state.ballx >> game->exponent, state.bally >> game->exponent);
checkBallExit(game, &state, state.ballx >> game->exponent, state.bally >> game->exponent);

// draw ball in new position
drawBall(state.ballx >> game->exponent, state.bally >> game->exponent, state.ballxold >> game->exponent, state.ballyold >> game->exponent, game->ballsize );

// store old position to remove old pixels
state.ballxold = state.ballx;
state.ballyold = state.bally;

// increment velocity
state.velx = (20 + (state.score >> 3 )) * ( (state.velx > 0) - (state.velx < 0));
state.vely = (20 + (state.score >> 3 )) * ( (state.vely > 0) - (state.vely < 0));

// if no bricks go to next level
if (noBricks(game, &state) && level < GAMES_NUMBER) {
level++;
newGame( &games[level], &state, tft);
} else if ( state.remainingLives <= 0) {
gameOverTouchToStart();
state.score = 0;
level = 0;
newGame(game, &state, tft);
}
}


void newGame(game_type* newGame, game_state_type * state, MCUFRIEND_kbv &tft) {
game = newGame;
setupState(game, state, tft);

clearDialog(gameSize);
updateLives(game->lives, state->remainingLives);
updateScore(state->score);

setupWall(game, state);

touchToStart();

clearDialog(gameSize);
updateLives(game->lives, state->remainingLives);
updateScore(state->score);
setupWall(game, state);

}


void setupStateSizes(game_type* game, game_state_type * state, MCUFRIEND_kbv &tft) {
state->bottom = tft.height() - 30;
state->brickwidth = tft.width() / game->columns;
state->brickheight = tft.height() / 24;
}

void setupState(game_type* game, game_state_type * state, MCUFRIEND_kbv &tft) {
setupStateSizes(game, state, tft);
for (int i = 0; i < game->rows ; i ++) {
state->wallState[i] = 0;
}
state->playerx = tft.width() / 2 - game->playerwidth / 2;
state->remainingLives = game->lives;
state->bally = state->bottom << game->exponent;
state->ballyold = state->bottom << game->exponent;
state->velx = game->initVelx;
state->vely = game->initVely;
}

void updateLives(int lives, int remainingLives) {

for (int i = 0; i < lives; i++) {
tft.fillCircle((1 + i) * 15, 15, 5, BLACK);
}

for (int i = 0; i < remainingLives; i++) {
tft.fillCircle((1 + i) * 15, 15, 5, YELLOW);
}
}
void setupWall(game_type * game, game_state_type * state) {

int colors[] = {RED, RED, BLUE, BLUE, YELLOW, YELLOW, GREEN, GREEN};
state->walltop = game->top + 40;
state->wallbottom = state->walltop + game->rows * state->brickheight;
for (int i = 0; i < game->rows; i++) {
for (int j = 0; j < game->columns; j++) {
if (isBrickIn(game->wall, j, i)) {
setBrick(state->wallState, j, i);
drawBrick(state, j, i, colors[i]);
}
}
}
}
void drawBrick(game_state_type * state, int xBrick, int yBrickRow, uint16_t backgroundColor) {
tft.fillRect((state->brickwidth * xBrick) + game->brickGap,
state->walltop + (state->brickheight * yBrickRow) + game->brickGap ,
state->brickwidth - game->brickGap * 2,
state->brickheight - game->brickGap * 2, backgroundColor);
}

boolean noBricks(game_type * game, game_state_type * state) {
for (int i = 0; i < game->rows ; i++) {
if (state->wallState[i]) return false;
}
return true;
}

void drawPlayer(game_type * game, game_state_type * state) {
// paint
tft.fillRect(state->playerx, state->bottom, game->playerwidth, game->playerheight, YELLOW);
if (state->playerx != state->playerxold) {
// remove old pixels
if (state->playerx < state->playerxold) {
tft.fillRect(state->playerx + game->playerwidth, state->bottom, abs(state->playerx - state->playerxold), game->playerheight, backgroundColor);
}
else {
tft.fillRect(state->playerxold, state->bottom, abs(state->playerx - state->playerxold), game->playerheight, backgroundColor);
}

}
}

void drawBall(int x, int y, int xold, int yold, int ballsize) {
// remove old pixels
//if (xold != x && yold != y) {
if (xold <= x && yold <= y) {
tft.fillRect(xold , yold, ballsize, y - yold, BLACK);
tft.fillRect(xold , yold, x - xold, ballsize, BLACK);
} else if (xold >= x && yold >= y) {
tft.fillRect(x + ballsize , yold, xold - x, ballsize, BLACK);
tft.fillRect(xold , y + ballsize, ballsize, yold - y, BLACK);
} else if (xold <= x && yold >= y) {
tft.fillRect(xold , yold, x - xold, ballsize, BLACK);
tft.fillRect(xold , y + ballsize, ballsize, yold - y, BLACK);
} else if (xold >= x && yold <= y) {
tft.fillRect(xold , yold, ballsize, y - yold, BLACK);
tft.fillRect(x + ballsize, yold, xold - x, ballsize, BLACK);
}
// paint new ball
tft.fillRect(x , y, ballsize, ballsize, YELLOW);
// }

}


void touchToStart() {
drawBoxedString(0, 200, " BREAKOUT", 3, YELLOW, BLACK);
drawBoxedString(0, 240, " TOUCH TO START", 2, RED, BLACK);
while (waitForTouch() < 0) {}
}


void gameOverTouchToStart() {
drawBoxedString(0, 180, " GAME OVER", 3, YELLOW, BLACK);
drawBoxedString(0, 220, " TOUCH TO START", 2, RED, BLACK);
while (waitForTouch() < 0) {}
}


void updateScore (int score) {
char buffer[5];
snprintf(buffer, sizeof(buffer), scoreFormat, score);
drawBoxedString(tft.width() - 50, 6, buffer, 2, YELLOW, PRIMARY_DARK_COLOR);
}

void checkBrickCollision(game_type* game, game_state_type * state, uint16_t x, uint16_t y) {
int x1 = x + game->ballsize;
int y1 = y + game->ballsize;
int collissions = 0;
collissions += checkCornerCollision(game, state, x, y);
collissions += checkCornerCollision(game, state, x1, y1);
collissions += checkCornerCollision(game, state, x, y1);
collissions += checkCornerCollision(game, state, x1, y);
if (collissions > 0 ) {
tone(BUZZER_PORT, NOTE_A3, 4);
state->vely = (-1 * state->vely);
if ((((x % state->brickwidth) == 0) && ( state->velx < 0 ))
|| ((((x + game->ballsize) % state->brickwidth) == 0) && ( state->velx > 0 )) ) {
state->velx = (-1 * state->velx);
}
}

}
int checkCornerCollision(game_type * game, game_state_type * state, uint16_t x, uint16_t y) {
if ((y > state->walltop) && (y < state->wallbottom)) {
int yBrickRow = ( y - state->walltop) / state->brickheight;
int xBrickColumn = (x / state->brickwidth);
if (isBrickIn(state->wallState, xBrickColumn, yBrickRow) ) {
hitBrick(state, xBrickColumn, yBrickRow);
return 1;
}
}
return 0;
}
void hitBrick(game_state_type * state, int xBrick, int yBrickRow) {
state->score += pointsForRow[yBrickRow];
drawBrick(state, xBrick, yBrickRow, WHITE);
delay(16);
drawBrick(state, xBrick, yBrickRow, BLUE);
delay(8);
drawBrick(state, xBrick, yBrickRow, backgroundColor);
unsetBrick(state->wallState, xBrick, yBrickRow);
updateScore(state->score);
}

void checkBorderCollision(game_type * game, game_state_type * state, uint16_t x, uint16_t y) {
// check wall collision
if (x + game->ballsize >= tft.width()) {
state->velx = -abs(state->velx);
tone(BUZZER_PORT, NOTE_D4, 16);
}
if (x <= 0 ) {
state->velx = abs(state->velx);
tone(BUZZER_PORT, NOTE_D4, 16);
}
if (y <= SCORE_SIZE ) {
state->vely = abs(state->vely);
tone(BUZZER_PORT, NOTE_D4, 16);
}
if (((y + game->ballsize) >= state->bottom)
&& ((y + game->ballsize) <= (state->bottom + game->playerheight))
&& (x >= state->playerx)
&& (x <= (state->playerx + game->playerwidth))) {
// change vel x near player borders
if (x > (state->playerx + game->playerwidth - 6)) {
state->velx = state->velx - 1;
} else if (x < state->playerx + 6) {
state->velx = state->velx + 1;
}
state->vely = -abs(state->vely) ;
tone(BUZZER_PORT, NOTE_B5, 16);
}
}
void checkBallCollisions(game_type * game, game_state_type * state, uint16_t x, uint16_t y) {
checkBrickCollision(game, state, x, y);
checkBorderCollision(game, state, x, y);
}
void checkBallExit(game_type * game, game_state_type * state, uint16_t x, uint16_t y) {
if (((y + game->ballsize) >= tft.height())) {
state->remainingLives--;
updateLives(game->lives, state->remainingLives);
delay(500);
state->vely = -abs(state->vely) ;
}
}

void setBrick(int wall[], uint8_t x, uint8_t y) {
wall[y] = wall[y] | BIT_MASK[x];
}

void unsetBrick(int wall[], uint8_t x, uint8_t y) {
wall[y] = wall[y] & ~BIT_MASK[x];
}
boolean isBrickIn(int wall[], uint8_t x, uint8_t y) {
return wall[y] & BIT_MASK[x];
}
//////////////////////////////////////////////////////////////
// TFT SETUP
//////////////////////////////////////////////////////////////

void initTft(MCUFRIEND_kbv & tft) {
tft.reset();
uint16_t ID = tft.readID();
tft.begin(ID);
tft.setRotation(0);
}//////////////////////////////////////////////////////////////
// Screen Painting methods
//////////////////////////////////////////////////////////////

/**
Print a text in forecolor over a filled box with background color.
Rectangle size is calculated to include the whole text without margins

@param x horizontal coordinate in points left upper corner
@param y vertical coordinate in points left upper corner
@param fontsize font size of the text to print
@param foreColor forecolor of the text to print
@param backgroundColor color of the filled rect
@return void
*/
void drawBoxedString(const uint16_t x, const uint16_t y, const char* string, const uint16_t fontsize, const uint16_t foreColor, const uint16_t backgroundColor) {
tft.setTextSize(fontsize);
int16_t x1, y1;
uint16_t w, h;
tft.getTextBounds(string, x, y, &x1, &y1, &w, &h);
tft.fillRect(x, y, w, h, backgroundColor);
tft.setCursor(x, y);
tft.setTextColor(foreColor);
tft.print(string);
}/**
Clear the screen to the default backgrounds
@param void
@return void
*/
void clearDialog(gameSize_type gameSize) {
tft.fillRect(gameSize.x, gameSize.y, gameSize.width, gameSize.height, backgroundColor);
tft.fillRect(gameSize.x, gameSize.y, gameSize.width, SCORE_SIZE, PRIMARY_DARK_COLOR);
}

//////////////////////////////////////////////////////////////
// READ UI SELECTION
//////////////////////////////////////////////////////////////

/*
Checks if the user is selecting any of the visible enabled ui elements
The onTap callback of the selected element is called and it set as pressed

@param lastSelected the last selection
@return the new selection

*/
int readUiSelection(game_type * game, game_state_type * state, const int16_t lastSelected ) {
int16_t xpos, ypos; //screen coordinates
TSPoint tp = ts.getPoint(); //tp.x, tp.y are ADC values

// if sharing pins, you'll need to fix the directions of the touchscreen pins
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
// we have some minimum pressure we consider 'valid'
// pressure of 0 means no pressing!

if (tp.z > MINPRESSURE && tp.z < MAXPRESSURE) {
xpos = map(tp.x, TS_RT, TS_LEFT, 0, tft.width());
ypos = map(tp.y, TS_BOT, TS_TOP, 0, tft.height());
// are we in buttons area ?

if (xpos > tft.width() / 2) {
state->playerx += 2;
} else {
state->playerx -= 2;
}
if (state->playerx >= tft.width() - game->playerwidth) state->playerx = tft.width() - game->playerwidth;
if (state->playerx < 0) state->playerx = 0;
return 1;
}
#ifdef DEMO_MODE
state->playerx = (state->ballx >> game->exponent) - game->playerwidth / 2;
if (state->playerx >= tft.width() - game->playerwidth) state->playerx = tft.width() - game->playerwidth;
if (state->playerx < 0) state->playerx = 0;
#endif
return -1;
}
int waitForTouch() {
int16_t xpos, ypos; //screen coordinates
TSPoint tp = ts.getPoint(); //tp.x, tp.y are ADC values

// if sharing pins, you'll need to fix the directions of the touchscreen pins
pinMode(XM, OUTPUT);
pinMode(YP, OUTPUT);
// we have some minimum pressure we consider 'valid'
// pressure of 0 means no pressing!
if (tp.z > MINPRESSURE && tp.z < MAXPRESSURE) {
return 1;
}
return -1;
}

بعد ذلك ارفع الكود البرمجي للوحة الاردوينو.

شرح خطوات اللعبة

اللعبة لها 3 واجهات:

  • الواجهة الأولى: التحقق من جاهزية اللاعب للبدء في اللعبة وذلك بالنقر على TOUCH TO START.

Breakout game

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

تحطيم اللبنات

  • الواجهة الثالثة: هي واجهة تفيد اللاعب بانتهاء كل المحاولات GAME OVER وعليه البدء من جديد وذلك بالنقر على TOUCH TO START.

Breakout game

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

 




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

مقدمة

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

كهربية القلب

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

دولار نينجا

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

ninja-dollar-lcd-game

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

دولار نينجا

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

الأردوينو

مستشعر كهربية القلب (ECG)

حساس تخطيط كهربية القلب

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

يمكننا تمثيل كهربية القلب بيانيًا على هيئة مخطط ECG.

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

يحتوي الحساس على منافذ أساسية SDN ، LO + ، LO- ، OUTPUT ، 3.3V ، GND لتشغيله مع الاردوينو.
ومنافذ أخرى RA (الذراع الأيمن) و LA (الذراع اليسرى) و RL (الساق اليمنى) لتوصيل واستخدام أجهزة الاستشعار، أيضًا هناك ضوء LED يومض على صوت ضربات القلب.

ويوجد منفذ جانبي كما هو ظاهر باللون الأسود لتوصيل الأقطاب.

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

وصل الدائرة الكهربائية كما في الشكل:

كهربية القلب

البرمجة

ارفع كود تخطيط كهربية القلب على لوحة الاردوينو باستخدام برنامج Arduino IDE.

void setup() 
{
Serial.begin(9600);
pinMode(8, INPUT); // Setup for leads off detection LO +
pinMode(9, INPUT); // Setup for leads off detection LO -
}
void loop() 
{
if((digitalRead(8) == 1) || (digitalRead(9) == 1)){ //check if leads are removed
Serial.println("leads off!");
}
else{
Serial.println(analogRead(A0));
}
delay(1);
}

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

في الدالة ()setup يتم تعريف المتغيرات المستخدمة لقراءة المدخلات والتي تم توصيلها على لوحة الأردوينو مع المنافذ 8 و 9.

ويتم أيضًا تهيئة شاشة الاتصال التسلسلي استعدادًا لطباعة البيانات المقروءة.

void setup() 
{
Serial.begin(9600);
pinMode(8, INPUT); // Setup for leads off detection LO +
pinMode(9, INPUT); // Setup for leads off detection LO -
}

في الدالة ()loop يتم قراءة المدخلات من المنافذ 8و9 إذا كانت تحمل إشارة سيتم طباعة القيمة.

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

وستظهر رسالة leads off!.

void loop() 
{
if((digitalRead(8) == 1) || (digitalRead(9) == 1)){ //check if leads are removed
Serial.println("leads off!");
}
else
{
Serial.println(analogRead(A0));
}
delay(1);
}

من قائمة Tools نختار Serial Plotter ستظهر مباشرة النتائج.الاردوينو

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

 




اصنع راديو باستخدام اردوينو اونو ووحدة (TEA5767 FM)

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

 

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

 

اردوينو

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

سلك اردوينو

X1 سلك اردوينو

tea5767 fm radio

1X وحدة راديو (TEA5767 FM)

push buttons

X2 ضغاط التحكم 

مقاومة 220

X1 مقاومة 220 اوم

10 كيلو أوم

X2 مقاومة 10 كيلو أوم 

breadboard

1X لوحة تجارب

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

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

2*12 lcd

X1 شاشة كرستالية

40 رأس دبوس

X1 40 رأس دبوس

أسلاك توصيل

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

أسلاك توصيل

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

speaker 3.5 mm jack

1 X1 مكبر صوت بمنفذ 3.5 مللي متر

 

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

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

وحدة (TEA5767 FM)

وحدة راديو (TEA5767 FM) عبارة عن شريحة واحدة، تدعم بروتوكول الاتصال IC للتطبيقات منخفضة الجهد وتمثل راديو مصغر مضبوط رقميًا. يعمل في نطاقات الترددات الأوروبية والأمريكية واليابانية. تتكون الوحدة من منفذين 3.5 ملم، يستخدم الأول لتوصيل الهوائي، و زيادة مساحة التقاط الموجات والآخر منفذ السماعات.

tea5767 ports

كما تحتوي على 4 دبابيس:

VCC: وحدة إمداد الطاقة – 5 فولت
GND: الأرض
SLC: تزامن بروتوكول I2C يوصل مع المنفذ التناظري A5
SDA: بيانات بروتوكول I2C يوصل مع المنفذ التناظري A4

tea5767 pins

البرمجة

بالبداية ستحتاج إلى تحميل مكتبة (TEA5767 FM) و من ثم تثبيتها في بيئة تطوير الاردوينو (اردوينو IDE) يمكنك الاطلاع على الدرس الأول -تنصيب برنامج اردوينو  لمعرفة كيف يتم تثبيت المكتبات

 

#include<Wire.h>

#include <TEA5767Radio.h> 

#include <LiquidCrystal.h>

TEA5767Radio radio = TEA5767Radio();            
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);              
float frequency = 0;                            
int fUP = A0;                                   
int fDOWN = A1;                                
int count = 0;                                   
void setup()
{
  delay(1000);
  Wire.begin();
  frequency = 91.1;                       //starting frequency
  radio.setFrequency(frequency);
  lcd.begin(16,2);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("FM: ");
  lcd.setCursor(0, 1);
  lcd.print(frequency);
}void loop()
{
  
  if(digitalRead(fUP))                      
  {
    count=0;
    while (digitalRead(fUP))
    {
      count++;
      if(count > 0 && count <= 6)          
      {
        frequency += 0.1;
        frequencyUpdate();
        delay(200);
      }
      else if (count > 6 && count <= 2)   
      {
        frequency += 0.1;
        frequencyUpdate();
        delay(80);
      }
      else                                
      {
        frequency += 0.1;
        frequencyUpdate();
        delay(5);
      }
    }}
  
  
  
   if(digitalRead(fDOWN))                   
   {
     count = 0;
    while (digitalRead(fDOWN))            
    {
      count--;
      if(count < 0 && count >= -6)
      {
        frequency -= 0.1;
        frequencyUpdate();
        delay(200);
      }
      else if (count < -6 && count >= -12)
      {
        frequency -= 0.1;
        frequencyUpdate();
        delay(80);
      }
      else                                 
      {
        frequency -= 0.1;
        frequencyUpdate();
        delay(5);
      }
    }
   }  
  
}
void frequencyUpdate()                    
{
  frequency = constrain(frequency, 88.0, 108.0);
  lcd.setCursor(0,0);
  lcd.print("ESTACION DE FM:");
  lcd.setCursor(0,1);
  lcd.print(frequency);
  radio.setFrequency(frequency);
}

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

بالبدية سيتم استدعاء المكتبات الضرورية في المشروع

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

نعرف كائن باسم (radio)

TEA5767Radio radio = TEA5767Radio();

نعرف منافذ الشاشة الكرستالية

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

نعرف متغيرات تشيل إلى التردد و منفذ ضغاط التحكم لزيادة و تنقيص قيمة التردد
يمثل ضغاط التحكم الموصل مع المنفذ A0 مفتاح التحكم بزيادة التردد بقيمة 0.1
و يمثل ضغاط التحكم الموصل مع المنفذ A1 مفتاح التحكم بتنقيص التردد بقيمة 0.1

float frequency = 0; 
int fUP = A0; 
int fDOWN = A1; 
int count = 0;

في دالة التهيئة نهيئة الاتصال و الشاشة الكرستالية

void setup()
{
delay(1000);
Wire.begin();
frequency = 91.1; //starting frequency
radio.setFrequency(frequency);
lcd.begin(16,2);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("FM: ");
lcd.setCursor(0, 1);
lcd.print(frequency);
}

في دالة ال(void loop) نبرمج دوال التحكم زيادة ونقصان قيمة التردد بناء على مدخلات مفاتيح التحكم

void loop()
{

if(digitalRead(fUP)) 
{
count=0;
while (digitalRead(fUP))
{
count++;
if(count > 0 && count <= 6) 
{
frequency += 0.1;
frequencyUpdate();
delay(200);
}
else if (count > 6 && count <= 2) 
{
frequency += 0.1;
frequencyUpdate();
delay(80);
}
else 
{
frequency += 0.1;
frequencyUpdate();
delay(5);
}
}}



if(digitalRead(fDOWN)) 
{
count = 0;
while (digitalRead(fDOWN)) 
{
count--;
if(count < 0 && count >= -6)
{
frequency -= 0.1;
frequencyUpdate();
delay(200);
}
else if (count < -6 && count >= -12)
{
frequency -= 0.1;
frequencyUpdate();
delay(80);
}
else 
{
frequency -= 0.1;
frequencyUpdate();
delay(5);
}
}
} 

}

في دالة (frequencyUpdate) يتم تحديث قيم التررد بناء على المدخلات من المفاتيح

void frequencyUpdate() 
{
frequency = constrain(frequency, 88.0, 108.0);
lcd.setCursor(0,0);
lcd.print("STACION FM:");
lcd.setCursor(0,1);
lcd.print(frequency);
radio.setFrequency(frequency);
}

 




اختبر مهاراتك في الرياضيات مع الاردوينو

مقدمة

في هذا الدرس سنبرمج نظام يقيس مستوى مهاراتك في الرياضيات من خلال سلسلة من العمليات الرياضية مقسمة إلى 3 مستويات يمكنك اختيار المستوى المناسب لك بالبداية وسيظهر مؤقت يحسب الوقت المستغرق لحل هذه السلسلة، سنبرمج النظام باستخدام لوحة الاردوينو ولوحة المفاتيح والشاشة الكرستالية.

diy-arduino-lcd-keypad-maths

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

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

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

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

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

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

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

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

1× 40 رأس دبوس

تحديد الاتجاهات

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

تحديد الاتجاهات

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

diy-arduino-lcd-keypad-maths

1× 2C / IIC Serial Interface Module

diy-arduino-lcd-keypad-maths

لوحة مفاتيح

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

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

للمزيد حول لوحة المفاتيح يمكنك الرجوع للدرس لوحة المفاتيح Keypad.

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

البرمجة

ارفع الكود البرمجي على لوحة الاردوينو باستخدام برنامج اردوينو IDE.

#include <Keypad.h>
#include <LiquidCrystal_I2C.h> //LCD library
LiquidCrystal_I2C lcd(0x27, 16, 2);

byte sad[8] = 
{
B00000,
B01010,
B01010,
B00000,
B00000,
B01110,
B10001,
};


unsigned long time=0;
unsigned long inicio=0;
int m, mu=0,md=0; 
int s, su=0,sd=0;
int c,cu,cd=0;
byte intento=0;


char level;
boolean modePlay=false;
int cifra_azar;
int numero1=0;
int numero2=0;
int numero3=0;
int temp=0;
int resultado;
String operando="";
String sResultado;
String sLevel;
int largo=0;
boolean activar=false;
boolean temporizar = false;

char cifra_jugador[4]; //Stores the number of the player

String numero_jugador = String(); //Almacena las 4 cifras del jugador
String sNumero_jugador;

int cuenta=0;
int i,j,x;
int puntos,famas=0;
int intentos=0;
int maximo_intentos=10;


const byte ROWS=4;
const byte COLS=4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {13,6,5,4}; //Filas(pines del 9 al 6)
byte colPins[COLS] = {3,2,1,0}; //Columnas (pines del 5 al 2)
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


void game_over()
{
temporizar=false;
modePlay=false;
lcd.clear();
lcd.setCursor(4,0);
lcd.print("Game Over");
lcd.setCursor(0,1);
lcd.print("Time: ");
lcd.print(md);
lcd.print(mu);
lcd.print(":");
lcd.print(sd);
lcd.print(su); 
lcd.print(":");
lcd.print(cd);
lcd.print(cu);
}

void generate_random()
{
lcd.clear();
cuenta=0;
sResultado="";
sNumero_jugador="";

randomSeed(analogRead(analogRead(0)));

switch(level)
{
case '1':

numero1=random(1,11); //Generates a number between un número aleatorio entre 1 and 10
numero2=random(1,11); //Generates a number between un número aleatorio entre 1 and 10 
break;

case '2':

numero1=random(50,100); //Generates a number between un número aleatorio entre 50 and 99
numero2=random(1,11); //Generates a number between un número aleatorio entre 1 and 10
break;

case '3':

numero1=random(50,100); //Generates a number between un número aleatorio entre 1 y 99
numero2=random(50,100); //Generates a number between un número aleatorio entre 1 y 10 
break;

} 

numero3=random(1,5); //Generates a number between 1 and 4

switch(numero3)
{
case 1:
operando="+";
resultado=numero1+numero2;
break;
case 2:
operando="-";
if(numero1<numero2)
{
temp=numero1;
numero1=numero2;
numero2=temp;
}
resultado=numero1-numero2; 

break;
case 3:
operando="*";
resultado=numero1*numero2; 
break;
case 4:
operando="/";
resultado=numero1%numero2;
if(resultado!=0) //If not is zero the mod
{
operando="*";
resultado=numero1*numero2; 
}
else //The mod is zero
{
resultado=numero1/numero2;
}
}

sResultado= String(resultado);

lcd.setCursor(0,0);
lcd.print(numero1);
lcd.setCursor(2,0);
lcd.print(operando);
lcd.setCursor(3,0);
lcd.print(numero2); 
lcd.setCursor(12,1);
lcd.print(cuenta);

lcd.setCursor(0,1); 
lcd.print(" ");
lcd.setCursor(0,1);

}

void timer()
{

if(modePlay==true)
{
time = millis()-inicio; 

m=(time/1000)/60; //Minutes
mu=m%10; 
md=(m-mu)/10; 

s=(time/1000)%60; //Seconds
su=s%10; 
sd=(s-su)/10;

c=(time/100)%60;
cu=c%10;
cd=(c-cu)/10;

lcd.setCursor(8,0);
lcd.print(md);
lcd.print(mu);
lcd.print(":");
lcd.print(sd);
lcd.print(su); 
lcd.print(":");
lcd.print(cd);
lcd.print(cu); 


}

}


void setup() 
{
// put your setup code here, to run once:
lcd.createChar(1,sad); 
lcd.begin(); 
choose(); //Displays the select level mode

}

//********************************************************
void verificar()
{
if(sNumero_jugador==sResultado)
{ 

lcd.setCursor(6,0);
lcd.print("G");

intento=intento+1; 

generate_random();

}

else

{


lcd.setCursor(0,1); 

cuenta=0;

sNumero_jugador="";

lcd.setCursor(6,1);
lcd.write(byte(1)); //this writes the sad face



}


lcd.setCursor(8,1);
lcd.print("Try:");
lcd.print(intento);
lcd.print("/10");

if(intento==maximo_intentos)
{
game_over(); // Ends the game
}

}


void choose()
{
modePlay=false;
intento=0;
lcd.clear();
lcd.setCursor(2,0);
lcd.print("Select level");
lcd.setCursor(0,1);
lcd.print("1-E 2-M 3-H");

}

void conteo()
{
lcd.clear();

lcd.setCursor(4,0);

lcd.print(sLevel);

delay(300);

lcd.clear();

for(int x=3;x>=1;x--)
{
lcd.setCursor(8,0);

lcd.print(x);

delay(300);


}

lcd.clear();

lcd.setCursor(7,0);

lcd.print("Go");

delay(600);


generate_random();

modePlay=true;

if(activar==false)
{
inicio=millis();
activar=true;
}
}


void loop()
{
timer();

// put your main code here, to run repeatedly:

char key = keypad.getKey();

if(key)

{ 
//If is the select level display
if(modePlay==false) 
{
if(key=='1' || key=='2' || key=='3')
{
level=key;

lcd.clear();

lcd.setCursor(5,1);

switch(level)
{
case '1':
sLevel="Easy Level"; 
break;
case '2':
sLevel="Medium Level";
break;
case '3':
sLevel="Hard Level";
break;

} //end switch

conteo();

} // end IF key select level

} //end IF mode play off

else
//Mode player

{

temporizar=true;

if(activar==false)
{
inicio=millis();
activar=true;
lcd.clear();
} 


if(key!='A' && key!='B' && key!='C' && key!='D')
{

cifra_jugador[cuenta] = key;

numero_jugador = String(key);

sNumero_jugador=sNumero_jugador+numero_jugador;

lcd.setCursor(0+cuenta,1);

lcd.print(cifra_jugador[cuenta]);

cuenta++; 

largo=sResultado.length();

if(cuenta==largo)
{
// lcd.setCursor(10,1);
verificar();
}


} //End if key!= 


} // End else mode player


if(key=='A')
{ 
modePlay=false;
inicio=millis();
activar=false; 
choose();
}

} //End if Key main

} //End loop

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

سنقوم في البداية باستدعاء مكتبة (LiquidCrystal_I2C.h) الخاصة بوحدة i2c والتي تحتوي على مجموعة أوامر برمجية نحتاجها في مشروع تحديد الاتجاهات.

ثم نضيفها للاردوينو IDE.

بتتبع المسار التالي:

Sketch > Include libraries > Add ZIP library 

ونضيف المجلد الذي قمنا بتحميله.

#include <LiquidCrystal_I2C.h>

ثم نستدعي مكتبة لوحة المفاتيح.

ثم نضيفها للاردوينو IDE.

بتتبع المسار التالي:

Sketch > Include libraries > Add ZIP library 

ونضيف المجلد الذي قمنا بتحميله. 

#include <Keypad.h>

نعرف عنوان وحدة i2c.

LiquidCrystal_I2C lcd(0x27, 16, 2);

نهيئ مصفوفة الشاشة الكرستالية لطباعة الوجه الحزين.

byte sad[8] = 
{
B00000,
B01010,
B01010,
B00000,
B00000,
B01110,
B10001,
};

نقوم بتهيئة المتغيرات التي سنحتاجها بالمشروع.

unsigned long time=0;
unsigned long inicio=0;
int m, mu=0,md=0; 
int s, su=0,sd=0;
int c,cu,cd=0;
byte intento=0;


char level;
boolean modePlay=false;
int cifra_azar;
int numero1=0;
int numero2=0;
int numero3=0;
int temp=0;
int resultado;
String operando="";
String sResultado;
String sLevel;
int largo=0;
boolean activar=false;
boolean temporizar = false;

char cifra_jugador[4]; //Stores the number of the player

String numero_jugador = String(); //Almacena las 4 cifras del jugador
String sNumero_jugador;

int cuenta=0;
int i,j,x;
int puntos,famas=0;
int intentos=0;
int maximo_intentos=10;


const byte ROWS=4;
const byte COLS=4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {13,6,5,4}; //Filas(pines del 9 al 6)
byte colPins[COLS] = {3,2,1,0}; //Columnas (pines del 5 al 2)
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

نهيئ دالة game_over والتي تخبرنا بانتهاء اللعبة وظهور الوقت المستغرق لإتمام اللعبة من قبل اللاعب.

void game_over()
{
temporizar=false;
modePlay=false;
lcd.clear();
lcd.setCursor(4,0);
lcd.print("Game Over");
lcd.setCursor(0,1);
lcd.print("Time: ");
lcd.print(md);
lcd.print(mu);
lcd.print(":");
lcd.print(sd);
lcd.print(su); 
lcd.print(":");
lcd.print(cd);
lcd.print(cu);
}

نهيئ دالة generate_random والتي تقوم بطباعة الدوال والأرقام بشكل تلقائي.

void generate_random()
{
lcd.clear();
cuenta=0;
sResultado="";
sNumero_jugador="";

randomSeed(analogRead(analogRead(0)));

switch(level)
{
case '1':

numero1=random(1,11); //Generates a number between un número aleatorio entre 1 and 10
numero2=random(1,11); //Generates a number between un número aleatorio entre 1 and 10 
break;

case '2':

numero1=random(50,100); //Generates a number between un número aleatorio entre 50 and 99
numero2=random(1,11); //Generates a number between un número aleatorio entre 1 and 10
break;

case '3':

numero1=random(50,100); //Generates a number between un número aleatorio entre 1 y 99
numero2=random(50,100); //Generates a number between un número aleatorio entre 1 y 10 
break;

} 

numero3=random(1,5); //Generates a number between 1 and 4

switch(numero3)
{
case 1:
operando="+";
resultado=numero1+numero2;
break;
case 2:
operando="-";
if(numero1<numero2)
{
temp=numero1;
numero1=numero2;
numero2=temp;
}
resultado=numero1-numero2; 

break;
case 3:
operando="*";
resultado=numero1*numero2; 
break;
case 4:
operando="/";
resultado=numero1%numero2;
if(resultado!=0) //If not is zero the mod
{
operando="*";
resultado=numero1*numero2; 
}
else //The mod is zero
{
resultado=numero1/numero2;
}
}

sResultado= String(resultado);

lcd.setCursor(0,0);
lcd.print(numero1);
lcd.setCursor(2,0);
lcd.print(operando);
lcd.setCursor(3,0);
lcd.print(numero2); 
lcd.setCursor(12,1);
lcd.print(cuenta);

lcd.setCursor(0,1); 
lcd.print(" ");
lcd.setCursor(0,1);

}

في دالة timer نهيئ المؤقت لبدء حساب الوقت أثناء اللعب.

void timer()
{

if(modePlay==true)
{
time = millis()-inicio; 

m=(time/1000)/60; //Minutes
mu=m%10; 
md=(m-mu)/10; 

s=(time/1000)%60; //Seconds
su=s%10; 
sd=(s-su)/10;

c=(time/100)%60;
cu=c%10;
cd=(c-cu)/10;

lcd.setCursor(8,0);
lcd.print(md);
lcd.print(mu);
lcd.print(":");
lcd.print(sd);
lcd.print(su); 
lcd.print(":");
lcd.print(cd);
lcd.print(cu); 


}

}

في الدالة ()setup نقوم بتهيئة الشاشة الكرستالية استعدادًا لطباعة العمليات الحسابية عليها.

void setup() 
{
// put your setup code here, to run once:
lcd.createChar(1,sad); 
lcd.begin(); 
choose(); //Displays the select level mode

}

في الدالة choose يتم الطباعة على الشاشة بمستويات اللعبة تبدأ من المستوى 1 وحتى المستوى 3.

void choose()
{
modePlay=false;
intento=0;
lcd.clear();
lcd.setCursor(2,0);
lcd.print("Select level");
lcd.setCursor(0,1);
lcd.print("1-E 2-M 3-H");

}

في الدالة main يتم مناداة الدوال السابقة استعدادًا لطباعة مدلولاتها على الشاشة وأخذ القيم من لوحة المفاتيح.

void loop()
{
timer();

// put your main code here, to run repeatedly:

char key = keypad.getKey();

if(key)

{ 
//If is the select level display
if(modePlay==false) 
{
if(key=='1' || key=='2' || key=='3')
{
level=key;

lcd.clear();

lcd.setCursor(5,1);

switch(level)
{
case '1':
sLevel="Easy Level"; 
break;
case '2':
sLevel="Medium Level";
break;
case '3':
sLevel="Hard Level";
break;

} //end switch

conteo();

} // end IF key select level

} //end IF mode play off

else
//Mode player

{

temporizar=true;

if(activar==false)
{
inicio=millis();
activar=true;
lcd.clear();
} 


if(key!='A' && key!='B' && key!='C' && key!='D')
{

cifra_jugador[cuenta] = key;

numero_jugador = String(key);

sNumero_jugador=sNumero_jugador+numero_jugador;

lcd.setCursor(0+cuenta,1);

lcd.print(cifra_jugador[cuenta]);

cuenta++; 

largo=sResultado.length();

if(cuenta==largo)
{
// lcd.setCursor(10,1);
verificar();
}


} //End if key!= 


} // End else mode player


if(key=='A')
{ 
modePlay=false;
inicio=millis();
activar=false; 
choose();
}

} //End if Key main

} //End loop

بعد اكتمال عملية الرفع يمكنك أن تختبر مهاراتك في الرياضيات.

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




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

مقدمة

في هذا الدرس سنستخدم لوحة الاردوينو وعصا التحكم والشاشة الكرستالية؛ لتتعرف على برمجة عصا التحكم وطباعة الاتجاهات على المحور. (x,y) على الشاشة الكرستالية
show-directions-using-arduino-joystick

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

اردوينو

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

سلك اردوينو

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

show-directions-using-arduino-joystick

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

دولار نينجا

1× 40 رأس دبوس

أسلاك توصيل

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

أسلاك توصيل

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

تحديد الاتجاهات

1× 2C / IIC Serial Interface Module

1×  عصا التحكم

show-directions-using-arduino-joystick

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

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

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

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

البرمجة

ارفع الكود البرمجي الخاص بمشروع تحديد الاتجاهات على لوحة الاردوينو باستخدام بيئة تطوير الاردوينو IDE.

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() 
{
analogReference(DEFAULT);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
lcd.begin(); 
}

void loop() {
int X, Y, P ;
Y = analogRead(A1); 
X = analogRead(A2); 
P = digitalRead(A0); 
lcd.setCursor(0, 0);
if ((480 <= X) & (530 >= X) & (480 <= Y) & (530 >= Y)) {
}

if ((531 <= X) & (480 <= Y) & (530 >= Y)) {
lcd.print("UP");
}

if ((X <= 479) & (480 <= Y) & (530 >= Y)) {
lcd.print("Down");
}

if ((480 <= X) & (530 >= X) & (531 <= Y)) {
lcd.print("Right");
}

if ((480 <= X) & (530 >= X) & (Y <= 479)) {
lcd.print("Left");
}

if ((531 <= X) & (531 <= Y)) {
lcd.print("UP and Right");
}

if ((531 <= X) & (Y <= 479)) {
lcd.print("UP and Left");
}

if ((X <= 479) & (531 <= Y)) {
lcd.print("Down and Right");
}

if ((X <= 479) & (Y <= 479)) {
lcd.print("Down and Left");
}

if (P == 0) {
lcd.setCursor(0, 1);
lcd.print("PUSH");
}

delay(300);
lcd.clear();
}

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

سنقوم في البداية باستدعاء مكتبة (LiquidCrystal_I2C.h) الخاصة بوحدة i2c والتي تحتوي على مجموعة أوامر برمجية نحتاجها في مشروع تحديد الاتجاهات.

ثم نضيفها للاردوينو IDE.

بتتبع المسار التالي:

Sketch > Include libraries > Add ZIP library .

ونضيف المجلد الذي قمنا بتحميله.

#include <LiquidCrystal_I2C.h>

نعرف عنوان وحدة i2c.

LiquidCrystal_I2C lcd(0x27, 16, 2);

في الدالة ()setup سنعرف متغيرات عصا التحكم والتي ستكون كمدخلات.

وسنقوم بتهيئة الشاشة الكرستالية استعدادًا لطباعة المخرجات.

void setup() 
{
analogReference(DEFAULT);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
lcd.begin(); 
}

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

الجهات التي سيتم قراءتها وطباعتها على الشاشة:

(الأعلى- الأسفل-يمين-يسار- أعلى اليسار-أعلى اليمين-أسفل اليسار-أسفل اليمين).

void loop() {
int X, Y, P ;
Y = analogRead(A1); 
X = analogRead(A2); 
P = digitalRead(A0); 
lcd.setCursor(0, 0);
if ((480 <= X) & (530 >= X) & (480 <= Y) & (530 >= Y)) {
}

if ((531 <= X) & (480 <= Y) & (530 >= Y)) {
lcd.print("UP");
}

if ((X <= 479) & (480 <= Y) & (530 >= Y)) {
lcd.print("Down");
}

if ((480 <= X) & (530 >= X) & (531 <= Y)) {
lcd.print("Right");
}

if ((480 <= X) & (530 >= X) & (Y <= 479)) {
lcd.print("Left");
}

if ((531 <= X) & (531 <= Y)) {
lcd.print("UP and Right");
}

if ((531 <= X) & (Y <= 479)) {
lcd.print("UP and Left");
}

if ((X <= 479) & (531 <= Y)) {
lcd.print("Down and Right");
}

if ((X <= 479) & (Y <= 479)) {
lcd.print("Down and Left");
}

if (P == 0) {
lcd.setCursor(0, 1);
lcd.print("PUSH");
}

delay(300);
lcd.clear();
}

بعد اكتمال عملية الرفع يمكنك اختبار نظام تحديد الاتجاهات.

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




صنع جهاز للتحكم عن بعد بالروبوت باستخدام (NRF24L01)

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

NRF24L01

 

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

اردوينو

2X اردوينو أونو

سلك اردوينو

X1 سلك أردوينو

وحدة وحدة الارسال و الاستقبال (NRF24L01)

2X وحدة إرسال و استقبال (NRF24L01)

هيكل الروبوت

1X هيكل روبوت بأربع عجلات

1X عصا تحكم

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

1X لوحة تجارب

بطارية مع حامل بطارية

6X بطاريات (يفضل توفير حامل بطاريات لعدد 6)

l298

1X دائرة التحكم بالمحركات (L298)

أسلاك توصيل

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

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

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

 

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

بالبداية قم بالاطلاع على الدرس لمعرفة كيفة تثبيت هيكل الروبوت

أولا: توصيل دائرة المستقبل

دائرة المستقبل NRF24L01

توصيل المحركات :
L298H-bridge أسلاك المحركات
OUT  2 الأسلاك ذات اللون الأسود على جهة اليسار (-)
OUT 1 الأسلاك ذات اللون الأحمر على جهة اليسار (+)
OUT 4 الأسلاك ذات اللون الأسود على جهة اليمين (-)
OUT 3 الأسلاك ذات اللون الأحمر على جهة اليمين (+)
توصيل المنافذ الرقمية من الاردوينو مع  L298H-bridge :
L298H-bridge الأردوينو
enA 6
IN 1 7
IN 2 5
IN 3 4
IN4 2
enB 3

 

توصيل وحدة إرسال و استقبال (NRF24L01)

NRF24L01 الأردوينو
VCC 3.3
CSN 9
MOSI 11
GND GND
CE 8
SCK 13
MISO 12

ثانيا: توصيل دائرة المرسل

سيتم توصيل وحدة اللإرسال و الاستقبال (NRF24L01) بنفس الربط في المستقبل

دائرة المرسل NRF24L01

 

وحدة الارسال و الاستقبال (NRF24L01)

تسمح وحدة الإرسال والاستقبال nRF24L01 لمتحكمين أو أكثر بالتواصل مع بعضهما البعض لاسلكيًا وتعمل على بروتوكول SPI. ونطاق ترددها 2.4 جيجا هرتز ونطاق الاتصال يصل إلى 100 متر.

NRF24L01الجدول يوضح وظائف المنافذ للوحدة

المنفذ الوظيفة
VCC يستخدم هذا المنفذ لتزويد الوحدة بالطاقة. يمكن أن يتراوح الجهد الكهربائي من 1.9 إلى 3.9 فولت
CSN (Chip Select Not) وهو عبارة عن منفذ نشط منخفض. نحتاج إلى إبقائه مرتفعًا إلا عندما نرسل للجهاز أمر SPI أو عندما نتلقى بيانات على ناقل SPI من وحدة التحكم.
MOSI (Master Out Slave In) مدخل SPI إلى nRF24L01. يتم استخدامه لتلقي البيانات من متحكم
IRQ إرسال إشارة تنبيه عند توفر بيانات جديدة للمعالجة.
MISO (Master In Slave Out) إنه خرج SPI من nRF24L01. يتم استخدامه لإرسال البيانات إلى وحدة التحكم
SCK (Serial Clock) يقبل نبضات الساعة التي يوفرها مدير ناقل SPI.
CE (Chip Enable) منفذ التمكين الخاص بالوحدة المستخدمة لتحديد وضع nRF24L01 مرسل أو مستقبل، اعتمادًا على الوضع الموجود فيه حاليًا.
GND الأرضي

 

 

 

 

البرمجة

بالبداية تحتاج إلى تحميل مكتبة (RF24 master ) موجدة في إدارة المكتبات في بيئة تطوير الأردوينو،

يمكن معرفة اضافة المكتبات من خلال الرابط 

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

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(8,9); // CE, CSN
const byte address[6] = "00001";
char xyData[32] = "";
int joystick[2];
void setup() {
Serial.begin(9600);
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MAX);
radio.stopListening();
}
void loop() {

joystick[0] = analogRead(A4);
joystick[1] = analogRead(A3);

radio.write( joystick, sizeof(joystick) );
}

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

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define enA 6 
#define in1 7
#define in2 5
#define enB 3 
#define in3 4
#define in4 2

RF24 radio(8,9); // CE, CSN
const byte address[6] = "00001";
char receivedData[32] = "";
int xAxis, yAxis;
int motorSpeedA = 0;
int motorSpeedB = 0;
int joystick[2];

void setup() {
pinMode(enA, OUTPUT);
pinMode(enB, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);
Serial.begin(9600);
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MAX);
radio.startListening();

digitalWrite(in1, LOW);
digitalWrite(in2, LOW);
digitalWrite(in3, LOW);
digitalWrite(in4, LOW);
}
void loop() {

if (radio.available()) { // If the NRF240L01 module received data

radio.read( joystick, sizeof(joystick) );

radio.read(&receivedData, sizeof(receivedData));
yAxis = joystick[0];
xAxis = joystick[1];

Serial.println(yAxis);
Serial.println(xAxis);

}

if (yAxis < 470) {

digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);

digitalWrite(in3, HIGH);
digitalWrite(in4, LOW);

motorSpeedA = map(yAxis, 470, 0, 0, 255);
motorSpeedB = map(yAxis, 470, 0, 0, 255);
}
else if (yAxis > 550) {

digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);

digitalWrite(in3, LOW);
digitalWrite(in4, HIGH);

motorSpeedA = map(yAxis, 550, 1023, 0, 255);
motorSpeedB = map(yAxis, 550, 1023, 0, 255);
}

else {
motorSpeedA = 0;
motorSpeedB = 0;
}

if (xAxis < 470) {

int xMapped = map(xAxis, 470, 0, 0, 255);

motorSpeedA = motorSpeedA - xMapped;
motorSpeedB = motorSpeedB + xMapped;
// Confine the range from 0 to 255
if (motorSpeedA < 0) {
motorSpeedA = 0;
}
if (motorSpeedB > 255) {
motorSpeedB = 255;
}
}
if (xAxis > 550) {

int xMapped = map(xAxis, 550, 1023, 0, 255);

motorSpeedA = motorSpeedA + xMapped;
motorSpeedB = motorSpeedB - xMapped;

if (motorSpeedA > 255) {
motorSpeedA = 255;
}
if (motorSpeedB < 0) {
motorSpeedB = 0;
}
}

if (motorSpeedA < 70) {
motorSpeedA = 0;
}
if (motorSpeedB < 70) {
motorSpeedB = 0;
}
analogWrite(enA, motorSpeedA); // Send PWM signal to motor A
analogWrite(enB, motorSpeedB); // Send PWM signal to motor B
}

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

 

تضمين المكتبات الضرورية مثل (SPI,nRF24L01,RF24)

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

إنشاء كائن (RF24) يحتوي على معاملين و هي منافذ (CE, CSN)

RF24 radio(8,9); // CE, CSN

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

const byte address[6] = "00001";

نحدد حجم ونوع البيانات التي سيتم إرسالها

char xyData[32] = "";

نعرف مصفوفة من متغيرين ستمثل قيمة محاور عصا التحكم (x,y)

int joystick[2];

في دالة التهئية نهيئ الاتصال التسلسلي و الراديو

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

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

radio.openWritingPipe(address);

ثم باستخدام وظيفة radio.setPALevel () ، نعين مستوى مضخم الطاقة ، وفي حالتنا سأقوم بتعيينه على الحد الأعلى حتى نتمكن من التحكم بالروبوت على مسافات أكبر.

radio.setPALevel(RF24_PA_MAX);

نستخدم دالة radio.stopListening(); لتحديد أن الوحدة هي وحدة الإرسال

radio.stopListening();
}

في دالة void loop نحدد مصفوفة لقيم محور y والتي تحدد حركة الماتور إلى الأمام والخلف ومصفوفة لقيم محور xالتي تحدد اتجاه الحركة إلى اليمين أو اليسار

void loop() {

joystick[0] = analogRead(A4);
joystick[1] = analogRead(A3);

نستخدم دالة radio.write لإرسال البيانات و تحتوي الدالة على معيارين الأول يمثل قيمة عصا التحكم و الثاني حجم البيانات التي سيتم نقلها و حددناها (sizeof) بمعنى العدد الفعلي من البايتات (Byts) لمصفوفة (Joystick))

radio.write( joystick, sizeof(joystick) );
}

 

شرح الشفرة البرمجية للمستقبل

 

تضمين المكتبات الضرورية مثل (SPI) و (nRF24L01) و(RF24)

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

تعريف منافذ وحدة L298

#define enA 6 
#define in1 7
#define in2 5
#define enB 3 
#define in3 4
#define in4 2

إنشاء كائن (RF24) يحتوي على معاملين و هي منافذ (CE, CSN)

RF24 radio(8,9); // CE, CSN

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

const byte address[6] = "00001";

نحدد حجم ونوع البيانات التي سيتم استقبالها

char receivedData[32] = "";

نعرف متغيران لقيمة (X,Y)

int xAxis, yAxis;

نحدد قيمة افتراضية تساوي 0 لسرعة المحركات motorSpeedA و motorSpeedB

int motorSpeedA = 0;
int motorSpeedB = 0;

نعرف مصفوفة من متغيرين ستمثل قيمة محاور عصا التحكم (x,y)

int joystick[2];

في دالة التهيئة نعرف منافذ الدخل و الخرج لوحدة L298

void setup() {
pinMode(enA, OUTPUT);
pinMode(enB, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);

نهيئ الإتصال التسلسلي و الراديو

Serial.begin(9600);
radio.begin();

نحتاج إلى تهيئة كائن الراديو باستخدام وظيفة radio.openReadingPipe () ونعين عنوان جهاز الإرسال الذي سنستلم منه البيانات

radio.openReadingPipe(0, address);

ثم باستخدام وظيفة radio.setPALevel () ، نعين مستوى مضخم الطاقة ، وفي حالتنا سأقوم بتعيينه على الحد الأعلى حتى نتمكن من التحكم بالروبوت على مسافات أكبر.

radio.setPALevel(RF24_PA_MAX);

نستخدم دالة radio.startListening(); لتحديد أن الوحدة هي وحدة الاستقبال

radio.startListening();

اعطاء إشارة منخفضة لجميع منافذ التحكم في وحدة

digitalWrite(in1, LOW);
digitalWrite(in2, LOW);
digitalWrite(in3, LOW);
digitalWrite(in4, LOW);
}

نحتاج العبارة المنطقية if لنتحقق من وصول إشارة

if (radio.available()) { // If the NRF240L01 module received data

نستخدم دالة radio.read لاستقبال البيانات و تحتوي الدالة على معيارين الأول يمثل قيمة عصا التحكم و الثاني حجم البيانات التي سيتم استلامها و حددناها (sizeof) بمعنى العدد الفعلي من البايتات (Byts) لمصفوفة (Joystick)

radio.read( joystick, sizeof(joystick) );

تخزين البيانات في مصفوفة joystick

radio.read(&receivedData, sizeof(receivedData));
yAxis = joystick[0];
xAxis = joystick[1];

نستخدمة دالة Serial.println لطباعة قيم عصا التحكم (ان احتجت للتحقق من القراءات)

Serial.println(yAxis);
Serial.println(xAxis);}

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

if (yAxis < 470) {

يتحرك الروبوت إلى الخلف

digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);

digitalWrite(in3, HIGH);
digitalWrite(in4, LOW);

قم بتحويل قراءات المحور Y المتدنية للعودة للخلف من 470 إلى 0 إلى قيمة 0 إلى 255 لإشارة PWM لزيادة سرعة المحرك

motorSpeedA = map(yAxis, 470, 0, 0, 255);
motorSpeedB = map(yAxis, 470, 0, 0, 255);
}

يتحرك الروبوت إلى الأمام

else if (yAxis > 550) {

digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);

digitalWrite(in3, LOW);
digitalWrite(in4, HIGH);

قم بتحويل قراءات المحور Y المتدنية للتحرك للأمام من 470 إلى 0 إلى قيمة 0 إلى 255 لإشارة PWM لزيادة سرعة المحرك

motorSpeedA = map(yAxis, 550, 1023, 0, 255);
motorSpeedB = map(yAxis, 550, 1023, 0, 255);
}

إذا بقيت عصا التحكم في المنتصف فإن المحركات لا تتحرك

else {
motorSpeedA = 0;
motorSpeedB = 0;
}

يتحكم محور X بالمحركات للتحرك باتجاه اليمين و اليسار

if (xAxis < 470) {

قم بتحويل قراءات المحور X المتزايدة من 0 إلى 470 إلى قيمة 0 إلى 255

int xMapped = map(xAxis, 470, 0, 0, 255);

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

motorSpeedA = motorSpeedA - xMapped;
motorSpeedB = motorSpeedB + xMapped;
// Confine the range from 0 to 255
if (motorSpeedA < 0) {
motorSpeedA = 0;
}
if (motorSpeedB > 255) {
motorSpeedB = 255;
}
}
if (xAxis > 550) {

قم بتحويل قراءات المحور X المتزايدة من 550 إلى 1024 إلى قيمة 0 إلى 255

int xMapped = map(xAxis, 550, 1023, 0, 255);

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

motorSpeedA = motorSpeedA + xMapped;
motorSpeedB = motorSpeedB - xMapped;

if (motorSpeedA > 255) {
motorSpeedA = 255;
}
if (motorSpeedB < 0) {
motorSpeedB = 0;
}
}

if (motorSpeedA < 70) {
motorSpeedA = 0;
}
if (motorSpeedB < 70) {
motorSpeedB = 0;
}

إرسال اشارة تضمين عرض النبضة

analogWrite(enA, motorSpeedA); // Send PWM signal to motor A
analogWrite(enB, motorSpeedB); // Send PWM signal to motor B
}

 




اتصال لاسلكي بين لوحتين اردوينو باستخدام (RF 433Mhz)

المقدمة

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

RF-COMMUNICATION اتصال

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

اردوينو

X2 اردوينو اونو

سلك اردوينو

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

محول طاقة

X2 محول طاقة

RF مرسل و مستقبل

X1 المستقبل والمرسل لموجات الراديو الترددي (RF 433Mhz)

لوحة تجارب

X2 لوحة تجارب

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

X1 مقاومة متغيرة

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

X1 الثنائي المشع للضوء (LED)

مقاومة 220

X1 مقاومة 220 أوم

أسلاك توصيل

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

المستقبل والمرسل لموجات الراديو الترددي (RF 433Mhz)

يمكن ارسال البيانات بين لوحتين اردوينو باستخدام وحدة المستقبل والمرسل لموجات الراديو الترددي (RF 433Mhz). حيث ترسل البيانات بشكل متسلسل من جهاز المرسل إلى جهاز المستقبل.
تعمل وحدات المرسل لموجات الراديو الترددي المستخدمة بالدرس بتردد محدد يبلغ 433 ميجاهرتز. تُستخدم هذه الوحدات في الاتصالات قصيرة المدى. ويتم استخدامه في مجالات مختلفة مثل أجهزة التحكم في الإضاءة عن بُعد، وأنظمة RFID، والإنذار اللاسلكي وأنظمة الأمان وما إلى ذلك.
تعمل وحدة المرسل لموجات الراديو الترددي على اصدار موجات بشكل مستمر وتؤثر اشارة البيانات على سعة الموجة ما يعرف بتقنية تضمين إزاحة السعة (Amplitude Shift Keying). عندما تكون إشارة البيانات (1)، يتم تشغيل إشارة الناقل وعندما تكون إشارة البيانات (0) يتم إيقاف تشغيل إشارة الناقل.

موجات RF

وتحتوي وحدة المستقبل على دارة RF لتضخيم الموجة الحاملة المستقبلة من جهاز الإرسال.

 

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

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

توصيل دائرة المستقبل

توصيل دائرة المستقبل RF

البرمجة

بالبداية ستحتاج إلى تحميل مكتبة (VirtualWire) و من ثم تثبيتها على بيئة تطوير الاردوينو (Arduino IDE) يمكنك الاطلاع على الدرس الأول -تنصيب برنامج اردوينو (IDE) على جهازك من سلسة تعلم الاردوينو لمعرفة كيف يتم تثبيت المكتبات

برمجة المرسل 

#include <VirtualWire.h> 
#define size 1
int pot = A0;
byte TX_buffer[size]={0};
byte i;


void setup() 
{

vw_set_tx_pin(3); // pin
vw_setup(2000); // bps
for(i=0;i<size;i++)
{
TX_buffer[i]=i;
}
}

void loop()
{ 
int val = map(analogRead(pot),0,1024,0,255);
TX_buffer[0] = val; 
vw_send(TX_buffer, size); 
vw_wait_tx(); 
delay(10); 
}

برمجة المستقبل 

 

#include <VirtualWire.h> 
byte message[VW_MAX_MESSAGE_LEN]; // a buffer to store the incoming messages
byte messageLength = VW_MAX_MESSAGE_LEN; // the size of the message
int received_number = 0;
int LED = 5;

void setup()
{
Serial.begin(9600);
pinMode(LED, OUTPUT);
Serial.println("Ready...");
vw_set_rx_pin(3); // pin
vw_setup(2000); // bps
vw_rx_start();
}

void loop()
{
if (vw_get_message(message, &messageLength)) // non-blocking
{
Serial.print("Potentiometer: ");
for (int i = 0; i < messageLength; i++)
{
//Serial.print(message[i]);
received_number = message[i];
}
Serial.println(received_number);
analogWrite(LED, received_number);
}
}

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

أولا المرسل

في البداية سنقوم باستدعاء مكتبة (VirtualWire) والتي تتضمن دوال تساعدنا على برمجة الوحدة، ونعرف منفذ المقاومة المتغيرة

#include <VirtualWire.h> 
#define size 1
int pot = A0;
byte TX_buffer[size]={0};
byte i;

في دالة void setup()  نهيئ منفذ الارسال

void setup() 
{

vw_set_tx_pin(3); // pin
vw_setup(2000); // bps
for(i=0;i<size;i++)
{
TX_buffer[i]=i;
}
}

في دالة void loop() نأخذ قراءة من المقاومة المتغيرة ضمن مدى من 0 إلى 255 والتي  تمثل شدة الإضاءة المطلوبة، و نرسل الإشارة

void loop()
{ 
int val = map(analogRead(pot),0,1024,0,255);
TX_buffer[0] = val; 
vw_send(TX_buffer, size); 
vw_wait_tx(); 
delay(10); 
}

ثانيا المستقبل

نقوم باستدعاء مكتبة VirtualWire وتعريف الإشارة المستقبلة وحجم الرسالة، وتعريف منفذ الثنائي المشع للضوء (LED)

#include <VirtualWire.h> 
byte message[VW_MAX_MESSAGE_LEN]; 
byte messageLength = VW_MAX_MESSAGE_LEN; 
int received_number = 0;
int LED = 5;

في دالة void setup() نقوم بتهيئة منفذ الاتصال التسلسلي حتى نتمكن من طباعة القيم المستقبلة على شاشة الاتصال  وكمت سيتم تعريف منافذ الخرج

void setup()
{
Serial.begin(9600);
pinMode(LED, OUTPUT);
Serial.println("Ready...");
vw_set_rx_pin(3); // pin
vw_setup(2000); // bps
vw_rx_start();
}

في دالة void loop() نستقبل الرسالة و نعكس قيمتها ليتم تحديد شدة اضاءة الثنائي المشع للضوء (LED)

void loop()
{
if (vw_get_message(message, &messageLength)) // non-blocking
{
Serial.print("Potentiometer: ");
for (int i = 0; i < messageLength; i++)
{
//Serial.print(message[i]);
received_number = message[i];
}
Serial.println(received_number);
analogWrite(LED, received_number);
}
}