יום שבת, 28 בדצמבר 2013

Zen Load Balancer Guide




לאחר שבנינו סביבה וירטואלית בעזרת Proxmox וחברנו מערכת אחסון בעזרת FreeNas השלב הבא מתמקד במשאבי הרשת בענן, על מנת שנוכל לתת שירות לכמות משתמשים ששרת בודד אינו יכול לתת, עלינו לחבר רכיב נוסף בענן שינתב את הבקשות בין מספר שרתים, הרכיב נקרא Load Balancer בחלק מהמקרים הוא מגיע עם מערכות FireWall מתקדמות אבל לרוב זאת יחידה נפרדת שמתחברת ל Router וממנו לעולם.

Load Balance


הרעיון מאוד פשוט מדובר על תוכנית שמנתבת את הבקשות לפי סוגים של מתודות שנקבעו מראש, ניתן לחבר מספר שרתים על אותו Balancer או לחבר מספר שכבות של Balancers ביחד במקרה שיש לנו תנועה כבדה לפיזור העמוסים בין שאר השרתים ב Cluster.

 Zen Load Balancer

כמו שראינו בשאר המאמרים בנושא גם פה נעשה שימוש ב Linux, מדובר על פרויקט חינמי שבנוי על מודל עסקי דומה כמו של Proxmox ו FreeNAS שהתשלום עבור תמיכה בלבד, כך שניתן להוריד ולהשתמש בו בחינם, בנוסף ניתן לקנות באתר הרשמי חומרה יעודית.

דרישות:
  • 2 מכונות Linux וירטואליות עם Apache.
  • Zen Load Balancer - האתר הרשמי
תהליך ההתקנה מאוד פשוט מכניסים את הדיסק ומתחילים לרוץ.

דומה לצורת ההתקנה של Proxmox ו FreeNAS

תפריט מסודר
טעינה ראשונית לזיכרון
בחירת כתובת סטטית
בחירת Netmask
בחירת Default Gateway
הגדרת סיסמא ל Root
הגדרות הכנון הקשיח
כתיבה לכונן הקשיח והפעלה מחדש

בסיום ההתקנה נפעיל את המכונה מחדש נפתח את הדפדפן בכתובת שהכנסנו בהתקנה ובפורט 444 שיכניס אותנו לממשק ניהול, עם הכניסה נבקש להכניס שם משתמש וסיסמא זמניים (admin , admin) ואח"כ נעבור לממשק, זהו סיימנו את ההתקנה בהצלחה.

בניתי 2 מכונות נוספות מבוססות על Fedora יחד עם Apache בכל יחד יש קובץ Index.htm שמתאר את השרת.



תחילה נבנה Farm (חווה) , נלחץ על Manage-> Farms, נבסס את ה Balancer על כתובת הראשית (192.168.1.10), נגדיר את הפרוטוקול ואת הפורטים שאותם נפצל לצורך הדוגמה מדובר בחווה של שרתי HTTP, אם לא בא לנו להשתמש בכתובת הראשית ניתן להוסיף Interfaces נוספים.


בהגדרות החווה נקבע את אופי ההתנהגות של ה Load Balancer ונחבר אליו את השרתים האמיתיים שנותנים את השירות:



הפונקציה החשובה ביותר היא בחירת האלגוריתם המתאים לפיצול העומסים, Zen Load Balancer תומך במספר אלגוריתמים כפי שנראה בתמונה:


  • Round Robin - פיצול שווה בין השרתים האמיתיים.
  • Hash Sticky Client - כל כתובת שמגיעה ל Balancer מקבלת מחרוזת ייחודית שמחברת אותה לשרת האמיתי כלומר התעבורה תמיד תעבור דרך אותו שרת.
  • Weight - חלוקה של ביצועים לפי השרתים האמיתיים כלומר השרת עם ה Weight הגבוה ביותר יקבל יותר תעבורה משרת עם Weight נמוך יותר, התעבורה תתחלק באופן יחסי לפי ה Weight.
  • Priority - השרת עם התעדוף הגבוה ביותר יקבל את התעבורה.

החווה שלנו דורשת עקביות בחיבור, במיוחד בשרותי Web שבהם יש Session שנוצר על המכונה האמיתית, נניח שיש לנו אתר עם חשבון אישי שהמידע נשמר ב Session, ברגע שהלקוח מתחבר לאתר נרצה שתמיד יגיע לאותו שרת על מנת לשמר את ה Session, לכן ה Load Balancer מכיל טבלה בה כתוב לאיזה שרת אמיתי כל כתובת התחברה ופעם הבאה שתגיע בקשה הוא ידאג להעביר אותה לשרת המקורי.


יש התייחסות חשובה לחומרה ב Load Balancer, יש להגדיר כמות זיכרון מספקת, ומעבדים יחסית חזקים במקרה שיש לנו תעבורה כבדה או שפשוט מייצרים Cluster של Load Balancer שמחלק בין עצמו את העמוסים לפני השרתים האמיתיים.

לאחר שסיימנו להגדיר את ההגדרות הכלליות של החווה השלב הבא הוא לשלב את השרתים האמיתיים, נכניס את הכתובות של השרתי Fedora:


זהו אפשר לומר שסיימנו וברגע שתגיע בקשה לכתובת ה Load Balancer הוא יעביר את הבקשה לאחד השרתים לפי האלגוריתם שנבחר.



סיכום:

בסדרת המאמרים בנושא ראינו כיצד לבנות ענן בעזרת תוכנות Open Source בלבד, שמבוססות על Linux והחלק האחרון הוא פתרון Firewall שאותו נכיר בהמשך, האמת שהמאמר הזה הוא סיבה טובה למסיבה, הוא המאמר האחרון לשנה והוא המאמר ה 100  בבלוג אז נתראה בשנה הבאה.

בהצלחה...

יום שבת, 21 בדצמבר 2013

Arduino Photoresistor Guide




חיישן נוסף שחשוב להכיר הוא ה Photoresistor שמאפשר למדוד את עוצמת האור, את שלי קניתי בחנות האלקטרוניקה שקרובה לביתי אבל ניתן למצוא אותו ב DX במחיר מצחיק של מספר שקלים בודדים, על הדרך לקחתי גם Speaker ישן שמגיע עם כל לוח אם והחלטתי לחבר בניהם, כאשר עוצמת האור יורדת מערך מסויים ה Speaker מתחיל לצפצף, בדומה לתאורת חצר שנדלקת כאשר יורד הלילה.

דרישות
  • Arduino UNO
  • Photoresistor
  • 10k Resistor
  • PC Speaker
  • BreadBorad
  • Wires







Photoresistor



בגדול זה נגד שמשנה את התנגדות שלו לפי האור, הוא מאוד זול וחסכוני אבל לא כל כך מדויק, בסה"כ יש לו 2 רגלים, רגל אחת מחברים ל 5 V ואת הרגל השניה מפצלים לרגל אחת שמתחברת ל Analog Input והשניה מתחברת ל 10K Resistor שמתחבר ל GND.

קוד

#define speakerPin 2
#define lightPin 1

int photocellReading = 0;

void playTone(int tone, int duration) {
  for (long i = 0; i < duration * 1000L; i += tone * 2) {
    digitalWrite(speakerPin, HIGH);
    delayMicroseconds(tone);
    digitalWrite(speakerPin, LOW);
    delayMicroseconds(tone);
  }
}


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

void loop() {
    getLight();
    //if light level lower play sound
    if(photocellReading < 100)
          //send different number to change tone
          playTone(1915,300);
    delay(300); 
}

void getLight()
{
  //reading the light level from analog input
  photocellReading = analogRead(lightPin);  
  Serial.print(photocellReading);
  Serial.print("\n");
}



מבנה סופי

מצפצף כאשר עוצמת האור יורדת



סרט הדגמה





סיכום

השנה קרובה לסופה , זאת היתה שנה עמוסה במאמרים בתחומים שונים וגם השנה Arduino כיכב בבלוג, במהלך השנה באה ניקח אותו לקצה ונבנה פרויקטים מורכבים יותר.

בהצלחה...

יום שישי, 15 בנובמבר 2013

Kernel Hooking



זאת לא הפעם הראשונה שאני כותב על Hooking ובד"כ המושג הזה נשמע מאיים במיוחד בתחום אבטחת המידע אבל ב Linux זאת טכניקה לגיטימית בפיתוח ב Kernel ובגלל שמדובר בקוד פתוח המידע נגיש וחשוף לכולם בניגוד ל Windows שעושה את החיים לא קלים ומקשה מאוד על כתיבה ב Kernel, האפשרות לשלוט ב Kernel בצורה כלכך חופשית מאפשרת למפתחים למצוא פתרונות ופטנטים ייחודיים עבור המערכות שלהם.

שימו לב!

  • עבודה מול ה Kernel עלולה להזיק למחשב שלך, מומלץ להשתמש במכונה וירטואלית.
  • המאמר נכתב על Fedora 14 גרסת Kernel - 2.6.35.
  • אני לא אחראי על אופי השימוש בתוכן.

Hooking




אז למי ששכח (לדוג) Hooking זו שיטה שמאפשרת להחליף את הקריאות לפונקציות המקוריות במערכת עם פונקציות אחרות שנטענו ב Module, זאת נקודת בניים שהמתכנת יכול להחליט עם להעביר את המידע לפונקציה המקורית או להפעיל פונקציות אחרות, המשחק עצמו הוא סביב טבלה שמכילה את כל הכתובות לפונקציות של ה System Calls ב Windows זה (כמעט...) בלתי אפשרי למצוא אותה אבל ב Linux זה פשוט מאוד, קיים קובץ בתיקיית ה boot שמכיל את הכתובות לפונקציות של ה System Call, יש להכניס את הפקודה הבאה ב Terminal:



הכתובות לפונקציות של ה System Call נמצאות באיזור שמוגדר כ Read Only אבל בגלל שאנחנו ב Kernel אנחנו יכולים לשנות את ה Control Register שדרכו נהפוך את האיזור גם לכתיבה, נחתום את ה Pointer של הפונקציה החדשה ממקום הפונקציה הישנה.

#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/unistd.h>
#include <linux/string.h>

static char * filewatch;

//charp - type of the parameter is string
module_param(filewatch, charp,0);
MODULE_PARM_DESC(filewatch,"destination file pointer");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("simple hooking driver");

//extract using cat /boot/System.map | grep sys_call
unsigned long *syscall_table = (unsigned long *)0xc07b0328;

//original system call signature
asmlinkage int (*original_sys_open)(const char* filename,int flags,int mode);

//hook function
asmlinkage int fake_sys_open(const char* filename, int flags,int mode)
{
//check if open function requested on the filewatch
if(strcmp(filename,filewatch) == 0)
{
  //write to log
  printk("File open(\"%s\", %X, %X)\n", filename,flags,mode);

          //drop the request - operation not permitted
  return -EPERM;
}

//continue normal routine back to original function
return (*original_sys_open)(filename,flags,mode);
}

static int init_driver(void) {

    printk("load hook module\n");
 
    //change read only area to write availability
    write_cr0 (read_cr0 () & (~ 0x10000));

    //save the original __NR_write pointer using uistd.h defines
    original_sys_open = (void *)syscall_table[__NR_open];

    //write to pointer to the fake function
    syscall_table[__NR_open] = fake_sys_open;

    //change the write area back to read only
    write_cr0 (read_cr0 () | 0x10000);

    return 0;
}

static void clean_driver(void) {

    //change read only area to write availability
    write_cr0 (read_cr0 () & (~ 0x10000));

    //return the original pointer to the system call table
    syscall_table[__NR_open] = original_sys_open;

    //change the write area back to read only
    write_cr0 (read_cr0 () | 0x10000);

    printk("unload hook module\n");

    return;
}
//pointing to custom init function when the module loaded
module_init(init_driver);

//pointing to custom cleanup function when the module unloaded
module_exit(clean_driver);

התוכנית שבדוגמה מקבלת פרמטר חיצוני, בעזרת המאקרו module_param נעביר את הנתיב של הקובץ שרוצים לנעול, כאשר נפתח קובץ במערכת מופעלת הפונקציה open ב system call , בשלב טעינת ה Module נשנה את ה Pointer לפונקציה עם פונקציה מזוייפת וכאשר ינסו לפתוח את הקובץ יקבלו שגיאה, בשאר הקבצים הפונקציה תעביר את המידע לפונקציה המוקרית כאילו כלום לא קרה.



סיכום:

השליטה על ה System Call עלולה לפתוח דלת לפיתוחים זדוניים אבל מצד שני מאפשרת שליטה מלאה על המערכת, במאמרים האחרונים ראינו כיצד ניתן לממש Object Oriented Programming בסביבת ה Kernel ובעזרת C בלבד.

בהצלחה...

יום שישי, 8 בנובמבר 2013

Linux Kernel Guide



ברוב המאמרים בבלוג נגעתי בעיקר בפיתוח ב User Space והגיע הזמן להכיר את ה Kernel שמניע את כל המערכת, בסדרת המאמרים הקרובה אני אתמקד בפיתוח מול ה Kernel של Linux ואנסה לכסות כמה שאפשר מהתחום ההענק הזה, אז כמו תמיד המאמר הראשון הוא לצורך הכרות לפני שנצלול פנימה.

Kernel

אם חשבתם שאתם בעל הבית על המחשב שלכם אז טעיתם בגדול אתם סה"כ משתמשים פשוטים שנשמעים להוראות של בעל הבית האמיתי שנקרא Kernel, זו תוכנית שרצה בעצמה וממש לא צריכה את העזרה שלנו, המטרה שלה לגרום לכל העסק לתקתק, היא אחראית על הכל ואין דבר שנסתר ממנה (כמעט...).

שימו לב!
  • עבודה מול ה Kernel עלולה להזיק למחשב שלך, מומלץ להשתמש במכונה וירטואלית.
  • המאמר נכתב על Fedora 14.

תוכניות שרצות בעולם ה User Space צריכות לגשת לחומרה והן יכולות לעשות זאת רק בעזרת ה Kernel, לצורך העניין תוכנית שרוצה לקרוא מקובץ צריכה להפעיל פונקציה משכבת ה Api של ה Kernel שנקראת System Call שתפעיל את המנגנונים המתאימים על מנת לגשת לחומרה.





Example Module

הדוגמה הראשונה תעשה הכרות עם תהליך כתיבת ה Module וטעינה למערכת, התוכנית עצמה מאוד פשוטה מדובר על HelloWorld שרץ ב Kernel, אז חבל על הדיבורים וקדימה לעבודה, ניצור קובץ helloworld.c בעזרת gedit או כל עורך אחר ונעתיק את הקוד הבא:

#include <linux/module.h> //all kernel modules
#include <linux/init.h> //init and exit macros

int init_driver(void)
{
//write to kernel log
printk("Hello Kernel \n");
return 0;
}

void cleanup_driver(void)
{
//write to kernel log
printk("Goodbye Kernel \n");
}

//pointing to custom init function when the module loaded
module_init(init_driver);
//pointing to custom cleanup function when the module unloaded
module_exit(cleanup_driver);

על מנת שנוכל להפעיל את ה Module יש לבנות פונקציה Init ופונקציה Cleanup ולשלוח את ה Pointer שלהם ל Macros  מיוחדים - module_init ו module_exit , ברגע שנפעיל את ה Module הפונקציה Init תרוץ וכאשר נוריד את ה Module פונקצית ה Cleanup תרוץ ונוצרה התחלה וסוף עבור ה Module ,בעזרת הפקודה dmesg נוכל לראות את הפלט של printk.

Makefile

קובץ הגדרות עבור ה Compiler שאוסף Headers מתיקיית המקור של ה Linux ומשלב אותם בתוכנית, הקובץ עדין במרחקים בין השורות ויש להשים לב שהוא במבנה המתאים, לרוב נכתוב את הקובץ פעם אחת ונשתמש בו לאורך התוכנית.

obj-m := helloworld.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

all:
$(MAKE) -C $(KDIR) M=$(PWD) modules

clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean

יש לשמור את הקובץ בשם Makefile בתיקייה שמכילה את הקובץ helloworld.c ולאחר מכן נריץ את הפקודה make ב Terminal חשוב לוודא ש GCC מותקן על המכונה:
[root@localhost helloKernel]# make

התוצר הסופי הוא קובץ בסיומת (KO (Kernel Object שהוא ה Driver, על מנת שנטען אותו נשתמש בפקודה insmod והשם של ה Module.
[root@localhost helloKernel]# insmod helloworld.ko

על מנת להסיר את ה Driver יש להשתמש בפקודה rmmod והשם של ה Module.

[root@localhost helloKernel]# insmod helloworld.ko

העבודה עם insmod ו rmmod מאפשרת לטעון באופן דינמי Drivers ללא צורך של Compile חדש לכל ה Kernel והופכת את העבודה לנוחה יותר.

Character Device Driver

ב Linux קיימים 3 סוגים של (Device Driver (Character,Block,Network, החלטתי בהתחלה להתמקד ב Character Device שמאפשר קריאה וכתיבה של Char בודד בכל פעם, הוא נפוץ מאוד ויש לו שימושים רבים כמו במקלדות,עכברים, מודמים וכו, אבל לפני שנכנס לקוד צריך להכיר מספר דברים במערכת ההפעלה, יש משפט עתיק על Linux שאומר Everything Is A File , לכל רכיב חומרתי יש קובץ מייצג שדרכו ניתן לגשת לחומרה, הדגמתי באחד המאמרים בבלוג כיצד ניתן לשלוט בהתקנים בעזרת כתיבה או קריאה מקובץ באמצעות BeagleBone , נכנס לתיקייה dev/ ונרשום את הפקודה ls-l על מנת לראות את כל ההתקנים במערכת.



האות הראשונה בהרשאות ב Linux מייצגת את השיוך, כפי שניתן לראות ההתקן Loop7 מתחיל עם האות b כלומר מדובר ב Block Device ו lp0 מתחיל ב c כלומר Character Device,  קיימים 2 מספרים חשובים עבור כל התקן Major ו Minor שיחד הם מזהה יחודי עבור ה Device.

השלב הבא הוא יצירת ה Device File שמאפשר ל User Space לדבר עם ה Kernel Space כפי שנראה בהמשך, לא ניתן לייצר קובץ בספריית ה dev/ ככה סתם ויש להשתמש בפקודה מיוחדת שנקראת mknod כפי שניתן לראות בדוגמה:

[root@localhost dev]# mknod /dev/cdev c 89 1

בהרצת הפקודה נוצר קובץ בתיקיית dev/ מסוג Character Device עם מספרי ה Major וה Minor, יש הרשאה בלעדית ל Root לכתוב או לקרוא מהקובץ, ניתן לשנות את ההרשאות בעזרת הפקודה chmod, וניתן להסיר את הקובץ בעזרת פקודת rm פשוטה.

לאחר יצירת קובץ של Device File , נבנה את ה Driver שהולך ל"התלבש" על הקובץ, כל עוד שה Driver לא נטען למערכת לא ניתן לכתוב או לקרוא מהקובץ, יש לממש מספר פונקציות שדרכן נוכל להתממשק ל Device, הרעיון שלוקחים את ה Structure  של File Operations שמייצג קבוצה של פונקציות לעבודה עם קבצים ודורסים את הפונקציות עם פונקציות שלנו, כך שבכל פעם שמתבצעת קריאה לקובץ הפונקציות של ה Driver יקפצו ויעשו את העבודה.


#include <linux/module.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/init.h>

//must write license
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("simple character device");

#define MAX 100
#define MIN 0
static char buffer[MAX] = {MIN};

//prototype of the overridden functions
static int c_open(struct  inode * , struct file *);
static int c_close(struct  inode * , struct file *);
static ssize_t c_read(struct file *, char *,size_t,loff_t *);
static ssize_t c_write(struct file *, const char *,size_t,loff_t *);

//overridden file operations structure
static struct file_operations fops = 
{
.read = c_read,
.open = c_open,
.write = c_write,
.release = c_close,
};

//load module and override file operation structure for the file with new methods
int init_driver(void)
{
int check = register_chrdev(89,"cdev",&fops);
if(check<0){
printk("Error register device \n");}
else
{
printk("Device register complete \n");
}
return check;
}

//remove module
void cleanup_driver(void)
{
unregister_chrdev(89,"cdev");
}

static ssize_t c_read(struct file *dfile, char *buff,size_t len,loff_t *off)
{
printk("File device Read \n");
unsigned short ret;

//get the minimum number between 2 numbers
int bytes = min(MAX - (int)(*off),(int)len);
//no more bytes to read
if(bytes == MIN){
return MIN;}

//copy from kernel space address to user space address
ret = copy_to_user(buff,*off+buffer,bytes);
if(ret){
//error: bad address
return -EFAULT;
}
else{
//change pointer offset position by bytes to reads
*off = *off + bytes;
return bytes;
}
   
}

static ssize_t c_write(struct file *dfile,const char *buff,size_t len,loff_t *off)
{

  printk("File device Write \n");
unsigned short ret;

//any write reset buffer
memset(buffer,MIN,MAX);
//copy from user space address to kernel space address
ret = copy_from_user(*off+buffer,buff,len);
if(ret){
 //error: bad address
         return -EFAULT;}
else {
//change pointer offset position by length
*off = *off + len;
return len;
}
}

static int c_open(struct inode *inod, struct file *flip)
{
   printk("Device file open \n");
   return MIN;
}

static int c_close(struct inode *inod, struct file *flip)
{
   printk("Device file closed \n");
   return MIN;
}

//pointing to custom init function when the module loaded
module_init(init_driver);
//pointing to custom cleanup function when the module unloaded
module_exit(cleanup_driver);

ההתנהגות לחומרה כקובץ זה אחד היתרונות החזקים ב Kernel של Linux וזה יוצר סביבה גנרית להתממשקות כפי שניתן לראות בקוד נדרסו 4 פונקציות (Open, Close, Read, Write) ובכל רגע שתוכנית מה User Space תעבוד מול ה Device File הפונקציות יופעלו, חשוב מאוד שבשלב טעינת ה Module להתחבר ל Device File בעזרת הפונקציה register_chrdev שולחים את מספר ה Major ,שם ה Device File ואת ה File Operations Structure על מנת שנדרוס את הפונקציות,סגירת ה Device File מתבצעת בעזרת הפונקציה unregister_chrdev.


User Space

ניתן לכתוב או לקרוא מה Device File בעזרת הכלים הבסיסיים ב Console כמו cat ו echo אבל על מנת להשלים את התמונה ניצור תוכנית ב User Space שתכתוב ותקרא מהחומרה כאילו זה קובץ ב File System:

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>

#define MAX 100
int main(int args, char *argv[]) {

//the name of the application will be first
if(args == 1)
{
printf("no args...\n");
return EXIT_SUCCESS;
}

char buffer[MAX];
memset(buffer,0,MAX);

printf("input from user space:%s \n",argv[1]);

//open function in the driver execute
int fd = open("/dev/cdev", O_RDWR);

if(fd !=-1)
{
//write function in the driver execute
write(fd,argv[1],strlen(argv[1]));

//set position of the offset pointer back to beginning of file,
//function not overridden in the file operations structure
lseek(fd,0,SEEK_SET);

//read function in the driver execute
   read(fd,&buffer,strlen(argv[1]));

   //close function in the driver execute
   close(fd);

printf("output from kernel space:%s \n",&buffer);

}

return EXIT_SUCCESS;
}



סיכום

המאמר הזה הוא טיפה בים הענק והמסובך הזה שנקרא Kernel, במהלך המאמרים הבאים אני יעבור על מספר מנגנונים שנמצאים ב Kernel ועל סוגי התקנים נוספים מה שבטוח זה לא יהיה קל אבל מאוד מעניין.

מקורות מידע




יום שבת, 19 באוקטובר 2013

Android OpenGL And Gyroscope



אחרי שהכרנו כיצד לבנות ולצבוע אובייקטים השלב הבא הוא תנועה במרחב של המנוע אבל לפני שניכנס לכפתורים ולממשק המשתמש נתחיל דווקא עם החיישנים שנמצאים בכל מכשיר מתקדם, אבל חשוב לזכור שהם משתנים ממכשיר למכשיר ולא זמינים בכולם.

שימו לב! - המאמר מתבסס על Samsung Galaxy S3 שבו יש Gyroscope מובנה.

Gyroscope

מכשיר מדידה שמאפשר לחשב את זווית הסטייה כאשר הגוף לא במצב אופקי, כלומר כל הטיה של המכשיר תשנה את המיקום על 3 הצירים התלת מימדיים (X,Y,Z),  על מנת להפעיל אותו ב Android עלינו להשתמש במספר מחלקות שיעשנו לנו את העבודה עבור כל החיישנים שנראה במאמרים הבאים:

private SensorManager mSensorManager;
private Sensor mSensor;
אובייקטים שמייצגים את החיישנים.

mSensorManager = (SensorManager)con.getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);

מבקשים  את השירות של החיישנים במערכת, מתחברים לחיישן ספציפי שבמקרה שלנו הוא ה Gyroscope אבל ניתן לקבל חיישנים נוספים כמו: Accelerometer, Ambient Temperature,Gravity ועוד (לרשימה המלאה).

לאחר מכן מתחברים ל Event של החיישן בעזרת SensorEventListener ודוגמים אותו, השתמשתי בהגדרה קיימת במערכת שמתאימה למשחקים ודוגמת את החיישן כל 20 ms.

mSensorManager.registerListener(new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
}, mSensor, SensorManager.SENSOR_DELAY_GAME);


OpenGL

אחד השינויים הוא בפונקציה Draw של האובייקט שמוכרת מהמאמרים האחרונים בנושא, נוספו 3 פרמטרים עבור X,Y,Z שאותם נשלח ל OpenGL בעזרת הפונקציה glTranslatef.

public void draw(float x,float y,float z)
{
//set gl focus to cube
gl.glLoadIdentity();
gl.glTranslatef(x, y, z);
   //...
}

קוד


דגימת החיישן מתבצעת ה GLSurfaceView.Renderer בעזרת EventListener שממתין לאירוע מהחיישן ומעדכן פרמטרים גלובלים במחלקה שנשלחים אח"כ לפונקציה Draw של האובייקט.



package com.example.openglgyroscope;

import java.text.DecimalFormat;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.opengl.GLSurfaceView;
import android.opengl.GLU;


public class OpenGLSensorRender implements GLSurfaceView.Renderer  {


private CubeNaked mCube;
//remember the last values of the Axis
private float mLastX = 0, mLastY = 0, mLastZ = 0;
Context con;
private SensorManager mSensorManager;
private Sensor mSensor;
public OpenGLSensorRender(Context _con)
{
con = _con;
mSensorManager = (SensorManager)con.getSystemService(Context.SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mSensorManager.registerListener(new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
//taking sample from the sensor
DecimalFormat _fp = new DecimalFormat("#.#");
float x = Float.valueOf( _fp.format(event.values[0]));
float y = Float.valueOf( _fp.format(event.values[1]));
float z = Float.valueOf( _fp.format(event.values[2]));
mLastX = mLastX + x;
mLastY = mLastY+ y;
   //not in use for now
//mLastZ = z;
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {}


}, mSensor, SensorManager.SENSOR_DELAY_GAME);
}
@Override
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
//draw the cube ,sending the new axis
mCube.draw(mLastX,mLastY,mLastZ);
}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
//enter to Perspective Projection Mode Settings
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f,
100.0f);
gl.glViewport(0, 0, width, height);
//enter to ModelView Projection Mode Settings
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig arg1) {
// TODO Auto-generated method stub
//clear the suraface color to black
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
gl.glClearDepthf(1.0f);
gl.glEnable(GL10.GL_DEPTH_TEST);
gl.glDepthFunc(GL10.GL_LEQUAL);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
mCube = new CubeNaked(gl);
}

}


סרט הדגמה





קבצי מקור


סיכום:

עבודה עם חיישנים נפוצה מאוד במיוחד במשחקים, הם מאפשרים לנו לשלוט על האובייקטים במשחק מבלי שנצטרך לגעת במסך שגם ככה יחסית קטן וחוסכים לנו ממשק משתמש שמתלבש על המסך שעלול להסתיר חלקים מהמשחק, אבל צריך להתרגל לצורת המשחק החדשה ולא תמיד זה נוח למי שרגיל לכפתורים הסטנדרטים.

שחק איתו!

יום שבת, 5 באוקטובר 2013

Arduino LCD Guide



האמת שיש נושא שעדיין לא סגור כמו שצריך, במאמר Arduino And .Net השתמשתי במסך שמגיע כחלק מ Shield, אבל עדיין לא הסברתי כיצד לחבר מסך LCD ישירות ל Arduino והגיע הזמן לעשות זאת, יש שפע של מסכים מסוגים שונים בשוק את שלי מצאתי כמו תמיד ב DX במחיר של 4 דולר.


דרישות:

  • Arduino Uno
  • (LCD Screen (16X2
  • Potentiometer
  • BreadBoard
  • Wires




Liquid-Crystal Display

רוב מסכי ה LCD מגיעים עם 16 פינים אבל לא צריך להשתמש בכולם, על מנת לחבר את המסך ל Arduino נצטרך רק 9 פינים את חלקם נעביר לספרייה LiquidCrystal שמגיעה עם הסביבה של Arduino ונחבר פוטנציומטר על מנת שנשלוט בחוזק התאורה האחורית (K, A).

6 כניסות דיגיטליות, GND ו 5V

מבנה סופי

הטקסט תקין למרות שבתמונה נראה שלא...



קוד

בשביל שלא יהיה משעמם, הקוד מכניס את הטקסט משמאל לימין, כל הודעה לשורה אחרת במסך.

#include <LiquidCrystal.h>

LiquidCrystal lcd(7,6,5,4,3,2);
char screen[16];
char message1[] = {' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ','P','r','o','x','y','T','y','p','e',' ','B','l','o','g'};
char message2[] = {' ',' ',' ',' ',' ',' ',' ',' ',' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ','P','r','o','x','y','T','y','p','e','.','b','l','o','g','s','p','o','t','.','c','o','m'};

void setup()
{
      //set the screen chars and rows
     lcd.begin(16,2 );
}

void loop()
{

    //send the first message to buffer
    sendMsg1();
    delay(100);
    
    cleanArray();
    
    //send the second message to buffer
    sendMsg2();
   delay(100);
    cleanArray();
}

void sendMsg1()
{
   int msgsize = sizeof(message1) / sizeof(char);
  for(int i = msgsize; i > 0;i--)
  {
      pushArray(message1[i],0);
       delay(200);
  }  
}

void sendMsg2()
{
   int msgsize = sizeof(message2) / sizeof(char);
  for(int i = msgsize; i >0;i--)
  {
      pushArray(message2[i],1);
       delay(200);
  }  
}


void pushArray(char c, int line)
{

  lcd.clear();
  lcd.setCursor(0,line);
  char tmp = screen[0];
  screen[0] = c;
  
  for (int i = 1; i < 16; i++)
     {
                char locker;

                    locker = screen[i];
                screen[i] = tmp;

                if (i == 16)
                    break;

                tmp = locker; 
       }
       
         lcd.print(screen);
}

void cleanArray()
{
    //clear screen buffer
    for(int i=0; i < 17; i++)
    {
      screen[i] = '\0';
    }
}


סרט הדגמה




סיכום 

בד"כ Shields של LCD מגיעים עם רכיבים נוספים כמו כפתורים ו Leds ולא תמיד יש בהם שימוש וברוב המקרים אנחנו צריכים רק את המסך, מסכים הם אחד הרכיבים הנחוצים עבור חווית משתמש ונשלב אותם כחלק מהפרויקט או ככלים לניטור שננתק אח"כ.

בהצלחה...

יום שבת, 21 בספטמבר 2013

Arduino MicroSD



עד עכשיו דגמנו חיישנים והעברנו את המידע דרך החיבור הסיראלי בחזרה למשתמש אבל זה לא מספיק, נניח שיש לנו חיישן שאנחנו רוצים לנטר אותו לאורך טווח ולאסוף עליו סטטיסטיקות ,אין מספיק שטח אחסון על ה Arduino שישמור לנו את המידע ולכן צריכים שטח אחסון חיצוני, אחת הדרכים הנפוצות היא לעבוד עם MicroSD Card Module , את שלי מצאתי ב DX במחיר של 4 דולר ולצורך הדוגמה אני אשתמש בו בשביל לשמור נתונים שמגיעים מחיישן טמפרטורה.


דרישות:

  • Arduino Uno
  • MicroSD Module
  • (MicroSD 4GB (FAT32 Limit
  • TMP36
  • Breadboard
  • Wires




Serial Peripheral Interface

ממשק ותיק להעברת נתונים בין רכיבים בניגוד לממשק ה Uart שמורכב סה"כ מ 2 חיבורים (RX,TX) , ב SPI נדרשים 4 חיבורים לפחות על מנת להשלים את ההתחברות וניתן לחבר עוד רכיבים על אותו ממשק, לכל רכיב שתומך ב  SPI יש 4 פינים שאותם מחברים ל (Master (Arduino ובעזרתו מנתבים את המידע בין הרכיבים השונים.
  • SCK - פעימת שעון מה Master.
  • MISO- Master In Slave Out - מידע שעובר מה Master ל Slave.
  • MOSI - Master Out Slave In - מידע שעובר מה Slave ל Master.
  • CS - Chip Select - בחירת הרכיב שאיתו מדבר ה Master, כמות החיבורים משתנה ב Master לפי כמות הרכיבים.
במקרה שנרצה לשלוח מידע לרכיב ספציפי נוריד את ה Chip Select של אותו רכיב ל LOW ובשאר הרכיבים ל HIGH על מנת שהתעלמו מהמידע, ניתן לשלוח את אותו מידע גם בצורה פרללית לכל הרכיבים  כשנוריד את ה Chip Select לכולם או להפך בשביל שהתעלמו. 


Serial Peripheral Interface


ב Arduino יש מספר פינים קבועים שדרכם מבצעים את ההממשקות ל SPI אבל הם עלולים להשתנות בין הדגמים ניתן לדעת אותם במדוייק באתר הרשמי של Arduino.





קוד

התוכנית עצמה מאוד פשוטה , דוגמים את החיישן בכל שנייה, אוספים את הנתונים ורושמים אותם לכרטיס ה SDcard, משתמשים בספריה מוכנה (SD.h) שבאה עם הסביבה של ה Arduino שמנהלת את שטח אחסון ומאפשרת לעבוד עם Filesystem מסוג FAT עד לנפח 4 GB.

#include <SD.h>

int tempPin = 0;
int csPin = 10;

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

void loop(){
  //get the milivolts
  float temp  = getVoltage(tempPin);

  float Celsius = 0;
  float Fahrenheit = 0;

  //convert the milivolts to Fahrenheint
  Fahrenheit = (((temp -.5)*100)*1.8)+32;
  
  //convert Fahrenheint to Celsius
  Celsius = ((Fahrenheit - 32) * 5 ) /9;
  
  //write values to SD card
  writeSD(Celsius,Fahrenheit);
  
  delay(1000);
}

//check if the chip select available
void initSD(){
   pinMode(csPin, OUTPUT);
    if (!SD.begin(csPin)) {
    Serial.println("Load SD failed!");
  }
  Serial.println("Load SD complete.");
}

//reading the analogpin and getting 
//the milivoltages.
float getVoltage(int pin)
{
  return (analogRead(pin) * .004882814);
}

void writeSD(int Celsius, int Fahrenheit){
  
  String str = "Celsius: " + String(Celsius) + " Fahrenheit: " + String(Fahrenheit);
  
  //try to write file
  File dataFile = SD.open("log.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println(str);
    dataFile.close();
    Serial.println(str);
  }  
  else {
    Serial.println("error opening file");
  } 
  
}


מבנה סופי




סיכום

אין גבול למה שאפשר לעשות עם Arduino וראינו את זה לאורך כל המאמרים בנושא, שטח אחסון הוא דבר קריטי במקרה שנרצה לאסוף מידע לאורך זמן והפתרון עם MicroSD בהחלט עושה את העבודה ונותן לנו אפשרויות שלא היו לפני כמו לעבוד עם קבצי הגדרות, לכתוב ולקרוא מידע ולשמור עליו לפעמים הבאות.

איזה שכלול...