diff --git a/.gitignore b/.gitignore
index 7d02334923b666cb56277048564d1384de68b160..7a0e4764231555729f59151231b6fc87f1fbe8a6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,3 +15,4 @@
 .externalNativeBuild
 .cxx
 local.properties
+*.swp
diff --git a/README.md b/README.md
index 2059317bbd0122f91ad0f3424aa878738def58b6..ae4a61bd02947e09e6a21bf73bf75126d73547f1 100644
--- a/README.md
+++ b/README.md
@@ -31,3 +31,7 @@ you can call the remote procedure `startActivity()` etc:
     >>> s.startActivity()
     >>> s.startAdvertising()
     >>> s.startDiscovery()
+
+There's a mobly testbed configuration and script configured in
+`mobly/mesh-run` see [mobly/mesh-run/README.md](mobly/mesh-run/README.md)
+for instructions on how to work with that.
diff --git a/mobly/mesh-run/.gitignore b/mobly/mesh-run/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..6d9594aa4f0c609df1c6f831eda740c4baf5e9a0
--- /dev/null
+++ b/mobly/mesh-run/.gitignore
@@ -0,0 +1 @@
+/logs/
diff --git a/mobly/mesh-run/README.md b/mobly/mesh-run/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..af2ec23e1e07600f0a853bba9a43010271fdc047
--- /dev/null
+++ b/mobly/mesh-run/README.md
@@ -0,0 +1,48 @@
+# Mobly test case
+
+To run this, prepare two devices by installing the testbed app from this
+branch onto them.
+Then connect the devices to your host machine using USB and run this:
+
+    python3 public_mesh_test.py -c public_mesh.yml
+
+This is a typical output:
+
+```
+[PublicMeshTestBed] 11-25 12:45:50.931 INFO ==========> PublicMeshTest <==========
+[PublicMeshTestBed] 11-25 12:45:51.309 INFO [AndroidDevice|PT99651AA1AC1803610] Initializing the snippet package org.briarproject.publicmesh.
+[PublicMeshTestBed] 11-25 12:46:02.428 INFO [AndroidDevice|ZY32BSN89S] Initializing the snippet package org.briarproject.publicmesh.
+[PublicMeshTestBed] 11-25 12:46:04.028 INFO [Test] test_basic_discovery
+[PublicMeshTestBed] 11-25 12:46:14.258 INFO [Test] test_basic_discovery PASS
+[PublicMeshTestBed] 11-25 12:46:28.831 INFO Summary for test class PublicMeshTest: Error 0, Executed 1, Failed 0, Passed 1, Requested 1, Skipped 0
+[PublicMeshTestBed] 11-25 12:46:28.831 INFO Summary for test run PublicMeshTestBed@11-25-2022_12-45-50-929:
+Total time elapsed 37.901148029006436s
+Artifacts are saved in "/PATH/TO/public-mesh-testbed/mobly/mesh-run/logs/PublicMeshTestBed/11-25-2022_12-45-50-929"
+Test results: Error 0, Executed 1, Failed 0, Passed 1, Requested 1, Skipped 0
+```
+
+and the logs end up in `logs` in the current directory:
+
+```
+logs/
+└── PublicMeshTestBed
+    ├── 11-25-2022_12-44-13-683
+    │   ├── PublicMeshTest
+    │   │   ├── AndroidDevicePT99651AA1AC1803610
+    │   │   │   └── logcat,PT99651AA1AC1803610,wolverine_00eea,11-25-2022_12-44-13-925.txt
+    │   │   └── AndroidDeviceZY32BSN89S
+    │   │       └── logcat,ZY32BSN89S,capri_retailen,11-25-2022_12-44-14-021.txt
+    │   ├── test_log.DEBUG
+    │   ├── test_log.INFO
+    │   └── test_summary.yaml
+    ├── 11-25-2022_12-45-50-929
+    │   ├── PublicMeshTest
+    │   │   ├── AndroidDevicePT99651AA1AC1803610
+    │   │   │   └── logcat,PT99651AA1AC1803610,wolverine_00eea,11-25-2022_12-45-51-197.txt
+    │   │   └── AndroidDeviceZY32BSN89S
+    │   │       └── logcat,ZY32BSN89S,capri_retailen,11-25-2022_12-45-51-298.txt
+    │   ├── test_log.DEBUG
+    │   ├── test_log.INFO
+    │   └── test_summary.yaml
+    └── latest -> /PATH/TO/public-mesh-testbed/mobly/mesh-run/logs/PublicMeshTestBed/11-25-2022_12-45-50-929
+```
diff --git a/mobly/mesh-run/public_mesh.yml b/mobly/mesh-run/public_mesh.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ff8d6ddbdb8aec14fac4a95d350e3beb766215ea
--- /dev/null
+++ b/mobly/mesh-run/public_mesh.yml
@@ -0,0 +1,6 @@
+MoblyParams:
+    LogPath: './logs'
+TestBeds:
+  - Name: PublicMeshTestBed
+    Controllers:
+        AndroidDevice: '*'
diff --git a/mobly/mesh-run/public_mesh_test.py b/mobly/mesh-run/public_mesh_test.py
new file mode 100644
index 0000000000000000000000000000000000000000..3579fe2645c85d50a853e3531ab94e485aaf5446
--- /dev/null
+++ b/mobly/mesh-run/public_mesh_test.py
@@ -0,0 +1,26 @@
+from mobly import base_test
+from mobly import test_runner
+from mobly.controllers import android_device
+import time
+
+class PublicMeshTest(base_test.BaseTestClass):
+
+  def setup_class(self):
+    # Registering android_device controller module declares the test's
+    # dependency on Android device hardware. By default, we expect at least one
+    # object is created from this, here we expect at least 2 devices.
+    self.ads = self.register_controller(android_device, min_number=2)
+    self.droid1 = self.ads[0]
+    self.droid2 = self.ads[1]
+    self.droid1.load_snippet('pm', 'org.briarproject.publicmesh')
+    self.droid2.load_snippet('pm', 'org.briarproject.publicmesh')
+
+  def test_basic_discovery(self):
+    self.droid1.pm.startActivity()
+    self.droid2.pm.startActivity()
+    self.droid1.pm.startAdvertising()
+    self.droid2.pm.startDiscovery()
+    time.sleep(10)
+
+if __name__ == '__main__':
+  test_runner.main()