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.setValue
import androidx.core.app.ActivityCompat
import java.util.UUID
class BleManager(private val context: Context) {
val deviceUUIDs = DeviceUUIDs
@@ -72,11 +73,11 @@ class BleManager(private val context: Context) {
Log.d("ESP32_BLE", "4. Verbunden! Starte discoverServices()...")
gatt.discoverServices()
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.d("ESP32_BLE", "5. Getrennt worden.")
isConnected = false
gatt.close()
bluetoothGatt = null
Log.d("ESP32_BLE", "5. Getrennt worden.")
}
} else {
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")
if (status == BluetoothGatt.GATT_SUCCESS) {
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.")
}
}
@@ -106,8 +107,20 @@ class BleManager(private val context: Context) {
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
val gatt = bluetoothGatt
if (gatt == null) {
@@ -116,14 +129,14 @@ class BleManager(private val context: Context) {
}
// 1. Service holen
val service = gatt.getService(deviceUUIDs.SERVICE_UUID)
val service = gatt.getService(SERVICE_UUID)
if (service == null) {
Log.d("ESP32_BLE", "Fehler: Service UUID nicht gefunden!")
return
}
// 2. Characteristic holen
val characteristic = service.getCharacteristic(deviceUUIDs.TIME_CHARACTERISTIC_UUID)
val characteristic = service.getCharacteristic(TIME_CHARACTERISTIC_UUID)
if (characteristic == null) {
Log.d("ESP32_BLE", "Fehler: Characteristic UUID nicht gefunden!")
return
@@ -3,6 +3,6 @@ package com.example.smart_watch_android.BLE
import java.util.UUID
object DeviceUUIDs {
val SERVICE_UUID = UUID.fromString("791cadd7-de87-49f9-8986-94b7ae11940b")
val TIME_CHARACTERISTIC_UUID = UUID.fromString("3e4eac5b-eaa2-4e0c-a705-dcac4f606e02")
val SERVICE_UUID: UUID? = UUID.fromString("791cadd7-de87-49f9-8986-94b7ae11940b")
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
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.Bundle
import android.util.Log
@@ -15,8 +7,10 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi
import androidx.annotation.RequiresPermission
import androidx.core.app.ActivityCompat
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import com.example.smart_watch_android.BLE.BleManager
class MainActivity : ComponentActivity() {
@@ -29,13 +23,16 @@ class MainActivity : ComponentActivity() {
bleManager.checkAndRequestBluetoothPermissions()
enableEdgeToEdge()
setContent {
ConnectCircleScreen(
isConnected = bleManager.isConnected,
onConnectClick = {
Log.d("ESP32_BLE", "Button wurde gedrückt!")
bleManager.connectToEsp32("10:BD:A3:8F:01:0E")
MaterialTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
// 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
}
)
}
}
}