我目前正在尝试开发一个 Android 应用程序,以使用蓝牙串行通信(SPP)与 ESP32 进行 Android 通信,但有一个问题我几乎一周都无法解决;我在这个类别中非常陌生,所以我需要帮助,ESP32 正在尝试通过蓝牙将传感器数据发送到 Android 设备,这部分按预期工作,我可以编写基本代码,以便通过以下命令成功检索数据几乎是个好方法,但是当我尝试使用 FAB setOnClickistener 将示例请求从 Android 应用程序发送到 ESP32 时,outputStream 不断变为 null 并且通信中断,因此 ESP 无法获取任何内容,我不知道发生了什么在这里,
我试图找出问题所在,但最后的猜测是FAB以错误的方式调用sendData函数,因此该函数无法正常工作,因为我在连接和outputStream建立后立即使用了上述函数,然后它就工作了完美,但是当我尝试使用FAB的sendData函数时,它不起作用,下面我将与您分享编写的代码和日志,感谢您的帮助
这是 HomeFragment(重要的几行是粗体):
package com.example.voltage.ui.home
class HomeFragment : Fragment(), BluetoothReceiver.DataCallback {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
private lateinit var bluetoothReceiver: BluetoothReceiver
private val homeViewModel: HomeViewModel by viewModels()
private lateinit var bluetoothHandler: BluetoothHandler
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// Set up the TextView
val textView: TextView = binding.textHome
homeViewModel.text.observe(viewLifecycleOwner) {
textView.text = it
}
// Set up the Gauge ComposeView
binding.composeViewGauge.setContent {
HomeGauge(homeViewModel)
}
// Set up the Chart ComposeView
binding.composeViewChart.setContent {
VoltageChart()
}
**// Initialize BluetoothHandler after the view is created
initializeBluetoothHandler()
bluetoothReceiver = BluetoothReceiver(requireContext(), "E0:E2:E6:22:46:F2")
setupFAB()**
}
private fun initializeBluetoothHandler() {
val mainActivity = activity as? MainActivity
mainActivity?.let {
bluetoothHandler = it.bluetoothHandler
bluetoothHandler.bluetoothReceiver.dataCallback = this
}
}
override fun onDataReceived(date: String, time: String, temperature: Float, voltage: Float) {
viewLifecycleOwner.lifecycleScope.launch {
homeViewModel.updateVoltage(voltage)
homeViewModel.updateText(date, time, temperature)
binding.batteryView.setBatteryLevel(voltage) // Assuming you have a batteryView in your layout
}
}
override fun onDestroyView() {
super.onDestroyView()
bluetoothReceiver.closeSocket() // Ensure socket is closed when the activity is destroyed
_binding = null
}
**private fun setupFAB(){
binding.fab.setOnClickListener { view ->
val text = "trying to Send... !"
val make = Snackbar.make(view, text, Snackbar.LENGTH_SHORT)
make.setAction("Action", null).setAnchorView(R.id.fab).show()
// Check if BluetoothReceiver is connected before sending data
if (BluetoothReceiver.isConnected) {
if (bluetoothReceiver.outputStream != null) {
bluetoothReceiver.sendData(bluetoothReceiver.message)
Log.e("HomeFragment", "the sendData is called")
Toast.makeText(requireContext(), "the sendData is called", Toast.LENGTH_SHORT).show()
} else {
Log.e("HomeFragment", "the outputStream is null")
Toast.makeText(requireContext(), "the outputStream is null", Toast.LENGTH_SHORT).show()
}
} else {
Log.e("HomeFragment", "Not connected to Bluetooth device")
Toast.makeText(requireContext(), "Not connected to Bluetooth device", Toast.LENGTH_SHORT).show()
}
}
}**
}
这是蓝牙接收器(重要的线条是粗体):
package com.example.voltage.bluetooth
class BluetoothReceiver(private val context: Context, private val deviceAddress: String) {
private var socket: BluetoothSocket? = null
private var inputStream: InputStream? = null
private val coroutineScope = CoroutineScope(Dispatchers.Default)
var outputStream: OutputStream? = null
val message = "...Hello ESP...\n"
interface DataCallback {
fun onDataReceived(date: String, time: String, temperature: Float, voltage: Float)
}
interface ConnectionCallback {
fun onConnected()
fun onDisconnected()
fun onConnectionFailed()
}
var dataCallback: DataCallback? = null
private var connectionCallback: ConnectionCallback? = null
fun setConnectionCallback(callback: ConnectionCallback) {
this.connectionCallback = callback
}
private fun notifyConnected() {
connectionCallback?.onConnected()
isConnected = true
}
private fun notifyDisconnected() {
connectionCallback?.onDisconnected()
isConnected = false
}
private fun notifyConnectionFailed() {
connectionCallback?.onConnectionFailed()
isConnected = false
}
@SuppressLint("MissingPermission")
fun connect() {
Log.d("BluetoothReceiver", "Attempting to connect...")
val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
val adapter: BluetoothAdapter? = bluetoothManager.adapter
if (adapter == null) {
Log.e("BluetoothReceiver", "Bluetooth is not supported on this device")
notifyConnectionFailed()
return
}
val device: BluetoothDevice = adapter.getRemoteDevice(deviceAddress)
try {
socket = device.createRfcommSocketToServiceRecord(UUID.fromString(MY_UUID))
socket?.connect()
inputStream = socket?.inputStream
outputStream = socket?.outputStream
outputStream?.write("Test".toByteArray())
outputStream?.flush()
Log.d("BluetoothReceiver", "Test data sent immediately after connect")
Log.d("BluetoothReceiver", "Connected: Socket: $socket, InputStream: $inputStream, OutputStream: $outputStream")
if (outputStream != null) {
notifyConnected()
listenForData()
**sendData(message)**
} else {
Log.e("BluetoothReceiver", "OutputStream is not initialized after connection")
notifyConnectionFailed()
closeSocket()
}
} catch (e: IOException) {
Log.e("BluetoothReceiver", "Connection failed", e)
notifyConnectionFailed()
closeSocket()
}
}
private fun listenForData() {
Thread {
val buffer = ByteArray(1024)
var bytes: Int
while (socket?.isConnected == true) {
try {
bytes = inputStream?.read(buffer) ?: break
val receivedData = String(buffer, 0, bytes)
parseData(receivedData)
} catch (e: IOException) {
Log.e("BluetoothReceiver", "Error reading data", e)
notifyDisconnected()
closeSocket()
break
}
}
}.start()
}
private fun parseData(data: String) {
val regex = """(\d{4}/\d{2}/\d{2}) (\d{2}:\d{2}:\d{2}) Temperature: ([\d.]+) C Voltage: ([\d.]+) V""".toRegex()
val matchResult = regex.find(data)
matchResult?.let {
val (date, time, temperature, voltage) = it.destructured
coroutineScope.launch {
dataCallback?.onDataReceived(date, time, temperature.toFloat(), voltage.toFloat())
}
Log.d("ParsedData", "Date: $date, Time: $time, Temp: $temperature C, Voltage: $voltage V")
}
}
fun closeSocket() {
try {
socket?.close()
} catch (e: IOException) {
Log.e("BluetoothReceiver", "Error closing socket", e)
} finally {
socket = null
isConnected = false
}
}
**// Function to send data
fun sendData(message: String) {
Log.d("BluetoothReceiver", "Attempting to send data...")
Thread {
try {
if (outputStream != null) {
outputStream?.write(this.message.toByteArray())
outputStream?.flush()
Log.d("BluetoothReceiver", "Data sent successfully as ${this.message}")
} else {
Log.e("BluetoothReceiver", "Output stream is null, attempting to reconnect...")
}
} catch (e: IOException) {
Log.e("BluetoothReceiver", "Failed to send data", e)
}
}.start()
}
**
companion object {
private const val MY_UUID = "00001101-0000-1000-8000-00805F9B34FB" // Standard UUID for SPP
var isConnected: Boolean = false
}
}
还有一个用于权限和连接的BluetoothHandler,我认为没有必要共享它
这是 Android Studio 的 logcat(重要行是粗体):
DexFile /data/data/com.example.voltage/code_cache/.studio/instruments-bd0f66b3.jar is in boot class path but is not in a known location
Redefining intrinsic method java.lang.Thread java.lang.Thread.currentThread(). This may cause the unexpected use of the original definition of java.lang.Thread java.lang.Thread.currentThread()in methods that have already been compiled.
Redefining intrinsic method boolean java.lang.Thread.interrupted(). This may cause the unexpected use of the original definition of boolean java.lang.Thread.interrupted()in methods that have already been compiled.
Compat change id reported: 171979766; UID 10210; state: ENABLED
Application com.example.voltage is waiting for the debugger on port 8100...
Sending WAIT chunk
Debugger has connected
waiting for debugger to settle...
debugger has settled (1372)
Unable to open '/data/data/com.example.voltage/code_cache/.overlay/base.apk/classes4.dm': No such file or directory
Unable to open '/data/data/com.example.voltage/code_cache/.overlay/base.apk/classes5.dm': No such file or directory
Unable to open '/data/app/~~YZZsLNt1JivN9IVaEThM3g==/com.example.voltage-29J_4xe-pvQupPi9wt05ZA==/base.dm': No such file or directory
Unable to open '/data/app/~~YZZsLNt1JivN9IVaEThM3g==/com.example.voltage-29J_4xe-pvQupPi9wt05ZA==/base.dm': No such file or directory
ANGLE Developer option for 'com.example.voltage' set to: 'default'
ANGLE GameManagerService for com.example.voltage: false
Neither updatable production driver nor prerelease driver is supported.
No Network Security Config specified, using platform default
No Network Security Config specified, using platform default
Checking for metadata for AppLocalesMetadataHolderService : Service not found
Attempting to connect...
Test data sent immediately after connect
Connected: Socket: XX:XX:XX:22:46:F2, InputStream: android.bluetooth.BluetoothInputStream@ca9a972, OutputStream: android.bluetooth.BluetoothOutputStream@60a11c3
Compat change id reported: 147798919; UID 10210; state: ENABLED
**Attempting to send data...
Data sent successfully as ...Hello ESP...**
Date: 1403/05/22, Time: 15:20:57, Temp: 26.00 C, Voltage: 13.62 V
Date: 1403/05/22, Time: 15:20:57, Temp: 26.00 C, Voltage: 13.61 V
Compat change id reported: 210923482; UID 10210; state: ENABLED
Date: 1403/05/22, Time: 15:20:57, Temp: 26.00 C, Voltage: 13.61 V
Date: 1403/05/22, Time: 15:20:57, Temp: 26.00 C, Voltage: 13.60 V
Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (unsupported, reflection, allowed)
Compat change id reported: 237531167; UID 10210; state: DISABLED
Expecting binder but got null!
Skipped 137 frames! The application may be doing too much work on its main thread.
Date: 1403/05/22, Time: 15:20:57, Temp: 26.00 C, Voltage: 13.61 V
Date: 1403/05/22, Time: 15:20:57, Temp: 26.00 C, Voltage: 13.61 V
Date: 1403/05/22, Time: 15:20:57, Temp: 26.00 C, Voltage: 13.62 V
Date: 1403/05/22, Time: 15:20:58, Temp: 26.00 C, Voltage: 13.60 V
QUALCOMM build : 82d27a90b3, I4ee8f6bced
Build Date : 07/12/23
OpenGL ES Shader Compiler Version: EV031.35.01.12
Local Branch :
Remote Branch : refs/tags/AU_LINUX_ANDROID_LA.UM.9.14.11.00.00.571.148
Remote Branch : NONE
Reconstruct Branch : NOTHING
Build Config : S P 10.0.7 AArch64
Driver Path : /vendor/lib64/egl/libGLESv2_adreno.so
Error opening trace file: No such file or directory (2)
PFP: 0x016ee190, ME: 0x00000000
Date: 1403/05/22, Time: 15:20:58, Temp: 26.00 C, Voltage: 13.62 V
Unable to match the desired swap behavior.
Adding additional valid usage bits: 0x8202400
Date: 1403/05/22, Time: 15:20:58, Temp: 26.00 C, Voltage: 13.61 V
Davey! duration=3005ms; Flags=1, FrameTimelineVsyncId=3832605, IntendedVsync=71334324533905, Vsync=71336609392642, InputEventId=0, HandleInputStart=71336617324868, AnimationStart=71336617331483, PerformTraversalsStart=71336617598879, DrawStart=71337246589972, FrameDeadline=71334345533905, FrameInterval=71336617123149, FrameStartTime=16677801, SyncQueued=71337295414764, SyncStart=71337295520597, IssueDrawCommandsStart=71337304460389, SwapBuffers=71337322172420, FrameCompleted=71337330516274, DequeueBufferDuration=39219, QueueBufferDuration=411927, GpuCompleted=71337330516274, SwapBuffersCompleted=71337324590285, DisplayPresentTime=104837, CommandSubmissionCompleted=71337322172420,
Skipped 43 frames! The application may be doing too much work on its main thread.
Davey! duration=753ms; Flags=0, FrameTimelineVsyncId=3832841, IntendedVsync=71336624950047, Vsync=71337341731108, InputEventId=0, HandleInputStart=71337351709660, AnimationStart=71337351715649, PerformTraversalsStart=71337354339451, DrawStart=71337361297368, FrameDeadline=71337364215886, FrameInterval=71337351537524, FrameStartTime=16669327, SyncQueued=71337364705337, SyncStart=71337364775180, IssueDrawCommandsStart=71337364931847, SwapBuffers=71337368734399, FrameCompleted=71337378086743, DequeueBufferDuration=20208, QueueBufferDuration=294636, GpuCompleted=71337378086743, SwapBuffersCompleted=71337369878045, DisplayPresentTime=105573, CommandSubmissionCompleted=71337368734399,
Date: 1403/05/22, Time: 15:21:00, Temp: 26.00 C, Voltage: 13.60 V
Date: 1403/05/22, Time: 15:21:00, Temp: 26.00 C, Voltage: 13.61 V
Date: 1403/05/22, Time: 15:21:01, Temp: 26.00 C, Voltage: 13.60 V
Date: 1403/05/22, Time: 15:21:01, Temp: 26.00 C, Voltage: 13.61 V
A resource failed to call close.
type=1400 audit(0.0:3661): avc: denied { getopt } for path="/dev/socket/usap_pool_primary" scontext=u:r:untrusted_app:s0:c210,c256,c512,c768 tcontext=u:r:zygote:s0 tclass=unix_stream_socket permissive=0 app=com.example.voltage
Date: 1403/05/22, Time: 15:21:01, Temp: 26.00 C, Voltage: 13.61 V
Date: 1403/05/22, Time: 15:21:02, Temp: 26.00 C, Voltage: 13.72 V
**the outputStream is null**
Date: 1403/05/22, Time: 15:21:02, Temp: 26.00 C, Voltage: 13.71 V
Installing profile for com.example.voltage
Date: 1403/05/22, Time: 15:21:02, Temp: 26.00 C, Voltage: 13.72 V
**the outputStream is null**
Date: 1403/05/22, Time: 15:21:02, Temp: 26.00 C, Voltage: 13.71 V
Date: 1403/05/22, Time: 15:21:06, Temp: 26.00 C, Voltage: 13.61 V
---------------------------- PROCESS ENDED (21822) for package com.example.voltage ----------------------------
最后,这是 ESP32 输出日志(重要的行是粗体):
[ 461][W][Wire.cpp:296] begin(): Bus already started in Master Mode.
[ 970][W][sd_diskio.cpp:109] sdWait(): Wait Failed
[ 974][W][sd_diskio.cpp:485] ff_sd_initialize(): sdWait fail ignored, card initialize continues
[ 983][W][sd_diskio.cpp:187] sdCommand(): token error [59] 0x5
[ 989][W][sd_diskio.cpp:187] sdCommand(): token error [8] 0x5
[ 1853][W][STA.cpp:537] disconnect(): STA already disconnected.
Wi-Fi is now disabled.
NDA 24 NDA 24 NDA 23 NDA 24 NDA 24 NDA 24 NDA 24 NDA 23 NDA 24 NDA 24 NDA 24 NDA 24 NDA 23 NDA 24 NDA 24
NDA 24 NDA 24 NDA 28 NDA 24 NDA 24 NDA 24 NDA 24 NDA 24 NDA 24 **Received String: Test...Hello ESP...**
25 NDA 26 NDA 24 NDA 24 NDA 24 NDA 24
NDA 25 NDA 24 NDA 24 NDA 24 NDA 24 NDA 25 NDA 24 NDA 24 NDA 24 NDA 24 NDA 25 NDA 24 NDA 24
抱歉文字太多 这是 ESP32 代码 FIY:
// rest of the code like functions and setup
void loop() {
currentMillis = millis();
char logMessage[128];
float temperature = rtc.getTemperature(); //get the temperature from DS3231
Voltage = Average_Read(Avg_Number); //function for read Avg_number times and return the average value
Voltage = readADC_Cal(Voltage); //function for calibrate the value obtaind from previous function
Voltage = vDivCalculator(Voltage); //calculate the Vs from the voltage divider Resistors of known values R_1, R_2
Voltage = (float)Voltage / 1000; //convert mV to V
// Get the current date and time from the RTC
DateTime now = rtc.now();
current_year = now.year();
current_month = now.month();
current_day = now.day();
//Convert the date to the Shamsi calendar
dateC.ToShamsi(current_year, current_month, current_day);
snprintf(logMessage, sizeof(logMessage), "%04d/%02d/%02d %02d:%02d:%02d Temperature: %.2f C Voltage: %.2f V\n",
dateC.global_year, dateC.global_month, dateC.global_day,
now.hour(), now.minute(), now.second(), temperature, Voltage);
if (currentMillis - previousMillis_1 >= interval_1 && counter > 80) {
// Save the last time the function was run
previousMillis_1 = currentMillis;
// Call your function
appendFile(SD, logFileName, logMessage);
// checkSDCard();
Serial.println("");
Serial.print("the counter is: -> ");
Serial.print(counter);
Serial.println("");
counter = 0;
}
if (SerialBT.available()) {
uint8_t buffer[1024];
int bytesRead = SerialBT.readBytesUntil('\n', buffer, sizeof(buffer));
if (bytesRead > 0) {
buffer[bytesRead] = '\0'; // Null terminate the string
String incomingString = String((char*)buffer);
Serial.print("Received String: ");
Serial.println(incomingString);
} else {
Serial.print("No data read ");
}
} else {
Serial.print("NDA ");
}
if(currentMillis - previousMillis_2 >= interval_2 && counter > 20){
previousMillis_2 = currentMillis;
//Send the log message over Bluetooth
SerialBT.write((uint8_t *)logMessage, strlen(logMessage));
SerialBT.write((uint8_t *)delimiterMessage, strlen(delimiterMessage));
SerialBT.flush();
// Serial.println(" ");
// Serial.print("Voltage: ->");Serial.print(Voltage);
// Serial.println(" ");
}
counter +=1;
endMillis = millis();
loopTime = endMillis - currentMillis;
if((counter % 15) == 0){
Serial.print(loopTime);
Serial.println(" ");
}else{
Serial.print(loopTime);
Serial.print(" ");
}
Serial.flush();
}
谢谢你的提问。
我们建议直接联系您的原始设备制造商 (OEM)(例如:三星、LG、Apple),因为他们将能够提供全面且准确的解决方案。