当前位置: 首页 > news >正文

从零打造可落地的直流电机 PID 驱动系统(四):Android 蓝牙控制 APP 开发(新手友好版)

前言

大家好!在上一篇《从零打造可落地的直流电机 PID 驱动系统(二):增加蓝牙远程控制功能》中,我们基于STM32F103C8T6 主控 + DRV8833 驱动 + HC-05 经典蓝牙模块搭建了完整的无线电机控制系统。

与 iOS 需要 MFi 认证才能使用经典蓝牙不同,Android 原生完全支持 HC-05 等经典蓝牙设备,无需更换任何硬件,直接复用你上一篇的成品即可。本文将使用 Android 官方推荐的 Kotlin 语言和 Jetpack Compose UI 框架,从零教你开发一款功能完整的电机控制 APP,实现电机启停、无级调速、PID 参数在线修改和实时转速显示。全程代码带详细注释,零基础也能一步步跟着做。

一、开发前准备(新手必看)

1.1 硬件准备

  • 已完成的直流电机 PID 驱动系统(上一篇成品,HC-05 模块无需更换)
  • Android 手机 / 平板(系统版本 Android 6.0/API 23 及以上,覆盖 99% 以上现役设备)
  • USB 数据线(用于电脑连接手机调试)

1.2 软件准备

  • Android Studio Hedgehog 2023.1.1 及以上(官方免费下载,新手推荐最新稳定版)
  • 串口助手(如 SSCOM,用于提前测试 HC-05 模块通信)
  • 基础 Kotlin 语法知识(只需了解变量、函数、类的基本概念即可)

1.3 关键前提说明

验证来源:Android 官方蓝牙开发文档

  1. 本文使用经典蓝牙 (RFCOMM)开发,完美兼容原系统的 HC-05 模块,硬件零改动
  2. HC-05 默认串口波特率 9600,与上一篇 STM32 的 UART 配置完全一致
  3. 所有代码均基于 Android 官方标准 API 编写,无第三方依赖,稳定性有保障

2.1 核心概念

Android 经典蓝牙通信采用客户端 - 服务器架构:

  • 客户端:我们的 Android 手机,主动发起连接
  • 服务器:HC-05 蓝牙模块,工作在从模式,被动等待连接
  • RFCOMM 通道:蓝牙串口协议,实现透明数据传输,相当于无线串口
  • UUID:服务唯一标识符,HC-05 使用标准串口服务 UUID:00001101-0000-1000-8000-00805F9B34FB必须正确,否则连接失败

2.2 完整通信流程

三、新建 Android 项目与权限配置

3.1 创建新项目

  1. 打开 Android Studio,点击 "New Project"
  2. 选择 "Phone and Tablet" → "Empty Activity",点击 "Next"
  3. 填写项目名称(如MotorControl),语言选择 "Kotlin",最小 SDK 选择 "API 23: Android 6.0 (Marshmallow)"
  4. 点击 "Finish",等待项目初始化完成

3.2 配置蓝牙权限(最容易踩坑的地方)

打开app/src/main/AndroidManifest.xml文件,添加以下权限(分 API 版本适配,缺一不可):

<!-- 所有Android版本通用 --> <uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" /> <!-- Android 6.0-11 (API 23-30) 蓝牙扫描需要位置权限 --> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" /> <!-- Android 12+ (API 31+) 新蓝牙权限 --> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation" /> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /> <!-- 声明APP需要蓝牙硬件 --> <uses-feature android:name="android.hardware.bluetooth" android:required="true" />

验证来源:Android 官方蓝牙权限配置指南 https://developer.android.com/guide/topics/connectivity/bluetooth/permissions

四、蓝牙管理类实现(核心代码)

新建一个 Kotlin 文件BluetoothManager.kt,这是整个 APP 的核心,负责所有蓝牙操作。使用单例模式,确保整个 APP 只有一个蓝牙连接实例。

import android.bluetooth.BluetoothAdapter import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothSocket import android.content.Context import android.util.Log import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch import java.io.IOException import java.io.InputStream import java.io.OutputStream import java.util.* // 蓝牙管理单例类 object BluetoothManager { private const val TAG = "BluetoothManager" // HC-05标准串口服务UUID(固定不变) private val MY_UUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter() private var bluetoothSocket: BluetoothSocket? = null private var inputStream: InputStream? = null private var outputStream: OutputStream? = null private var readJob: Job? = null // 状态回调接口,用于通知UI层蓝牙状态和数据 interface BluetoothCallback { fun onConnected(device: BluetoothDevice) fun onDisconnected() fun onDataReceived(data: String) fun onError(message: String) } var callback: BluetoothCallback? = null var isConnected: Boolean = false private set // 检查蓝牙是否开启 fun isBluetoothEnabled(): Boolean { return bluetoothAdapter?.isEnabled == true } // 获取已配对设备列表 fun getPairedDevices(): List<BluetoothDevice> { return bluetoothAdapter?.bondedDevices?.toList() ?: emptyList() } // 连接指定蓝牙设备 fun connect(device: BluetoothDevice) { if (isConnected) disconnect() CoroutineScope(Dispatchers.IO).launch { try { // 创建RFCOMM Socket bluetoothSocket = device.createRfcommSocketToServiceRecord(MY_UUID) // 取消扫描(扫描会降低连接速度) bluetoothAdapter?.cancelDiscovery() // 建立连接(阻塞操作,必须在后台线程执行) bluetoothSocket?.connect() // 获取输入输出流 inputStream = bluetoothSocket?.inputStream outputStream = bluetoothSocket?.outputStream isConnected = true callback?.onConnected(device) // 启动数据接收线程 startReading() } catch (e: IOException) { Log.e(TAG, "连接失败: ${e.message}") callback?.onError("连接失败: ${
http://www.jsqmd.com/news/859927/

相关文章:

  • 边仓线与线边仓详解:边仓线和线边仓如何协同优化物料流转效率?
  • 2026年盲审前论文降AI攻略:盲审阶段AI率超标4.8元一次过知网完整处理指南
  • 最好用的AI论文写作软件推荐(从开题选题到定稿排版全流程)适合全体毕业生
  • 血泪教训:一行 apt install docker* 让我搞了一晚上 Docker
  • LangChain与LangGraph详解:用法、差异与实战指南
  • Agent面试八股文
  • RPC 核心概念 02:IDL 与 Protobuf 详解
  • 升级 macOS Tahoe 26 后启动台消失?终端命令已失效,这个方案实测可用
  • 【linux使用技巧】复制粘贴快捷键
  • 初创公司如何利用Taotoken的Token Plan降低AI原型开发成本
  • 通过Taotoken CLI工具一键配置多款AI开发工具环境
  • 为什么很多政府部门的系统这么难用
  • Pearcleaner:macOS应用彻底清理的终极指南,3步告别垃圾文件
  • ElevenLabs新疆语语音生成合规红线清单(含《互联网信息服务深度合成管理规定》第12条实操解读)
  • Sunshine游戏串流终极指南:5大优化策略实现300%性能提升
  • 使命召唤21:黑色行动6 官方正版2026最新版pc免费下载(看到请立即转存 资源随时失效)手机版通用
  • 邻近连接技术伯远邻近连接技术深耕邻近连接技术
  • 3分钟掌握Windows右键菜单管理:ContextMenuManager终极优化指南
  • 企业内训系统集成AI问答时采用Taotoken的成本控制实践
  • 无需模拟器!在Windows上直接运行安卓应用的终极解决方案
  • Qt创建Pri文件(笔记)
  • Diablo Edit2:5分钟掌握暗黑破坏神2终极角色编辑器
  • 指纹浏览器技术实战:多账号环境配置与防关联策略
  • 智能体之间互相结算 怎么定价呢 评论区告诉我
  • 伺服电机的安装使用注意事项
  • 通过用量看板清晰追踪团队大模型API消费明细
  • IDM激活脚本:破解30天限制背后的注册表权限技术内幕
  • 一体化平台对决:多款主流CRM六大核心能力全面解析
  • okbiye 本科毕业论文写作全流程拆解:从选题到终稿的高校规范级落地指南
  • OpenBoardView:免费开源PCB查看器的5大核心功能与完整使用指南