Skip to main content
The Edge Impulse Data Collector Android app uploads data from four kinds of source to your Edge Impulse project: phone accelerometer and PPG sensors and the camera, a companion Zephyr device over BLE, a paired Wear OS watch, and Arduino boards over USB OTG. Use it to build a dataset, then train a model and deploy it with the other tutorials in this series.
Android Data Collector Collect tab with sensor selection, project connection fields, and logging controls
Zephyr BLE tab listing an EI-Monitor device and controls for labeled IMU capture
WearOS tab combining watch, phone, and Nesso N1 streams with voice control status
The video shows accelerometer data collected on the phone and uploaded as a labeled sample in the studio.

Download pre-built APK

Sideload the app onto a device running Android 9.0 (API 28) or later, without building it. To build from source, follow the steps below.

What you’ll build

An Android app with four tabs:

Prerequisites

  • An Edge Impulse account
  • An Edge Impulse project and API key
  • Android Studio (Ladybug 2024.2.2 or later)
  • An Android device running Android 9.0 (API 28) or later. Voice commands use on-device speech recognition on API 31 and later
  • For the Zephyr BLE tab: a device flashed with ei-zephyr-ble-gatt-client
  • For the WearOS tab: a paired Wear OS watch with the companion module installed
  • For the USB OTG tab: a supported Arduino board flashed with the matching sketch from sample-arduino/, and a USB OTG adapter for your phone

1. Clone the repository

2. Get your API key

  1. Open your project in Edge Impulse Studio.
  2. Go to Dashboard -> Keys.
  3. Click Add new API key and copy it (ei_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx).

3. Configure the API key

Add your key to ~/.gradle/gradle.properties, which keeps it out of source control:
Alternatively, add it to the local gradle.properties in the project root, but do not commit that file.
Never hard-code API keys in source code or commit them to version control.

4. Build and install

You can also open the project in Android Studio and run it on a connected device.

Install the Wear OS companion (optional)

To use the WearOS tab, install the wearosdatalogger module on your watch over Wi-Fi with adb:
  1. On the watch, go to Settings -> Developer options and turn on Wireless debugging.
  2. Tap Pair new device and note the IP:port and the 6-digit pairing code displayed.
  3. On your computer, connect to the same Wi-Fi network as the watch (not a guest or isolated network), turn off any VPN, and run:
  4. Build and install the watch APK, targeting the watch by serial:
    Or build and install in one command: ANDROID_SERIAL=$WATCH ./gradlew :wearosdatalogger:installDebug.
The pairing port and code work once and expire after about 60 seconds. Pass both on the adb pair command line rather than waiting for the interactive prompt, and use adb version 34 or later (brew install --cask android-platform-tools on macOS). If pairing fails, see Troubleshooting.
If Wi-Fi pairing keeps failing, connect through the phone instead. Turn on Debug over Bluetooth on the watch and in the Wear OS companion app on the phone, then run adb forward tcp:4444 localabstract:/adb-hub && adb connect 127.0.0.1:4444.
Launch WearOS Data Logger on the watch and open the WearOS tab in the phone app. The watch appears and starts streaming heart rate, accelerometer, and GPS data.

5. Collect sensor data

  1. Open the app and go to the Collect tab.
  2. Enter an Edge Impulse label, for example walking, idle, or running. The sample gets this label in your project.
  3. Select a Sensor: Accelerometer or PPG (Heart Rate).
  4. Tap Start. The live sensor values appear on screen.
  5. Tap Stop. The sample uploads to your project.
  6. Repeat with different labels to build a balanced dataset.
Aim for at least 2 to 3 minutes of data per class. The studio splits recordings into windows when you create your impulse.

6. Capture images

On the same Collect tab:
  1. Enter a label, for example crack or no_crack.
  2. Tap Capture & upload image. The rear camera takes a photo and uploads it with that label.
  3. Repeat to build an image dataset for classification or object detection.

7. Offline logging

If you have no data connection, log samples on the device and upload them later:
  1. Enable Log samples to CSV on device.
  2. Tap Start and Stop as usual. The samples are saved to a local CSV file.
  3. When you’re back online, tap Upload stored CSV to Edge Impulse to upload them all.

8. Multi-modal capture and auto source priority

The Multi-modal capture card on the Collect tab records from connected sources for a fixed duration and uploads each stream with the same label. By default, the app uses the highest-priority source that’s connected: Turn on Camera snapshot to also take one photo at the start of each capture. The app uploads the photo from memory without writing it to disk.

9. Voice control (“Hey Android”)

You can start and label captures by voice, for example when the phone is strapped to a wrist, helmet, or dashboard. The app combines a hey_android wake word model trained in Edge Impulse with Android’s SpeechRecognizer.
  1. Tap the microphone icon in the top app bar and grant the RECORD_AUDIO permission. The icon turns blue and a status banner appears under the tab bar.
  2. Say “Hey Android”. Wake word detection pauses and the speech recognizer listens for a command.
  3. Say a command in the form “capture N seconds with label LABEL”, for example:
    • “capture 5 seconds with label idle”
    • “record 10 seconds with label updown”
    • “start 20 seconds with label circle”
    The verb can be capture, record, or start. The duration can be digits or a spelled-out number up to sixty. The parser recognizes the labels idle, updown (also “up down”), and circle. To add labels, edit voice/VoiceCommandParser.kt.
  4. The app records from the highest-priority connected source and uploads the sample with the label you said. Wake word detection resumes about 300 ms after the capture ends.
The wake word model runs on the device with the Edge Impulse C++ SDK and needs no network. Speech recognition uses Android’s on-device recognizer on API 31 and later, and falls back to the network recognizer on devices without one.

10. Connect a Zephyr device (optional)

The Zephyr BLE tab scans for a device that advertises as EI-Monitor and runs the ei-zephyr-ble-gatt-client firmware. What the firmware sends depends on the board. Each board’s .conf file sets CONFIG_EI_SENSOR_LOCAL:
  1. Flash the firmware to your board. See the BLE GATT client tutorial.
  2. Open the Zephyr BLE tab.
  3. Tap Scan for EI-Monitor. The device appears in the list.
  4. Tap the device to connect. The banner turns green.
Zephyr BLE tab connected to EI-Monitor with inference results displayed

Connected to EI-Monitor with inference results streaming in

  1. The app uploads raw IMU windows and labeled inference results to your project as they arrive.
The app uploads an inference result only after it receives at least one raw sensor window on the same connection. If a result appears on screen but doesn’t upload, check that the firmware exposes the sensor characteristic. The app subscribes to it when it connects.
The app decodes the 52-byte inference_result_t binary notification from the firmware:

11. Connect a USB OTG Arduino device

The USB OTG tab makes the phone a USB host that reads CDC-ACM serial data from any Arduino that implements the wire protocol below. It needs no BLE, Wi-Fi, or network connection.

Supported boards and sketches

All sketches are in the sample-arduino/ directory of the repository.

Wire protocol

All sketches send the same text protocol at 115200 baud:
The app reads the sensor axes from the ! header and switches to image capture when it sees an IMG: header.

Flash and connect

  1. Open the sketch for your board in the Arduino IDE.
  2. Set the #define at the top of the file to match your board variant.
  3. Install the libraries listed in the sketch header from Tools -> Manage Libraries.
  4. Upload the sketch.
  5. Connect the board to the phone through a USB OTG adapter. For boards without native USB, such as the UNO classic and ESP32-CAM, use an FTDI, CP2102, or CH340 USB-serial adapter.
  6. Open the USB OTG tab in the app. The app detects the board and starts streaming IMU data.

Nano 33 BLE Sense + TinyML Kit (IMU + camera)

The nano33ble_ai_kit_otg sketch adds camera capture from the OV7675 on the Arduino TinyML Kit shield. IMU streaming starts on boot. Control the camera with these serial commands, sent from the USB OTG tab or any serial monitor at 115200 baud: Frame sizes: 19 200 bytes (GRAYSCALE 160 × 120) or 38 400 bytes (RGB565 160 × 120).
The Nano 33 BLE connects as a CDC-ACM device, so it doesn’t need a USB-serial adapter. Use BOARD_NANO33BLE for Rev1 (LSM9DS1) or uncomment BOARD_NANO33BLE_REV2 for Rev2 (BMI270 + BMM150). To set the pixel format, edit the CAM_FORMAT define or send g at runtime.

12. Verify your data in Edge Impulse Studio

  1. Open your project in studio.edgeimpulse.com.
  2. Go to Data acquisition. Your samples appear with their labels, timestamps, and sensor axes.
  3. Check the split between training and test data.
  4. Go to Create impulse to design and train your model.

How it works

Architecture diagram connecting phone sensors, BLE, Wear OS, USB OTG, local logging, and Edge Impulse APIs

Data collector system architecture

The app collects data from every connected source, buffers it in CSV files, and uploads it with the ingestion API. All sources go through one DataRepository, so every upload takes the same path.

Data flows

Permissions

The app requests these permissions on first launch. If you deny one, only the feature that needs it stops working.

Troubleshooting

Your API key is missing or wrong. Check that EI_API_KEY is set in ~/.gradle/gradle.properties and rebuild the app. The key must start with ei_.
  • Check that the device is advertising. In a serial monitor, look for Advertising as EI-Monitor.
  • On Android 12 and later, grant BLUETOOTH_SCAN and BLUETOOTH_CONNECT in Settings -> Apps -> Edge Impulse Data Collector -> Permissions.
  • On earlier Android versions, grant ACCESS_FINE_LOCATION, which Android requires for BLE scanning.
  • Check that Bluetooth is on and the device is within about 10 m.
  • Check the Zephyr serial log for BLE GATT server started.
  • Check that the firmware’s GATT service UUID matches the one in GattProfile.kt: 12345678-1234-5678-1234-56789abcdef0.
  • Disconnect and reconnect. The notification subscription sometimes fails on the first connection.
Grant the CAMERA permission in Settings -> Apps -> Edge Impulse Data Collector -> Permissions and try again. If your build is older than 25 May 2026, pull the latest source, which fixes dropped camera frames.
  • Check that you’re using an OTG adapter and that the cable is seated at both ends.
  • Some phones don’t support USB host mode on their USB-C port. Try another OTG adapter or another phone.
  • Let the Arduino finish booting the sketch before you plug it in.
  • The app includes drivers for CP2102, CH340, and FTDI FT232 USB-serial chips. It might not recognize other chips.
  • Check that you uploaded nano33ble_ai_kit_otg.ino or esp32_eye_otg.ino. The other sketches send IMU data only.
  • Tap the camera icon on the USB OTG tab, or send c, to capture a frame. The tab doesn’t stream camera frames continuously.
  • Check that the OV7675 module is seated in the TinyML Kit shield connector.
  • Open a serial monitor at 115200 baud. A # ERROR: camera not available line means the camera failed to start.
Fill in the Edge Impulse label field before you tap Upload stored CSV to Edge Impulse. The CSV file doesn’t store labels.
Install the Wear OS companion on your watch, then pair the watch again in the Wear OS app.
The pairing code and port work once and expire quickly. Close Pair new device on the watch, reopen it to get a new address and code, and pass both on the command line:
If nc -zv <WATCH_IP> <NEW_PORT> succeeds but adb pair still fails, the code has probably expired, so get a new one. If the port is unreachable, the watch and computer are on different networks, or a VPN or firewall is blocking the connection.

Next steps

Resources