Your First BLE Application¶
At the end of this tutorial the IPRO7AI Reef EVK runs the SDK's GATT reference application ipro_ble_remote: a phone can light the on-board RGB LED, receive key notifications and exchange text with the board. You then change the code so the board advertises as IPRO-Hello and beeps when a phone connects, and finally send new firmware to the board over BLE OTA, with no cable.
Prerequisites¶
- An IPRO7AI Reef EVK with SW7 on CAM and SW8 on PWR, powered from USB-C, with an IPRO Debug Probe or a 3.3 V USB-UART cable on U6 (115200 8N1; wiring in IPRO7AI Reef EVK). An OV5640 FLEX-24 camera module and a microSD card are optional, for the photo feature.
- First FreeRTOS Application done: the toolchain is in
/opt/toolchain/riscv_ipro7/andmakebuilds SDK apps. - IntelProCube 0.7.0.1, installed as in Set Up Your Host: the command-line flasher
ipro_iot_tool_macos,ipro_iot_tool_linuxoripro_iot_tool.exe, and the boot2 image that comes with it,chips/ipro7/builtin_imgs/boot2_v0.6.0/ipro_boot2_IPRO7_v0.6.0.bin. - A phone with either:
- Android: the demo app shipped in the app directory,
apps/bluetooth/ble/ipro_ble_remote/ipro_remote_demo.apk. - Android or iOS: a BLE tool app that reads and writes GATT characteristics, such as nRF Connect or LightBlue. The steps below use such a tool.
- Android: the demo app shipped in the app directory,
- For BLE OTA (steps 10 to 12): a macOS or Linux computer with Bluetooth LE, Python 3 and the
bleakpackage.
The Reef build uses 2 MB of flash, boot2 and an A/B partition table
The Reef configuration of ipro_ble_remote, defconfig_reef, links for 2 MB of flash (CONFIG_IPRO7_FLASH_2M=y) and turns on BLE OTA (CONFIG_IPRO_BLE_REMOTE_OTA=y). Flashing must also write boot2 and the app's own A/B table partition_cfg_2M_remote_ab.toml: FW A (0x12000, 824 KB) is the execute slot and FW B (0xE0000, 512 KB) holds the OTA package. build_freertos.sh cannot pass --boot2, so this tutorial runs the flasher directly.
Steps¶
-
Build the Reef configuration. From the SDK root:
DEFCONFIG=leaves.configuntouched and builds inbuild_reef/. A successful build ends with the linker's memory table (the flash row shows2 MB); the output isapps/bluetooth/ble/ipro_ble_remote/build_reef/build_out/ipro_ble_remote_IPRO7.bin. A plainmakewithoutDEFCONFIG=builds the IPRO7 EVB version, which does not run on the Reef. -
Put the Reef in download mode. Hold SW1 (BOOT), tap SW2 (RESET), release SW1. If the board already runs firmware with a shell, typing
reboot_dlat the console does the same. In download mode the console prints nothing. -
Flash boot2, the A/B table and the firmware. From the IntelProCube directory:
./ipro_iot_tool_macos --chipname ipro7 --chip-variant IPRO7AI \ --port /dev/cu.usbserial-XXXX --baudrate 921600 \ --boot2 chips/ipro7/builtin_imgs/boot2_v0.6.0/ipro_boot2_IPRO7_v0.6.0.bin \ --pt <SDK>/apps/bluetooth/ble/ipro_ble_remote/partition_cfg_2M_remote_ab.toml \ --firmware <SDK>/apps/bluetooth/ble/ipro_ble_remote/build_reef/build_out/ipro_ble_remote_IPRO7.bin./ipro_iot_tool_linux --chipname ipro7 --chip-variant IPRO7AI \ --port /dev/ttyUSB0 --baudrate 921600 \ --boot2 chips/ipro7/builtin_imgs/boot2_v0.6.0/ipro_boot2_IPRO7_v0.6.0.bin \ --pt <SDK>/apps/bluetooth/ble/ipro_ble_remote/partition_cfg_2M_remote_ab.toml \ --firmware <SDK>/apps/bluetooth/ble/ipro_ble_remote/build_reef/build_out/ipro_ble_remote_IPRO7.bin.\ipro_iot_tool.exe --chipname ipro7 --chip-variant IPRO7AI ` --port COM5 --baudrate 921600 ` --boot2 chips/ipro7/builtin_imgs/boot2_v0.6.0/ipro_boot2_IPRO7_v0.6.0.bin ` --pt <SDK>\apps\bluetooth\ble\ipro_ble_remote\partition_cfg_2M_remote_ab.toml ` --firmware <SDK>\apps\bluetooth\ble\ipro_ble_remote\build_reef\build_out\ipro_ble_remote_IPRO7.bin--chip-variant IPRO7AIselects the flash pins of the Reef (QFN56). The tool exits with 0 even when it fails, so check the output for[All Success]. With an IPRO Debug Probe, the port is the one whose name ends in02. Use this full command every time you reflash this app so that the A/B layout stays consistent. -
Open the console and reset. Open the U6 port at 115200 baud (see Flashing & Serial Console) and tap SW2 (RESET). boot2 prints first, then the application (the level and time fields at the start of each line are left out):
-
Connect from the phone.
- With the shipped Android app: copy
ipro_remote_demo.apkto the phone and open it to install (allow installs from unknown sources), or runadb install apps/bluetooth/ble/ipro_ble_remote/ipro_remote_demo.apkon your computer. - With a BLE tool app: scan for
IPRO-Remoteand connect.
The console prints:
- With the shipped Android app: copy
-
Use the GATT services. In the BLE tool app, find these characteristics:
Action Characteristic Result on the board Write 01LED State 0xFF11The WS2812 LED lights (white by default); the console prints [LED] State: ONWrite 00 FF 00LED Colour 0xFF81The LED turns green (3 bytes: R, G, B) Enable notifications, press SW3 Button State 0xFF21The phone receives 01(press) and00(release)Enable notifications UART TX 0xFF41The phone receives Welcome to IPRO Remote!Write the text helloUART RX 0xFF42The console prints [UART RX] hello; the phone receivesEcho: helloWrite E8 03 2C 01 1ETone 0xFF84The speaker (or headphones in J2) plays 1 kHz for 300 ms (frequency and duration are little-endian u16, the last byte is the volume in %) A short press on SW1 (BOOT) makes the app take a photo (camera required; with a microSD card it is also saved under
sd:/REMOTE/), so test the keys with SW3, SW4 or SW5. The console commandreef statusshows the board, LED, camera and temperature state. -
Change the code. Edit three places in
apps/bluetooth/ble/ipro_ble_remote/src/app/remote_app.c.The top of the file already includes
remote_reef.hinside aCONFIG_IPRO7AI_REEFblock. Addreef_hw.h, which declaresreef_hw_tone(), below it:src/app/remote_app.c#if defined(CONFIG_IPRO7AI_REEF) #include "remote_reef.h" #include "reef_hw.h" #endifChange the advertised name:
In
connected_cb(), add the beep after theConnected! Peerlog line:src/app/remote_app.cIPRO_LOGI(LOG_TAG, "Connected! Peer: %s", addr_str); #if defined(CONFIG_IPRO7AI_REEF) /* 1 kHz, 150 ms beep on the speaker and headphone jack when a phone connects */ (void)reef_hw_tone(1000, 150, 30); #endifreef_hw_tone()only queues the request for the task that owns the Reef hardware and returns, so it is safe to call from a BLE connection callback. The app compiles with-Werror: any warning fails the build. -
Rebuild and flash. Run the build command from step 1 again (only the changed file recompiles), type
reboot_dlat the console and flash with the command from step 3. -
Connect again. After tapping SW2, the console prints
REMOTE_APP: [BLE] Advertising: IPRO-Hello. Scan again on the phone and connect toIPRO-Hello; the Reef beeps as the connection comes up. -
Set up the OTA tool. From here on, updates go over BLE and U6 is not needed. In the SDK root, create a Python virtual environment with
bleak:Disconnect the phone first: the Reef build allows one connection only (
CONFIG_BT_CONN=1). -
Build an OTA package. Make any small change (for example, change the beep to
reef_hw_tone(2000, 150, 30)), rebuild (step 1), then package the build as a boot2 OTA package. The script has IntelProCube build the combined image and compresses it with the SDK'stools/create_ota.py. It takes the newest boot2 in the IntelProCube folder (v0.6.0 in 0.7.0.1):Output:
apps/bluetooth/ble/ipro_ble_remote/build_reef/build_out/ipro_ble_remote_reef.xz.ota(about 345 KB). OTA accepts only this.xz.otapackage, never the.bin. -
Send it over BLE and check.
reef_ota.pylooks for a device namedIPRO-Remoteby default; the board has been renamed, so setREMOTE_NAME:REMOTE_NAME=IPRO-Hello .venv/bin/python apps/bluetooth/ble/ipro_ble_remote/tools/reef_ota.py \ apps/bluetooth/ble/ipro_ble_remote/build_reef/build_out/ipro_ble_remote_reef.xz.otaThe tool prints the board's current version, the package size and CRC32, the transfer time and
verified, slot switched; rebooting; after the board restarts it connects again and prints the new version:running: <camera, SD card and build time> ipro_ble_remote_reef.xz.ota: <bytes> bytes, CRC32 <value>, <payload> bytes/packet sent in <seconds> s (<rate> KB/s) verified, slot switched; rebooting running: <camera, SD card and build time>Meanwhile the board's console prints
[OTA] START: …,[OTA] CRC32 OK! Switching partition...and[OTA] Ready to reboot, and on the restart boot2 prints[BOOT2] IPRO7 Boot2 v0.6.0and[BOOT2] OTA pending, checks the package's SHA256, unpacks it into FW A and prints[BOOT2] OTA okbefore it starts the app. On a chip without a signing key in eFuse, an extra line[OTA] no eFuse key, package unverifiedsays that the signature is not checked. The build time afterrunning:comes fromsrc/board/reef_hw.c, so it does not change when you only editremote_app.c. INTELPRO measured 13 to 17 s for a 345 KB package on macOS with MTU 247. The shipped Android demo app can send the same.xz.otapackage.
Do not OTA with the standard partition table
On the Reef, OTA works only with partition_cfg_2M_remote_ab.toml. If the board was flashed with IntelProCube's standard partition_cfg_2M.toml, an OTA writes its FW B (0x12A000) and switches to it, after which the board prints nothing at all and can only be recovered by reflashing in download mode.
Verify¶
- The console's advertising line shows
IPRO-Hello, and so does the phone's scan list. - The board beeps when the phone connects, and the console prints
Connected! Peer: …. - The LED, key and UART operations from step 6 still work.
- The console prints
[BOOT2] OTA okon the restart, and on the next connection you hear the beep you changed in step 11.
The Device Name: 'IPRO-Remote' in the main.c boot banner is a fixed string and does not affect the advertised name.
How it works¶
main.c initialises the platform and creates the BLERemote task, which calls remote_app_init(). BLE starts in a fixed order: the controller task, then the HCI driver, then the Zephyr host (from src/app/remote_app.c):
err = ipro_ble_ctlr_controller_init(configMAX_PRIORITIES - 1); /* IPRO BLE controller */
vTaskDelay(pdMS_TO_TICKS(50));
err = hci_driver_ipro_ble_ctlr_init(); /* HCI driver */
vTaskDelay(pdMS_TO_TICKS(50));
err = bt_enable(bt_ready_cb); /* Zephyr host */
Once the host is ready, bt_ready_cb() calls bt_set_name(DEVICE_NAME), registers the connection callbacks with bt_conn_cb_register(&conn_callbacks), initialises the GATT services and starts connectable advertising. The services are defined statically with BT_GATT_SERVICE_DEFINE() in src/services/remote_services.c. The advertising data and parameters are below; DEVICE_NAME feeds both the advertising data and bt_set_name():
static const struct bt_data ad[] = {
BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
BT_DATA(BT_DATA_NAME_COMPLETE, DEVICE_NAME, DEVICE_NAME_LEN),
};
struct bt_le_adv_param adv_param = {
.id = BT_ID_DEFAULT,
.options = BT_LE_ADV_OPT_CONN,
.interval_min = 160, /* 100ms (N * 0.625ms) */
.interval_max = 320, /* 200ms */
};
int err = bt_le_adv_start(&adv_param, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd));
The Reef build adds two services: OTA 0xFF60 (control 0xFF61, data 0xFF62, status 0xFF63) and the Reef hardware service 0xFF80 (LED Colour 0xFF81, Key Event 0xFF82, Sound Level 0xFF83, Tone 0xFF84, Board Info 0xFF85). The LED, key, speaker, SD card and camera drivers are the out-of-box firmware's sources in apps/platform/ipro7ai_reef_demo/src, used in place; src/board/reef_hw.c is the only task that touches that hardware, and the BLE callbacks only post requests to it.
Troubleshooting¶
| Symptom | Cause | Fix |
|---|---|---|
No [All Success] in the flasher output, or flash jedec id: 00000000 |
The board is not in download mode, or the flash pins are wrong | Enter download mode first; check that the command has --chip-variant IPRO7AI |
| Flashing succeeds but the console stays silent | boot2 and the A/B table were not written, or the baud rate is wrong | Reflash with the full command from step 3; set the console to 115200 |
The phone still shows IPRO-Remote |
The phone cached the old name | Check that the console advertises IPRO-Hello, then turn the phone's Bluetooth off and on and scan again |
| No LED or no beep | The WS2812 and the speaker amplifier run from USB VBUS; headphones in J2 mute the speaker | Power from USB-C, not from 3.3 V alone; unplug the headphones or listen on them |
| The build fails on a warning | The app compiles with -Werror |
Keep #include "reef_hw.h" inside the CONFIG_IPRO7AI_REEF block and the call inside the function |
make_reef_ota.sh prints IntelProCube tool not found |
IPRO_FLASH_TOOL is unset and the flasher is not on PATH |
Set IPRO_FLASH_TOOL=<IntelProCube>/ipro_iot_tool_macos (or _linux) |
make_reef_ota.sh prints build first: … |
There is no build in build_reef/ yet |
Run step 1 first |
make_reef_ota.sh prints boot2 image not found |
The IntelProCube folder that IPRO_FLASH_TOOL points to has no chips/ipro7/builtin_imgs/boot2_v* |
Point IPRO_FLASH_TOOL at the tool in an IntelProCube 0.7.0.1 folder, or set IPRO_BOOT2 to a boot2 .bin |
reef_ota.py prints IPRO-Remote not found |
The board was renamed, a phone is connected, or the computer's Bluetooth is off or not allowed | Set REMOTE_NAME=IPRO-Hello; disconnect the phone; on macOS, allow the terminal to use Bluetooth |
reef_ota.py prints device error … |
The board rejected the package or a write failed | Check that you sent the .xz.ota; read the [OTA] lines on the console |
| The board does not come back after OTA | When boot2 rejects a package it stays in its USB rescue mode | Enter download mode and reflash with the command from step 3 |
Next steps¶
- BLE Throughput Test: measure the data rate between two Reef boards.
- Getting Started with LE Audio: run the LE Audio headset example on the Reef.
- BLE Development: controller library selection, host configuration and APIs.
- OTA Updates: partition tables, the OTA package format and boot2's update flow.