2015年12月30日

[esp8266]HTTP GETする

検索して出てきたESP8266のサンプルを見ながら、HTTP GETするサンプルソースを書いた。

https://github.com/hirokuma/esp8266_httpget

 

user_init()がメイン関数代わりというわけではないんだな。
別のサンプルがタスクを使うタイプだったから、WiFiのイベントとタイマや通信、GPIOなどの割込みでタスクで回していくような考え方でよいのかな。

また、UART0が自分が出したメッセージ以外にもいろいろ出しているのがわかった。
他の機器と通信するなら、system_uart_swap()とかos_install_putc1()を使うか、system_set_os_print()を使うかするのが良いのか。

 

しかし、こういうSSIDやパスワードのような自分の環境に関する情報をソースに埋め込むタイプは、githubとかに置くときやログを載せるのに苦心するな。


追記:

espconn_sent_callback()というAPIを呼んでみたが、これはたぶん間違いだ。
https://github.com/hirokuma/esp8266_httpget/blob/master/user/user_main.c#L135

PDFに、espconn_send()の注記としてこう書いてあったのだ。

Please call espconn_send after espconn_sent_callback of the pre-packet.

私はこれを「espconn_sent_callback()のあとでespconn_send()を呼んでくれ」と解釈した。
でも、espconn_sent_callbackは関数じゃなくて、typedefした型名のようだ。
使っているのは、espconn_regist_sentcb()。
recvcb()があるのにsentの方はAPI名が違うんだなー、などとのんびり考えていたのだけど、ちゃんとあったのだ。

いま、手元に環境が無いので確認できないけど、たぶんそうだろう。

2015年12月29日

[esp8266]ICACHE_FLASH_ATTR?

SDKのサンプルを見ていると、ICACHE_FLASH_ATTRというマクロをしばしば使っている。
関数定義のところで使われている。
さて、なんだろうか?
名前からすると、FLASHの命令キャッシュに保存してよい、みたいな感じがするが。

気にしているのは、FLASHがSPIのところだ。
SPIってことは、シリアル。
データバスでアクセスするわけじゃないので、どこか一度RAM上に展開しないと実行できないのがノイマン型コンピュータの宿命だと思う。
メモリマップを見ると、iromはだいたい1MBまでになっているから、ESP8266の内蔵RAMが1MBあって、起動時に全部読込んでいるのでは?という予測をしていたのだ。

関数ごとにキャッシュに入れるとか入れないとかいう指定ができるなら、なんか関係していそうだ。


"ICACHE_FLASH_ATTR" macro - ESP8266 Developer Zone

FAQだった。

 

  • ICACHE_FLASH_ATTR付きの関数はコンパイル時にiromセクションに割り当てられる。
    CPUは必要なときにFLASHから関数コードを読み出す。
  • ICACHE_FLASH_ATTR無しの関数は起動時にiramセクションに読込まれる。

全部のコードをiramに置くことはできないので、FLASHに置いたままでも良い関数にはICACHE_FLASH_ATTRを付けてくれ。
でも、割り込みハンドラには付けるなよ。

 

とのこと。
CPUが自動的にFLASHから読み出すんだ。
読み出したコードは、実行したらすぐ捨てられるんだろうか?

リンカスクリプトを見ると、iramのサイズは0x8000だから、32KB。
割り込みハンドラや、あまり処理時間を掛けたくないところはそこに置くと良いのかな。

[esp8266]Non-OSのHello World

Linux環境でやることに決めたので、自分で書いてみよう。
前回とほぼ同じになるが、”Hello World”を115200bpsで出力するのだ。

https://github.com/hirokuma/esp8266_helloworld

 

最初は、user_init()を抜けていたのだけど、そうするとステーションモードになりそうなログを出していたので、while()で止めるようにした。

それだけだと寂しいので、system_deep_sleep()で眠っておいてもらおうとしたのだが、どうもWDTが作動して再起動してしまうようだ(再起動するときも115200bps設定のままになるためか、最初のログがちゃんと見える)。
なので、芸はないけれども、system_soft_wdt_feed()でWDTを起こすようにした。

Programming Guideを見ると、wdt_stop()もあったのだが、6秒以上止めるとHardware WDTでのリセットが作動するらしい。
じゃあ、API名がsoft_wdtと書いてあるけど、実際にはhardwareのWDTを触っているのか?
HDKのドキュメントを検索したけど、WDT関連を見つけられなかった。
うーん、気持ち悪いな。


ちなみに、起動時のログはこういうのを吐き出しているようだ。

 

ets Jan  8 2013,rst cause:1, boot mode:(3,5)

load 0x40100000, len 1396, room 16
tail 4
chksum 0x89
load 0x3ffe8000, len 776, room 4
tail 4
chksum 0xe8
load 0x3ffe8308, len 540, room 4
tail 8
chksum 0xc0
csum 0xc0

2nd boot version : 1.4(b1)
  SPI Speed      : 40MHz
  SPI Mode       : QIO
  SPI Flash Size & Map: 32Mbit(1024KB+1024KB)
jump to run user1 @ 1000

 

boot_v1.4はFLASHの先頭に焼いているのだが、これは2ndブートになるんだな。
ドキュメントには「プログラムできる内蔵ROMはない」とあるので、そこに1stブートがあるのだろう。
ということは、最初に出てくるログは止めようがないんだ。。。

image

[esp8266]Windowsでビルドするのはあきらめた

前の記事で、cygwin(+Arduino環境用GCC)でビルドしたuser1.binは実行できず、Linuxでビルドしたuser1.binは動いた、という結果に終わった。

それだけでは、考察が甘いだろう。
何がダメだったのかの理由まで調べておきたい。
ただ、本職じゃないので、ありがちなところだけチェックしておこう。


まずは、GCCのバージョンから見ていく。

  • Linux
    gcc version 4.8.2 (crosstool-NG 1.20.0)
  • cygwin+Arduino (xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2)
    gcc version 4.8.2 (GCC)
  • esp8266-gcc5.2.0-r5.exe
    gcc version 5.2.0 (GCC)

うーん、「まずは」と書いているが、実はここに期待していたのだ。
何かしらバージョンが違うことによって、どこかバイナリに食い違いが出たんじゃなかろうか、と。

当てが外れた。
ひとまず、5.2.0を評価するのはやめよう。


cygwinでビルドしたときのbinと、Linuxでビルドしたときのbinは、バイナリのくせにあまり違いがない。
なんとなくだが、数字の違いを見ていると、アラインメントに関係するような気がした。
04とか、10とか、そういう数字に違いが多いからだ。
違うかなぁ。

地道に生成しているファイルを比較したところ、オブジェクトファイルの段階で差があった。
readelfすると、Linuxではアラインメントが1なのに、cygwinでは4であつかっているところがある。
全部ではないのだが、ところどころ見られる。

やはりコンパイラをArduinoから間借りするというのがよくなかったか。


じゃあ、ちゃんとインストールした5.2.0だったらうまくいくかと思ったが、コンパイル中にあれこれ関数定義がないと言われる(ets_memcpyとか)。
インクルードが足りないのかと思ったが、インストールした5.2.0の中にもSDKがまるまる入っているなど、なんかいろいろ考えるところが発生しそうだ(そっちの環境も、それはそれでmake関係のエラーが出たり)。

 

うん、もう、めんどくさいからWindowsでビルドするのはあきらめよう。
ここでがんばる時間がもったいない。

2015年12月28日

[esp8266]UARTを115200bpsにする

RTOS版を保留して、まずはnon-OS版でビルドすることにした。
以前、ATコマンドのサンプルはビルドして動かしたことがあるので、もう少しシンプルなものにしたい。

選んだのは、smart_config。
何をするのかはわからないけど、最初にos_printf()とあるから、文字列をシリアル出力するところが見えればいいだろう。

ビルドして、焼くが、なんかシリアル出力がゴミ。
そういえば、あまり普通じゃない速度がデフォルトだったよな、と思って検索すると、デフォルトは74880bpsなので、uart_init()で変更すればよいとのこと。

ふむふむとuart_init()を呼び出したのだが、リンクエラー。
UartBautRate(Baud、じゃないんだ)というenum定義っぽいので、includeを検索するが出てこない。
なんだこれは?

 

調べた結果、uart_init()はexamples/driver_libにいることがわかった。
気付かんわ。。。
中身を自分のプロジェクトにコピーして、Makefileを書き換えるのだ。
そしたら、ちゃんと115200bpsで出力された。

なお、デフォルトではUART0に出力するようになっていて、WROOM-02なのかスイッチサイエンスさんのボードがそうなのかはわからんが、ピンのRX/TXで出ているのはUART0なので、気にせず使えるようになっている。

 

たぶん、コンパイルオプションでwarningがあまり出ないようになっているので、-Wallくらいは追加したいな。
また、使っていない関数をリンクから外したいのだけど、大丈夫かな?

 

それよりも今回の大きな発見は、cygwinでビルドしたものを焼いてもうまく動かなかった、ということだった。
こっちを使った方がよいのかな? 次回試そう。
http://gnutoolchains.com/esp8266/

[esp8266]ESP8266_RTOS_SDKのexamplesをcygwinでビルド (4)

バージョンのことはひとまず置いて、ビルドのことに戻ろう。

W-ROOM02は、4MBのSPI-FLASHがつながっている。
SDKのUser Manualでは、4MB時のメモリマップが2つ用意されている。
FOTAありと、FOTAなしだ。

FOTAあり

image

 

FOTAなし

image

 

Irom0text.binは、正式名称「eagle.irom0text.bin」で、実行するコードが入ったものだろう。
Flash.binは、正式名称「eagle.flash.bin」で、なんだろう? 本番アプリを起動するためのスタートアップ処理みたいなものか。

ドキュメントの最後の方にアドレスが書いてある。
4MB時のも書いてあるのだが、FOTAありのアドレスは512KB+512KBのときのアドレスだろう。

FOTAありの方は、user2.binが0x8_1000になってるけど、これって512KBのとこだから、説明と合わないなあ。
たぶん、user2.binは0x10_0000からで、master_device_key.binは0xF_E000からになるんじゃなかろうか。
リンカスクリプトのeagle.app.v6.new_2048.ldだと、そんな感じに見える。

しかし、リンカスクリプトの方はシステムパラメータの領域が0x0F_0000と0x1F_B000からになっている(説明図が)。
ちょっと領域が広いし、配置は1MBの最後になっているから、esp_init_data_default.binのアドレスと合わない。
うーん・・・。
ドキュメントが間違っているのか、スクリプトが間違っているのか、RTOS版はそういうものなのか・・・。

 

うーん、RTOSを使わないといかんほど、複雑なことをしなければよいんじゃないか?とすれ思い始めてしまった。
いや、そういう後ろ向きの考えじゃいかんな。
まずは、素のSDKで作ってみるべきじゃなかろうか?
うんうん、それがいいそれがいい!

[esp8266]ESP8266_RTOS_SDKのexamplesをcygwinでビルド (3)

あやふやな情報で進めてしまったのを反省し、ドキュメントをちゃんと読もうと思う。

 

最新のSDKは、こちら。
[Latest Release] ESP8266 SDK - ESP8266 Developer Zone

ドキュメント一覧は、こちら。
Complete Listing of Documentation Released (文档目录清单) - ESP8266 Developer Zone

SDKのドキュメントは、こちら。
[Document list 2A-2C] ESP8266 SDK Development Guide - ESP8266 Developer Zone

RTOS SDK版のプログラミングガイドは、こちら。
RTOS SDK Programming Guide - ESP8266 Developer Zone

SDKにはnon-OS版とRTOS版があるのだが、ドキュメントの階層を見るとこうなっているようだ。

  • ESP8266 IOT SDK (User Manual, Programming Guide)
    • non-OS SDK (Programming Guide, SSL User Manual)
    • RTOS SDK (Programming Guide, API Reference)

 

記事を書いている時点でのSDKはこういうバージョン。

image

うーん、このバージョンをどう見たらよいのか。。。
non-OSのv1.4.0が2015年9月18日、RTOSのv1.2は見当たらなくてv1.1が2015年8月21日。
普通に考えると、non-OS版を作ってそこにRTOSを移植するだろうなぁ。
RTOS v1.3.0のリリースを見ると、APIをnon-OS SDKから同期した、と書いてあるから、やはりそうなのか。
non-OS v1.5.0で追加されたespconn_abort()なんかはドキュメントに入ってないし、v1.4.0で追加されたsystem_show_malloc()も入っていない。
かといって、v1.3.0で追加されたsystem_soft_wdt_feed()は入ってないが、wifi_softap_get_dhcps_lease()は入ってるな・・・。
あ、v1.4.0追加のwifi_softap_set_dhcps_lease_time()は入ってる。
RTOSのwikiには、non-OSとAPIは一緒だ、と書かれている。

日付の順に出しているだけだったら、じゃあRTOSのv1.2.0がないのはなんでだ?
わからない、わからない・・・。

[esp8266]ESP8266_RTOS_SDKのexamplesをcygwinでビルド (2)

さっそくで申し訳ないが、前の記事の訂正だ。
訂正というか、私の認識違いだったのだけど、最後のログ出力を見て気付いた。

Generate user1.4096.new.6.bin successully in BIN_PATH

BIN_PATHは、GCCの実行ファイルが入ったパスじゃ無くて、インストール先のパスに近いもののようだ。
configureでいうところの、—prefixみたいなもの。
なので、こういう感じにした方がよさそうだ。

export SDK_PATH="e:/Prog/esp8622/ESP8266_RTOS_SDK"
export BIN_PATH=`pwd`/bin
export XTENSA_GCC_PATH="/cygdrive/c/Users/xxx/AppData/Local/Arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin"
export PATH=$XTENSA_GCC_PATH:$PATH
mkdir -p $BIN_PATH


前に調べた、アドレスを見ておこう。

image

image

 

不自然だ。
USER1とUSER2が同じソースでやるのだったら、User paramの16KBとSystem paramの16KBは同じ位置にいないといけない気がするのだ。
いけないというか、別にこういう持たせ方をしたくても悪くは無いのだけど、どこでそれを決めているのだろうか?
だって、4MBのFLASHがあって2分割するなら、2MB+2MBにするのが自然だと思うのだ。
でも、これは1MB+1MB+自由領域、みたいな割り方になっている。

USER2の領域にはUser paramの領域が無い。
これはSystem paramの領域でまかなうのだろうか?
そういうことを考えながらアプリを作ると、USER1と2のどっちで起動したかなど気にしたくないので、自由領域をUSER1/2で共通に使うようにするだろう。

そして今さら気づいたのだが、FOTA有り時と無し時でflash.binの位置が違う。
FOTA無し時はSPI-FLASHの先頭にあるのだ。
FOTA有り時には、先頭がBootになっている。
前回も、FLASHに焼くツールで最初に指定しているのはboot_v1.4(b1).binだ。

 

うん、まだまだ考え方が甘いな。
今日はここまで。

2015年12月27日

[esp8266]ESP8266_RTOS_SDKのexamplesをcygwinでビルド (1)

はあ、またBLEとESP8266が混在してしまった。。。

ESP8266をArduino上でビルドしたり焼いたりする環境を作ったが、RTOSもいいよ、という記事を読んだ。
Espressifが出した環境で使えるようにしておいた方が無難か、という気持ちもあり、とりあえずサンプルソースだけでもビルドしてみようとした。

うちは、Windows7 64bit + cygwin 32bitだ。


githubのespressif/ESP8266_RTOS_SDKからcloneする。
ここのREADMEにWindowsでビルドできるような感じで書かれていたのでコマンドプロンプトでやっていたのだが、Makefileでincludeするときにパスをどう書くのかわからなくなってきたので、cygwinでやることにしたのだ。

環境変数のSDK_PATHとBIN_PATHを指定する。
これは、gen_misc.shの中にある。
あるのだけど、ビルドの最後の方でpythonを呼び出し、そこからESP8266用のNMコマンドを呼び出そうとしている。
xtensa-lx106-elf-nmを呼び出すのだが、パス無しで書いてあるのでPATHに書いておくくらいしかやり方がわからんかった。

そしてまた、Windowsで動くESP8266用のGCCをインストールせんといかんのだが、Arduino環境を作るときにインストールしてくれているようなので、それを使うことにした。

cygwinでパスを書くとき、ドライブを指定する「c:/」みたいな書き方もできるし「/cygdrive/c/」みたいな書き方もできる。
でも、PATHに追加すると区切りが「:」なので、前者の形式で書いておくと都合が悪い。
しかしそちらに合わせておくと、gccがインクルードパスの解決をうまくやってくれなかった。

そういう、もろもろの事情があって、gen_misc.shの最初はこういう感じになった。

export SDK_PATH="e:/Prog/esp8622/ESP8266_RTOS_SDK"
export BIN_PATH="/cygdrive/c/Users/xxx/AppData/Local/Arduino15/packages/esp8266/tools/xtensa-lx106-elf-gcc/1.20.0-26-gb404fb9-2/bin"
export PATH=$BIN_PATH:$PATH

ここでPATHをやってるけど、結局これをコンソールで実行しているので、実は意味が無いかも。
ただ、ここに書いておくと、これをコピーして、コンソールにぺたっと貼り付ければ済むので、ついでに書いているのだ。

 

makeも、引数がいろいろ付けるのだが、毎回打ち込むのもめんどくさい。
なので、$inputがY以外のときに、こういう行を追加した。

make BOOT=new APP=1 SPI_SPEED=40 SPI_MODE=QIO SPI_SIZE_MAP=6

APPの指定は、実はどれがいいかよくわかってない。
前回調べたとき、FOTAで更新するときにはUSER1とUSER2をそれぞれ作るイメージだったので、まずはUSER1を作ろうとしている。
SIZE_MAPが6なのは、うちにあるESP8266モジュールのSPI-FLASHが4MBだからだ。

 

これでコンパイルが進むのだが、リンクで怒られた。
libhal.aがない、ということだ。
よくわからないが、検索するとesp8266-Arduinoのgithubにバイナリがあったので、何も考えずにダウンロードしてlibに置いた。

 

解決しても、次もリンクエラーだ。
.irom0.textがフィットしないとか、そんなエラー。
検索すると、リンカスクリプトの変更がいるみたいだ。
デフォルトでは$(SDK_PATH)/ld/eagle.app.v6.ldを使うのだけど、これはSPI-FLASHが512KB用の設定になっているみたい。
なので、これを4096KB用の設定にすればいいのだが、サンプルでは2048KB用までしかない。

とはいえ、前回IOT_SDK_User_Manual v1.4でも4096KBは(1024KB+1024KB)という扱いだった。
前半にUSER1とUSER2を詰めて、残りはシステムパラメータ以外なら好きに使ってよいというスタンス。
なので、eagle.app.v6.new.2048.ldをコピーして、eagle.app.v6.ldにリネームしておく。

 

それで進むようになったのだが、今度はpythonスクリプトで「no entry point!!」とか言われる。
スクリプトを読むと、eagle.app.symの中からcall_user_startを探しているようだ。
ないのかと思ったが、テキストエディタで開くと見つかる。
ということは、この正規表現がcygwinだとうまく動かんということか。。

p = re.compile('(\w*)(\sT\s)(call_user_start)$')

行末が「call_user_start」になっているところを探しているのだが、cygwinでコンパイルしているためSYMファイルがCR/LFで改行しているため、これにひっかからないみたい。

p = re.compile('(\w*)(\sT\s)(call_user_start).*$')

これだったら、call_user_startの後ろに1文字あってもなくてもよいのかな?
でも、call_user_start1にもヒットしてしまいそうだ。

p = re.compile('(\w*)(\sT\s)(call_user_start)(\r*)$')

こんなのの方が良いのかも。

 

ここまでやると、こういう出力が出て、終わった。

bin crc: 70347e4
Support boot_v1.4 and +
Generate user1.4096.new.6.bin successully in BIN_PATH
boot.bin------------>0x00000
user1.4096.new.6.bin--->0x01000
!!!

 

今回ビルドしたサンプルはwebsocket_demoという名前なのだが、はて、いったい何が動くのだろうね?
続きは次回だ!

[nrf51]PairingとBonding (2)

hiro99ma blog: [nrf51]PairingとBonding

そういえば、Bondingに対する私の理解が解決していなかったことを思い出した。
まあ、ありていにいえば「よくわかってない」なのだ。


Bonding without passkey is possible using nRF51822? - Nordic Developer Zone

パスキー無しでBondingできますか?という質問。
「Yes」、彼は軽やかに答える。

パスキー無しということは、お互いで同じパスキーを固定で持っているのと同じ、ということのようだ。
MITMビットを0に、IO capabilitiesをBLE_GAP_IO_CAPS_NONEにすると、Just Worksというペアリング動作になる。

In that senario、で、お互いパスキーが”000000”を使う、と書いてあるが、シーケンスにはパスキーの中身は出てこないので”000000”という値も固定なのかな?
質問者も同じことが気になったようだが、回答としては「S110は自動的に000000を使うが、電話メーカーによっては0のみのパスキーをエラー扱いにするところもあるので注意してね」ということらしい。

回答者は、BLEのセキュリティは”not very secure when using PIN authentication”という認識を持たれているようだ。
OOB > PIN > Just Worksの順にsecureで、Just Worksは全然secureじゃないよ、と。


という予備知識を身につけたところで、仕様書を確認しよう。
Vol.3 Part.H “Security Manager Specification”だ。

まず、このシーケンス図。

image

PDFのp.2285(Core_V4.2)に乗っているのだが、フェーズが3つある。
これが、NordicのBondingシーケンスに出てくるフェーズか。
なんの数字かわからないままだったのだ。

フェーズは、こういう名前が付いている。

  • Phase1 : Pairing Feature Exchange
  • Phase2 : (LE legacy pairing)  Short Term Key Generation
  • Phase2 : (LE Secure Connections)  Long Term Key Generation
  • Phase3 : Transport Specific Key Distribution

Phase2が2つある。
どちらもLEがついているのだけど、Part.H自体は表紙に”LE-only or BR/EDR/LE devices”とあるから、LE専用の章ではないのだと思う。
上の図だって「LE Paring Phases」なんだけど、それ以外のシーケンス図が見当たらない。
どこか読み飛ばしているのだろうか。。。

 

Connectionシーケンスの一部では無く、Connectionの後に行うシーケンスなのだ。
まあ、失敗したら切断するのだろうけども。

まず、フェーズ2で次のどれを使うか決めるために情報交換する。

  • Just Works
  • Numeric Comparison
  • Passkey Entry
  • Out Of Band

Numeric Comparison?初めて聞いた。
これはBLE専用らしい。
Core_V4.1のPDFには載っていないから、4.2から新しく追加されたのだ。

暗号化の細かいところは読み飛ばすが、AES-128bitのようだ。
128bitということは、16バイト。
データのどこから暗号化するのかはまだ読んでいないが、元が16バイト以内のデータなら16バイトに、32バイト以内のデータなら32バイトになるのだろう。
ブロック暗号だから、そうなるはず。。。自信はないけど。。。


パケットのフォーマットは、Vol.6 Part Bにあった。

image

一番短いパケットは80bit、一番長いパケットは2120bitとのこと。
そうだ、Core_V4.2からPDU長が拡大されたんだ。

BLEスニファでこういう風に見えているデータがあったとして、

image

これのうち、実際に無線として出回っているのはここまでということだ。

image

プリアンブルは、Advertisingのチャネル(37~39)で流す時とそれ以外でルールが違うようだ。
面白そうではあるが、そういうのは下回りを作る機会があったときために残しておこう。

 

このうちのどこが暗号化されるか。
アクセスアドレスの説明を読むと、ペアリングしたときの情報で生成する方法もあるようだから、AESで暗号化するのはせめてPDU以降ということか。
CRCはPDUの部分を計算するようだから、CRCも暗号化対象外だろう。
つまり、無線を受信したら、暗号化を解読せずにデータが正しいかどうか判断できるというわけだ。


長くなってきたので、今回はここまで。