Thread-Netzwerk mit Thread-Anmeldedaten von Google teilen

1. Hinweis

In unserem Codelab zu Thread-Border-Router (TBR) zeigen wir Ihnen, wie Sie einen Thread-Border-Router auf Basis eines Raspberry Pi erstellen. In diesem Codelab

  • Bidirektionale IP-Verbindung zwischen Thread- und WLAN-/Ethernet-Netzwerken herstellen
  • Stellen Sie eine bidirektionale Diensterkennung über mDNS (über WLAN/Ethernet-Link) und SRP (im Thread-Netzwerk) bereit.

Dieses Codelab baut auf dem vorherigen auf. Dabei geht es darum, wie dein eigener Border-Router und deine App mit Google-APIs interagieren können, um ein einzelnes Thread-Netzwerk zu erstellen. Die Zusammenführung von Thread-Anmeldedaten ist wichtig, da dies die Netzwerkstabilität erhöht und die Nutzerinteraktionen mit den Anwendungen vereinfacht, die auf Thread basieren.

Voraussetzungen

  • Schließe das OTBR-Codelab ab
  • Grundkenntnisse in Linux, Android/Kotlin und Thread-Netzwerken

Lerninhalte

  • Mit den Thread Sharing APIs Anmeldedatensätze abrufen und festlegen
  • Einen eigenen OpenThread-Border-Router mit denselben Anmeldedaten wie das Netzwerk von Google einrichten

Voraussetzungen

  • Raspberry Pi 4-Board oder ein anderes Linux-basiertes Board, auf dem der Open Thread Border Router (OTBR) ausgeführt wird
  • Board, das IEEE 802.15.4-Konnektivität als Radio Co-Processor (RCP) bereitstellt. Eine Liste der Repositories verschiedener SoC-Anbieter und deren Anleitungen finden Sie auf der OpenThread-GitHub-Seite.

2. HTTP-Dienst einrichten

Der erste erforderliche Baustein ist eine Schnittstelle, mit der aktive Anmeldedaten gelesen und ausstehende Anmeldedaten in den OTBR geschrieben werden können. Verwenden Sie beim Erstellen einer TBR Ihre eigenen Mechanismen, wie hier mit zwei Beispielen gezeigt. Die erste zeigt, wie eine lokale Schnittstelle mit dem OTBR-Agent über DBUS hergestellt werden kann, während die zweite die Rest API nutzt, die auf dem OTBR erstellt werden kann.

Keine der beiden Methoden ist sicher und sollte nicht in der vorliegenden Form in einer Produktionsumgebung verwendet werden. Ein Anbieter kann jedoch beide Methoden zur Verschlüsselung in einer Produktionsumgebung verwenden. Sie können auch Ihren eigenen Überwachungsdienst erweitern, um Loopback-HTTP- oder inhärent lokale DBUS-Aufrufe auszuführen.

Option 1: DBUS und HTTP API unter Python-Script

91e5fdeed83e9354.png

In diesem Schritt wird ein einfacher HTTP-Dienst erstellt, der zwei Endpunkte zum Lesen und Festlegen von Anmeldedaten zur Verfügung stellt und schließlich DBUS-Befehle aufruft.

Installieren Sie auf dem RPi, das als OTBR dient, die Python 3-Abhängigkeiten:

$ pip install dbus-python shlex json

Führen Sie das Skript aus als:

$  sudo python credentials_server.py 8081
serving at port 8081

Im Beispiel wird ein HTTP-Server an Port 8081 eingerichtet und der Stammpfad überwacht, ob eine GET-Anfrage zum Abrufen von Thread-Anmeldedaten oder eine POST-Anfrage zum Festlegen von Thread-Anmeldedaten vorliegt. Die Nutzlast ist immer eine JSON-Struktur mit dem TLV.

Durch die folgende PUT-Anfrage werden neue ausstehende Thread-Anmeldedaten über den Pfad /node/dataset/pending für den OTBR festgelegt. In diesem Fall werden die ausstehenden Anmeldedaten innerhalb von 10 Sekunden angewendet:

PUT /node/dataset/pending
Host: <IP>:8081
ContentType: "application/json"
acceptMimeType: "application/json"
...
{
        "ActiveDataset": "<TLV encoded new Thread Dataset>"
"PendingTimestamp": {
        "Seconds": <Unix timestamp in seconds>,
        "Ticks": 0,
        "Authoritative": false
},
"Delay": 10000 // in milliseconds
}

Eine GET-Anfrage an /node/dataset/active ruft die derzeit aktiven Anmeldedaten ab.

GET /node/dataset/active
Host: <IP>:8081
ContentType = "application/json"
acceptMimeType = "text/plain"
...
<TLV encoded Thread Dataset>

Das Skript ruft DBUS R/W-Befehle an den Buspfad io.openthread.BorderRouter.wpan0, Objektpfad /io/openthread/BorderRouter/wpan0 auf.

# D-BUS interface
def call_dbus_method(interface, method_name, *arguments):
    bus = dbus.SystemBus()
    obj = bus.get_object('io.openthread.BorderRouter.wpan0', '/io/openthread/BorderRouter/wpan0')
    iface = dbus.Interface(obj, interface)
    method = getattr(iface, method_name)
    res = method(*arguments)
    return res

def get_dbus_property(property_name):
    return call_dbus_method('org.freedesktop.DBus.Properties', 'Get', 'io.openthread.BorderRouter',
                                 property_name)

def set_dbus_property(property_name, property_value):
    return call_dbus_method('org.freedesktop.DBus.Properties', 'Set', 'io.openthread.BorderRouter',
                                 property_name, property_value)                               

DBUS ermöglicht die Introspektion seiner Funktionen. Sie können dies auf folgende Weise tun:

$ sudo dbus-send --system --dest=io.openthread.BorderRouter.wpan0 \
        --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 \
        org.freedesktop.DBus.Introspectable.Introspect

Hier finden Sie eine Liste der unterstützten Funktionen.

Option 2: Native HTTP Rest API für OTBR-Agent

c748ca5151b6cacb.png

Der OpenThread Border Router wird standardmäßig mit dem Flag REST_API=1 erstellt, wodurch die REST API aktiviert wird. Falls die REST API in Ihrem Build aus einem früheren Codelab nicht aktiviert wurde, erstellen Sie OTBR auf Ihrem RPi mit diesem Flag:

$ REST_API=1 INFRA_IF_NAME=wlan0 ./script/setup

Ein OTBR-Agent kann mit folgendem Befehl neu gestartet werden:

$ sudo systemctl restart otbr-agent.service

Der Agent startet einen HTTP-Server an Port 8081. Mit diesem Server können Nutzer- oder Monitorprogramme viele Aufgaben im OTBR ausführen (hier dokumentiert). Sie können Ihren Browser, curl oder wget verwenden, um den Inhalt zu prüfen. Zu den vielen unterstützten Pfaden gehören die oben beschriebenen Anwendungsfälle mit dem Verb GET auf /node/dataset/active und dem Verb PUT auf /node/dataset/pending

3. Credential Framework für Android einrichten

Bevorzugte Anmeldedaten

Google Play-Dienste unter Android ermöglichen und erwarten die Registrierung von Anmeldedaten für alle TBRs in Ihrem Netzwerk. Jeder wird anhand seiner Border Router Agent ID (BAID) identifiziert. Für diese Aufgabe verwenden Sie die Methode addCredentials() der ThreadNetworkClient-Oberfläche. Die ersten TBR, die dem Speicher der Google Play-Dienste hinzugefügt wird, bestimmt die bevorzugten Anmeldedaten für dieses Mobilgerät.

Die App, die ihrem BAID einen Satz von Thread-Netzwerkanmeldedaten hinzufügt, wird zum Inhaber der Anmeldedaten und verfügt über vollständige Berechtigungen für den Zugriff darauf. Wenn Sie versuchen, auf Anmeldedaten zuzugreifen, die von anderen Apps hinzugefügt wurden, erhalten Sie die Fehlermeldung PERMISSION_DENIED. Die bevorzugten Anmeldedaten sind jedoch immer für jede App verfügbar, sobald der Nutzer seine Einwilligung erteilt hat. Wir empfehlen, die in den Google Play-Diensten gespeicherten Anmeldedaten immer auf dem neuesten Stand zu halten, wenn das Thread-Border-Router-Netzwerk aktualisiert wird. Auch wenn diese Informationen derzeit nicht genutzt werden, werden wir in Zukunft möglicherweise noch bessere Funktionen anbieten.

Auch wenn die ersten TBR später ausgeschlossen werden, bleiben die bevorzugten Anmeldedaten auf dem Android-Gerät erhalten. Nach der Einrichtung können andere Apps, die Thread-Anmeldedaten verwalten, die Anmeldedaten über einen getPreferredCredentials()-Aufruf abrufen.

Google TBR Sync

Android-Geräte werden automatisch mit den TBRs von Google synchronisiert. Wenn auf Android-Geräten keine Anmeldedaten vorhanden sind, extrahieren die Geräte sie aus den Google-TBRs in Ihrem Netzwerk und diese werden zu den bevorzugten Anmeldedaten. Eine Synchronisierung zwischen TBRs und dem Android-Gerät erfolgt nur, wenn das TBR mit einem einzelnen Nutzer gekoppelt ist oder wenn es mit zwei Nutzern im selben Smart Home gekoppelt ist ( Struktur).

Das funktioniert auch, wenn ein anderer Google-Nutzer GHA für Android oder GHA für iOS verwendet und sich der Nutzer im selben Gebäude befindet. Bei GHA für iOS werden die bevorzugten Anmeldedaten auf dem iOS-Speicher festgelegt, wenn keine bevorzugten Anmeldedaten vorhanden sind.

Wenn sich zwei Android-Geräte (oder Android und iGHA) im selben Netzwerk mit unterschiedlichen bevorzugten Anmeldedaten befinden, hat das Gerät, mit dem die TBR ursprünglich konfiguriert wurde, in der TBR Vorrang.

Onboarding TBR von Drittanbietern

Das Smart Home des Nutzers ( Struktur) wird derzeit nicht auf den Speicher der Anmeldedaten beschränkt. Jedes Android-Gerät erhält seinen BAID-Speicher. Sobald sich jedoch eine Google-TBR im Netzwerk befindet, werden andere Android-Geräte und iOS-Geräte, auf denen die Google Home App für iOS ausgeführt wird, mit dieser TBR synchronisiert und versuchen, lokale Anmeldedaten im Smartphone-Speicher festzulegen.

Bevor ein neues OOB-TBR ein Netzwerk erstellt, muss geprüft werden, ob bereits ein bevorzugtes Netzwerk im Android-Speicher vorhanden ist.

  • Wenn ein bevorzugtes Netzwerk existiert, sollte der Anbieter es verwenden. Dadurch wird sichergestellt, dass Thread-Geräte nach Möglichkeit mit einem einzelnen Thread-Netzwerk verbunden sind.
  • Wenn kein bevorzugtes Netzwerk vorhanden ist, erstellen Sie einen neuen Anmeldedatensatz und weisen Sie ihn Ihrer TBR in den Google Play-Diensten zu. Android berücksichtigt diese Anmeldedaten als Standardanmeldedaten, die auf allen Google-basierten TBRs festgelegt werden. Andere Anbieter können die Reichweite und Stabilität des Mesh-Netzwerks durch zusätzliche Geräte verbessern.

cd8bc726f67b1fa1.png

4. Android-App klonen und ändern

Wir haben eine Android-App entwickelt, in der die wichtigsten Aufrufe der Thread API zu finden sind. Du kannst diese Muster in deiner App verwenden. In diesem Codelab wird die Google Home-Beispiel-App für Matter aus GitHub geklont.

Der gesamte hier angezeigte Quellcode ist bereits in der Beispiel-App codiert. Sie können ihn an Ihre Anforderungen anpassen, aber Sie können einfach die App klonen oder die vordefinierten Binärprogramme ausführen, um die Funktionalität zu prüfen.

  1. Du kannst es klonen mit:
$ git clone https://github.com/google-home/sample-apps-for-matter-android.git
  1. Laden Sie Android Studio herunter und öffnen Sie die App.
  2. Klicken Sie auf „File“ (Datei) > „Open“ (Öffnen) und zeigen Sie auf Ihr geklontes Repository.
  3. Aktivieren Sie den Entwicklermodus auf Ihrem Android-Smartphone.
  4. Schließen Sie ihn über ein USB-Kabel an Ihren Computer an.
  5. Führen Sie die App aus Android Studio über <Cmd+R> (OS X) oder <Strg+R> (Windows, Linux) aus.
  6. Gehen Sie zu Rad -> Developer Utilities -> Thread-Netzwerk.
  7. Interagieren Sie mit den verschiedenen verfügbaren Optionen. In den folgenden Abschnitten entpacken wir den Code, der für jede Schaltfläche ausgeführt wird.

Gibt es bevorzugte Anmeldedaten?

Die erste Frage, die ein Hersteller von TBR an Google stellen sollte, lautet, ob bereits ein bevorzugter Satz von Anmeldedaten auf dem Gerät vorhanden ist. Dies sollte der Ausgangspunkt für Ihren User Flow sein. Mit dem folgenden Code wird GPS das Vorhandensein von Anmeldedaten abgefragt. Es wird nicht zur Einwilligung des Nutzers aufgefordert, da keine Anmeldedaten weitergegeben werden.

/**
* Prompts whether credentials exist in storage or not. Consent from user is not necessary
*/

fun doGPSPreferredCredsExist(activity: FragmentActivity) {
 try {
   // Uses the ThreadNetwork interface for the preferred credentials, adding
   // a listener that will receive an intentSenderResult. If that is NULL, 
   // preferred credentials don't exist. If that isn't NULL, they exist.
   // In this case we'll not use it.

   ThreadNetwork.getClient(activity).preferredCredentials.addOnSuccessListener { intentSenderResult ->
     intentSenderResult.intentSender?.let { intentSender ->
       ToastTimber.d("threadClient: preferred credentials exist", activity)
       // don't post the intent on `threadClientIntentSender` as we do when
       // we really want to know which are the credentials. That will prompt a
       // user consent. In this case we just want to know whether they exist
     } ?: ToastTimber.d(
       "threadClient: no preferred credentials found, or no thread module found", activity
     )
   }.addOnFailureListener { e: Exception ->
     Timber.d("ERROR: [${e}]")
   }
 } catch (e: Exception) {
   ToastTimber.e("Error $e", activity)
 }
}

Bevorzugte GPS-Anmeldedaten erhalten

Sofern vorhanden, möchten Sie die Anmeldedaten lesen. Der einzige Unterschied zum vorherigen Code besteht darin, dass Sie nach Erhalt des intentSenderResult einen Intent mit diesem Ergebnis des Absenders erstellen und starten möchten.

In unserem Code verwenden wir für Organisations-/Architekturzwecke ein MutableLiveData<IntentSender?>, da sich der ursprüngliche Code im ViewModel (ThreadViewModel.kt) und die Intent-Beobachter im Activity-Fragment ( ThreadFragment.kt) befinden. Sobald also das intentSenderResult an die Live-Daten gesendet wurde, führen wir den Inhalt dieses Beobachters aus:

viewModel.threadClientIntentSender.observe(viewLifecycleOwner) { sender ->
 Timber.d(
   "threadClient: intent observe is called with [${intentSenderToString(sender)}]"
 )
 if (sender != null) {
   Timber.d("threadClient: Launch GPS activity to get ThreadClient")
   threadClientLauncher.launch(IntentSenderRequest.Builder(sender).build())
   viewModel.consumeThreadClientIntentSender()
 }
}

Dadurch wird die Nutzereinwilligung mit der Weitergabe von Anmeldedaten ausgelöst. Bei Genehmigung werden Inhalte über folgende Adresse zurückgegeben:

threadClientLauncher =
 registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
   if (result.resultCode == RESULT_OK) {
     val threadNetworkCredentials =
       ThreadNetworkCredentials.fromIntentSenderResultData(result.data!!)
     viewModel.threadPreferredCredentialsOperationalDataset.postValue(
       threadNetworkCredentials
     )
   } else {
     val error = "User denied request."
     Timber.d(error)
     updateThreadInfo(null, "")
   }
 }

Das Posten der Anmeldedaten für MutableLiveData<ThreadNetworkCredentials?> wird unten beschrieben.

GPS-Anmeldedaten einrichten

Unabhängig davon, ob sie existieren oder nicht, sollten Sie Ihre TBR in den Google Play-Diensten registrieren. Ihre App kann die Anmeldedaten, die mit der Border Agent-ID Ihrer TBR verknüpft sind, als einzige lesen. Wenn sich Ihre TBR jedoch als erste registriert, werden diese Anmeldedaten in das Set „Preferred Credentials“ (Bevorzugte Anmeldedaten) kopiert. Diese Informationen sind für jede App auf dem Smartphone zugänglich, solange der Nutzer sie autorisiert.

/**
* Last step in setting the GPS thread credentials of a TBR
*/
private fun associateGPSThreadCredentialsToThreadBorderRouterAgent(
 credentials: ThreadNetworkCredentials?,
 activity: FragmentActivity,
 threadBorderAgent: ThreadBorderAgent,
) {
 credentials?.let {
   ThreadNetwork.getClient(activity).addCredentials(threadBorderAgent, credentials)
     .addOnSuccessListener {
       ToastTimber.d("threadClient: Credentials added", activity)
     }.addOnFailureListener { e: Exception ->
       ToastTimber.e("threadClient: Error adding the new credentials: $e", activity)
     }
 }
}

Anmeldedaten für Ihr TBR-Produkt einrichten

Dieser Teil ist anbieterspezifisch und in diesem Codelab implementieren wir ihn entweder über einen DBUS+Python HTTP Rest Server oder den nativen HTTP Rest Server von OTBR.

/**
* Creates credentials in the format used by the OTBR HTTP server. See its documentation in
* https://github.com/openthread/ot-br-posix/blob/main/src/rest/openapi.yaml#L215
*/
fun createJsonCredentialsObject(newCredentials: ThreadNetworkCredentials): JSONObject {
 val jsonTimestamp = JSONObject()
 jsonTimestamp.put("Seconds", System.currentTimeMillis() / 1000)
 jsonTimestamp.put("Ticks", 0)
 jsonTimestamp.put("Authoritative", false)

 val jsonQuery = JSONObject()
 jsonQuery.put(
   "ActiveDataset",
   BaseEncoding.base16().encode(newCredentials.activeOperationalDataset)
 )
 jsonQuery.put("PendingTimestamp", jsonTimestamp)
 // delay of committing the pending set into active set: 10000ms
 jsonQuery.put("Delay", 10000)

 Timber.d(jsonQuery.toString())

 return jsonQuery
}

//(...)

var response = OtbrHttpClient.createJsonHttpRequest(
 URL("http://$ipAddress:$otbrPort$otbrDatasetPendingEndpoint"),
 activity,
 OtbrHttpClient.Verbs.PUT,
 jsonQuery.toString()
)

Anmeldedaten von Ihrem TBR-Produkt abrufen

Verwenden Sie wie bereits gezeigt das GET-HTTP-Verb, um die Anmeldedaten von Ihrem TBR zu erhalten. Hier finden Sie ein Beispielskript in Python.

Builds und Importe

Wenn Sie Ihre Android-App erstellen, müssen Sie Änderungen an Ihrem Manifest, Ihren Builds und Ihren Importen vornehmen, damit das Thread-Modul für Google Play-Dienste unterstützt wird. In den folgenden drei Snippets werden die meisten Ergänzungen zusammengefasst.

Beachte, dass unsere Beispiel-App in erster Linie für die Inbetriebnahme von Matter entwickelt wurde. Daher sind die Manifest- und Gradle-Dateien komplexer als die Ergänzungen, die nur für die Verwendung von Thread-Anmeldedaten erforderlich sind.

Manifeständerungen

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    (...)
    <!-- usesCleartextTraffic needed for OTBR local unencrypted communication -->
    <!-- Not needed for Thread Module, only used for HTTP -->
    <uses-feature
    (...)
        android:usesCleartextTraffic="true">

    <application>
    (...)
    <!-- GPS automatically downloads scanner module when app is installed -->
    <!-- Not needed for Thread Module, only used for scanning QR Codes -->
    <meta-data
        android:name="com.google.mlkit.vision.DEPENDENCIES"
        android:value="barcode_ui"/>
    </application>
</manifest>

Build.gradle

// Thread Network
implementation 'com.google.android.gms:play-services-threadnetwork:16.0.0'
// Thread QR Code Scanning
implementation 'com.google.android.gms:play-services-code-scanner:16.0.0'
// Thread QR Code Generation
implementation 'com.journeyapps:zxing-android-embedded:4.1.0'
// Needed for using BaseEncoding class
implementation 'com.google.guava:guava:31.1-jre'

Relevante Importe

// Thread Network Module
import com.google.android.gms.threadnetwork.ThreadNetworkCredentials
import com.google.android.gms.threadnetwork.ThreadBorderAgent
import com.google.android.gms.threadnetwork.ThreadNetwork

// Conversion of credentials to/fro Base16 (hex)
import com.google.common.io.BaseEncoding

// HTTP
import java.io.BufferedInputStream
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
import java.nio.charset.StandardCharsets

// Co-routines for HTTP calls
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch


// JSON
import org.json.JSONObject

// Logs
import timber.log.Timber

// mDNS/SD
import android.net.nsd.NsdServiceInfo

// QR Code reader / writer
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
import com.google.zxing.BarcodeFormat
import com.google.zxing.MultiFormatWriter
import com.journeyapps.barcodescanner.BarcodeEncoder

5. mDNS/SD-Erkennung

Unsere Beispiel-App verwendet die mDNS/SD-Erkennung, um eine Liste der verfügbaren Thread-Border-Router im Netzwerk sowie ihrer jeweiligen BAIDs zu erstellen.

Dies ist sehr hilfreich, wenn Sie die Informationen Ihrer TBR in den Speicher für GPS-Anmeldedaten eingeben. Ihre Verwendung wird in diesem Codelab jedoch nicht behandelt. Wir verwenden die Android Service Discovery-Bibliothek NSDManager. Der vollständige Quellcode ist in der Beispiel-App in ServiceDiscovery.kt verfügbar.

6. Zusammenfassung

Sobald du diese Aufrufe implementiert oder die Beispiel-App verwendet hast, kannst du deinen RPi-OTBR vollständig einrichten. Die Beispiel-App verfügt über acht Schaltflächen:

91979bf065e9673d.png

Eine mögliche Reihenfolge für das Onboarding Ihrer TBR ist:

  1. Abfrage, ob bevorzugte Anmeldedaten vorhanden sind (blau, erste Zeile)
  2. Je nach Antwort
  3. GPS-Anmeldedaten abrufen (blau, 2. Zeile)
  4. TBR-Anmeldedaten in GPS festlegen (blau, 3. Zeile) -> Wählen Sie Ihre TBR aus -> Create Random (Zufall erstellen) -> Gib den Netzwerknamen ein -> OK
  5. Nachdem du nun bevorzugte Anmeldedaten hast, lege sie mit Set RPi OTBRcredential (RPi-OTBR-Anmeldedaten festlegen) auf dein OTBR-Gerät fest. Dadurch werden diese Anmeldedaten auf den ausstehenden Satz angewendet.

Die Standardeinstellung für die Beispiel-App ist eine Verzögerung von 10 Sekunden. Daher werden nach diesem Zeitraum die Anmeldedaten Ihres RPi-TBR (und anderer Knoten, die möglicherweise in seinem Netzwerk vorhanden sind) zum neuen Dataset migriert.

7. Fazit

In diesem Codelab haben wir eine Android-Beispiel-App geklont und mehrere Code-Snippets analysiert, die die Thread Storage APIs der Google Play-Dienste nutzen. Wir haben diese APIs für ein gemeinsames Dataset verwendet, das wir auf einer RPi-TBR einrichten können, die die TBR eines Anbieters zeigt.

Wenn die gesamte TBR eines Nutzers im selben Netzwerk ist, verbessert das die Ausfallsicherheit und Reichweite des Thread-Netzwerks. Außerdem werden fehlerhafte User Journeys verhindert, bei denen Apps keine Thread-Geräte einrichten können, weil sie keinen Zugriff auf Anmeldedaten haben.

Wir hoffen, dass du mit diesem Codelab und diesen Beispiel-Apps deine eigene App und dein Thread-Border-Router-Produkt entwerfen und entwickeln kannst.

8. Verweise

RCP-Coprozessor

Logo: DBUS