Управление тележкой из android-a по BT

Особые благодарности доке Android SDK с ее примером BluetoothChat и неизвестному парню.
Скачку и установку Android SDK описывать не буду. Весит как чугунный мост и без бубна не заработает. Но, бубен у каждого свой, инет пестрит вариантами, каждый может выбрать на свой вкус. У мена заработал вариант с прямым прописывание пути (set java_exe=«C:\Windows\system32\java.exe») к java.exe в android.bat.
А дальше, чисто код. Код «прямой», без фантазии, как у чайника. Комментировать не стал, вроде и так все понятно, но, будут вопросы, отвечу, разжую. В принципе, эта программа может управлять и не тележкой. Изменить расположение кнопок в layout button_tab3, изменить надписи на кнопках и без изменения кода, можно управлять диммером, нагревателем или чем угодно.
Главная Activity и layout:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#000000" >
<Button
android:id="@+id/btButton"
android:layout_width="400px"
android:layout_height="100px"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="УПРАВЛЕНИЕ КНОПКАМИ"
android:textColor="#FFFFFF"
android:textStyle="bold" />
<Button
android:id="@+id/btTouch"
android:layout_width="400px"
android:layout_height="100px"
android:layout_above="@+id/btButton"
android:layout_alignLeft="@+id/btButton"
android:text="УПРАВЛЕНИЕ ПАЛЬЦЕМ"
android:textColor="#FFFFFF"
android:textStyle="bold" />
<Button
android:id="@+id/btAcsel"
android:layout_width="400px"
android:layout_height="100px"
android:layout_above="@+id/btTouch"
android:layout_alignLeft="@+id/btTouch"
android:text="УПРАВЛЕНИЕ НАКЛОНОМ"
android:textColor="#FFFFFF"
android:textStyle="bold" />
<!--
<Button
android:id="@+id/btBTConf"
android:layout_width="400px"
android:layout_height="100px"
android:layout_alignLeft="@+id/btButton"
android:layout_below="@+id/btButton"
android:layout_marginTop="100px"
android:text="НАСТРОЙКА BLUETOOTH"
android:textColor="#FFFFFF"
android:textStyle="bold" />
-->
<Button
android:id="@+id/btConf"
android:layout_width="400px"
android:layout_height="100px"
android:layout_alignLeft="@+id/btButton"
android:layout_alignParentBottom="true"
android:layout_marginBottom="50px"
android:text="НАСТРОЙКИ"
android:textColor="#FFFFFF"
android:textStyle="bold" />
</RelativeLayout>
Главное Activity, «Main Window». Первым появляется при запуске программы и в зависимости от желания пользователя вызывает остальные Activity.
package com.example.btrobotfull;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener{
private static final int REQUEST_ENABLE_BT = 1;
private BluetoothAdapter mBluetoothAdapter;
Button btnButton;
Button btnTouch;
Button btnAcsel;
//Button btnBTConf;
Button btnConf;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_tab3);
Подключаем кнопки и назначаем им обработчик нажатий. В качестве обработчика выступает Activity (public class MainActivity extends Activity implements OnClickListener).
btnButton = (Button) findViewById(R.id.btButton);
btnTouch = (Button) findViewById(R.id.btTouch);
btnAcsel = (Button) findViewById(R.id.btAcsel);
//btnBTConf = (Button) findViewById(R.id.btBTConf);
btnConf = (Button) findViewById(R.id.btConf);
btnButton.setOnClickListener(this);
btnTouch.setOnClickListener(this);
btnAcsel.setOnClickListener(this);
//btnBTConf.setOnClickListener(this);
btnConf.setOnClickListener(this);
Дальше, первым делом определяем, есть ли у нас вообще адаптер Bluetooth, если нет, закрываемся, если есть, но не включен, выводим стандартный диалог с запросом пользователю с просьбой включить BT.
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
errorExit("Bluetooth ERROR", "Can`t find bluetooth adapter");
}
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
Дошли до обработчика нажатий кнопок. Каждая кнопка вызывает новый Activity. Таким образом, получается «многооконное» приложение. С помощью кнопок вызываются окно настроек, управления кнопками и тд.
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btButton:
Intent intent_button = new Intent(this, ButtonActivity.class);
startActivity(intent_button);
break;
//case R.id.btTouch:
// Intent intent_touch = new Intent(this, TouchActivity.class);
// startActivity(intent_touch);
// break;
//case R.id.btBTConf:
// Intent intent_bt = new Intent(this, BTActivity.class);
// startActivity(intent_bt);
// break;
case R.id.btConf:
Intent intent_pr = new Intent(this, PreferencesActivity.class);
startActivity(intent_pr);
break;
default:
break;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
private void errorExit(String title, String message)
{
Toast.makeText(getBaseContext(), title + " - " + message, Toast.LENGTH_LONG).show();
finish();
}
}
Управление движением и скоростью кнопками.


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#000000">
<Button
android:id="@+id/btCenter"
android:layout_width="100px"
android:layout_height="100px"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="250px"
android:layout_marginTop="200px"
android:text="C"
android:visibility="invisible" />
<Button
android:id="@+id/btUp"
android:layout_width="150px"
android:layout_height="150px"
android:layout_alignBottom="@+id/btCenter"
android:layout_alignLeft="@+id/btCenter"
android:layout_marginBottom="100px"
android:textColor="#FFFFFF"
android:text="ВПЕРЕД" />
<Button
android:id="@+id/btRight"
android:layout_width="150px"
android:layout_height="150px"
android:layout_alignTop="@+id/btCenter"
android:layout_toRightOf="@+id/btCenter"
android:textColor="#FFFFFF"
android:text="ПРАВО" />
<Button
android:id="@+id/btDown"
android:layout_width="150px"
android:layout_height="150px"
android:layout_below="@+id/btCenter"
android:layout_toLeftOf="@+id/btRight"
android:textColor="#FFFFFF"
android:text="НАЗАД" />
<Button
android:id="@+id/bt100Speed"
android:layout_width="120px"
android:layout_height="100px"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="50px"
android:layout_marginRight="50px"
android:textColor="#FFFFFF"
android:text="100%" />
<Button
android:id="@+id/bt90Speed"
android:layout_width="120px"
android:layout_height="100px"
android:layout_above="@+id/bt100Speed"
android:layout_alignLeft="@+id/bt100Speed"
android:textColor="#FFFFFF"
android:text="90%" />
<Button
android:id="@+id/bt80Speed"
android:layout_width="120px"
android:layout_height="100px"
android:layout_above="@+id/bt90Speed"
android:layout_alignLeft="@+id/bt90Speed"
android:textColor="#FFFFFF"
android:text="80%" />
<Button
android:id="@+id/bt30Speed"
android:layout_width="120px"
android:layout_height="100px"
android:layout_alignLeft="@+id/bt40Speed"
android:layout_below="@+id/bt40Speed"
android:textColor="#FFFFFF"
android:text="30%" />
<Button
android:id="@+id/bt70Speed"
android:layout_width="120px"
android:layout_height="100px"
android:layout_above="@+id/bt80Speed"
android:layout_alignLeft="@+id/bt80Speed"
android:textColor="#FFFFFF"
android:text="70%" />
<Button
android:id="@+id/bt60Speed"
android:layout_width="120px"
android:layout_height="100px"
android:layout_above="@+id/bt80Speed"
android:layout_toLeftOf="@+id/bt80Speed"
android:textColor="#FFFFFF"
android:text="60%" />
<Button
android:id="@+id/bt50Speed"
android:layout_width="120px"
android:layout_height="100px"
android:layout_alignBaseline="@+id/bt60Speed"
android:layout_alignBottom="@+id/bt60Speed"
android:layout_toLeftOf="@+id/bt60Speed"
android:textColor="#FFFFFF"
android:text="50%" />
<Button
android:id="@+id/bt40Speed"
android:layout_width="120px"
android:layout_height="100px"
android:layout_above="@+id/bt80Speed"
android:layout_toLeftOf="@+id/bt50Speed"
android:textColor="#FFFFFF"
android:text="40%" />
<Button
android:id="@+id/bt20Speed"
android:layout_width="120px"
android:layout_height="100px"
android:layout_below="@+id/bt30Speed"
android:layout_toLeftOf="@+id/bt50Speed"
android:textColor="#FFFFFF"
android:text="20%" />
<Button
android:id="@+id/bt10Speed"
android:layout_width="120px"
android:layout_height="100px"
android:layout_below="@+id/bt20Speed"
android:layout_toLeftOf="@+id/bt50Speed"
android:textColor="#FFFFFF"
android:text="10%" />
<Button
android:id="@+id/btStop"
android:layout_width="240px"
android:layout_height="100px"
android:layout_below="@+id/bt90Speed"
android:layout_toLeftOf="@+id/bt100Speed"
android:textColor="#FFFFFF"
android:text="СТОП" />
<Button
android:id="@+id/btLeft"
android:layout_width="150px"
android:layout_height="150px"
android:layout_above="@+id/btDown"
android:layout_toLeftOf="@+id/btUp"
android:textColor="#FFFFFF"
android:text="ЛЕВО" />
</RelativeLayout>
Окно управления тележкой кнопками.
package com.example.btrobotfull;
import java.lang.ref.WeakReference;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class ButtonActivity extends Activity implements OnClickListener{
private cBluetooth bl;
//private String address = "00:01:95:0C:68:E7";
private String address = "";
private boolean BT_is_connect;
Button btnStop;
Button btn10Speed;
Button btn20Speed;
Button btn30Speed;
Button btn40Speed;
Button btn50Speed;
Button btn60Speed;
Button btn70Speed;
Button btn80Speed;
Button btn90Speed;
Button btn100Speed;
Button btnUp;
Button btnDown;
Button btnLeft;
Button btnRight;
SharedPreferences sPref;
final String SAVED_MAC = "00:00:00:00:00:00";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.button_tab3);
btnStop = (Button) findViewById(R.id.btStop);
btn10Speed = (Button) findViewById(R.id.bt10Speed);
btn20Speed = (Button) findViewById(R.id.bt20Speed);
btn30Speed = (Button) findViewById(R.id.bt30Speed);
btn40Speed = (Button) findViewById(R.id.bt40Speed);
btn50Speed = (Button) findViewById(R.id.bt50Speed);
btn60Speed = (Button) findViewById(R.id.bt60Speed);
btn70Speed = (Button) findViewById(R.id.bt70Speed);
btn80Speed = (Button) findViewById(R.id.bt80Speed);
btn90Speed = (Button) findViewById(R.id.bt90Speed);
btn100Speed = (Button) findViewById(R.id.bt100Speed);
btnUp = (Button) findViewById(R.id.btUp);
btnDown = (Button) findViewById(R.id.btDown);
btnLeft = (Button) findViewById(R.id.btLeft);
btnRight = (Button) findViewById(R.id.btRight);
btnUp.setOnClickListener(this);
btnDown.setOnClickListener(this);
btnLeft.setOnClickListener(this);
btnRight.setOnClickListener(this);
btnStop.setOnClickListener(this);
btn10Speed.setOnClickListener(this);
btn20Speed.setOnClickListener(this);
btn30Speed.setOnClickListener(this);
btn40Speed.setOnClickListener(this);
btn50Speed.setOnClickListener(this);
btn60Speed.setOnClickListener(this);
btn70Speed.setOnClickListener(this);
btn80Speed.setOnClickListener(this);
btn90Speed.setOnClickListener(this);
btn100Speed.setOnClickListener(this);
«Описали» кнопки. Дальше, загружаем в переменную address MAC BT адаптера нашей тележки, если она была сохранена в настройках.
sPref = getSharedPreferences("RobotPref", MODE_PRIVATE);
address = sPref.getString(SAVED_MAC, "");
Toast.makeText(getBaseContext(), "Подключение к адресу: " + address, Toast.LENGTH_LONG).show();
bl = new cBluetooth(this, mHandler);
bl.checkBTState();
}
Дальше, шлем в зависимости от нажатых кнопок на экране, коды нашему роботу. Тут можно поменять коды под любое свое устройство.
@Override
public void onClick(View v) {
switch (v.getId())
{
case R.id.btUp:
if(BT_is_connect) bl.sendData("8");
break;
case R.id.btDown:
if(BT_is_connect) bl.sendData("2");
break;
case R.id.btLeft:
if(BT_is_connect) bl.sendData("4");
break;
case R.id.btRight:
if(BT_is_connect) bl.sendData("6");
break;
case R.id.btStop:
if(BT_is_connect) bl.sendData(" ");
break;
case R.id.bt10Speed:
if(BT_is_connect) bl.sendData("Q");
break;
case R.id.bt20Speed:
if(BT_is_connect) bl.sendData("W");
break;
case R.id.bt30Speed:
if(BT_is_connect) bl.sendData("E");
break;
case R.id.bt40Speed:
if(BT_is_connect) bl.sendData("R");
break;
case R.id.bt50Speed:
if(BT_is_connect) bl.sendData("T");
break;
case R.id.bt60Speed:
if(BT_is_connect) bl.sendData("Y");
break;
case R.id.bt70Speed:
if(BT_is_connect) bl.sendData("U");
break;
case R.id.bt80Speed:
if(BT_is_connect) bl.sendData("I");
break;
case R.id.bt90Speed:
if(BT_is_connect) bl.sendData("O");
break;
case R.id.bt100Speed:
if(BT_is_connect) bl.sendData("P");
break;
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.button, menu);
return true;
}
private static class MyHandler extends Handler {
private final WeakReference<ButtonActivity> mActivity;
public MyHandler(ButtonActivity activity) {
mActivity = new WeakReference<ButtonActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
ButtonActivity activity = mActivity.get();
if (activity != null) {
switch (msg.what) {
case cBluetooth.BL_NOT_AVAILABLE:
Toast.makeText(activity.getBaseContext(), "Bluetooth is not available", Toast.LENGTH_SHORT).show();
activity.finish();
break;
case cBluetooth.BL_INCORRECT_ADDRESS:
Toast.makeText(activity.getBaseContext(), "Incorrect Bluetooth address", Toast.LENGTH_SHORT).show();
break;
case cBluetooth.BL_REQUEST_ENABLE:
BluetoothAdapter.getDefaultAdapter();
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(enableBtIntent, 1);
break;
case cBluetooth.BL_SOCKET_FAILED:
Toast.makeText(activity.getBaseContext(), "Socket failed", Toast.LENGTH_SHORT).show();
activity.finish();
break;
}
}
}
}
private final MyHandler mHandler = new MyHandler(this);
Если свернули приложение или сменили ориентацию экрана восстанавливаем коннект с роботом.
@Override
protected void onResume() {
super.onResume();
BT_is_connect = bl.BT_Connect(address, false);
}
@Override
protected void onPause() {
super.onPause();
bl.BT_onPause();
}
}
Адрес подключения сохраняется в настройках. Можно прописывать руками, можно выбирать из найденого или списка доверенных устройств bluetooth.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:orientation="vertical" >
<TextView
android:id="@+id/textMAC"
android:layout_width="match_parent"
android:layout_height="75px"
android:text="Текущий МАС адрес по умолчанию для подключения:"
android:textColor="#FFFFFF" />
<TextView
android:id="@+id/textView1"
android:layout_width="match_parent"
android:layout_height="75px"
android:text="Ведите MAC адрес по умолчанию для подключения (XX:XX:XX:XX:XX:XX): "
android:textColor="#FFFFFF"/>
<EditText
android:id="@+id/editMAC"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:textColor="#FFFFFF" >
<requestFocus />
</EditText>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center" >
<Button
android:id="@+id/btSave"
android:layout_width="250px"
android:layout_height="75px"
android:text="Сохранить"
android:textColor="#FFFFFF"/>
<Button
android:id="@+id/btCancel"
android:layout_width="250px"
android:layout_height="75px"
android:text="Отмена"
android:textColor="#FFFFFF"/>
</LinearLayout>
<Button
android:id="@+id/btFindMAC"
android:layout_width="500px"
android:layout_height="75px"
android:layout_gravity="center"
android:text="Найти MAC"
android:textColor="#FFFFFF"/>
</LinearLayout>
package com.example.btrobotfull;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class PreferencesActivity extends Activity implements OnClickListener {
EditText etMAC;
TextView txtMAC;
Button btnSave;
Button btnCancel;
Button btnFindMAC;
SharedPreferences sPref;
final String SAVED_MAC = "00:00:00:00:00:00";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.preferences_tab3);
btnSave = (Button) findViewById(R.id.btSave);
btnCancel = (Button) findViewById(R.id.btCancel);
btnFindMAC = (Button) findViewById(R.id.btFindMAC);
btnSave.setOnClickListener(this);
btnCancel.setOnClickListener(this);
btnFindMAC.setOnClickListener(this);
txtMAC = (TextView) findViewById(R.id.textMAC);
etMAC = (EditText) findViewById(R.id.editMAC);
sPref = getSharedPreferences("RobotPref", MODE_PRIVATE);
String savedText = sPref.getString(SAVED_MAC, "");
txtMAC.setText("Текущий МАС адрес по умолчанию для подключения: " + savedText);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.preferences, menu);
return true;
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btSave:
saveText();
break;
case R.id.btCancel:
finish();
break;
case R.id.btFindMAC:
Intent intent_bt = new Intent(this, BTActivity.class);
startActivity(intent_bt);
break;
default:
break;
}
}
void saveText() {
sPref = getSharedPreferences("RobotPref", MODE_PRIVATE);
Editor ed = sPref.edit();
ed.putString(SAVED_MAC, etMAC.getText().toString());
ed.commit();
String savedText = sPref.getString(SAVED_MAC, "");
txtMAC.setText("Текущий МАС адрес по умолчанию для подключения: " + savedText);
}
@Override
protected void onResume() {
super.onResume();
sPref = getSharedPreferences("RobotPref", MODE_PRIVATE);
String savedText = sPref.getString(SAVED_MAC, "");
txtMAC.setText("Текущий МАС адрес по умолчанию для подключения: " + savedText);
}
}
Список доверенных и найденных устройств bluetooth. Нажатие на любого из списка, заносит его в настройки по умолчанию для дальнейших подключений.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:orientation="vertical" >
<TextView
android:id="@+id/textPairedDevices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Доверенные устройства"
android:textColor="#FFFFFF" />
<ListView
android:id="@+id/listPairedDevices"
android:layout_width="match_parent"
android:layout_height="300px"
android:background="#615e54"
android:textColor="#FFFFFF" >
</ListView>
<TextView
android:id="@+id/textNewDevices"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Новые устройства"
android:textColor="#FFFFFF" />
<ListView
android:id="@+id/listNewDevices"
android:layout_width="match_parent"
android:layout_height="300px"
android:background="#615e54"
android:textColor="#FFFFFF" >
</ListView>
<Button
android:id="@+id/btScan"
android:layout_width="match_parent"
android:layout_height="75px"
android:text="Сканировать"
android:textColor="#FFFFFF" />
</LinearLayout>
package com.example.btrobotfull;
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.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class BTActivity extends Activity implements OnClickListener {
public static String EXTRA_DEVICE_ADDRESS = "device_address";
private BluetoothAdapter mBluetoothAdapter;
private ArrayAdapter<String> mPairedDevicesArrayAdapter;
private ArrayAdapter<String> mNewDevicesArrayAdapter;
private String info;
private String address;
ListView lvPairedDevices;
ListView lvNewDevices;
SharedPreferences sPref;
final String SAVED_MAC = "00:00:00:00:00:00";
Button btnScan;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bt_tab3);
lvPairedDevices = (ListView) findViewById(R.id.listPairedDevices);
lvNewDevices = (ListView) findViewById(R.id.listNewDevices);
btnScan = (Button) findViewById(R.id.btScan);
btnScan.setOnClickListener(this);
mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
lvPairedDevices.setOnItemClickListener(mDeviceClickListener);
lvNewDevices.setOnItemClickListener(mDeviceClickListener);
lvPairedDevices.setAdapter(mPairedDevicesArrayAdapter);
lvNewDevices.setAdapter(mNewDevicesArrayAdapter);
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
this.registerReceiver(mReceiver, filter);
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
this.registerReceiver(mReceiver, filter);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
}
}
@Override
public void onClick(View arg0) {
doDiscovery();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mBluetoothAdapter != null) {
mBluetoothAdapter.cancelDiscovery();
}
this.unregisterReceiver(mReceiver);
}
private void doDiscovery() {
setProgressBarIndeterminateVisibility(true);
if (mBluetoothAdapter.isDiscovering()) {
mBluetoothAdapter.cancelDiscovery();
}
mBluetoothAdapter.startDiscovery();
}
private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
mBluetoothAdapter.cancelDiscovery();
info = ((TextView) v).getText().toString();
address = info.substring(info.length() - 17);
sPref = getSharedPreferences("RobotPref", MODE_PRIVATE);
Editor ed = sPref.edit();
ed.putString(SAVED_MAC, address);
ed.commit();
String savedText = sPref.getString(SAVED_MAC, "");
Toast.makeText(getBaseContext(),"Установлен адрес подключения по умолчанию: " + savedText, Toast.LENGTH_LONG).show();
finish();
}
};
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
}
}
}
};
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.bt, menu);
return true;
}
}
Еще понадобился дополнительный класс:
package com.example.btrobotfull;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.UUID;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
public class cBluetooth {
private BluetoothSocket btSocket;
private OutputStream outStream;
private BluetoothAdapter mBluetoothAdapter;
private ConnectedThread mConnectedThread;
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private final Handler mHandler;
public final static int BL_NOT_AVAILABLE = 1;
public final static int BL_INCORRECT_ADDRESS = 2;
public final static int BL_REQUEST_ENABLE = 3;
public final static int BL_SOCKET_FAILED = 4;
public final static int RECIEVE_MESSAGE = 5;
cBluetooth(Context context, Handler handler){
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
mHandler = handler;
if (mBluetoothAdapter == null) {
mHandler.sendEmptyMessage(BL_NOT_AVAILABLE);
return;
}
}
public void checkBTState() {
if(mBluetoothAdapter == null) {
mHandler.sendEmptyMessage(BL_NOT_AVAILABLE);
} else {
if (!mBluetoothAdapter.isEnabled()) {
mHandler.sendEmptyMessage(BL_REQUEST_ENABLE);
}
}
}
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
if(Build.VERSION.SDK_INT >= 10){
try {
final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
return (BluetoothSocket) m.invoke(device, MY_UUID);
} catch (Exception e) { }
}
return device.createRfcommSocketToServiceRecord(MY_UUID);
}
public boolean BT_Connect(String address, boolean listen_InStream) {
boolean connected = false;
if(!BluetoothAdapter.checkBluetoothAddress(address)){
mHandler.sendEmptyMessage(BL_INCORRECT_ADDRESS);
return false;
}
else{
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
try {
btSocket = createBluetoothSocket(device);
} catch (IOException e1) {
mHandler.sendEmptyMessage(BL_SOCKET_FAILED);
return false;
}
mBluetoothAdapter.cancelDiscovery();
try {
btSocket.connect();
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
mHandler.sendEmptyMessage(BL_SOCKET_FAILED);
return false;
}
}
try {
outStream = btSocket.getOutputStream();
connected = true;
} catch (IOException e) {
mHandler.sendEmptyMessage(BL_SOCKET_FAILED);
return false;
}
if(listen_InStream) {
mConnectedThread = new ConnectedThread();
mConnectedThread.start();
}
}
return connected;
}
public void BT_onPause() {
if (outStream != null) {
try {
outStream.flush();
} catch (IOException e) {
mHandler.sendEmptyMessage(BL_SOCKET_FAILED);
return;
}
}
if (btSocket != null) {
try {
btSocket.close();
} catch (IOException e2) {
mHandler.sendEmptyMessage(BL_SOCKET_FAILED);
return;
}
}
}
public void sendData(String message) {
byte[] msgBuffer = message.getBytes();
if (outStream != null) {
try {
outStream.write(msgBuffer);
} catch (IOException e) {
mHandler.sendEmptyMessage(BL_SOCKET_FAILED);
return;
}
}
}
private class ConnectedThread extends Thread {
private final InputStream mmInStream;
public ConnectedThread() {
InputStream tmpIn = null;
try {
tmpIn = btSocket.getInputStream();
} catch (IOException e) {}
mmInStream = tmpIn;
}
public void run() {
byte[] buffer = new byte[256];
int bytes;
while (true) {
try {
bytes = mmInStream.read(buffer);
mHandler.obtainMessage(RECIEVE_MESSAGE, bytes, -1, buffer).sendToTarget();
} catch (IOException e) {
break;
}
}
}
}
}
«Мозг» будущей тележки на основе stm32 сигналы из программы принимает адекватно, радостно отвечает помаргиванием и текстом на LCD. Обработкой полученных сигналов занимается обработчик прерывания:
void USART2_IRQHandler(void)
{
if (USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
{
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
UART2IRQbuf[UART2Count] = USART2->DR;
if (!ConfigMode)
{
switch (UART2IRQbuf[UART2Count])
{
case 81:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 87:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 69:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 82:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 84:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 89:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 85:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 73:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 79:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 80:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 32:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 61:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 45:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 56:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 50:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 52:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case 54:
CommandChar = UART2IRQbuf[UART2Count];
UART2Count = -1;
isr_evt_set (EVT_MOVE_COMMAND, t_MOVE);
break;
case _CR:
strlcpy(Command, UART2IRQbuf, UART2Count+1);
UART2Count = -1;
isr_evt_set (EVT_TERM_COMMAND, t_TERMINAL);
break;
case _ESC:
ConfigMode = 1;
MotorEnable = 0;
MotorsStop ();
UART2Count = -1;
Usart2_putString ("\n\n<<<< CONFIG MODE >>>\n\n");
break;
}
}
else
{
switch (UART2IRQbuf[UART2Count])
{
case _ESC:
ConfigMode = 0;
MotorEnable = 1;
UART2Count = -1;
Usart2_putString ("\n\n<<<< MOVE MODE>>>\n\n");
break;
case _CR:
strlcpy(Command, UART2IRQbuf, UART2Count+1);
UART2Count = -1;
isr_evt_set (EVT_CONFIG_COMMAND, t_CONFIG);
break;
}
}
UART2Count++;
if ( UART2Count == UART2SIZE )
{
UART2Count = 0;
}
}
}
Который дает сигнал задачам KEIL RTOS на обработку.
__task void f_MOVE (void)
{
uint8_t c[2] = "0";
while (1)
{
os_evt_wait_or (EVT_MOVE_COMMAND, 0xffff);
sprintf (c, "%02d", CommandChar);
LCDgotoxy(7,0);
LCDputs (C);
switch (CommandChar)
{
case 32:
MotorsStop ();
break;
case 61:
MotorSpeedUp ();
break;
case 45:
MotorSpeedDown ();
break;
case 56:
MoveForward ();
break;
case 50:
MoveBack ();
break;
case 52:
MoveLeft ();
break;
case 54:
MoveRight ();
break;
case 81:
Motor2Speed = 60;
Motor4Speed = 60;
break;
case 87:
Motor2Speed = 120;
Motor4Speed = 120;
break;
case 69:
Motor2Speed = 180;
Motor4Speed = 180;
break;
case 82:
Motor2Speed = 240;
Motor4Speed = 240;
break;
case 84:
Motor2Speed = 300;
Motor4Speed = 300;
break;
case 89:
Motor2Speed = 360;
Motor4Speed = 360;
break;
case 85:
Motor2Speed = 420;
Motor4Speed = 420;
break;
case 73:
Motor2Speed = 480;
Motor4Speed = 480;
break;
case 79:
Motor2Speed = 540;
Motor4Speed = 540;
break;
case 80:
Motor2Speed = 600;
Motor4Speed = 600;
break;
}
}
}

Пока не работают управление пальцем по экрану и акселерометр. Это уже на будущее, когда тележка будет собрана, что бы было реальное устройство для отладки.
PS. Картинки уменьшить не могу.
- +4
- 25 октября 2013, 22:15
- lexanet
- 1
Файлы в топике:
BTRobotFull.zip
lexanet , простите за то что начну комментарии с замечания:
мне, как человеку мало что понимающему в программировании под Android, все таки хотелось бы видеть пояснения к выше приведенным строкам кода. Потому как не вижу смысла его(код) приводить непосредственно в статье без пояснений(комментариев) — тогда уж добавить в архив и прикрепить к посту.
мне, как человеку мало что понимающему в программировании под Android, все таки хотелось бы видеть пояснения к выше приведенным строкам кода. Потому как не вижу смысла его(код) приводить непосредственно в статье без пояснений(комментариев) — тогда уж добавить в архив и прикрепить к посту.
Как абсолютно такой же далекий от андроида человек, могу сказать, комментарии кода тут не помогут. Если это «первый раз», чтение книг и написание собственного «Hello World» просто необходимы. Иначе, даже про подключение одной кнопки придется целую статью писать. Но, наверно Вы в чем то правы. Трудновато читать. Разобью код на блоки и добавлю комментарии в текст.
А еще, рекомендую: http://startandroid.ru/ru/uroki/vse-uroki-spiskom.html. На русском и доступным языком с самых азов.
PS. Архив с кодом конечно прилеплен :)
А еще, рекомендую: http://startandroid.ru/ru/uroki/vse-uroki-spiskom.html. На русском и доступным языком с самых азов.
PS. Архив с кодом конечно прилеплен :)
Воу-воу-воу, паллехче!
Тут в редакторе можна установить на эти все портянки скрытия текста. И он будет розворачеватся при нажатии на плюсик.
Даже не стал читать, увидев столько портянок.
Хотя статейка должна быть полезной.
Я как то пробовал на Visual C# писать, порт фреймвлрка Моно, но меня это утомило и забросил.
Тут в редакторе можна установить на эти все портянки скрытия текста. И он будет розворачеватся при нажатии на плюсик.
Даже не стал читать, увидев столько портянок.
Хотя статейка должна быть полезной.
Я как то пробовал на Visual C# писать, порт фреймвлрка Моно, но меня это утомило и забросил.
просмотрел, всё классно. кажется, не хватает только быстрой инструкции по установке софта на андроид и быстрого описания протокола — для тех, кто будет делать начинку на другом мк, и хотел бы воспользоваться готовым андроид-клиентом без вникания в код. впрочем, по onclick вроде бы понятно — оно отрабатывает только однократные касания?
- ptichkipichuzhki
- 26 октября 2013, 22:24
- ↓
Если вникать нет необходимости, то можно установить RC управление с маркета. Там полно бесплатных программ для управления машинками по bluetooth.
Да, вся настройка под свою начинку, это коды отправляемых кнопок. Счас немного переделаю код, добавлю двухстороннюю связь и продолжу разбивать портянки и добавлять комменты.
Как все это работает в живую я не знаю. Контроллер готов, прошивка написана, прога для управления с телефоном тоже, а самой тележки еще нет. Несут почтальоны. Поэтому, все на уровне догадок и предположений. Контроллер, конечно, снабжен системой индикации и экранчик к нему прилеплен, но, реальной картины это не даст.
Да, вся настройка под свою начинку, это коды отправляемых кнопок. Счас немного переделаю код, добавлю двухстороннюю связь и продолжу разбивать портянки и добавлять комменты.
Как все это работает в живую я не знаю. Контроллер готов, прошивка написана, прога для управления с телефоном тоже, а самой тележки еще нет. Несут почтальоны. Поэтому, все на уровне догадок и предположений. Контроллер, конечно, снабжен системой индикации и экранчик к нему прилеплен, но, реальной картины это не даст.
они там тоже грешат тем, что как правило поставляются as is и без описания протоколов :-(
и вообще, чтобы найти в плеймаркете что-то нужное и полностью покрывающее потребности — нужно проявить чудеса выдержки.
в данном случае прогнозирую возможное желание сделать так, чтобы тележка двигалась только до тех пор, пока палец касается экрана. а еще круче — разбить экран на 8 секторов и при перемещении пальца по секторам — пускай меняются значения (направления движения относительно оси тележки), палец отпущен — движение остановлено.
кстати, вот бы для андроида еще заиметь приложение, чтобы через блютус серийный порт получать пачки значений и выводить в риалтайме на стрелочных циферблатах…
и вообще, чтобы найти в плеймаркете что-то нужное и полностью покрывающее потребности — нужно проявить чудеса выдержки.
в данном случае прогнозирую возможное желание сделать так, чтобы тележка двигалась только до тех пор, пока палец касается экрана. а еще круче — разбить экран на 8 секторов и при перемещении пальца по секторам — пускай меняются значения (направления движения относительно оси тележки), палец отпущен — движение остановлено.
кстати, вот бы для андроида еще заиметь приложение, чтобы через блютус серийный порт получать пачки значений и выводить в риалтайме на стрелочных циферблатах…
- ptichkipichuzhki
- 27 октября 2013, 00:18
- ↑
- ↓
о, а чем дальше палец уведён от центра экрана, тем выше скорость тележки (в заданном направлении)! уау! я дизайнер интерфейсов! :=)
- ptichkipichuzhki
- 27 октября 2013, 00:21
- ↑
- ↓
Вот у «того парня», ссылку на которого я приводил в самом начале, вроде, реализовано управление пальцем по экрану. Я тоже собираюсь такое сделать. Но, потом :)
А протокол, так он простой и у всех один, кто управляет по синезубу (удлинителю уарт-а по сути). Кнопку на анрофоне жмешь, onClick посылает ASCII код назначенной буквы в эфир:
На машинке или бесконечный цикл или прерывание слетят за входящими символами, поступающими по уарт-у и в зависимости от кода символа что то делают:
И все. Любому нужно просто или поменять символы в noClick под свою существующую тележку или в прошивке тележки поменять код символа, на приход которого нужно реагировать.
Палец на кнопке — едем, палец подняли — остановились, это уже в прошивке тележки лучше реализовать, чтобы она не убежала от пользователя, когда связь пропадет :) Грубо говоря, как то так:
Т.е. нажатие словили, тележка какое то время едет и останавливается до следующего нажатия. Палец на кнопке, сигналы нажатия идут непрерывно, тележка постоянно едет. Палец с кнопкт убрали, тележка остановилась через заданное время.
По уарту можно получать все что угодно. Хоть роман Толстова. Была бы возможность разобрать его на составляющие :) У меня с тележки на телефон каждую секунду отсылается строка:
Значение RTC, напряжение аккумулятора и значения PWM левого и правого мотора. Я это разбиваю и вывожу в окна управления на андроиде.
А протокол, так он простой и у всех один, кто управляет по синезубу (удлинителю уарт-а по сути). Кнопку на анрофоне жмешь, onClick посылает ASCII код назначенной буквы в эфир:
case R.id.btUp:
if(BT_is_connect) bl.sendData("8");
break;
На машинке или бесконечный цикл или прерывание слетят за входящими символами, поступающими по уарт-у и в зависимости от кода символа что то делают:
case 56:
MoveForward ();
break;
И все. Любому нужно просто или поменять символы в noClick под свою существующую тележку или в прошивке тележки поменять код символа, на приход которого нужно реагировать.
Палец на кнопке — едем, палец подняли — остановились, это уже в прошивке тележки лучше реализовать, чтобы она не убежала от пользователя, когда связь пропадет :) Грубо говоря, как то так:
void MoveForward (void)
{
if (MotorEnable)
{
LeftMotorForward ();
RightMotorForward ();
TIM3_OCInitStructure.TIM_Pulse = Motor2Speed;
TIM_OC2Init(TIM3, &TIM3_OCInitStructure);
TIM3_OCInitStructure.TIM_Pulse = Motor4Speed;
TIM_OC4Init(TIM3, &TIM3_OCInitStructure);
Delay_MS (500);
TIM3_OCInitStructure.TIM_Pulse = 0;
TIM_OC2Init(TIM3, &TIM3_OCInitStructure);
TIM3_OCInitStructure.TIM_Pulse = 0;
TIM_OC4Init(TIM3, &TIM3_OCInitStructure);
}
}
Т.е. нажатие словили, тележка какое то время едет и останавливается до следующего нажатия. Палец на кнопке, сигналы нажатия идут непрерывно, тележка постоянно едет. Палец с кнопкт убрали, тележка остановилась через заданное время.
По уарту можно получать все что угодно. Хоть роман Толстова. Была бы возможность разобрать его на составляющие :) У меня с тележки на телефон каждую секунду отсылается строка:
08:45:07, 12.2V, 000-000>
Значение RTC, напряжение аккумулятора и значения PWM левого и правого мотора. Я это разбиваю и вывожу в окна управления на андроиде.

ну вот не у всех так. колупался в джава-коде кривой j2me управлялки для нокии — там в зависимости от нажатия/отпускания изменялись биты в постоянно передаваемом байте.
какие-то клиенты отслеживают отпускание кнопки, какие-то нет. не говоря уже о том, что каждый клиент отправляет свои символы.
такую реализацию, как предложена выше (Т.е. нажатие словили, тележка какое то время едет и останавливается до следующего нажатия. Палец на кнопке, сигналы нажатия идут непрерывно, тележка постоянно едет. Палец с кнопкт убрали, тележка остановилась через заданное время.) уже реализовывал — насколько я помню, были какие-то неустранимые дергания. в итоге, насколько помню, перешел на алгоритм без проверки на отвал блютуса…
какие-то клиенты отслеживают отпускание кнопки, какие-то нет. не говоря уже о том, что каждый клиент отправляет свои символы.
такую реализацию, как предложена выше (Т.е. нажатие словили, тележка какое то время едет и останавливается до следующего нажатия. Палец на кнопке, сигналы нажатия идут непрерывно, тележка постоянно едет. Палец с кнопкт убрали, тележка остановилась через заданное время.) уже реализовывал — насколько я помню, были какие-то неустранимые дергания. в итоге, насколько помню, перешел на алгоритм без проверки на отвал блютуса…
- ptichkipichuzhki
- 27 октября 2013, 17:33
- ↑
- ↓
Комментарии (13)
RSS свернуть / развернуть