본문 바로가기

프로젝트/휴대폰을 디스플레이로 활용하기

기능 개선판 아두이노 Bluetooth Display

기능개선판 동영상 보기

앱인벤트 Timer는 interval을 0으로 세팅해도 평균 50ms이상으로(관련 글로 이동) 반응한다.

블루투스 수신을 확인 하기 위하여 Timer를 사용 하므로  선 하나 점 하나 그리는 시간이 빨라도

50ms가 걸린다는 것이다. 그래서 느림.. 그러면 혹시...........

타이머 이벤트가 발생 하면 한번에 테이타를 전송 하면 어떨까?

스스로 실험을 해보면 알겠지만 Timer Event 루프를 빠져 나오기 전까지 아무것도 안 보인다.

왜냐면 Timer Event가 끝나야 Paint Event가 발생하는 데 캔버스에 그리는 속도 보다 아두이노에서

전송되는 데이타가 더 빨라 누적되어 루프를 빠져 나오지 못한다.

 

그래서 연구 한 결과 기존에 그리기 함수마다 있는 응답을 기다리는 wait()를 삭제하여 응답을 기다리지 않고

한번의 Timer Event 동안 계속 그리기 데이타를 전송하게 하여 속도를 향상 시켰다. 물론 화면에는 아무것도 안 나타난다.

여기서 완성된 그래픽을 보기 위해. echo()라는 함수를 만들었다.  아두이노에서 앱인벤트로  'e'를 보내고

응답까지 기다린다. 이렇게 더 이상 전송하지 않고 기다리면 앱 인벤터에서는 루프를 빠져 나올수 있다.

루프를 빠져나오면 Paint Event가 발생하여 화면에 그동안 전송한 데이타로 작성된 그래픽이 보인다.

 

그래서 속도도 향상되고 그래픽도 깜박임 없이 구현된다.

 

기능개선판 앱인벤터 소스

필요시 echo 버튼이 나타남.

( 처음에는 안보임 . 블루투스 연결하면 메뉴에 나타나고 아두이노가 대기모드에서 빠져 나오면  다시 사라짐 )

아두이노가 먼저 시작후 블루투스 연결을 하므로 첫번째 echo()의 신호를 앱이 못 받음.그래서 아두이노에게 답을 할수 도 없어 아두이노는 계속 대기 상태임. 이때 아두이노의 리셋 버튼을 눌러주면 프로그램이 다시 시작 하면서 echo()신호를 앱이 받을수 있고 여기에 다시 아두이노에 echo()신호를 보낼수 있음.

이렇게 하는 대신 echo버튼만 눌러 주면 대기중인 아두이노에게 echo신호를 보내므로 간편함

Display.aia
0.05MB
Display.apk
3.36MB

동심원을 그려보자

  for( int r=0 ; r <150 ; r+=5)  Display.circle( 150, 150 ,  r , false);   

  //  앱인벤터에는 한번에 데이타가 전송 되어 고속으로 그려 지지만 눈에는 안 보인다.

 echo();  //  앱인벤터 응답대기....  앱인벤터는 버퍼에 더 이상 데이타가 없어 루프에서 빠져 나오고

                                                       Paint Event로 그려진 캔버스가 눈에 보이게 된다.

echo()를 필요 한 곳에 코딩만 잘하면 된다.

개선된 Display.H
 
#ifndef _Display_
#define _Display_
#include "ARGB_COLOR.H"

#define atLeft        0
#define atCenter   1
#define atRight     2

/*       명령어 코드
   cmd
    a   원호 그리기
    A   텍스트 정열
    b   배경색상
    c   색상
    C   원 그리기 
    e   echo 
    G   점 색상 읽기 
    H   화면 Height 얻기   
    l   배경 이미지 불러오기  
    L   선그리기 
    P   점 그리기 
    R   사각형 그리기
    s   화면 저장하기
    S   텍스트 사이즈
    t   선두께
    T   텍스트 그리기 
    W   화면 Wigth  얻기 
    Z   화면 지우기
   
*/

class Display
{  
  public:
     void begin();    
     void echo()      
   
     void clear();
     void color(unsigned long c);
     void bkColor(unsigned long c);
     int  width();
     int  height();
     void tick( byte _t );

     void circle( int x, int y, int r, bool f);
     void arc( int x, int y, int x2, int y2, int start, int sweep, bool center , bool fill);
     void line( int x, int y, int x2, int y2);
     void rec( int x, int y, int x2, int y2,bool f);
     void pixel( int x, int y);
     unsigned long getPixel(int x, int y);
     
     void text( int x, int y, String msg);
     void textSize( int s);
     void textAt( byte a); // atLeft  0  atMid   1  atRight 2
     
     void save(String name);
     void load(String name);
 
  private:  
   
     void send2Byte( int n );  
};

 void Display::begin()
 {
   _BT.begin(115200);  
 }    


void Display::tick( byte _t)
{
    _BT.write('t');
    _BT.write( _t );
}

int Display::width()
{
   int i;

   byte buff[2];
   _BT.write('W');
    while( !_BT.available() ){}
   _BT.readBytes(buff,2);
   i= buff[0]+buff[1]*0x100;
   return i;
}

int Display::height()
{
   int i;

   byte buff[2];
   _BT.write('H');
    while( !_BT.available() ){}
   _BT.readBytes(buff,2);
   i= buff[0]+buff[1]*0x100;
   return i;  
}
void Display::save(String name)
{
   _BT.write('s');
  send2Byte( name.length() );
   _BT.println(name);  
}

void Display::load(String name)
{
   _BT.write('l');
  send2Byte( name.length() );
   _BT.println(name);  
}

void Display::clear()
{
  _BT.write('Z');
}

unsigned long Display::getPixel(int x, int y)
{
 
  unsigned long c=0;
  byte buff[4];
   _BT.write('G');
   send2Byte(x);
   send2Byte(y);
    while( !_BT.available() ){}
   _BT.readBytes(buff,4);
   c= buff[0]+buff[1]*0x100+buff[2]*0x10000+buff[3]*0x1000000;    
   return c;
}

 void Display::textAt( byte a)
 {
     _BT.write('A');
     _BT.write(a);    
 }

 void  Display::textSize( int s)
 {
   _BT.write('S');
   send2Byte(s);
 }

 void Display::text( int x, int y, String msg)
 {
   _BT.write('T');
   send2Byte( msg.length() );
   send2Byte( x ) ;
   send2Byte( y );
   _BT.println(msg);  
 }
 void Display::pixel( int x, int y)
 {
  _BT.write('P');
   send2Byte( x ) ;
   send2Byte( y );
 }

void  Display::color(unsigned  long c)
{
    _BT.write('c');
   send2Byte( c &0xffff ) ;
   send2Byte( (c>>16) &0xffff );
}

void  Display::bkColor(unsigned  long c)
{
    _BT.write('b');
   send2Byte( c &0xffff ) ;
   send2Byte( (c>>16) &0xffff );
}

void Display::echo()
{
 _BT.write('e');
 while( !_BT.available() ){}
 _BT.read();
}

void Display::send2Byte( int n)
{
  _BT.write( n &0xff  );
  _BT.write( (n>>8) & 0xff );
}

void Display::circle( int x, int y, int r, bool f)
{
   _BT.write('C');
   send2Byte( x );
   send2Byte( y );
   send2Byte( r );
   _BT.write( f ); 
}

void Display::arc( int x, int y, int x2, int y2, int start, int sweep, bool center , bool fill)
{
   _BT.write('a');
   send2Byte( x );
   send2Byte( y );
   send2Byte( x2 );
   send2Byte( y2 );
   send2Byte( start);
   send2Byte( sweep );
   _BT.write( center);
   _BT.write( fill ); 
}
void Display::line( int x, int y, int x2, int y2)
{
    _BT.write('L');
    send2Byte( x );
    send2Byte( y );
    send2Byte( x2 );
    send2Byte( y2); 
}

void Display::rec( int x, int y, int x2, int y2,bool f)
{
   _BT.write('R');
    send2Byte( x );
    send2Byte( y );
    send2Byte( x2 );
    send2Byte( y2);
     _BT.write( f ); 
}
#endif
수정된 아두이노 소스
  #define  _BT  Serial
#include "D:\\myHeader\\Display.H"
//#define _APK_
#ifdef  _APK_
  String path="/storage/emulated/0/Android/data/appinventor.ai_구글ID.Display/files/";
#else
  String path="/storage/emulated/0/Android/data/edu.mit.appinventor.aicompanion3/files/";
#endif

Display hp;
int w,h,x=-130,d=1;

int ax( int _x )  //일반 좌표를  디스플레이 디바이스 좌표로 변환
{
  return  _x+w/2;
}

int ay( int _y)   //일반 좌표를  디스플레이 디바이스 좌표로 변환
{
  return h-(_y+h/2) ;
}
int r(int _x , int _r)  // X좌표와 거리로 Y좌표 구하기    //   
{
  return   sqrt( _r*_r - _x*_x )   ;
}

void setup() 
{
  hp.begin(); 
  hp.echo();      // 디스플레이가 있다먄 응답을 받아 시작하기 위해
                         //  이미 연결 되어  있다면 바로 다음으로  실행 (예로 연결 상태에서 리셋버튼 누르기) 
                         //   연결 안되면 그냥 대기.. 이때 앱의 echo버튼을 누르면 바로 다음으로 실행
  
  hp.load("");
  hp.bkColor(MediumBlue);
  hp.clear();
  
  w=hp.width();
  h=hp.height();

/////////////////////////////////////////////////   배경화면 만들기   echo()를 사용 안함 그래서 눈에 안보임
  hp.tick(1);
  hp.textSize(20);
    
  hp.color(White);  
 
  hp.text(0,20,"화 면  중 앙 을");
  hp.text(0,50,"0,0 좌표로 설정");
 
  hp.color(Yellow);
  hp.text(ax(0),ay(0),"(0,0)");
  hp.text(ax(0),40,String(h/2-20) );
  hp.text(ax(0),h,"-"+String(h/2) );
  hp.text(10,h/2,"-"+String(w/2));
  hp.text(w-50,h/2,String(w/2)); 
 
  hp.line(0, h/2, w, h/2);
  hp.line(w/2, 0, w/2, h);   
 
  hp.circle( ax(0), ay(0), 130,false);
 
  hp.save("/bk.png");
///////////////////////////////////////////////////////////////// 배경 만들기 끝  bk.png로 저장 ( 여기까지 작업 내용 눈에 안보임)
  hp.clear(); 
  hp.color(White);
  for(int i=1; i<200 ;i+=10)
     hp.circle( w/2,h/2,i,false);  // 동심원 그리기( 눈에 안보임)
  hp.tick(5);
  hp.echo();               //   다 그려진 동심원이 눈에 보임
  delay(1000); 
}


void loop() 
{   
   hp.load( path+ "bk.png" );   //  배경 이미지 로드
   int yy=r(x,130)*d;
   
   hp.color(Red);
    
   hp.line(ax(x),ay(yy),ax(-x),ay(-yy));
   hp.color(Yellow);
   hp.circle( ax(x) ,ay( yy  ), 15  ,true );   
   hp.color(Green);
   hp.circle( ax(-x) ,ay( -yy  ), 15  ,true);   
   x=x+30*d;
   if( x >130 ){ x=130; d= -1; } 
   if( x <-130 ) {  x=-130;  d=1;   }
   hp.echo();   //   다그려진 화면이 보임 그래서 깜박임이 없음
   
   
}