sending of message and working connect/disconnect

This commit is contained in:
Gitea committed 2026-10-16 22:18:35 +02:00
1 parent 8e1c8d14f5
commit 21883af93b
7 files changed
+206 -69

No files matched your search

@@ -18,6 +18,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.core.app.ActivityCompat import androidx.core.app.ActivityCompat
import java.util.UUID
class BleManager(private val context: Context) { class BleManager(private val context: Context) {
val deviceUUIDs = DeviceUUIDs val deviceUUIDs = DeviceUUIDs
@@ -72,11 +73,11 @@ class BleManager(private val context: Context) {
Log.d("ESP32_BLE", "4. Verbunden! Starte discoverServices()...") Log.d("ESP32_BLE", "4. Verbunden! Starte discoverServices()...")
gatt.discoverServices() gatt.discoverServices()
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) { } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.d("ESP32_BLE", "5. Getrennt worden.")
isConnected = false isConnected = false
gatt.close() gatt.close()
bluetoothGatt = null bluetoothGatt = null
Log.d("ESP32_BLE", "5. Getrennt worden.")
} }
} else { } else {
Log.d("ESP32_BLE", "Fehler in onConnectionStateChange! Status-Code: $status") Log.d("ESP32_BLE", "Fehler in onConnectionStateChange! Status-Code: $status")
@@ -88,7 +89,7 @@ class BleManager(private val context: Context) {
Log.d("ESP32_BLE", "6. onServicesDiscovered aufgerufen mit Status: $status") Log.d("ESP32_BLE", "6. onServicesDiscovered aufgerufen mit Status: $status")
if (status == BluetoothGatt.GATT_SUCCESS) { if (status == BluetoothGatt.GATT_SUCCESS) {
isConnected = true isConnected = true
writeToEsp32("Hallo ESP32") writeToEsp32("Hallo ESP32", deviceUUIDs.TIME_CHARACTERISTIC_UUID, deviceUUIDs.TIME_CHARACTERISTIC_UUID)
Log.d("ESP32_BLE", "7. Services erfolgreich gefunden! Verbindung komplett erfolgreich.") Log.d("ESP32_BLE", "7. Services erfolgreich gefunden! Verbindung komplett erfolgreich.")
} }
} }
@@ -106,8 +107,20 @@ class BleManager(private val context: Context) {
Log.d("ESP32_BLE", "Exception gefangen: ${e.message}") Log.d("ESP32_BLE", "Exception gefangen: ${e.message}")
} }
} }
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
fun disconnectEsp32() {
// Nur den Disconnect-Befehl senden.
// Das Schließen (close) überlassen wir dem Callback, wenn es wirklich so weit ist!
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED) {
bluetoothGatt?.disconnect()
Log.d("ESP32_BLE","disconnectEsp32")
}
else {
Log.d("BLE_DEBUG", "Permission BLUETOOTH_CONNECT fehlt!")
}
}
fun writeToEsp32(dataString: String) { fun writeToEsp32(dataString: String, SERVICE_UUID: UUID?, TIME_CHARACTERISTIC_UUID: UUID?) {
// Prüfen, ob die Verbindung steht und bluetoothGatt nicht null ist // Prüfen, ob die Verbindung steht und bluetoothGatt nicht null ist
val gatt = bluetoothGatt val gatt = bluetoothGatt
if (gatt == null) { if (gatt == null) {
@@ -116,14 +129,14 @@ class BleManager(private val context: Context) {
} }
// 1. Service holen // 1. Service holen
val service = gatt.getService(deviceUUIDs.SERVICE_UUID) val service = gatt.getService(SERVICE_UUID)
if (service == null) { if (service == null) {
Log.d("ESP32_BLE", "Fehler: Service UUID nicht gefunden!") Log.d("ESP32_BLE", "Fehler: Service UUID nicht gefunden!")
return return
} }
// 2. Characteristic holen // 2. Characteristic holen
val characteristic = service.getCharacteristic(deviceUUIDs.TIME_CHARACTERISTIC_UUID) val characteristic = service.getCharacteristic(TIME_CHARACTERISTIC_UUID)
if (characteristic == null) { if (characteristic == null) {
Log.d("ESP32_BLE", "Fehler: Characteristic UUID nicht gefunden!") Log.d("ESP32_BLE", "Fehler: Characteristic UUID nicht gefunden!")
return return
@@ -3,6 +3,6 @@ package com.example.smart_watch_android.BLE
import java.util.UUID import java.util.UUID
object DeviceUUIDs { object DeviceUUIDs {
val SERVICE_UUID = UUID.fromString("791cadd7-de87-49f9-8986-94b7ae11940b") val SERVICE_UUID: UUID? = UUID.fromString("791cadd7-de87-49f9-8986-94b7ae11940b")
val TIME_CHARACTERISTIC_UUID = UUID.fromString("3e4eac5b-eaa2-4e0c-a705-dcac4f606e02") val TIME_CHARACTERISTIC_UUID: UUID? = UUID.fromString("3e4eac5b-eaa2-4e0c-a705-dcac4f606e02")
} }
@@ -1,46 +0,0 @@
package com.example.smart_watch_android
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun ConnectCircleScreen(
isConnected: Boolean,
onConnectClick: () -> Unit
) {
// Ein Container, der den gesamten Bildschirm ausfüllt
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center // Zentriert den Inhalt exakt in der Mitte
) {
// Der Kreis-Button
Box(
modifier = Modifier
.size(180.dp) // Größe des Kreises
.clip(CircleShape) // Macht das Element zu einem perfekten Kreis
.background(if (isConnected) Color.Green else Color.Blue) // Grün = verbunden, Blau = Start/Verbinden
.clickable { onConnectClick() }, // Was passiert beim Drücken
contentAlignment = Alignment.Center
) {
Text(
text = if (isConnected) "Verbunden" else "Connect",
color = Color.White,
fontSize = 20.sp,
fontWeight = FontWeight.Bold
)
}
}
}
@@ -1,13 +1,5 @@
package com.example.smart_watch_android package com.example.smart_watch_android
import android.Manifest
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothManager
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
@@ -15,8 +7,10 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.annotation.RequiresPermission import androidx.compose.foundation.layout.fillMaxSize
import androidx.core.app.ActivityCompat import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import com.example.smart_watch_android.BLE.BleManager import com.example.smart_watch_android.BLE.BleManager
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@@ -29,13 +23,16 @@ class MainActivity : ComponentActivity() {
bleManager.checkAndRequestBluetoothPermissions() bleManager.checkAndRequestBluetoothPermissions()
enableEdgeToEdge() enableEdgeToEdge()
setContent { setContent {
ConnectCircleScreen( MaterialTheme {
isConnected = bleManager.isConnected, Surface(
onConnectClick = { modifier = Modifier.fillMaxSize(),
Log.d("ESP32_BLE", "Button wurde gedrückt!") color = MaterialTheme.colorScheme.background
bleManager.connectToEsp32("10:BD:A3:8F:01:0E") ) {
// Hier wird deine AppNavigation aufgerufen,
// die die Screens steuert!
AppNavigation(bleManager)
}
} }
)
} }
} }
} }
@@ -0,0 +1,30 @@
package com.example.smart_watch_android.Screens
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
@Composable
fun ConnectScreen(isConnected: Boolean, onConnectClicked: () -> Unit) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Button(
onClick = onConnectClicked,
colors = if (isConnected) {
ButtonDefaults.buttonColors(containerColor = Color(0xFF4CAF50)) // Grün
} else {
ButtonDefaults.buttonColors()
}
) {
Text(text = if (isConnected) "Verbunden" else "Connect")
}
}
}
@@ -0,0 +1,76 @@
package com.example.smart_watch_android.Screens
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.example.smart_watch_android.BLE.BleManager
import com.example.smart_watch_android.BLE.DeviceUUIDs
@Composable
fun ControlPanelScreen(
bleManager: BleManager,
onDisconnectClicked: () -> Unit
) {
var textInput by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
// Eingabefeld für Text / Uhrzeit / Befehle
OutlinedTextField(
value = textInput,
onValueChange = { textInput = it },
label = { Text("Nachricht an ESP32") },
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(16.dp))
// Send Button
Button(
onClick = {
if (textInput.isNotEmpty()) {
bleManager.writeToEsp32(
textInput, DeviceUUIDs.SERVICE_UUID, DeviceUUIDs.TIME_CHARACTERISTIC_UUID
)
textInput = "" // Feld danach leeren
}
},
modifier = Modifier.fillMaxWidth()
) {
Text("Send")
}
Spacer(modifier = Modifier.height(32.dp))
// Disconnect Button (z.B. in Rot, um ihn abzusetzen)
Button(
onClick = onDisconnectClicked,
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFE53935)),
modifier = Modifier.fillMaxWidth()
) {
Text("Disconnect")
}
}
}
@@ -0,0 +1,67 @@
package com.example.smart_watch_android
import android.Manifest
import androidx.annotation.RequiresPermission
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.example.smart_watch_android.BLE.BleManager
import com.example.smart_watch_android.BLE.DeviceUUIDs
import com.example.smart_watch_android.Screens.ConnectScreen
import com.example.smart_watch_android.Screens.ControlPanelScreen
enum class Screen {
CONNECT,
CONTROL_PANEL
}
@RequiresPermission(Manifest.permission.BLUETOOTH_CONNECT)
@Composable
fun AppNavigation(BLEManager: BleManager) {
var currentScreen by remember { mutableStateOf(Screen.CONNECT) }
val bleManager = BLEManager
if (bleManager.isConnected && currentScreen == Screen.CONNECT) {
currentScreen = Screen.CONTROL_PANEL
} else if (!bleManager.isConnected && currentScreen == Screen.CONTROL_PANEL) {
currentScreen = Screen.CONNECT
}
// Je nach Zustand den entsprechenden Screen anzeigen
when (currentScreen) {
Screen.CONNECT -> {
ConnectScreen(
isConnected = bleManager.isConnected,
onConnectClicked = {
bleManager.connectToEsp32("10:BD:A3:8F:01:0E")
}
)
}
Screen.CONTROL_PANEL -> {
ControlPanelScreen(
bleManager,
onDisconnectClicked = {
bleManager.disconnectEsp32()
// Screen wechselt automatisch über den Check oben
}
)
}
}
}