基于STM32的智能小车app自学梳理
Posted 旭日初扬
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于STM32的智能小车app自学梳理相关的知识,希望对你有一定的参考价值。
文章目录
目录
前言
想搞一个蓝牙app与单片机通信,控制小车的前进后退。在Git上找了好多源码,这看看那看看整得,好像都不是那么回事。以前也没整过android,整得我好费劲呀!还是老老实实选一份看看、好好的梳理梳理。
一、基本逻辑梳理
用android定义几个个按钮(前后左右),按下按钮则通过android蓝牙发出一个字符给单片机的蓝牙模块接收。蓝牙模块与单片机是串口通信。故比较接收的字符与定义的字符是否一致,一致则执行指定操作。
通过android蓝牙t调试宝测得CH06的的UUID为:00001101-0000-1000-8000-00805f9b34fb(同一类型设备通用)
MAC:98:DA:E0:01:0B:4E(每个蓝牙模块MAC唯一)
单片机部分代码:
u8 reclen=0;
if(USART2_RX_STA&0X8000) //接收到一次数据了
reclen=USART2_RX_STA&0X7FFF; //得到数据长度
USART2_RX_BUF[reclen]=0; //加入结束符
printf("USART2_RX_BUF:%s\\n",USART2_RX_BUF);
printf("reclen:%d\\n",reclen);
if(reclen==1||reclen==2) //控制DS1检测
if(strcmp((const char*)USART2_RX_BUF,"A")==0)
u2_printf("go forward!");
ZYSTM32_run(80,100);
else if(strcmp((const char*)USART2_RX_BUF,"E")==0)
u2_printf("go back!");
ZYSTM32_back(80,100);
else if(strcmp((const char*)USART2_RX_BUF,"C")==0)
u2_printf("go right!");
ZYSTM32_Right(80,100);
else if(strcmp((const char*)USART2_RX_BUF,"G")==0)
u2_printf("go left!");
ZYSTM32_Left(80,100);
else if(strcmp((const char*)USART2_RX_BUF,"X")==0)
u2_printf("Stop!");
ZYSTM32_brake(100);
else if(strcmp((const char*)USART2_RX_BUF,"Y")==0)
u2_printf("Stop!");
ZYSTM32_brake(100);
USART2_RX_STA=0;
二、android部分蓝牙扫描
2.1、权限申请
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.example.blueboothproject_01">
<!--请求旧设备上的遗留蓝牙权限。-->
<!--允许程序连接到已配对的蓝牙-->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!--BLE feature(特征)-->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
<!--允许程序连接到已配对的蓝牙-->
<uses-permission android:name="android.permission.BLUETOOTH"/>
<!--只有当您的应用程序与已经配对的蓝牙通信时才需要-->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!--只有当应用程序寻找蓝牙设备时才需要。 必须向此权限添加属性ACCESS_FINE_LOCATION权限,取决于您的时候的结果在应用程序中检查位置使用情况-->
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!---->
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<!--允许程序访问CellID或WIFI热点来获取粗略的位置-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--允许程序访问精确位置-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!--只有当您的应用程序使设备可被蓝牙发现时才需要设备。-->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-feature android:name="android.hardware.location.gps"/>
<application
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.BlueBoothProject_01"
tools:targetApi="31">
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Ble_Activity" android:exported="true"/>
<service
android:name="com.example.blueboothproject_01.service.BluetoothleService"
android:enabled="true">
</service>
<activity android:name=".DebugActivity"
android:resizeableActivity="true"
/>
</application>
</manifest>
2.2、相关变量
// 通过按键扫描蓝牙
private Button BlueBooth_Scan_btn;
// 获取蓝牙适配器
private BluetoothAdapter mbluetoothAdapter;
// 低功耗蓝牙扫描回调类
private ScanCallback mScanCalBack;
// 低功耗蓝牙扫描装置
private BluetoothLeScanner mBluetoothLeScanner;
// 蓝牙信号强度
private ArrayList<Integer> rssis;
// 自定义适配器
private LeDeviceListAdapter mleDeviceListAdpater;
// 显示扫描到的蓝牙信息 Listview
private ListView lv;
// 蓝牙的扫描状态
// 扫描中
private boolean mScanming;
// 扫描标记
private boolean Scan_Flag;
// 操作类
private Handler mhandler;
// 请求使能
int REQUEST_ENABLE_BIT = 1;
// 蓝牙扫描时间
private static final long SCAN_PERIOD = 4000;
2.3、onCreate一些初始化操作
/**
* 一个activity启动调用的第一个函数就是onCreate,
* 它主要做这个activity启动时一些必要的初始化工作
* @param savedInstanceState
*/
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
// 加载activity_main.xml
setContentView(R.layout.activity_main);
// 申请位置权限
Permissions_Init();
// 控件初始化
UI_Init();
// 蓝牙初始化
BlueTooth_Init();
// 绑定蓝牙 Build.VERSION_CODES.R sdk版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
// 回调函数
setScanCallBack();
// 开始扫描
boolean Scan_Flag = true;
// 创建一个自定义适配器类型对象
mleDeviceListAdpater = new LeDeviceListAdapter();
// 为listview指定适配器
lv.setAdapter(mleDeviceListAdpater);
// listview点击函数
lv.setOnItemClickListener(new AdapterView.OnItemClickListener()
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
// 获取列表中指定位置的元素
final BluetoothMessage bluetoothMessage = mleDeviceListAdpater.getDevice(position);
// 判空
if(bluetoothMessage==null)
return;
final Intent intent = new Intent(MainActivity.this, com.example.blueboothproject_01.Ble_Activity.class);
// 向意图添加扩展数据。名称必须包含包前缀,
// 例如应用程序com.android.contacts将使用“com.android.contacts. showall”这样的名称。
// 蓝牙名称
intent.putExtra(com.example.blueboothproject_01.Ble_Activity.EXTRAS_DEVICE_NAME,
bluetoothMessage.getName()!=null?bluetoothMessage.getName():bluetoothMessage.getDevice().getName());
// 蓝牙地址
intent.putExtra(com.example.blueboothproject_01.Ble_Activity.EXTRAS_DEVICE_ADDRESS,
bluetoothMessage.getDevice().getAddress());
// 信号强度
intent.putExtra(com.example.blueboothproject_01.Ble_Activity.EXTRAS_DEVICE_RSSI,
rssis.get(position).toString());
if(mScanming)
// 停止扫描
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.R)
// 停止正在进行的蓝牙LE扫描
mBluetoothLeScanner.stopScan(mScanCalBack);
else
// 停止正在进行的蓝牙LE设备扫描。
mbluetoothAdapter.stopLeScan(mLeScanCallback);
mScanming= false;
try
// 启动booth_activity组
startActivity(intent);
catch (Exception e)
// 异常处理
e.printStackTrace();
);
// 蓝牙扫描 设置一个点击事件
BlueBooth_Scan_btn.setOnLongClickListener(new View.OnLongClickListener()
@Override
public boolean onLongClick(View v)
startActivity(new Intent(MainActivity.this,DebugActivity.class));
return false;
);
2.3.1、位置权限
private void Permissions_Init()
// 确定是否已授予您特定的权限。
if (ContextCompat.checkSelfPermission(this,
Manifest.permission_group.LOCATION)!= PackageManager.PERMISSION_GRANTED)
// 获取wifi连接需要定位权限,没有获取权限
ActivityCompat.requestPermissions((Activity) this, new String[]
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_WIFI_STATE,
, 1);
2.3.2、控件初始化
/**
* ********************************************************************************
* 控件初始化
* ********************************************************************************
*/
private void UI_Init()
BlueBooth_Scan_btn = this.findViewById(R.id.scan_btn);
BlueBooth_Scan_btn.setOnClickListener(this);
ListView lv = this.findViewById(R.id.lv);
mhandler = new Handler();
2.3.3、蓝牙初始化
/**
* ********************************************************************************
* 蓝牙初始化
* ********************************************************************************
*/
private void BlueTooth_Init()
// 一、手机硬件是否支持蓝牙
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE))
Toast.makeText(this, "Non-Supported BlueBooth-ble-devices", Toast.LENGTH_SHORT).show();
finish();
// 二、从手机获取本机蓝牙适配器
final BluetoothManager mbluetootManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
// AnBuild.VERSION.SDK_INT是一个静态变量,代表运行该应用的手机系统的SDK版本
// Build.VERSION_CODES.R 当前编译器的版本号
// startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
// 从系统服务中获取蓝牙管理器
mbluetoothAdapter = mbluetootManager.getAdapter();
else
// 获取系统默认的蓝牙适配器
mbluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
// 三、 le设备的扫描
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
mBluetoothLeScanner = mbluetoothAdapter.getBluetoothLeScanner();
// 打开蓝牙权限
if (mbluetoothAdapter == null || !mbluetoothAdapter.isEnabled())
Intent enableBtIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BIT);
/* 打开蓝牙权*/
// Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
//,打开一个 Activity ,在打开的 Activity 中操作之后并获得返回结果。
// startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
/*
* 1、Log.v 的调试颜色为黑色的,任何消息都会输出,这里的v代表verbose啰嗦的意思,平时使用就是Log.v("","");
2、Log.d的输出颜色是蓝色的,仅输出debug调试的意思,但他会输出上层的信息,过滤起来可以通过DDMS的Logcat标签来选择.
3、Log.i的输出为绿色,一般提示性的消息information,它不会输出Log.v和Log.d的信息,但会显示i、w和e的信息
4、Log.w的意思为橙色,可以看作为warning警告,一般需要我们注意优化Android代码,同时选择它后还会输出Log.e的信息。
5、Log.e为红色,可以想到error错误,这里仅显示红色的错误信息,这些错误就需要我们认真的分析,查看栈的信息了。*/
// 打开蓝牙
2.4、蓝牙回调函数
2.4.1、高版本蓝牙回调
/**
* ********************************************************************************
* 高版本回调函数
* ********************************************************************************
*/
private void setScanCallBack()
// 蓝牙LE扫描回调。使用这些回调来报告扫描结果。
mScanCalBack = new ScanCallback()
// 重写扫描结果 回调类型 扫描结果集
public void onScanResult(int callbackType, final ScanResult result)
// 获取蓝牙设备
final BluetoothDevice device = result.getDevice();
// 获取蓝牙设备信息
final BluetoothMessage bluetoothMessage = new BluetoothMessage(device);
// 获取蓝牙设备信息
if (null != device || null != result.getScanRecord())
try
// 蓝牙设备名字非空
if (device.getName() != null)
// 获取设备名称
byte[] name = ParseLeAdvDate.adv_report_parse(
ParseLeAdvDate.BLE_GAP_AD_TYPE_COMPLETE_LOCAL_NAME,
result.getScanRecord().getBytes());
if (name != null)
bluetoothMessage.setName(new String(name, "utf-8"));
catch (UnsupportedEncodingException e)
e.printStackTrace();
// 在UI线程上运行指定的操作。如果当前线程是UI线程,那么操作将立即执行。
// 如果当前线程不是UI线程,则动作被提交到UI线程的事件队列中。
runOnUiThread(new Runnable()
@Override
public void run()
// 讲扫描到的信息输出到ListView的适配器 蓝牙信号强度
mleDeviceListAdpater.addDevice(bluetoothMessage, result.getRssi());
// 数据更新 通知附加的观察者基础数据已经被更改,任何反映数据集的视图都应该刷新自己。
mleDeviceListAdpater.notifyDataSetChanged();
);
@Override
public void onScanFailed(final int errorCode)
super.onScanFailed(errorCode);
runOnUiThread(new Runnable()
@Override
public void run()
// LENGTH_SHORT:显示短时间内的视图或文本通知。这一次可能是用户可定义的。这是默认值。
Toast.makeText(MainActivity.this,"扫描出错:"+errorCode,Toast.LENGTH_SHORT).show();
);
;
24.2、低版本蓝牙回调函数
/***************************************************************
* 低版本蓝牙回调函数
* ************************************************************/
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback()
@Override
public void onLeScan(final BluetoothDevice device, final int rssi,
byte[] scanRecord)
// TODO Auto-generated method stub
runOnUiThread(new Runnable()
@Override
public void run()
// 讲扫描到设备的信息输出到listview的适配器
BluetoothMessage bluetoothMessage = new BluetoothMessage(device);
mleDeviceListAdpater.addDevice(bluetoothMessage, rssi);
mleDeviceListAdpater.notifyDataSetChanged();
);
;
2.5、点击扫描按钮执行操作
2.5.1、点击事件
/********************************************************************
* 点击事件 ble_active
********************************************************************/
@Override
public void onClick(View v)
if(!isOpenGPS(this))
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this,AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);
builder.setTitle("提示")
.setMessage("请前往打开手机的位置权限!")
.setCancelable(false)
.setPositiveButton("确定", new DialogInterface.OnClickListener()
@Override
public void onClick(DialogInterface dialog, int which)
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivityForResult(intent, 10);
).show();
return;
if (Scan_Flag)
mleDeviceListAdpater = new LeDeviceListAdapter(); //自定义适配器
lv.setAdapter(mleDeviceListAdpater); //设置listview为自定义适配器
scanLeDevice(true); //点击按键开始扫描设备
else
scanLeDevice(false);
BlueBooth_Scan_btn.setText("扫描设备");
2.5.2、扫描事件
private void scanLeDevice(boolean enable)
//将定义的蓝牙适配器进行版本适配操作
if (mbluetoothAdapter == null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
mbluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
else
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mbluetoothAdapter = bluetoothManager.getAdapter();
//将蓝牙扫描器进行适配操作
if (mBluetoothLeScanner == null)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
mBluetoothLeScanner = mbluetoothAdapter.getBluetoothLeScanner();
if (enable)
// 10s后停止扫描
mhandler.postDelayed(new Runnable()
@Override
public void run()
mScanming = false;
Scan_Flag = true;
BlueBooth_Scan_btn.setText("扫描设备");
Log.i("SCAN", "stop.....................");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
mBluetoothLeScanner.stopScan(mScanCalBack);
else
mbluetoothAdapter.stopLeScan(mLeScanCallback);
, SCAN_PERIOD);//10s后启动停止线程
/* 开始扫描蓝牙设备,带mLeScanCallback 回调函数 */
Log.i("SCAN", "begin.....................");
mScanming = true;
Scan_Flag = false;
BlueBooth_Scan_btn.setText("停止扫描");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
mBluetoothLeScanner.startScan(mScanCalBack);
else
mbluetoothAdapter.startLeScan(mLeScanCallback);
else
Log.i("Stop", "stoping................");
mScanming = false;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
mBluetoothLeScanner.stopScan(mScanCalBack);
else
mbluetoothAdapter.stopLeScan(mLeScanCallback);
Scan_Flag = true;
2.5.3、GPS是否打开
private boolean isOpenGPS(final Context context)
LocationManager locationManager
= (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
// GPS定位
boolean gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
// 网络服务定位
boolean network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (gps || network)
return true;
return false;
三、自定义蓝牙适配器内部类
/********************************************************************
* 自定义适配器作为Listview的适配器
********************************************************************/
private class LeDeviceListAdapter extends BaseAdapter
/**ArrayList:
* 实现所有可选的列表操作,并允许所有元素,包括null。除了实现List接口之外,
*这个类还提供了一些方法来操作用于内部存储列表的数组的大小。
*(这个类大致相当于Vector,只是它是非同步的。)
* 泛型:
* 标记符号ArrayL<T> T--java类
* E - Element (在集合中使用,因为集合中存放的是元素)
* T - Type(Java 类)
* K - Key(键)
* V - Value(值)
* N - Number(数值类型)
* ? - 表示不确定的 java 类型
* */
// 低功耗设备
private ArrayList<BluetoothMessage> mLeDevice;
// 将布局XML文件实例化为相应的View对象。
// 它从不直接使用。相反,使用android.app.Activity.getLayoutInflater()或Context。getSystemService
// 来检索一个标准的LayoutInflater实例,该实例已经连接到当前上下文,并为正在运行的设备正确配置
private LayoutInflater mInflator;
public LeDeviceListAdapter()
super();
rssis = new ArrayList<Integer>();
mLeDevice = new ArrayList<BluetoothMessage>();
// 快速访问此窗口从其上下文检索的LayoutInflater实例。
//Layout: 一个用于加载布局的系统服务,就是实例化与Layout XML文件对应的View对象,不能直接使用,
// 需要通过getLayoutInflater( )方法或getSystemService( )方法来获得与当前Context绑定的 LayoutInflater实例!
mInflator = getLayoutInflater();
// 添加设备
public void addDevice(BluetoothMessage device,int rssi)
// 变量已添加的设备剔除相同地址的蓝牙
// 遍历已添加的设备 剔除相同地址的蓝牙
for(BluetoothMessage mLeDevice:mLeDevice)
if(mLeDevice.getDevice().getAddress().equals(device.getDevice().getAddress()))
return;
if(device.getName()!=null)
// 将指定的元素追加到列表的末尾。
mLeDevice.add(device);
rssis.add(rssi);
// 获取列表中元素的位置信息
public BluetoothMessage getDevice(int position)
// 返回位于列表中指定位置的元素
return mLeDevice.get(position);
// 从列表中删除所有元素。这个调用返回后,列表将为空。
public void Clear()
mLeDevice.clear();
rssis.clear();
// 返回列表中元素的个数。
@Override
public int getCount()
return mLeDevice.size();
// 返回位于列表中指定位置的元素。
@Override
public Object getItem(int i)
return mLeDevice.get(i);
@Override
public long getItemId(int i)
return i;
@Override
public View getView(int i, View view, ViewGroup parent)
view = mInflator.inflate(R.layout.listtiem,null);
// 初始化三个textview 显示蓝牙信息
// 地址
TextView deviceAddress = view.findViewById(R.id.tv_deviceAddr);
// 设备名称
TextView deviceName = view.findViewById(R.id.tv_deviceName);
// 信号强度
TextView rssi = view.findViewById(R.id.tv_rssi);
BluetoothMessage bluetoothMessage = mLeDevice.get(i);
// 返回此蓝牙设备的硬件地址。
// 例如,“00:11:22:AA: BB: CC”。
deviceAddress.setText(bluetoothMessage.getDevice().getAddress());
// 如果布尔表达式的值为 true ,则返回 表达式1 的值,否则返回 表达式2 的值
// 布尔表达式 (bluetoothMessage.getName()!=null) ? 表达式1(bluetoothMessage.getName()):表达式2(bluetoothMessage.getDevice().getName())
deviceName.setText(bluetoothMessage.getName()!=null?bluetoothMessage.getName():bluetoothMessage.getDevice().getName());
rssi.setText(""+rssis.get(i));
return view;
四、实体类
public class BluetoothMessage
// 蓝牙设备
private BluetoothDevice mDevice;
// 设备名称
private String mName;
// 蓝牙远程设备
// 表示远程蓝牙设备。BluetoothDevice允许您创建与相应设备的连接,或者查询有关设备的信息,比如名称、地址、类和绑定状态。
public BluetoothMessage(BluetoothDevice device)
this.mDevice = device;
// 设置名称
public void setName(String name)
this.mName = name;
// 获取设备
public BluetoothDevice getDevice()
return mDevice;
// 获取名称
public String getName()
return mName;
以上是关于基于STM32的智能小车app自学梳理的主要内容,如果未能解决你的问题,请参考以下文章
毕业设计:基于单片机的超声波智能跟随小车 - 物联网 智能小车 嵌入式单片机 stm32 跟随小车