Zunächst möchte ich mich dafür entschuldigen, dass diese Anfrage ein wenig off-Topic ist, aber ich gerife mittleweile nach jedem Strohhalm. In anderen diversen (Programmierer)Foren kam bisher nichts brauchbares heraus. Offenbar auch weil viele Programmierer nichts von Hochfequenz und Protokollen verstehen. Auch ChatGPT hat nichts funktionierendes hervorgebracht. Nun zu meinem eigentlichen Anliegen : Ich entwickle derzeit für mich (und später auch für andere) eine Android-App mit Android-Studio und Kotlin, die sportliche Leistungen aufzeichnet. Diese App läuft bisher excellent ist aber noch nicht vollständig. Ich muss es noch schaffen, zu erkennen, ob sich ein Apple AirTag in meiner Nähe befindet. Leider muss es ein AirTag sein, weil nur das zuverlässig wasserfest und klein und leicht genug ist. Ich habe mittlerweile mehr als 40 Stunden mit Programmierexperimenten und Internetrecherchen verbracht und bin kurz vor der Aufgabe des Projets. Hiiiiiiiillllllffffffeeeeee !!!! Ich habe bisher mit der "altbeacon Android Beacon Libary" getestet. Im Referenz Demoprogramm sehe ich den AirTag, allerdings ohne RSSI-Wert. Das Demoprogramm ist dermassen aufgebläht, dass es trotz einiger Kommentierungen nur schwer nachzuvollziehen ist. Ausserdem bin ich Kotlin Anfänger, kenne mich aber gut in anderen Programmiersprachen, Bluetooth und Elektronik aus. Gibt es nicht einen einfacheren Weg den RSSI-Wert eines empfangenen Bluetooth-Pakets zu ermitteln. Mein Wunsch wäre es, ca. jede Sekunde nachzuschauen ob sich das AirTag im Bereich von ca. 3 Metern befindet. Also sowas wie empfangene Bluetooth Pakete nach der der AirTag UUID durchsuchen und den dazugehörigen RSSI-Wert übergeben. Im Voraus Danke für Eure Antworten und nochmals sorry für offtopic. Auch Vorschläge für andere kompetente Foren sind willommen. Gruß Armin ------------------------------------------------------------------ Ich weis noch auf welcher Seite man den Lötkolben anfasst :-)
Ich kann auch nur vermuten: Die funktechnische "Anwesenheit" (und damit der RSSI-Wert) ist doch nicht das Gleiche, wie eine Kommunikation (OSI lässt grüßen). Das Eine setzt natürlich das Andere voraus ... Worauf ich hinaus will: Kann man den RSSI-Wert nicht aus "weiter unten" liegenden Schichten des Betriebssystems auslesen? Per Syscall oder über eine Systemvariable ... oder wie immer das bei Android gelöst ist. Also näher an die Hardware. Android ist doch Java? Spontan gefunden: https://stackoverflow.com/questions/15312858/get-bluetooth-signal-strength
:
Bearbeitet durch User
Liegt da ein Linux drunter? Dann kann man doch einfach mit btmon scannen. Überhaupt gibt es unter Linux viele Möglichkeiten. Ansonsten einen Raspberry Pi aufsetzen und die Sachen per beliebiger Schnittstelle an das Zielsystem.
Danke @Frank E. Der Hinweis auf den Stackflow Artikel ist sehr interessant. Das war ja genau mein Gedanke, einfach "weiter unten" ab zu greifen. Ich brauche keine echte Kommunikation, sondern nur den Empfang. Leider fehlt mir heute die Zeit die Vorschläge des Artikels zu testen. Ich werde das aber baldmöglichst testen und berichten. Android ist nicht Java, aber die Sprache Kotlin in der ich schreibe, ist eine Weiterentwicklung von Java. Android ist ein Betriebssystem und im weitesten Sinne Linux ähnlich. Danke auch @Harald A. Wie oben gesagt ich entwickle für ein Android Samrtphone, und das Betriebssystem ist Android, und Linux nur im weitesten Sinne ähnlich. Danke auch für den Tip mit externer Hardware. So bin ich mit meinem Projekt angefangen, nämlich mit einem auf ESP32 basierendem Bluetooth Interface. Da war das Auslesen der UUID und des RSSI Wertes ganz einfach. Ich hatte sogar schon ein Frontend mit großen 7-Segment Ziffern dazu gebaut. Als ich mein Projekt dann bei befreundeten Sportlern ansprach ergab sich so großes Interesse daran, dass ich beschlossen habe, das Rad nicht ein zweites mal zu erfinden sondern auf Massenware, nämlich ein Smartphone zu setzen. Große oder stromfressende zusatz-Hardware scheidet aus. Das ganze muss klein und praktikabel zum Mitnehmen sein. Ich hatte auch schon den Ansatz eine Tastatur zu schlachten und die üblicherweise kleine Platine per USB an das Smartphone zu koppeln oder sofort eine Bluetooth Tastatur zu schlachten und dann damit die App zu steuern. Aber das sind dann halt Individuallösungen, die nie für die Allgemeinheit brauchbar sind. Noch mal herzlichen Dank an Euch. Gruß Armin ------------------------------------------------------------------ Ich weis noch auf welcher Seite man den Lötkolben anfasst :-)
@Frank E. Heureka ! Es funktioniert !!! Das Programm in dem Link den Du gepostet hattest funktionierte zwar auch nicht, aber es hat mich auf den richtigen Weg gebracht. Ich habe jetzt ein neues Programm selbst geschrieben. Der Trick besteht darin, das man den "BluetoothLeScanner" nutzen muss. Sobald ich meinen Test etwas geordnet habe werde ich ihn hier veröffentlichen. Vielleicht helfe ich damit ja auch anderen, denn über die Zusammenarbeit von AirTag und Android gibt es nur wenig im Netz. Noch mal herzlichen Dank an Frank E. Gruß Armin ------------------------------------------------------------------ Ich weis noch auf welcher Seite man den Lötkolben anfasst :-)
Wie versprochen hier nun die Lösung wie man den RSSI-Wert eines Apple AirTag als Beacon ausliest. Es funktioniert für nahezu alle BLE Beacons. Der RSSI Wert des Beacon wird bei jedem gesendeten Paket angezeigt, ohne sich per Bluetooth mit dem Beacon zu verbinden. Ein Apple AirTag Beacon sendet im unkonfigurierten Zustand ca. 1x pro Sekunde. Somit sind auch schnelle Erfassungen möglich. Als 'Bonbon' speichert die App auch noch die angezeigten Werte ab wenn man auf die Stop Taste drückt. Die Datei befindet sich in dem Ordner Dokumente/Beacontest/ Es ist ein Smartphone mit mindestens API34 extension 7 erforderlich. Das ist mit Android 14 erfüllt. Beim Debug unter AndroidStudio ist es relativ einfach die angeforderten Rechte frei zu geben. Dazu einfach in Debug Kopfzeile auf "app" clicken, dann Edit Configuations und im Feld "Install Flags" "-g" eintragen. Nun zum Code. Zuerst die activity_main.xml : <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/textView1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="-XX dBm" android:textAlignment="center" android:textColor="@color/purple_700" android:textSize="60sp" android:textStyle="bold" /> <ScrollView android:id="@+id/TimelineScrollbar" android:layout_width="361dp" android:layout_height="545dp" android:layout_marginLeft="10dp" android:scrollIndicators="right" android:scrollbarAlwaysDrawVerticalTrack="true" android:scrollbarSize="6dp" android:scrollbarStyle="insideOverlay" android:scrollbarThumbHorizontal="@color/purple_700" android:scrollbarThumbVertical="@color/purple_700" android:scrollbars="vertical" android:textAlignment="gravity"> <TextView android:id="@+id/textView2" android:layout_width="match_parent" android:layout_height="match_parent" android:text="BeaconList" android:textColor="@color/black" android:textSize="24sp" /> </ScrollView> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:gravity="center" android:orientation="horizontal"> <Button android:id="@+id/button_start" android:layout_width="150dp" android:layout_height="wrap_content" android:layout_marginLeft="15dp" android:layout_marginRight="10dp" android:background="#009688" android:gravity="center|center_horizontal|center_vertical" android:text="Start" android:textSize="24sp" /> <Button android:id="@+id/button_stop" android:layout_width="150dp" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginRight="10dp" android:background="#E91E63" android:gravity="center|center_horizontal|center_vertical" android:text="Stop" android:textSize="24sp" /> </LinearLayout> </LinearLayout> Dann die MainActivity.kt : package com.example.beacontest6 import android.Manifest import android.annotation.SuppressLint import android.app.Activity import android.bluetooth.BluetoothManager import android.bluetooth.le.ScanCallback import android.bluetooth.le.ScanFilter import android.bluetooth.le.ScanResult import android.bluetooth.le.ScanSettings import android.content.ContentValues import android.content.Context import android.content.pm.PackageManager import android.os.Bundle import android.os.Environment import android.provider.MediaStore import android.util.Log import android.view.View import android.widget.Button import android.widget.ScrollView import android.widget.TextView import android.widget.Toast import androidx.core.app.ActivityCompat /* ---------------------------------------------------------- - Programm : Beacontest6 - - Usage : Recognize Bluetooth BLE Becaon and get Infos - - like Devicename, Deviceadress and RSSI Value - - Copyright : 2024 Armin Graewe, Muenster, Germany - ----------------------------------------------------------*/ class MainActivity : Activity() { public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val bluetoothLeScanner = (getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter.bluetoothLeScanner val rssi_msg = findViewById<View>(R.id.textView1) as TextView val rssi_list = findViewById<View>(R.id.textView2) as TextView val scroll = findViewById<View>(R.id.TimelineScrollbar) as ScrollView val BuStart = findViewById<View>(R.id.button_start) as Button val BuStop = findViewById<View>(R.id.button_stop) as Button val settings = ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .build() val filters = mutableListOf<ScanFilter>() // Leere Liste von Filtern sieht alle devices //filters.add(ScanFilter.Builder().setDeviceName("AirTag").build()) filters.add(ScanFilter.Builder().setDeviceAddress("D6:49:48:05:D3:51").b uild()) // Replace MAC Address with your own value or rem the above line out to see all Beacon val scanCallback = object : ScanCallback() { override fun onScanResult(callbackType: Int, result: ScanResult) { //Toast.makeText(getApplicationContext(), " Callback triggered ", Toast.LENGTH_SHORT).show() //rssi_msg.text = "Callback Triggered" //if (result.device.name != null && result.device.name == "AirTag") { //if (result.device.name != null) { //val name = result.device.name //"" //result.device.name val deviceAddress = result.device.address val rssi = result.rssi // Hier können Sie den RSSI-Wert des AirTag-Beacons verwenden Log.d("RSSI", "Device: $deviceAddress" + " " + "RSSI: $rssi") rssi_list.text = rssi_list.text.toString() + deviceAddress + " => " + rssi + "dBm\n" scroll.fullScroll(View.FOCUS_DOWN) rssi_msg.text = String.format("%02d dBm", rssi) //} } } BuStart.setOnClickListener { if (ActivityCompat.checkSelfPermission( this, Manifest.permission.BLUETOOTH_SCAN ) != PackageManager.PERMISSION_GRANTED ) { // TODO: Consider calling // ActivityCompat#requestPermissions // here to request the missing permissions, and then overriding // public void onRequestPermissionsResult(int requestCode, String[] permissions, // int[] grantResults) // to handle the case where the user grants the permission. See the documentation // for ActivityCompat#requestPermissions for more details. Toast.makeText(getApplicationContext(), " No Scan Permission ", Toast.LENGTH_SHORT) .show() rssi_list.text = " No Scan Permission \n" return@setOnClickListener } else { // Hier wird der Scanner gestartet //bluetoothLeScanner?.startScan(filters, settings, scanCallback) bluetoothLeScanner?.startScan(filters, settings, scanCallback) Toast.makeText(getApplicationContext(), " Scanner Started ", Toast.LENGTH_SHORT) .show() rssi_list.text = " Scanner Started \n" } } BuStop.setOnClickListener { // Hier wird der Scanner gestopped bluetoothLeScanner?.stopScan(scanCallback) Toast.makeText(getApplicationContext(), " Scanner Stopped ", Toast.LENGTH_SHORT) .show() rssi_list.text = rssi_list.text.toString() + " Scanner Stopped \n" scroll.fullScroll(View.FOCUS_DOWN) // Hier wird die Liste respeichert (for API 34 ext7 only) // Zu finden unter : /storage/emulated/0/Documents/YourCustomDirectory/RSSIwerte.txt val content = rssi_list.text as String val fileName = "RSSIwerte.txt" saveStringToMediaStore(this, fileName, content) } } // allgemeine File-Speicherfunktion (for API 34 ext7 only) // Files werden in : Documents/BeaconTest/ abgelegt fun saveStringToMediaStore(context: Context, fileName: String, content: String) { val values = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, fileName) put(MediaStore.MediaColumns.MIME_TYPE, "text/plain") put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOCUMENTS + "/BeaconTest") } val uri = context.contentResolver.insert(MediaStore.Files.getContentUri("external" ), values) uri?.let { outputStream -> context.contentResolver.openOutputStream(outputStream).use { it?.write(content.toByteArray()) } } } @SuppressLint("MissingPermission") override fun onDestroy() { super.onDestroy() //bluetoothLeScanner?.stopScan(scanCallback) } } Und zuletzt die AndroidManifest.xml : <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.BeaconTest6" tools:targetApi="31"> <activity android:name=".MainActivity" android:exported="true" android:label="@string/app_name" android:theme="@style/Theme.BeaconTest6"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> Ich hoffe jemandem damit geholfen zu haben :-) Kommentare sind willkommen. Gruß Armin ------------------------------------------------------------------ Ich weis noch auf welcher Seite man den Lötkolben anfasst :-)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.