2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

nRF52のDFU試験2

Last updated at Posted at 2020-09-26

概要

NordicのBLEモジュールnRF52のファームウェアを、アプリを使用して無線で更新(OTA)します。

前回は1回更新すると、2回目の更新ができなくなる問題があったので、繰り返し更新できるサンプルプログラムを活用します

(追記)DFUサービス追加方法

既存のアプリケーションにDFUサービスを追加する手順を紹介したサイトがありました。
Keilのプロジェクトを対象にしていますが、SESにも問題なく適用できます。
https://www.codetd.com/ja/article/10955888

(2021/6/25 上記サイトが消えていたので、手順を以下に記します。)

1. ファイルの追加
以下のファイルをプロジェクトに追加します

  • components\ble\ble_services\ble_dfu\ble_dfu.c
  • components\ble\ble_services\ble_dfu\ble_dfu_bonded.c
  • components\ble\ble_services\ble_dfu\ble_dfu_unbonded.c
  • components\libraries\bootloader\dfu\nrf_dfu_svci.c

2. インクルードファイルの設定追加
以下のPathをビルドのインクルードファイル設定に追加します

  • Libraries\components\libraries\svc
  • Libraries\components\libraries\bootloader
  • Libraries\components\libraries\bootloader\dfu
  • Libraries\components\libraries\bootloader\ble_dfu
  • Libraries\components\ble\ble_services\ble_dfu
  • Libraries\components\softdevice\mbr\nrf52832\headers

3. ビルドDefine設定追加

  • BLE_SETTINGS_ACCESS_ONLY
  • NRF_DFU_SVCI_ENABLED
  • NRF_DFU_TRANSPORT_BLE = 1

4. sdk_define.hの設定

# ifndef BLE_DFU_ENABLED
# define BLE_DFU_ENABLED 1
# endif
# ifndef NRF_SDH_BLE_VS_UUID_COUNT 
# define NRF_SDH_BLE_VS_UUID_COUNT 2
# endif

5. メモリ

分かりにくいのは、UUIDを増やした分のRAMサイズ修正でした。
例えば、UUIDを1つ増やしたのに対応して、RAMサイズを0x20だけ増やす時、RAM STARTを0x20002270から0x20002290にします。

6. コードの修正

  • ヘッダーファイルの追加
# include "nrf_power.h"
# include "nrf_bootloader_info.h"
# include "nrf_dfu_ble.h"
# include "ble_dfu.h"
# include "nrf_dfu_ble_svci_bond_sharing.h"
# include "nrf_svci_async_function.h"
# include "nrf_svci_async_handler.h"
  • dfu
/**@brief Function for handling dfu events from the Buttonless Secure DFU service
 *
 * @param[in]   event   Event from the Buttonless Secure DFU service.
 */
static void ble_dfu_evt_handler(ble_dfu_buttonless_evt_type_t event)
{
    switch (event)
    {
        case BLE_DFU_EVT_BOOTLOADER_ENTER_PREPARE:
        {
            NRF_LOG_INFO("Device is preparing to enter bootloader mode.");

            // Prevent device from advertising on disconnect.
            ble_adv_modes_config_t config;
            advertising_config_get(&config);
            config.ble_adv_on_disconnect_disabled = true;
            ble_advertising_modes_config_set(&m_advertising, &config);

            // Disconnect all other bonded devices that currently are connected.
            // This is required to receive a service changed indication
            // on bootup after a successful (or aborted) Device Firmware Update.
            uint32_t conn_count = ble_conn_state_for_each_connected(disconnect, NULL);
            NRF_LOG_INFO("Disconnected %d links.", conn_count);
            break;
        }

        case BLE_DFU_EVT_BOOTLOADER_ENTER:
            // YOUR_JOB: Write app-specific unwritten data to FLASH, control finalization of this
            //           by delaying reset by reporting false in app_shutdown_handler
            NRF_LOG_INFO("Device will enter bootloader mode.");
            break;

        case BLE_DFU_EVT_BOOTLOADER_ENTER_FAILED:
            NRF_LOG_ERROR("Request to enter bootloader mode failed asynchroneously.");
            // YOUR_JOB: Take corrective measures to resolve the issue
            //           like calling APP_ERROR_CHECK to reset the device.
            break;

        case BLE_DFU_EVT_RESPONSE_SEND_ERROR:
            NRF_LOG_ERROR("Request to send a response to client failed.");
            // YOUR_JOB: Take corrective measures to resolve the issue
            //           like calling APP_ERROR_CHECK to reset the device.
            APP_ERROR_CHECK(false);
            break;

        default:
            NRF_LOG_ERROR("Unknown event from ble_dfu_buttonless.");
            break;
    }
}

/**@brief Function for initializing services that will be used by the application.
 */
static void services_init(void)
{
    ret_code_t err_code;
    ble_smartlock_init_t smartlock_init;
    nrf_ble_qwr_init_t qwr_init = { 0 };
    ble_dfu_buttonless_init_t dfus_init = { 0 };

    // Initialize Queued Write Module.
    qwr_init.error_handler = nrf_qwr_error_handler;

    err_code = nrf_ble_qwr_init(&m_qwr, &qwr_init);
    APP_ERROR_CHECK(err_code);

# if defined(CHARI_WITHOUT_DFU)
# else
    // Initialize DFU.
    dfus_init.evt_handler = ble_dfu_evt_handler;

    err_code = ble_dfu_buttonless_init(&dfus_init);
    APP_ERROR_CHECK(err_code);
# endif

    // Initialize the Test Service.
    memset(&smartlock_init, 0, sizeof(smartlock_init));
    smartlock_init.smartlock_write_handler = smartlock_write_handler;

    err_code = ble_smartlock_init(&m_smartlock, &smartlock_init);
    APP_ERROR_CHECK(err_code);
}
int main(void)
{
    ret_code_t err_code;

    // Initialize.
# if defined(CHARI_WITHOUT_DFU)
# else
    // Initialize the async SVCI interface to bootloader before any interrupts are enabled.
    err_code = ble_dfu_buttonless_async_svci_init();
    APP_ERROR_CHECK(err_code);
# endif

    cpu_init();

7. ビルド

必要なzipファイルを生成するスクリプトは、以下のGitHubで公開されているサンプルプログラムが参考になります。
プロジェクトは、そのまま使用できるので、DFUを実際に試すことができます。

環境

  • OS
    • Windows 10
  • ソフト:
    • Cygwin
    • Anaconda3
    • SES
  • SDK
    • 16.0.0
  • Softdevice
    • s140_nrf52_7.0.1
  • BLEモジュール
    • nRF52840

サンプルプログラムのビルド

手順はは前回と同じです。
今回は使用するサンプルプログラムだけが異なります。

SESにおいて作業します。
nRF52840に対応したサンプルプログラムを開きます。

nRF5SDK160098a08e2\examples\ble_peripheral\ble_app_buttonless_dfu\pca10056\s140\ses\ble_app_buttonless_dfu_pca10056_s140.emProject

ビルドに成功すると
nRF5SDK160098a08e2\examples\ble_peripheral\ble_app_buttonless_dfu\pca10056\s140\ses\Output\Release\Exeにble_app_buttonless_dfu_pca10056_s140.hexが生成されます。

前回との違いはこれだけです。

次は、前回と同じようにzipファイルを生成します。
ただし、今回のサンプルプログラムにNRF_DFU_HW_VERSIONに相当するものはないので、--hw-versionは任意の値で問題ありません。

nrfutil pkg generate --hw-version 1 --application-version 1 --application ble_app_buttonless_dfu_pca10056_s140.hex --sd-req 0xCA --key-file ./private.key  ble_app_buttonless_dfu_pca10056_s140.zip

iPhoneでDFUを実行すると、前回と同じようにDfuTargを選択し、生成したble_app_buttonless_dfu_pca10056_s140.zipで更新します。

前回と異なるのは、再度DFUを実行してファームウェア更新可能なことです。

今回のサンプルプログラムにはセキュアブート機能が実装されているので、何回でも更新できます。
(もちろん、セキュアブート機能のないファームウェアを更新すれば、それ以降は更新できなくなります。)

次にすること

簡単な紹介になりましたが、これで前回の最大の問題点、繰り返し更新できない、は解決しました。

このサンプルプログラムを元に修正すれば、オリジナルのBLEサービスを実装したり、ハードウェアバージョンの追加もできるはずです。

ですが、私のしたいことはサーバを介したファームウェア更新なので、このサンプルプログラムの解析は行いません。

サーバを介したファームウェア更新をするために、次回はBackground DFUについて調べます。

(追記)
既存のBLEアプリケーションにDFU機能を追加する方法を解説した記事を見つけました。

nrf52832によって開発されたDFUサービスを追加

また、以下の記事とGithubもDFU機能を追加する際の参考になります。
nRF52でBLEデバイスを開発する(2)OTAアップデート Buttonless DFUとSecure DFU

(追記)
DFUとアプリケーションのUUIDが同じになってしまう現象が確認できました。
これは不便なので、以下のようにアプリケーションUUIDを変更するとDFUのUUIDとアプリケーションのUUIDが区別できます。

変更前

static void advertising_init(void)
{
    ret_code_t             err_code;
    ble_advertising_init_t init;

    ble_uuid_t m_adv_uuids[] =
    /**< Universally unique service identifiers. */
    {
        {APPLICATION_UUID_SERVICE, BLE_UUID_TYPE_VENDOR_BEGIN  }
    };

変更後

static void advertising_init(void)
{
    ret_code_t             err_code;
    ble_advertising_init_t init;

    ble_uuid_t m_adv_uuids[] =
    /**< Universally unique service identifiers. */
    {
        {APPLICATION_UUID_SERVICE, BLE_UUID_TYPE_VENDOR_BEGIN+1  }
    };

(追記)sdk_configの設定

ボタンレスDFUモードを使用する際はsdk_config.hの以下の設定を変更しないといけません。

# define NRF_BL_DFU_ENTER_METHOD_BUTTON 0
# define NRF_BL_DFU_ENTER_METHOD_BUTTONLESS 1
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?