[14호] 안드로이드를 이용한 탱크 조종
2012 DIY 프로젝트 작품 공모전 – 도전상
안드로이드를 이용한
탱크 조종
글 | 김종욱 bear1215@korea.com
심사평
근래 관심을 받는 안드로이드와 일반 주행 로봇을 결합한 작품이다. 무선조종기 대신 안드로이드 OS를 이용하여 해당 OS가 설치된 장비(폰, 태블릿 등)에서 로봇을 조종할 수 있도록 되어있다. 일반 사용자의 DIY 컨셉에 맞춰 본다면 쉽게 접근할 수 있는 내용이다. 단, 안드로이드 측 프로그램에 대한 구현방법에 대한 설명 등이 문서상 미흡한 것이 아쉬움으로 남는다.
개발 동기 및 목적
안드로이드를 이용한 탱크 조종을 하고자 하였다. 통신 방식은 블루투스를 이용하고 탱크측 제어는 Attiny2313를 이용하였고, 단순 전후진만이 아닌 다른 동작들을 추가 하였다. 1.전후진, 2.좌회전, 3.우회전, 4.좌후진, 5.우후진, 6.포탑 회전(좌우), 7.포신의 상하 이동, 8.포탄 발사(BB탄 발사), 9.전조등/후미등 켜기
단계별 과정
1. 구조도(상세 블럭도 및 회로도 참조)
전원은 기존의 밧데리를 이용한다. (9.7V 1000mA)
정전원 변환은 LM2575-5.0를 사용하였다. 전원은 9.7V를 모터 구동 전원으로 사용하고 제어부인 Attiny2313은 5.0V 전원을 사용한다.
탱크부 구성
기존의 RF 제어부를 제거하고 모터, 밧데리 등은 그대로 사용한다. 탱크의 구성은 아래의 표와 같다.
탱크 제어는 Attiny2313을 사용한다.
가. MPU : Attiny2313
나. 주모터 제어 : L293B(정역 회전용 2set)
다. 포탑 모터 : LB1630(정역 회전용 1set)
라. 포신 모터 : ULN2803(1/8)
마. 포 발사 모터 : ULN2803(1/8)
바. 전조등 : ULN2803(1/8)
사. 후미등 : ULN2803(1/8)
4. 탱크 제어부 통신 모듈
탱크 제어부와 안드로이드 폰 연결은 bluetooth모듈을 사용하였다.
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
7.3 L293B : 전후진용 주 모터 (좌우, 정, 역 회전 : 양방향)
7.4 LB1630 : 포탑 (정. 역 회전 : 양방향)
7.5 ULN2803 : 포신, 포발사, 전조등, 후미등 (정회전:단방향)
7.6 bluetooth : myBluetooh-EX 모듈
소요비용
탱크 : 75,000원
Attiny2313 보드 : 11,000원
제어 보드 제작 : 약 20,000원(손실까지)
블루투스 모듈 : 26,300원
전체 : 132,300원
참고 웹사이트
참고 Site