April 28, 2024

디바이스마트 미디어:

[66호] 원하는 색상으로 제어가 가능한 아두이노 IoT 스마트 무드등 키트 -

2021-06-25

★2021 ICT 융합 프로젝트 공모전 결과 발표! -

2021-05-12

디바이스마트 국내 온라인 유통사 유일 벨로다인 라이다 공급! -

2021-02-16

★총 상금 500만원 /2021 ICT 융합 프로젝트 공모전★ -

2021-01-18

디바이스마트 온라인 매거진 전자책(PDF)이 무료! -

2020-09-29

[61호]음성으로 제어하는 간접등 만들기 -

2020-08-26

디바이스마트 자체제작 코딩키트 ‘코딩 도담도담’ 출시 -

2020-08-10

GGM AC모터 대량등록! -

2020-07-10

[60호]초소형 레이더 MDR, 어떻게 제어하고 활용하나 -

2020-06-30

[60호]NANO 33 IoT보드를 활용한 블루투스 수평계 만들기 -

2020-06-30

라즈베리파이3가 드디어 출시!!! (Now Raspberry Pi 3 is Coming!!) -

2016-02-29

MoonWalker Actuator 판매개시!! -

2015-08-27

디바이스마트 레이저가공, 밀링, 선반, 라우터 등 커스텀서비스 견적요청 방법 설명동영상 입니다. -

2015-06-09

디바이스마트와 인텔®이 함께하는 IoT 경진대회! -

2015-05-19

드디어 adafruit도 디바이스마트에서 쉽고 저렴하게 !! -

2015-03-25

[29호] Intel Edison Review -

2015-03-10

Pololu 공식 Distributor 디바이스마트, Pololu 상품 판매 개시!! -

2015-03-09

[칩센]블루투스 전 제품 10%가격할인!! -

2015-02-02

[Arduino]Uno(R3) 구입시 37종 센서키트 할인이벤트!! -

2015-02-02

[M.A.I]Ahram_ISP_V1.5 60개 한정수량 할인이벤트!! -

2015-02-02

[14호]윈도우 WHQL 테스팅, 왜 진행해야 할까요?

디바이스마트의 인증의뢰 페이지

윈도우 WHQL 테스팅, 왜 진행해야 할까요?

글 | 볼트마이크로 김선아 주임연구원

윈도우 사용자라면 한번쯤 아래와 같은 경고 창을 본적이 있을 것 입니다.

Windows XP Logo Warning / Windows 7 Logo Warning

Windows XP Logo Warning / Windows 7 Logo Warning

Windows XP Logo Warning / Windows 7 Logo Warning

Windows XP Logo Warning / Windows 7 Logo Warning

소프트웨어를 설치할 때 경고창이 발생하면 바이러스처럼 악성 소프트웨어는 아닐까 한번쯤 고민하게 됩니다.
이처럼 Microsoft Windows에서는 디지털 서명되지 않은 드라이버나 소프트웨어를 설치 시 경고창을 발생시키거나 실행을 차단하는 정책을 적용하고 있습니다. 바이러스 및 불법 소프트웨어를 설치 시도하는 사용자에게 경고하기 위한 방안이기도 하지만 WHQL 인증 (디지털 서명) 및 Windows Logo 부착을 권고함으로써 Microsoft OS의 요구에 맞게 디자인되고 테스트가 되었음을 증명할 수도 있습니다. 또한 사용자에게 제품 품질에 신뢰를 주고, 타사 제품과의 차별성과 경쟁력을 확보할 수 있는 정책으로써의 역할을 하고 있습니다.
그럼 WHQL 테스팅 및 윈도우 로고 획득에 관하여 더 자세히 알아보도록 하겠습니다.

1. WHQL 이란 무엇인가?

·Windows Hardware Quality Labs Testing(WHQL Testing)은 Microsoft Windows 운영체제에서 하드웨어와 소프트웨어가 안정적인 동작을 할 수 있도록 호환성에 대한 명확한 기준을 제시하고 있는데 이에 대한 인증 프로세스를 의미합니다.
·WHQL 인증 프로세스를 통과하게 되면 Microsoft에서는 디지털 서명 파일을 제공하고 Windows Logo를 사용할 수 있게 됩니다.

2. WHQL의 장점

·Windows Logo가 부착되었거나 WHQL 인증을 받은 제품은 Microsoft OS의 요구에 맞게 디자인 되고 테스트가 되었음을 증명합니다. 그렇기 때문에 사용자에게 제품 품질에 신뢰를 줄 수 있으며 타사 제품과의 차별성과 경쟁력을 확보 할 수 있습니다.
·DTM 테스트 (자동화된 스트레스 테스트)로 OS 별 제품의 문제점을 검증하고 Microsoft로부터 Windows Error Reporting과 Windows Update(WU)등의 서비스를 제공 받아 기술 지원 비용을 감소 시킬 수 있습니다.
·WHQL인증 및 Windows Logo를 획득한 제품들은 Windows Catalog와 HCL(Hardware Compatibility List)에 등록을 할 수 있습니다. 이로 인한 제품 홍보 효과 또한 기대할 수 있습니다.

3. WHQL의 종류

WHQL Testing 방법에는 아래와 같이 3가지로 구분이 가능합니다.

그림 2. WHQL 인증의 종류

그림 2. WHQL 인증의 종류

여기에서는 Windows Logo와 Signature 2가지에 대해 살펴보도록 하겠습니다.
i. Unclassified = signature only

·Windows Logo Program Category에 속하지 않는 종류의 Device 이거나 단순히 드라이버 설치할 경우 경고 창이 뜨는 것을 막고 싶을 때 unclassified 클래스로 드라이버 인증을 진행합니다.
·드라이버 singing을 목적으로 디바이스의 드라이버와 Windows OS와의 호환성 테스트만 진행 됨으로 Windows Logo를 제품에 부착할 수 없습니다.
·Unclassified(signature-only)로 진행하더라도 Windows Update에서 Driver 배포가 가능합니다.

ii. Windows Logo : 하드웨어 & 시스템 인증

·Windows Logo Program Category에 포함되는 장치로써 해당 Logo 테스트에 통과한 제품은 Windows Logo를 부착함으로써 타 제품과 구분을 하도록 자격을 부여하는 것 입니다.
·윈도우 로고 획득이 가능한 category의 목록은 아래 링크에서 확인이 가능합니다.

http://msdn.microsoft.com/en-us/windows/hardware/gg462990

·Windows Logo를 부착함에 따라 Windows에서 동작하는 제품에 대한 호환성 보장 그리고 품질 및 고객의 신뢰도 향상을 가져올 수 있습니다.

그림 3. 윈도우 OS 로고

그림 3. 윈도우 OS 로고

4. WHQL 인증 절차

i. WHQL 인증 준비 단계

A. VeriSign Class 3 ID 발급

·Windows Logo를 신청하기 위해서는 기업 인증 디지털 ID로 서명된 파일만이 사용되며, 이를 위해 VeriSign ID를 발급받아야 합니다.
·VeriSign ID를 발급 받음으로써 submission Package가 도용되거나 악성코드로부터 보호가 가능합니다.
·드라이버에 Embedded-sign을 적용함으로써 booting Time이 빨라질 수 있습니다.
·VeriSign 발급에는 일주일에서 한달 정도의 시간이 소요되어 미리 준비가 필요합니다.
·아래 링크된 한국 VeriSign 홈페이지에서 VeriSign발급에 대한 도움을 받으실 수 있습니다.
https://www.verisign.com.au/ts-sem-kr/?sl=6D09U-0000-01-00&gclid=CP7v-oyD_60CFUlU4god2UT8uw

그림4. 한국 Verisign 홈페이지 화면

그림4. 한국 Verisign 홈페이지 화면

B. sysdev.microsoft.com ( 구 Winqual ) 계정 가입

·https://sysdev.microsoft.com : Submssion 제출을 위해서는 DevCenter의 계정이 필요합니다. DevCenter에서는 인증을 제외하고도 Windows Hardware Development 와 관련된 Tool 문서 및 기술지원을 하고 있습니다.

그림 5. sysdev.microsoft.com의 회원가입 화면

그림 5. sysdev.microsoft.com의 회원가입 화면

ii. WHQL 인증 테스트 단계

A. Windows Logo requirement 확인 및 WLK(HCK) 다운로드 및 설치

·Windows Logo requirement 확인 : Windows Logo를 받기 위하여 필요한 기본적인 Spec을 확인하여 미리 준비한다면 테스트 중에 Fail이 발생하여 다시 수정하는 등의 초과시간 및 인력을 절약할 수 있습니다.
·WLK(HCK)설치 : WHQL인증에 필요한 관련 테스트를 할 수 있는 Tool입니다. Windows 7 OS까지는 WLK(Windows Logo Kit)를 사용하여 WHQL 인증 테스트를 진행하였지만 Windows 8이 출시됨에 따라 Windows 8까지 지원 가능한 HCK(Hardware Certification Kit)가 배포 되었으며 해당 Tool을 사용하여 아래 [그림6]과 같이 테스트 환경을 구축해야 합니다.

그림 6. HCK를 사용한 테스트 환경 구성도

그림 6. HCK를 사용한 테스트 환경 구성도

iii. WHQL 테스트 결과 제출 단계

A. WHQL Submission

·WHQL 인증 테스트 항목이 모두 Pass 되고 모든 항목의 테스트가 끝나면 테스트 결과 Log 파일(.cab)이 생성되게 됩니다. On-line 으로 Windows dashboard에 테스트 결과 Log 파일이나 제품을 보내어 인증을 받는 과정을 의미합니다.( 재 신청 시 비용 및 Package 재발송이 이루어져야 합니다. )

B. Submission 비용

·한 OS군(ex: XP32bit/64bit) 당 $250 의 비용이 발생합니다.
·Windows 8 OS의 경우 현재 정식으로 출시 된 버전이 아니라 Preview 버전이기 때문에 Preview 기간 동안에는 Windows 8 OS군에 한 하여 100$의 비용으로 Submission을 진행하고 있습니다. Windows 8의 정식 출시 이후에는 타 OS 와 동일하게 250$의 비용이 청구되게 됩니다.
·DUA(Driver Update Acceptable) Submission : 드라이버파일의 변경사항은 없지만 inf 파일에서의 변경사항이 발생하였을 경우에는 WHQL 인증 테스트를 처음부터 다시 진행하지 않고 inf파일만 수정하여 Submission을 진행할 수 있도록 간략한 Submission 절차인 DUA Submission 방법을 제공하고 있습니다. 해당 방법을 사용할 경우에는 100$의 비용이 청구되게 됩니다.

14호 Feature

14호 Feature

C. Submission 검토기간

·사정에 따라 최소 1일에서 최대 7일까지 소요됩니다. 그렇기 때문에 테스트 일정을 조금 여유롭게 계획하여 진행하는 것이 좋습니다.

D. WHQL Test and Review

·Submission 에서 Fail 이 발생할 경우 다시 WHQL 인증을 신청해야 하며, 재 신청 시 비용 및 Package 재 발송을 필요로 합니다. Pass를 할 경우에는 Windows Logo를 사용할 수 있으며 제품관련 정보를 Windows Catalog와 HCL(Hardware Compatibility List)에 등록 가능합니다.

 

5. Windows 8 정식 출시와 HCK

Windows 8의 정식 출시가 얼마 남지 않았습니다. 확연히 달라진 UI로 사람들의 기대를 증폭 시키고 있습니다. Windows 8의 출시에 앞서 개발자 및 기타 사용자들을 위해 Release Preview 버전을 MS사에서 배포하고 있습니다. 불법 소프트 웨어를 위한 피해 대비 및 경쟁력 강화를 위해 Windows Logo 및 인증 또한 제공하고 있습니다. WHQL 인증을 위해 사용해 오던 Tool이 WLK대신 HCK 라는 Tool로 변경된 차이점도 있습니다.
WLK에서 HCK로 Tool이 변화 됨에 따라 WLK와 어떠한 차이점이 있는지 또 어떠한 부분을 더 추가적으로 준비해야 하는지 알아 보도록 하겠습니다.
i. HCK 란?

·Hardware Certification Kit 로서 Windows 8이 출시됨에 따라 Windows 8의 인증을 지원하는 WLK의 업그레이드 버전 입니다.

ii. HCK 다운로드 및 설치

·http://msdn.microsoft.com/en-us/windows/hardware/hh852366 링크에서 다운로드 한 후에 설치합니다.
설치환경 : HCK Studio의 경우 Windows Server 2008 R2 English 버전에서만 설치 할 수 있습니다. 테스트 컴퓨터에 클라이언트 소프트웨어를 설치하면 자동으로 Studio에 등록됩니다.

iii. WLK1.6 VS HCK ?

WLK 1.6의 unclassified와 Windows Logo Class로 나누어져 있던 부분이 HCK에서는 Signature only와 Windows Logo 그리고 UEFI submission으로 변경되었습니다.

A. Signature-only 란?

·WLK의 unclassified와 동일한 Class로써 Windows Logo Class Category에 포함되지 않는 Device일 경우 HCK에서 Signature-only Class로 인식하여 테스트 진행을 도와주고 있습니다.

B. Windows Logo Class 란?

·WLK1.6과 동일하게 Windows Logo 획득이 가능한 Category에 포함되는 Device로서 해당 테스트를 모두 만족하면 Windows Logo 및 MS에서 제공하는 모든 Service를 이용할 수 있습니다.

C. UEFI 서명이란?

·Dash board에서 제공하는 새로운 서비스입니다. 이 서비스를 사용하면 x86 또는 x64 컴퓨터를 대상으로 지정한 UEFI 펌웨어 이진에 서명하여 Windows 8 PC에 설치할 수 있습니다. EBC(EFI 바이트코드) 파일은 /ALIGN:32 플래그를 사용하여 컴파일 해야 프로세스에 성공할 수 있습니다. 제출 패키지는 폴더가 없고 서명할 *.efi 파일만 포함된 CAB 라이브러리여야 합니다.
·WLK의 경우 WHQL인증 요청자가 Unclassified 또는 Windows Logo Class를 선택하여 테스트 진행이 가능하였습니다. 하지만 HCK의 경우 HCK Studio에서 자동으로 디바이스를 인식하여 Window Logo 획득에 가능한 테스트 항목을 모두 보여주는 방식으로 변경되었습니다.

그림 7. HCK에서 Device를 자동으로 인식하여 필요한 테스트 항목을 나열해 준다.

그림 7. HCK에서 Device를 자동으로 인식하여 필요한 테스트 항목을 나열해 준다.

iv. HCK Tool 및 Windows 8 인증을 받기 위하여 주의해야 할 점은?

WLK1.6에서 HCK로 툴이 변화함에 따라 테스트 항목 및 테스트 방법 또한 수정되었습니다. WLK와 동일한 테스트 항목이라 할지라도 HCK에서 Fail이 발생할 수 있기 때문에 HCK에서 요구하는 Spec을 미리 살펴보고 필요한 부분은 수정을 해야 합니다.

Windows Hardware Certification Requirement 다운로드 링크
http://msdn.microsoft.com/library/windows/hardware/jj128252

WLK1.6에서는 사용자가 테스트 Class를 선택하여 진행하였기 때문에 Windows Logo Class에 존재하는 Device라 할지라도 unclassified인증으로 Driver Signature-only가 가능했었습니다. 하지만 현재 HCK의 경우 HCK Studio에서 직접 디바이스를 인식하여 테스트 항목을 보여주는 형식으로 진행 되기 때문에 Windows Logo Category에 포함되는 Device일 경우 Signature-only로 인증 진행이 불가능하다는 점을 명심해야 합니다.

위 내용은 Windows 8 Release Preview Version을 기준으로 작성 되었습니다. Windows 8 정식 출시 버전과는 차이가 발생할 수 있는 점 알려드립니다.

 

6. WHQL Testing의 HCK? WLK? 너무 어려워요.

앞서 HCK와 WLK 및 WHQL 인증에 대하여 상세히 설명해 드렸지만 아직도 WHQL 인증을 시작하기에 막막함을 느끼는 분들이 많으실 것으로 예상됩니다. 볼트마이크로는 다년간 WHQL Testing을 대행하면서 쌓은 노하우로 가장 빠르고 안전하게 제품 테스트를 돕는 WHQL Testing 대행 서비스를 제공하고 있습니다 또한 해당 문제점을 분석하여 해결할 수 있는 솔루션 제공까지 가능한 기술력을 보유하고 있습니다.

■ 볼트마이크로 WHQL Testing 대행 절차 소개 ■

그림 8. 볼트마이크로의 WHQL 인증 절차

그림 8. 볼트마이크로의 WHQL 인증 절차

(1) 볼트마이크로에 WHQL 인증 Testing 의뢰하기
·디바이스마트의 커스텀 서비스를 사용하여 편리하게 윈도우 인증의뢰가 가능합니다.
·제품에 대한 설명, 지원 OS 등 제품의 정보를 함께 전달해 주셔야 합니다.

디바이스마트의 인증의뢰 페이지

디바이스마트의 인증의뢰 페이지

http://www.devicemart.co.kr/design/index.php?tpl=window.htm
(2) DTM 테스트 접수 확인
·볼트마이크로에서 DTM 테스트 접수 확인 후에 견적서 및 인증에 필요한 기타 사항에 대하여 준비를 요청 드립니다
Ex) VeriSign ID 발급 및 Microsoft DashBoard 가입, 견적서 전달

(3) 디바이스 및 테스트 드라이버 전달
·테스트에 필요한 디바이스 및 드라이버를 전달 받습니다. 인증을 의뢰한 업체에서는 테스트 비용을 지급합니다.

(4) WHQL 테스트 진행
·의뢰 받은 모든 OS에 대하여 테스트를 진행합니다.
·테스트 중 문제점이 발생하였을 경우 Issue Report 전달해 드립니다. 인증을 의뢰한 업체에서는 해당 문제점을 수정하여 다시 볼트마이크로로 전달합니다.

(5) Submission 제출
·모든 테스트가 통과 되면 Microsoft에 최종 테스트 결과를 제출합니다.
·Submission이 통과 되면 서명된 Catalog File 및 Logo artwork를 다운로드 하여 인증의뢰 업체에 전달하게 됩니다.

 

볼트마이크로는 거의 모든 디바이스 그룹의 WHQL Testing을 수행한 경험이 있으며, 벤더로 문제점의 원인과 WHQL Testing통과를 위한 정확한 가이드를 제공하고 있습니다. WHQL 인증에 어려움을 느끼고 계신 모든 업체들의 길잡이가 되어 드릴 수 있도록 하겠습니다.

 

[14호]국제 LED EXPO & OLED EXPO 2012

14hotledexpo010
 

 국제 LED EXPO & OLED EXPO 2012

글 | 이용세 edgar@ntrex.co.kr

 

2012년 6월 26일부터 29일까지 일산 킨텍스에서 국내 최대 규모의 LED 관련 행사인 ‘국제 LED EXPO & OLED EXPO 2012’가 개최되었다. 올해로 10회째를 맞이하는 이번 전시회는 LED, OLED 관련 부품, 소재를 다루는 13개국의 300여개 업체가 600여 부스 규모로 참가했다.

올해 주요 참가 기업으로는 포스코LED, 금호전기, LGMMA, 필룩스, 스타넷, 에디슨솔라이텍, 루멘스 등의 조명 관련 업체들과 장비, 부품 업체가 참가하고 있었으며, ST마이크로일렉트로닉스, 한양반도체 같은 OLED 관련 업체들도 대거 참가하였다.

LED 시장은 해마다 20% 이상의 성장률을 기록하고 있으며, 적용 범위가 점차 넓어지는 것을 느낄 수 있다. 이번 전시회에서는 실용성을 겸비한 다양한 디자인의 LED 램프, LED 가로등 및 Fixture module 등의 다양한 제품을 선보였으며, 패턴 설계 기술, 회로기술 등 여러 발전된 기술력과 자료들을 볼 수 있었다.

14hotledexpo01 14hotledexpo11

실용성을 겸비한 디자인은 얼마나 발전 했을까? 전력 소모량이 적고 설치가 간편하며, 부피가 작다는 점은 디자인적으로 활용 범위가 너무도 많다. 이러한 장점을 살려 인테리어에 적용한 부스를 많이 볼 수 있었다.

스마트 시트로 불리우며, LED 관련 전시회에서 여러 번 볼 수 있었던 발광 시트지는 간판이나 광고를 목적으로 활용하기 좋은 상품이었다. 후면에 많은 발광체를 두지 않고 위 아래의 빛만으로도 전면에 빛을 환하게 비출 수 있는 도트형태의 발광 시트지이다. 시트지를 투명 아크릴에 붙여주기만 하면 사용이 가능한 신개념 시트지로 이전의 V컷팅 보다 효율이 더 높아졌다는 것이 장점이다.

광고 보드에 활용 한다면, 크고 전력이 많이 들어가는 형광등을 대신하여 전력소모량도 낮추고 아크릴을 활용하면 가볍게도 만들 수 있을 것이다.

14hotledexpo07 14hotledexpo08 14hotledexpo09

인테리어에 사용한 조명. 발열로 인하여 뜨겁고, 또 전력 소모가 많았던 할로겐 램프는 이제 더 이상 볼 수 없을 정도로 LED의 활용 범위가 커지고 있다. 천장의 조명을 LED 램프로 활용하여 보다 스마트한 공간으로 보이게 할 수 있다는 것 또한 LED의 매력.

14hotledexpo8 14hotledexpo7

신개념 타입의 Flexible LED Bar. 좌우상하 어떤 형태로든 변형이 쉽고 양면 테이프로 고정이 가능하여 활용 범위가 상당히 많아 보였다. 원하는 글자 모양을 직접 만들 수도 있으며, 칩LED 5050 사이즈를 사용하여, 밝기 또한 뛰어났다.

14hotledexpo02 14hotledexpo03
14hotledexpo05 14hotledexpo04

전시장 한편에서는 조명 박물관과 2012 빛공해 전시전이 열렸다. 빛공해란. 불필요하거나 필요 이상의 인공적인 빛이 인체나 자연환경 등에 피해를 주는 것으로서, 예를 들어 과도한 도심의 불빛이 별빛과 달빛을 가려 철새의 이동 경로에 혼란을 주거나 비행기 항로 이탈과 같은 문제를 일으키는 것을 말한다.

LED는 아직까지 도입 비용은 높지만 기존의 조명제품과 비교하여 높은 에너지 효율성을 보여주고 있다.
앞으로 보다 획기적인 제품들이 출시되어 저렴하고 손쉽게 다가갈 수 있는 제품들이 많이 나타나길 바라며, 다음 LED EXPO를 기대해본다.

[14호] 안드로이드를 이용한 탱크 조종

안드로이드를 이용한 탱크 조종

14ptband032012 DIY 프로젝트 작품 공모전 – 도전상

안드로이드를 이용한

탱크 조종

글 | 김종욱 bear1215@korea.com

 

심사평

근래 관심을 받는 안드로이드와 일반 주행 로봇을 결합한 작품이다. 무선조종기 대신 안드로이드 OS를 이용하여 해당 OS가 설치된 장비(폰, 태블릿 등)에서 로봇을 조종할 수 있도록 되어있다. 일반 사용자의 DIY 컨셉에 맞춰 본다면 쉽게 접근할 수 있는 내용이다. 단, 안드로이드 측 프로그램에 대한 구현방법에 대한 설명 등이 문서상 미흡한 것이 아쉬움으로 남는다.

개발 동기 및 목적

안드로이드를 이용한 탱크 조종을 하고자 하였다. 통신 방식은 블루투스를 이용하고 탱크측 제어는 Attiny2313를 이용하였고, 단순 전후진만이 아닌 다른 동작들을 추가 하였다. 1.전후진, 2.좌회전, 3.우회전, 4.좌후진, 5.우후진, 6.포탑 회전(좌우), 7.포신의 상하 이동, 8.포탄 발사(BB탄 발사), 9.전조등/후미등 켜기

 

단계별 과정

1. 구조도(상세 블럭도 및 회로도 참조)

구조도
2. 전원

14ptband01

전원은 기존의 밧데리를 이용한다. (9.7V 1000mA)
정전원 변환은 LM2575-5.0를 사용하였다. 전원은 9.7V를 모터 구동 전원으로 사용하고 제어부인 Attiny2313은 5.0V 전원을 사용한다.

14ptband02
탱크부 구성
기존의 RF 제어부를 제거하고 모터, 밧데리 등은 그대로 사용한다. 탱크의 구성은 아래의 표와 같다.

14ptband014
3. 탱크 제어부 구성

14ptband03
탱크 제어는 Attiny2313을 사용한다.
가. MPU : Attiny2313
나. 주모터 제어 : L293B(정역 회전용 2set)
다. 포탑 모터 : LB1630(정역 회전용 1set)
라. 포신 모터 : ULN2803(1/8)
마. 포 발사 모터 : ULN2803(1/8)
바. 전조등 : ULN2803(1/8)
사. 후미등 : ULN2803(1/8)

4. 탱크 제어부 통신 모듈
탱크 제어부와 안드로이드 폰 연결은 bluetooth모듈을 사용하였다.

14ptband04

my Bluetooth-EX 모듈 구입하러 가기

5. 안드로이드 프로그램
Program : android측
tankControl.java

tankControl.java 소스보기

package kr.sunejune;

import android.R;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.view.View.OnClickListener;

public class TankControl extends Activity implements OnClickListener {

private static final String TAG = “TankControl”;
private static final boolean D = false;

// Message types sent from the BluetoothChartService Handler
public static final int MESSAGE_STATE_CHANGE = 1;
public static final int MESSAGE_READ = 2;
public static final int MESSAGE_DEVICE_NAME = 4;
public static final int MESSAGE_TOAST = 5;

// Key names received from the BluetoothChartService Handler
public static final String DEVICE_NAME = “device_name”;
public static final String TOAST = “toast”;

// Name of the connected device
private String mConnectedDeviceName = null;

// Intent request codes
private static final int REQUEST_CONNECT_DEVICE = 1;
private static final int REQUEST_ENABLE_BT = 2;

// Local Bletooth adapetr
private BluetoothAdapter mBluetoothAdapter = null;
// Member object for the chat services
private SvrServer mSvcServer = null;

private TextView mStatus;

finish();
/*
// Called when the activity is first created.
static final int[] BUTTONS = {
R.id.ball,
R.id.turrent_left,
R.id.turrent_stop,
R.id.turrent_right,
R.id.f_center,
R.id.f_left,
R.id.f_right,
R.id.t_center,
R.id.t_left,
R.id.t_right,
R.id.c_center,
R.id.c_left,
R.id.c_right,
R.id.gun_up,
R.id.gun_dn,
R.id.gun_stop
};
*/
int ball_f = 0;
TextView _out1 = null;
TextView _out2 = null;
TextView _out3 = null;

@Override
public void onCreate(Bundle savedInstanceState) {

// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.tankcontrol);
setOnClickListener((ViewGroup)findViewById(R.id.tblButtons));

mStatus = (TextView)findViewById(R.id.txtStatus);

mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

if( mBluetoothAdapter == null){
Toast.makeText(this, “Bluetooth is not available”, Toast.LENGTH_SHORT).show();
finish();
return;
}

_out1 = (TextView)findViewById(R.id.text1);
_out2 = (TextView)findViewById(R.id.text2);
_out3 = (TextView)findViewById(R.id.text3);

_out1.setText(“Hello”);
_out1.setBackgroundColor(0xFFFFFF00);
_out1.setGravity(Gravity.CENTER);

_out2.setText(“멈춤”);
_out2.setBackgroundColor(0xFFFF00FF);
_out2.setGravity(Gravity.CENTER);

_out3.setText(“멈춤”);
_out3.setBackgroundColor(0xFF00FFFF);
_out3.setGravity(Gravity.CENTER);

}

@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
/*
if(D) Log.e(TAG,”++ ON START ++”);

if( !mBluetoothAdapter.isEnabled() ) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT );
}
else {
if(mSvcServer == null) {
mSvcServer = new SvrServer(this, mHandler);
}
}
*/
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.option_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()){
case R.id.scan :
/*
Intent serverIntent = new Intent(this, DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
*/

Intent serverIntent = new Intent(this, DeviceListActivity.class);
startActivityForResult(serverIntent , REQUEST_CONNECT_DEVICE);
return true;
}
return false;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(D) Log.d(TAG,”onActivityResult”+resultCode);

switch(requestCode){
case REQUEST_CONNECT_DEVICE :
if(resultCode == Activity.RESULT_OK){
String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
mSvcServer.connect(device);
}
break;
case REQUEST_ENABLE_BT :
if(resultCode == Activity.RESULT_OK){
if(mSvcServer == null) {
mSvcServer = new SvrServer(this, mHandler);
}
}
else {
Log.d(TAG,”BT not enable”);
Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
finish();
}
break;
}
}

public void onInit(int status) {
if(D) Log.d(TAG, “onInit”);
}

private View[] getChildViews(ViewGroup group){
int childCount = group.getChildCount();
final View[] childViews = new View[childCount];
for(int index = 0; index < childCount; index++) {
childViews[index] = group.getChildAt(index);
}
return childViews;
}

private void setOnClickListener(ViewGroup group){
View[] childViews = getChildViews(group);
for(View view:childViews){
if(view instanceof Button) {
view.setOnClickListener(this);
}
else if(view instanceof ViewGroup) {
setOnClickListener((ViewGroup) view);
}
}
}

private int sendToQue(String data){
if(mSvcServer.getState() != SvrServer.STATE_CONNECTED) {
Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
return -1;
}
return mSvcServer.toQue(data);
}

private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_STATE_CHANGE:
if (D)
Log.i(TAG, “MESSAGE_STATE_CHANGE: ” + msg.arg1);
switch (msg.arg1) {
case SvrServer.STATE_CONNECTED:
mStatus.setText(R.string.status_connected_to);
mStatus.append(mConnectedDeviceName);
break;
case SvrServer.STATE_CONNECTING:
mStatus.setText(R.string.status_connecting);
break;
case SvrServer.STATE_LISTEN:
case SvrServer.STATE_NONE:
mStatus.setText(R.string.status_not_connected);
break;
}
break;
// TODO: remove MESSAGE_READ
case MESSAGE_READ:
byte[] readBuf = (byte[]) msg.obj;
// construct a string from the valid bytes in the buffer
String readMessage = new String(readBuf, 0, msg.arg1);
// mConversationArrayAdapter.add(mConnectedDeviceName+”: ” +
// readMessage);
break;
case MESSAGE_DEVICE_NAME:
// save the connected device’s name
mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
Toast.makeText(getApplicationContext(),
“Connected to ” + mConnectedDeviceName,
Toast.LENGTH_SHORT).show();
break;
case MESSAGE_TOAST:
Toast.makeText(getApplicationContext(),
msg.getData().getString(TOAST), Toast.LENGTH_SHORT)
.show();
break;
}
}
};

public void gun_text()
{
Button b_up = (Button)findViewById(R.id.gun_up);
Button b_dn = (Button)findViewById(R.id.gun_dn);
Button b_st = (Button)findViewById(R.id.gun_stop);
b_up.setText(“포신 UP”);
b_dn.setText(“포신 DN”);
b_st.setText(“포멈춤”);
}

public void turrent_text()
{
Button b_up = (Button)findViewById(R.id.turrent_left);
Button b_dn = (Button)findViewById(R.id.turrent_right);
Button b_st = (Button)findViewById(R.id.turrent_stop);
b_up.setText(“포탑_좌”);
b_dn.setText(“포탑_우”);
b_st.setText(“포탑멈춤”);
}
public void move_text()
{
Button b1_1 = (Button)findViewById(R.id.f_left);
Button b1_2 = (Button)findViewById(R.id.f_center);
Button b1_3 = (Button)findViewById(R.id.f_right);
b1_1.setText(“전_좌”);
b1_2.setText(“전_전”);
b1_3.setText(“전_우”);
Button b2_1 = (Button)findViewById(R.id.c_left);
Button b2_2 = (Button)findViewById(R.id.c_center);
Button b2_3 = (Button)findViewById(R.id.c_right);
b2_1.setText(“좌_좌”);
b2_2.setText(“멈춤”);
b2_3.setText(“우_우”);
Button b3_1 = (Button)findViewById(R.id.t_left);
Button b3_2 = (Button)findViewById(R.id.t_center);
Button b3_3 = (Button)findViewById(R.id.t_right);
b3_1.setText(“후_좌”);
b3_2.setText(“후_후”);
b3_3.setText(“후_우”);
}

@Override
public void onClick(View v){
if(v instanceof Button) {
int btn_id = v.getId();
Button btn = (Button)findViewById( btn_id );
switch ( btn_id ) {
case R.id.ball :
if( ball_f == 0 ) {
if(sendToQue(“A”)==0) {
btn.setText(“발사중(A)”);
_out1.setText(“포 발사”);
ball_f = 1;
}
}
else {
sendToQue(“B”);
btn.setText(“발사대기(B)”);
_out1.setText(“Hello”);
ball_f = 0;
}
break;
case R.id.gun_up :
if(sendToQue(“C”) == 0) {
gun_text();
btn.setText(“..ing(C)”);
_out1.setText(“포신 UP”);
}
break;
case R.id.gun_dn :
if(sendToQue(“E”) == 0) {
gun_text();
btn.setText(“..ing(E)”);
_out1.setText(“포신 DOWN”);
}
break;
case R.id.gun_stop :
if(sendToQue(“D”) == 0) {
gun_text();
btn.setText(“STOP(D)”);
_out1.setText(“포신 STOP”);
}
break;
case R.id.turrent_left :
if(sendToQue(“F”) == 0) {
turrent_text();
btn.setText(“..ing(F)”);
_out2.setText(“좌”);
}
break;
case R.id.turrent_right :
if(sendToQue(“H”) == 0) {
turrent_text();
btn.setText(“..ing(H)”);
_out2.setText(“우”);
}
break;
case R.id.turrent_stop :
if(sendToQue(“G”) == 0) {
turrent_text();
btn.setText(“STOP(G)”);
_out2.setText(“멈춤”);
}
break;
case R.id.f_left :
if(sendToQue(“I”) == 0) {
move_text();
btn.setText(“..ing(I)”);
_out3.setText(“전_좌 “);
}
break;
case R.id.f_center :
if(sendToQue(“J”) == 0) {
move_text();
btn.setText(“..ing(J)”);
_out3.setText(“전_전 “);
}
break;
case R.id.f_right :
if(sendToQue(“K”) == 0) {
move_text();
btn.setText(“..ing(K)”);
_out3.setText(“전_우 “);
}
break;
case R.id.c_left :
if(sendToQue(“L”) == 0) {
move_text();
btn.setText(“..ing(L)”);
_out3.setText(“좌_좌 “);
}
break;
case R.id.c_center :
if(sendToQue(“M”) == 0) {
move_text();
btn.setText(“STOP(M)”);
_out3.setText(“멈춤”);
}
break;
case R.id.c_right :
if(sendToQue(“N”) == 0) {
move_text();
btn.setText(“..ing(N)”);
_out3.setText(“우_우 “);
}
break;
case R.id.t_left :
if(sendToQue(“O”) == 0) {
move_text();
btn.setText(“..ing(O)”);
_out3.setText(“후_좌 “);
}
break;
case R.id.t_center :
if(sendToQue(“P”) == 0) {
move_text();
btn.setText(“..ing(P)”);
_out3.setText(“후_후”);
}
break;
case R.id.t_right :
if(sendToQue(“Q”) == 0) {
move_text();
btn.setText(“..ing(Q)”);
_out3.setText(“후_우 “);
}
break;
}
}
}

}

Program : android측
SvrServer.java

SvrServer.java 소스보기

package kr.sunejune;

import java.util.UUID;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Handler;
import android.os.Bundle;
import android.os.Message;
import android.util.Log;

public class SvrServer {
// Debug
private static final String TAG = “SvrServer”;
private static final boolean D = false;

// Name for the SDP record when creating server socket
private static final String NAME =”TankCon”;

// Generic UUID for SPP
private static final UUID MY_UUID = UUID.fromString(“00001101-0000-1000-8000-00805F9B34FB”);

private final BluetoothAdapter mAdapter;
private final Handler mHandler;
private AcceptThread mAccepThread;
private ConnectThread mConnectThread;
private ConnectedThread mConnectedThread;
private int mState;

// Constants that indicate the current connection state
public static final int STATE_NONE = 0;
public static final int STATE_LISTEN = 1;
public static final int STATE_CONNECTING = 2;
public static final int STATE_CONNECTED = 3;

public SvrServer(Context context, Handler handler) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_NONE;
mHandler = handler;
}

private synchronized void setState(int state) {
if( D ) Log.d(TAG, “setState()”+mState+”==>”+state);
mState = state;

mHandler.obtainMessage(TankControl.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
}

public synchronized int getState() {
return mState;
}

public synchronized void start() {
if(D) Log.d(TAG, “start” );
if(mConnectThread != null) { mConnectThread.cancel(); mConnectThread = null; }
if(mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; }
if(mAccepThread == null) { mAccepThread = new AcceptThread(); mAccepThread.start(); }
setState(STATE_LISTEN);
}

public synchronized void connect(BluetoothDevice device){
if(D) Log.d(TAG,”connect to:” + device);

if(mState == STATE_CONNECTING) {
if(mConnectThread != null) { mConnectThread.cancel(); mConnectThread = null; }
}

if(mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; }

mConnectThread = new ConnectThread(device);
mConnectThread.start();
setState(STATE_CONNECTING);
}

public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
if(D) Log.d(TAG, “connected”);

if(mConnectThread != null) { mConnectThread.cancel(); mConnectThread = null; }
if(mConnectedThread != null) { mConnectedThread.cancel(); mConnectThread = null; }
if(mAccepThread != null) { mAccepThread.cancel(); mAccepThread = null; }

mConnectedThread = new ConnectedThread(socket);
mConnectedThread.start();

Message msg = mHandler.obtainMessage( TankControl.MESSAGE_DEVICE_NAME );
Bundle bundle = new Bundle();
bundle.putString( TankControl.DEVICE_NAME, device.getName() );
msg.setData(bundle);
mHandler.sendMessage(msg);

setState(STATE_CONNECTED);

}

public synchronized void stop() {
if(D) Log.d(TAG,”stop”);
if(mConnectThread != null) { mConnectThread.cancel(); mConnectThread = null; }
if(mConnectedThread != null) { mConnectedThread.cancel(); mConnectedThread = null; }
if(mAccepThread != null) { mAccepThread.cancel(); mAccepThread = null; }
setState(STATE_NONE);
}

public int toQue(String data) {
ConnectedThread r; // temp
synchronized(this) {
if(mState != STATE_CONNECTED ) return -1;
r = mConnectedThread;
}
return r.toQue(data);
}

private void connectionFail() {
if(D) Log.d(TAG,”connectionFail”);
setState(STATE_LISTEN);

Message msg = mHandler.obtainMessage( TankControl.MESSAGE_TOAST );
Bundle bundle = new Bundle();
bundle.putString(TankControl.TOAST, “Unable to connect device”);
msg.setData(bundle);
mHandler.sendMessage(msg);
}

private void connectionLost() {
if(D) Log.d(TAG, “connectionLost”);
setState(STATE_LISTEN);

Message msg = mHandler.obtainMessage(TankControl.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(TankControl.TOAST, “Device connection was lost”);
msg.setData(bundle);
mHandler.sendMessage(msg);
}
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;

public AcceptThread() {
BluetoothServerSocket tmp = null;

try {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
} catch ( IOException e ) {
Log.e(TAG, “listen() failed”, e);
}
mmServerSocket = tmp;
}

@Override
public void run() {
if(D) Log.d(TAG, “BEGIN mAcceptThread”+this);
setName(“AcceptThread”);
BluetoothSocket socket = null;

while( mState != STATE_CONNECTED ) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
Log.e(TAG, “accept() failed”, e);
break;
}

if(socket != null) {
synchronized ( SvrServer.this ) {
switch(mState) {
case STATE_LISTEN :
case STATE_CONNECTING :
connected(socket, socket.getRemoteDevice());
break;
case STATE_NONE :
case STATE_CONNECTED :
try {
socket.close();
}catch (IOException e) {
Log.e(TAG,”Could not close unwanted socket”, e);
}
break;
}
}
}
}
if(D) Log.d(TAG,”END mAcceptThread”);
}

public void cancel() {
if(D) Log.d(TAG,”cancel”+this);
try {
mmServerSocket.close();
}catch(IOException e) {
Log.e(TAG,”close() of server failed”,e);
}
}
}

private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;

public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;

try {
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
}catch(IOException e) {
Log.e(TAG,”create() failed”, e);
}
mmSocket = tmp;
}

@Override
public void run() {
Log.i(TAG,”BEGIN ConnectThrtead”);
setName(“ConnectThread”);

mAdapter.cancelDiscovery();

try{
mmSocket.connect();
}catch(IOException e1) {
connectionFail();
try {
mmSocket.close();
}catch (IOException e2) {
Log.e(TAG,”unable to close() socket during connection failure”, e2);
}
SvrServer.this.start();
return;
}

synchronized ( SvrServer.this ) {
mConnectThread = null;
}

connected(mmSocket, mmDevice );
}

public void cancel() {
try {
mmSocket.close();
}catch(IOException e) {
Log.e(TAG,”close() of connect socket failed”, e);
}
}
}

private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;

public ConnectedThread(BluetoothSocket socket) {
Log.d(TAG, “create ConnectedThread”);
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;

// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, “temp sockets not created”, e);
}

mmInStream = tmpIn;
mmOutStream = tmpOut;
}

@Override
public void run() {
Log.i(TAG, “BEGIN mConnectedThread”);
byte[] buffer = new byte[1024];
int bytes;

// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
// TODO: add dogkick!!
bytes = mmInStream.read(buffer);

// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(TankControl.MESSAGE_READ, bytes, -1, buffer).sendToTarget();
} catch (IOException e) {
Log.e(TAG, “disconnected”, e);
connectionLost();
break;
}
}
}

public int toQue(String in_data) {
int ret = 0;
try {
String data = in_data;
/*
if (0 < left) data += ‘F’;
else if (0 > left) {data += ‘B’; left = -left;}
else data +=’H';
if (0 < right) data += ‘F’;
else if (0 > right) {data += ‘B’; right = -right;}
else data +=’H';

if (left != 0) data += (char)(left&0xff);
if (right != 0) data += (char)(right&0xff);
*/
mmOutStream.write(data.getBytes());
ret = 0;
} catch (IOException e) {
Log.e(TAG, “Exception during write”, e);
ret = -1;
}
return ret;
}

public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, “close() of connect socket failed”, e);
}
}

}

}

Program : android측
DeviceListActivity.java

DeviceListActivity.java 소스보기

/*

* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the “License”);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an “AS IS” BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package kr.sunejune;

import java.util.Set;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;

/**
* This Activity appears as a dialog. It lists any paired devices and
* devices detected in the area after discovery. When a device is chosen
* by the user, the MAC address of the device is sent back to the parent
* Activity in the result Intent.
*/
public class DeviceListActivity extends Activity {
// Debugging
private static final String TAG = “DeviceListActivity”;
private static final boolean D = false;

// Return Intent extra
public static String EXTRA_DEVICE_ADDRESS = “device_address”;

// Member fields
private BluetoothAdapter mBtAdapter;
private ArrayAdapter<String> mPairedDevicesArrayAdapter;
private ArrayAdapter<String> mNewDevicesArrayAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Setup the window
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
setContentView(R.layout.device_list);

// Set result CANCELED incase the user backs out
setResult(Activity.RESULT_CANCELED);

// Initialize the button to perform device discovery
Button scanButton = (Button) findViewById(R.id.button_scan);
scanButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
doDiscovery();
v.setVisibility(View.GONE);
}
});

// Initialize array adapters. One for already paired devices and
// one for newly discovered devices
mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);

// Find and set up the ListView for paired devices
ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
pairedListView.setAdapter(mPairedDevicesArrayAdapter);
pairedListView.setOnItemClickListener(mDeviceClickListener);

// Find and set up the ListView for newly discovered devices
ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
newDevicesListView.setOnItemClickListener(mDeviceClickListener);

// Register for broadcasts when a device is discovered
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(mReceiver, filter);

// Register for broadcasts when discovery has finished
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(mReceiver, filter);

// Get the local Bluetooth adapter
mBtAdapter = BluetoothAdapter.getDefaultAdapter();

// Get a set of currently paired devices
Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();

// If there are paired devices, add each one to the ArrayAdapter
if (pairedDevices.size() > 0) {
findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
for (BluetoothDevice device : pairedDevices) {
mPairedDevicesArrayAdapter.add(device.getName() + “\n” + device.getAddress());
}
} else {
String noDevices = getResources().getText(R.string.none_paired).toString();
mPairedDevicesArrayAdapter.add(noDevices);
}
}

@Override
protected void onDestroy() {
super.onDestroy();

// Make sure we’re not doing discovery anymore
if (mBtAdapter != null) {
mBtAdapter.cancelDiscovery();
}

// Unregister broadcast listeners
this.unregisterReceiver(mReceiver);
}

/**
* Start device discover with the BluetoothAdapter
*/
private void doDiscovery() {
if (D) Log.d(TAG, “doDiscovery()”);

// Indicate scanning in the title
setProgressBarIndeterminateVisibility(true);
setTitle(R.string.scanning);

// Turn on sub-title for new devices
findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);

// If we’re already discovering, stop it
if (mBtAdapter.isDiscovering()) {
mBtAdapter.cancelDiscovery();
}

// Request discover from BluetoothAdapter
mBtAdapter.startDiscovery();
}

// The on-click listener for all devices in the ListViews
private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
// Cancel discovery because it’s costly and we’re about to connect
if (mBtAdapter.isDiscovering()) {
mBtAdapter.cancelDiscovery();
}

// Get the device MAC address, which is the last 17 chars in the View
String info = ((TextView) v).getText().toString();
String address = info.substring(info.length() – 17);

// Create the result Intent and include the MAC address
Intent intent = new Intent();
intent.putExtra(EXTRA_DEVICE_ADDRESS, address);

// Set result and finish this Activity
setResult(Activity.RESULT_OK, intent);
finish();
}
};

// The BroadcastReceiver that listens for discovered devices and
// changes the title when discovery is finished
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();

// When discovery finds a device
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// Get the BluetoothDevice object from the Intent
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
// If it’s already paired, skip it, because it’s been listed already
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
mNewDevicesArrayAdapter.add(device.getName() + “\n” + device.getAddress());
}
// When discovery is finished, change the Activity title
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
setProgressBarIndeterminateVisibility(false);
setTitle(R.string.select_device);
if (mNewDevicesArrayAdapter.getCount() == 0) {
String noDevices = getResources().getText(R.string.none_found).toString();
mNewDevicesArrayAdapter.add(noDevices);
}
}
}
};

}

6. AVR 프로그램
Program : ATTINY2313측

Program : ATTINY2313측 소스보기

/*

Project : USART TEST1
tiny2313_uart1.c
Date : 2008-05-23
Company : AvrMall.com
Chip type : ATtiny2313-10SI
Clock : 12.000000 MHz
Comment : ATTINY2313으로 UART송수신 테스트 프로그램
Used with AVRSTUDIO V4.14 (WINAVR) .
*/

#include <avr/io.h>

#define F_CPU 12000000UL // 16 MHz
//#define F_CPU 14.7456E6
//#include <util/delay.h>
// Common Functions.
void Delay_us(unsigned int time_us);
void Delay_ms(unsigned int time_ms);

void USART_Init(void);
void putchar_0(char data);
void com_puts(char sbuf[]);
void putchar_0(char data);
int getchar_0(void);
void cpu_setup(void);
void Delay_us(unsigned int time_us)
{
unsigned int i;

for(i=0; i<time_us; i++) // 4 cycle +
{
asm(“PUSH R0″); // 2 cycle +
asm(“POP R0″); // 2 cycle +
asm(“PUSH R0″); // 2 cycle +
asm(“POP R0″); // 2 cycle + =12 cycle for 11.0592MHZ
//asm(“PUSH R0″); // 2 cycle +
//asm(“POP R0″); // 2 cycle = 16 cycle = 1us for 16MHz
}
}

void Delay_ms(unsigned int time_ms)
{
unsigned int i;

for(i=0; i<time_ms;i++)
Delay_us(1000);
}
void USART_Init(void)
{
// 9600Bps, 8Data, 1 Stop, No Parity
UCSRA=0×00;
UCSRB=0×18;
UCSRC=0×06;
UBRRH=0×00;
UBRRL=0×12;
}
// COM1 스트링 송신 //
void com_puts(char sbuf[])
{
char ch;
int i = 0;

ch = sbuf[i++]; // 전송할 데이터
while(ch != 0){
putchar_0(ch); // 1바이트 송신
ch = sbuf[i++]; // 전송할 데이터
}
}

void putchar_0(char data)
{
/* Wait for empty transmit buffer */
while (!((UCSRA) & (1<<UDRE)));

/* Put data into buffer, sends the data */
UDR = data;
}

int getchar_0(void)
{
/* Wait for data to be received */
while ( !(UCSRA & (1<<RXC)) );

/* Get and return received data from buffer */
return(UDR);
}

void cpu_setup(void)
{
USART_Init();//USART 초기화
DDRB = 0b01111111; // Port B IO mode : b7 (Input), b6-b0(Output)
DDRD &= 0b01111100; // Port B IO mode : d0-1 (UART), b6-b2(Output)
}
int main(void)
{
unsigned char cmd;
unsigned char i_h=0;
unsigned char i_t=0;
unsigned char i_f=0;
unsigned char i_u=0;

cpu_setup(); // cpu 초기화

com_puts(“\r\n”);
com_puts(“- ATTINY2313 USART TEST -\r\n”);
com_puts(“Press a or b \r\n”);

while(1)
{
cmd = getchar_0();//PC로부터 받은 값을 d에 저장한다.

switch (cmd) {
case ‘i’ :
PORTB = 0b00000000;
break;

// 전조등(h:4) / 후미등(t:5) , off(o)
case ‘h’ :
case ‘R’ :
if(i_h == 1) {
PORTB &= 0b11001111;
i_h=0;
}
else {
PORTB |= 0b00010000;
i_h=1;
}
break;
case ‘t’ :
case ‘S’ :
if(i_t == 0) {
PORTB |= 0b00100000;
i_t=1;
}
else {
PORTB &= 0b11001111;
i_t=0;
}
break;

case ‘o’ :
case ‘T’ :
PORTB &= 0b11001111;
break;
// 포탄 발사(f:3:A) off(w:B)
case ‘f’ :
case ‘A’ :
if(i_f == 0) {
PORTB |= 0b00001000;
i_f=1;
}
else {
PORTB &= 0b11110111;
i_f=0;
}
break;
case ‘B’ :
case ‘q’ :
PORTB &= 0b11110111;
break;
// 포신 동작(u:2) off(c)
case ‘u’ :
case ‘C’ :
PORTB |= 0b00000100;
i_u=1;
break;
case ‘E’ :
PORTB &= 0b11111011;
i_u=0;
break;
case ‘c’ :
case ‘D’ :
PORTB &= 0b11111011;
break;

// 포탑 좌(l:0) 우(r:1) , off(m)
case ‘l’ :
case ‘F’ :
PORTB &= 0b11111100;
PORTB |= 0b00000001;
break;
case ‘r’ :
case ‘H’ :
PORTB &= 0b11111100;
PORTB |= 0b00000010;
break;
case ‘m’ :
case ‘G’ :
PORTB &= 0b11111100;
break;

// 이동 전진(w:2-1,3-0 , 4-1,5-0) 후진(x:2-0,3-1 , 4-0,5-1)
// 전좌(a:2-0,3-0 , 4-1,5-0) 전우(d:2-1,3-0 , 4-0,5-0) ,
// 후좌(a:2-0,3-1 , 4-0,5-0) 후우(d:2-0,3-0 , 4-1,5-0) ,
// off(s)
case ‘O’ : // 후좌
PORTD &= 0b10000011;
PORTD |= 0b01001000;
break;
case ‘Q’ : // 후우
PORTD &= 0b10000011;
PORTD |= 0b01010000;
break;
case ‘w’ :
case ‘J’ : // 전_전
PORTD &= 0b10000011;
PORTD |= 0b01010100;
break;
case ‘x’ :
case ‘P’ : // 후_후
PORTD &= 0b10000011;
PORTD |= 0b01101000;
break;
case ‘a’ :
case ‘I’ : // 전_좌
PORTD &= 0b10000011;
PORTD |= 0b01010000;
break;
case ‘d’ :
case ‘K’ : // 전우
PORTD &= 0b10000011;
PORTD |= 0b01000100;
break;
case ‘s’ :
case ‘M’ : // 멈춤
PORTD &= 0b10000011;
break;
default :
;
// putchar_0(‘c’);
}
Delay_ms(100);
}
}

7. 관련 회로도

7.1 전체 Block

14ptband06
7.2 ATTINY2313 Board V1.2 회로도

14ptband07
7.3 L293B : 전후진용 주 모터 (좌우, 정, 역 회전 : 양방향)

14ptband08

14ptband09
7.4 LB1630 : 포탑 (정. 역 회전 : 양방향)

14ptband010
7.5 ULN2803 : 포신, 포발사, 전조등, 후미등 (정회전:단방향)

14ptband011
7.6 bluetooth : myBluetooh-EX 모듈

14ptband012

14ptband013
8. 완성

14ptband05

소요비용

탱크 : 75,000원
Attiny2313 보드 : 11,000원
제어 보드 제작 : 약 20,000원(손실까지)
블루투스 모듈 : 26,300원
전체 : 132,300원

참고 웹사이트

참고 Site

탱크 조립

아두이노 모터 컨트롤 보드 만들기

아두이노+블루투스+안드로이드

완성후 게시물

 

[14호]조립용 심전도 키트(EMG-KIT) 출시!

피지오랩

피지오랩

(주)피지오랩(www.physiolab.co.kr)에서 EMG(근전도) 신호를 증폭하고 필터링 기능을 제공하는 교육용 EMG-KIT를 출시했다. 매뉴얼과 함께 부품 전체가 제공되어 학생들이 직접 조립 및 측정을 함으로써, 근전도 측정 장치의 구성요소, 필터링 기법 및 전자회로의 원리 등 근전도 측정의 이해를 돕는 제품이다. EMG-KIT는 동사의 μDAQ-KIT, SimDAQ-KIT 등 시험 지원 키트들과 인터페이스가 용이한 구조로 설계되었으며, 리드선을 통해 근전도 측정 및 일정한 크기 이상의 근전도 신호 검출을 위한 LED 표시 기능을 함께 내장한 제품이다.

제품 사양

·크기 : 12cm(W) x 10cm(L) x 2.5cm(H)
·입력전원 : [기본 제공] 6V(3V x 2ea), 리튬코인전지 [부가 기능] SimDAQ-KIT 연결 시 자동 전원공급
·Selector : 필터 등의 설정 변경에 의한 출력 파형 관찰
·Test point : 8개 (각 회로구성단 출력)
·I/O : 외부장치(입ㆍ출력) 삽입연결 컨넥터 내장
·측정 : 근전도, 근전도 레벨에 따른 LED 점등 기능

제품설명 더보기

상세설명
본 제품은 BME-KIT 시리즈 중 EMG(근전도) 신호를 증폭하고 필터링 기능을 제공하는 교육용 EMG-KIT 입니다. 사용자 매뉴얼과 함께 부품 전체가 제공되어 EMG-KIT를 학생들이 직접 조립 및 측정함으로, 근전도 측정 장치의 구성요소, 필터링 기법 및 전자회로의 원리 등 근전도 측정의 이해를 돕는 제품입니다. EMG-KIT는 당사의 μDAQ-KIT, SimDAQ-KIT 등 시험지원키트들과 인터페이스가 용이한 구조로 설계되었으며, 리드선을 통해 근전도 측정 및 일정한 크기 이상의 근전도 신호 검출을 위한 LED 표시 기능을 함께 내장한 제품입니다.

주요특징
  • 쉽게 조립 가능
  • 근전도 및 근전도 레벨 검출
  • 생체신호처리 구성단별 측정
  • 필터 차단주파수 선택기능
  • 리드선 통한 측정

주요사양
항목 내용
크기 12cm(W) * 10cm(L) * 2.5cm(H)
입력전원 [기본 제공] 6V(3V*2ea), 리튬코인전지

[부가 기능] SimDAQ-KIT 연결 시 자동전원공급
Selector 필터 등의 설정 변경에 의한 출력 파형 관찰
Test point 8개 (각 회로구성단 출력)
I/O 외부장치(입ㆍ출력) 삽입연결 컨넥터 내장
측정 근전도, 근전도 레벨에 따른 LED 점등 기능

제품 구입하러 가기

TEL. 051-325-2868
FAX. 051-325-2869

[14호]ARM1 S3C6410 Chip을 탑재한 Embedded Board, DSTARII70_CE 출시!

14pnj

14pnj

진영콘텍(www.jyct.com)에서 ARM1 S3C6410 Chip을 탑재한 Embedded Board를 출시했다. Windows CE 6.0 R3를 탑재하여 PC와 같은 사용자 인터페이스를 제공한다. Active Sync 지원으로 개발 환경의 다양화(File Viewer, Process Viewer 등 지원)를 요할 수 있다. 제품은 jEMB11 Main Board로 이뤄져 있으며, jEMB11 Sub Board는 다양한 LCD 지원을 위해 구성되며, TTL 및 LVDS, 터치스크린과 인버터 출력을 제공한다.

 

HARDWARE 사양
·CPU : samsung 53c6410, 32bit Risc
ARM1176JZF-S, 667Mhz (Async Mode)
·RAM : On Board 128M DDR(32bit Daa Bus)
·FLASH : On Board 256M SLC Nand Flash
·SD Memory : SD 지원(4GB) / SDHC 지원(16GB)
·Audio : Stereo Sound 입력 / 출력 지원
·Ethernet : 100Mbps (SMSC9220)
·Sound : 스테레오사운드 출력 및 입력 지원
·LCD : SAMSUNG 7inch Wide LMS700KF Resolution: 800×480
·Touch : 4선 저항 박막식 / Serial / USB 터치 지원
·POWER : DC 12V, 750mA
·Dimension : 172.4 x 114.5 x 33mm (WxHxD)

INTERFACE 사양
·Uart : RS232 : 2Ch / TTL : 1Ch
·Usb Host : Usb Host 1.1 지원 Keyboard / Mouse, Usb Memory 지원
·Usb Slave : Mini Usb Slave 2.0
·12C : 1Ch 지원
·SPI : 1Ch 지원
·IO : 8Port GPIO 지원
·Etc : RTC 지원

제품설명 더보기

제품 구매하러 가기

TEL. 02-890-6400
FAX. 02-890-6406