יום שבת, 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 ולא תמיד יש בהם שימוש וברוב המקרים אנחנו צריכים רק את המסך, מסכים הם אחד הרכיבים הנחוצים עבור חווית משתמש ונשלב אותם כחלק מהפרויקט או ככלים לניטור שננתק אח"כ.

בהצלחה...