Reputation: 11
I am currently trying to develop an Android app to communicate Android with ESP32 using Bluetooth Serial Communication (SPP) but there is an issue I cannot figure out for almost a week; I am very new in this category, so I need help, the ESP32 is trying to send a sensor data via Bluetooth to the Android device, this part is working as expected, I could write the essential code so the data retrieving is succeeded with an almost good approach, but when I try to send a sample request from Android app to the ESP32 using a FAB setOnClickistener, the outputStream constantly getting null and the communication disrupt so the ESP doesn't get anything, I don't know what is happening here,
I tried to find out what is wrong but the last guess was the FAB is calling the sendData function in a wrong way so the function doesn't work properly since I used the mentioned function immediately after the connection and outputStream established and then it works perfect but when I try to use the sendData function from the FAB, it doesn't work, in the following I will share with you the written codes and logs, thanks for any help
this is the HomeFragment (the important lines are Bold):
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()
}
}
}**
}
this is the BluetoothReciever (the important lines are Bold):
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
}
}
there is also a BluetoothHandler for permissions and connections, I think it is unnecessary to share it
this is the logcat from the Android Studio (the important lines are Bold):
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 ----------------------------
and finally, this is the ESP32 output log (the important lines are Bold):
[ 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
sorry for a lot of texts here it is the ESP32 codes 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();
}
Upvotes: 0
Views: 49