働きたくない

5000兆円欲しい

Particle.io Argonで詰まった点

Particle.ioで購入できるParticle ArgonというWi-Fi機能付きマイコンボード*1と最近格闘している。そのうち、悩んだりした点をメモとして残す。

目次

Firmwareをv1.2.1→v.1.4.1へとアップデート後、再起動する度Wi-Fi設定が保存されず消える

1日くらい原因が分からず困っていた。

【症状】

  • Particle CLIから$particle updateで更新したが、その後再起動するたびにWi-Fi設定が保存されなくなってしまう

【原因】

  • Bootloaderかsoftdeviceの更新漏れ
  • 確認方法としては、Particle CLI$particle serial inspectを実行。FAILした項目について修正

【対処法】

  • Device-OSのgithubからbootloader等の.binファイルを取得
  • Particle CLIparticle flash --serial <bootloader>.binで書き込み(githubのRelease Noteに方法が記載されている)

(なぜ$particle updateでbootloaderが更新されなかったのかは分からないが)

【その他】

  • ダウングレードする際は、先にUser applicationもdowngradeした後のOSバージョンで動くようコンパイル・書き込みしておくべき?

v1.2.1→v1.4.1へとアップデート後、bootloader等を更新する前後の$particle serial inspectの結果を、以下に示す。Dependencyで示されたversionと、Bootloaderのバージョンがズレていたことが分かる。

D:\Users\\Desktop\1.2.1\argon\release>particle serial inspect
Platform: 12 - Argon
Modules
Bootloader module #0 - version 311, main location, 49152 bytes max size
Integrity: PASS
Address Range: PASS
Platform: PASS
Dependencies: PASS
System module #1 - version 1404, main location, 671744 bytes max size
Integrity: PASS
Address Range: PASS
Platform: PASS
Dependencies: FAIL
Bootloader module #0 - version 501
Radio stack module #0 - version 202

User module #1 - version 6, main location, 131072 bytes max size
UUID: DA2E920A883A1D6D53FCE0F96C8A9C148C3DD980989964EAC610D336964C1CFA
Integrity: PASS
Address Range: PASS
Platform: PASS
Dependencies: PASS
System module #1 - version 1213
NCP module #0 - version 5, main location, 1536000 bytes max size
Integrity: PASS
Address Range: PASS
Platform: PASS
Dependencies: PASS
Radio stack module #0 - version 202, main location, 192512 bytes max size
Integrity: PASS
Address Range: PASS
Platform: PASS
Dependencies: PASS
D:\Users\\Desktop\1.2.1\argon\release>

C:\Users\>particle serial inspect
Platform: 12 - Argon
Modules
Bootloader module #0 - version 501, main location, 49152 bytes max size
Integrity: PASS
Address Range: PASS
Platform: PASS
Dependencies: PASS
System module #1 - version 1404, main location, 671744 bytes max size
Integrity: PASS
Address Range: PASS
Platform: PASS
Dependencies: PASS
Bootloader module #0 - version 501
Radio stack module #0 - version 202

User module #1 - version 6, main location, 131072 bytes max size
UUID: DA2E920A883A1D6D53FCE0F96C8A9C148C3DD980989964EAC610D336964C1CFA
Integrity: PASS
Address Range: PASS
Platform: PASS
Dependencies: PASS
System module #1 - version 1213
NCP module #0 - version 5, main location, 1536000 bytes max size
Integrity: PASS
Address Range: PASS
Platform: PASS
Dependencies: PASS
Radio stack module #0 - version 202, main location, 192512 bytes max size
Integrity: PASS
Address Range: PASS
Platform: PASS
Dependencies: PASS
C:\Users\>

電源を入れるとListening modeに入る

自分が知りうる限りでは以下のどちらかが原因。

  1. ネットワーク設定がおかしい(→スマフォのアプリから設定)
  2. setupが完了したフラグが設定されていない(→下で解説)

2.だが、基本的には以下の公式ページを参照。

docs.particle.io

要点としては、基本的にはsetup完了フラグがupdate時に付与されるが、ならない時もたまにある(手動アップデートした場合等)。ページにあるようなプログラムを一度書き込めば解消する。

その他

  • ArgonのLi-Poコネクタは恐らくJST PHコネクタ(2mmピッチ)
  • WebIDEよりParticle CLIの方が扱いやすい(WebIDEでは、コンパイルメッセージが操作をすると消えてしまうが、Particle CLIではそうはならない。また、ファイルを複数使用可能)
  • Listening-modeのon/off等、モードの切替は$particle usbコマンドで可能
  • Serial通信を見る際は$particle serial monitor
  • Wi-Fi設定は$particle serial wifi
  • $particle doctorはMesh device(Argon等)に対応していない、らしい

Stringのc_str()を誤って使っていた

ParticleのStringがstd::string(std::basic_string)なのかは知らないが、次のようなコードを書いてしまって上手くcloudに関数が登録されていなかった。

 int register_to_cloud(){
        unsigned int retval = 0;
        bool is_success = false;

        // setctrl<number>の登録
        const char* name1 = (String("setctrl") + String(this_class_identify_id)).c_str();
        Log.info("register_to_cloud() try : %s", name1);
        is_success = Particle.function(name1_s, &Channel::change_controllability, this);
        
        retval |= static_cast<unsigned int>(is_success);
        
        if (!is_success){
            Log.info("register_to_cloud() failed : %s", name1_s);
        } else {
            Log.info("register_to_cloud() succeeded : %s", name1_s);
        }
}

Ideoneで再現すると次のような感じ。文字列"Hello world"が正しく表示されない:https://ideone.com/HFXHPH

【原因】

  • 一時オブジェクト(String)のメンバ変数へのポインタを使っていた

詳細

  • (String("setctrl") + String(this_class_identify_id))は一時オブジェクトであり、その寿命はconst char* name1の代入式だけ
  • 一方、(少なくとも)std::basic_stringのc_str()は、privateなメンバ変数である配列へのポインタを返す(下記ソースコード参考)*2
  • そのため、代入式以降はname1の参照先がStringの文字列と一致するとは限らない*3

【参考】

// 21.3  Template class basic_string
template<typename _CharT, typename _Traits, typename _Alloc>
class basic_string
{
       typedef typename _Alloc::template rebind<_CharT>::other _CharT_alloc_type;

       // Use empty-base optimization: http://www.cantrip.org/emptyopt.html
       struct _Alloc_hider : _Alloc
       {
         _Alloc_hider(_CharT* __dat, const _Alloc& __a)
         : _Alloc(__a), _M_p(__dat) { }
         
         _CharT* _M_p; // The actual data.
       };
       
     private:
       // Data Members (private):
       mutable _Alloc_hider  _M_dataplus;
       
       _CharT*
       _M_data() const
       { return  _M_dataplus._M_p; }
       
       _CharT*
       _M_data(_CharT* __p)
       { return (_M_dataplus._M_p = __p); }

       // String operations:
       /**
       *  @brief  Return const pointer to null-terminated contents.
       *
       *  This is a handle to internal data.  Do not modify or dire things may
       *  happen.
       */
       const _CharT*
       c_str() const
       { return _M_data(); }
};

*1:ボードだけでなく、ライブラリ・コンパイラ等も用意されているので、Arduinoのようなもの。日本だとマイナーか

*2:よく知らないが、C++の標準ライブラリの実装はコンパイラ毎に違うんだろうか

*3:よく知らないが、一時オブジェクトはヒープ上に作られる? ヒープは再利用されうる? とすると適当な時に上書きされる?

*4:どの本に載っているのだろう