2019/12/07

[golang]あきらめてパッチにした

GitHubにあったgolangアプリのリポジトリをforkして改造しようとしていた話の続き。

アプリの本体だけならまだよかったのだが、アプリが使っている別リポジトリのライブラリも改造内容に関わっていて、そっちも変更が必要だ。
go.modで置き換えようとしていたのだが、その置き換えだけのためにライブラリをforkして・・・ということをやっていると、もう私の精神力が持たなかった。

 

というわけで、こうした。

  • 置き換えたいリポジトリたちは、go getではなくgit cloneで$GOPATH/srcの方にcloneする
  • cloneしたファイルを編集する
  • リポジトリごとにgit diff > xxx.patchなどとしてパッチファイルを取得する
  • 差分を取得してpatchにしたり、cloneしてpatchをあてるためのスクリプトを作る
  • 自分のリポジトリを作って、そのスクリプトやパッチだけを管理する

 

cloneするときに、forkしたものをオリジナルと同じディレクトリにするなりシンボリックリンクにするなりでもできそうだけど、自分で間違えてしまいそうな気がするのでね。

 

なんか、なんかよい方法があるんじゃないかと思うのだが、探しきれてないのよねぇ。
こちらの、オリジナルをgo getしてupstreamにforkした方を載せる、というのがgolangでのやり方なのかな。

github でホストされている go アプリを fork して PR する時には、まず go get から始めよう - Qiita
https://qiita.com/t-suwa/items/d384facb79866dc16cad

stackoverflowにも出てた。

Using forked package import in Go - Stack Overflow
https://stackoverflow.com/questions/14323872/using-forked-package-import-in-go

 

パッチでやるときの心配は、操作を間違えて消してしまうんじゃないだろうか、というものだ。
それに、一人でやっているからいいけど、複数人でやるとパッチファイルのマージなんかが起きたときに目も当てられないことになってしまいそうだ。

 

ああ、そうだ、その心配があったな。。。
やっぱりupstream方式にした方が無難か。

2019/11/18

[golang]githubでforkしてrenameした場合

golangで、よくgithub.comからパッケージを取得して使っている印象がある。
githubといえば、fork。
forkすると、パッケージまでのルートが変わる。
そして、githubではリポジトリの名前を変更することもできる。

その両方をやったとき、元のソースファイルをどう変更するのが良いのだろうか?


例題は、これにしよう。

hiro99ma blog: [golang]久々のgolang (3) - 他のpackage
https://hiro99ma.blogspot.com/2019/11/golanggolang-3-package.html

パッケージでこれを使っている。

https://github.com/hashicorp/logutils

これを、自分のところにforkして、かつリポジトリの名前を変えてみた。

https://github.com/hirokuma/logutilsutils

 

一番簡単なのは、一括置換だろう。
「元からそのパッケージを見ていました」ということになれば、なんの問題も無い。
しかし、その書き換えが大量になるのは苦痛だ。

かといって、symbolic linkなんかでごまかすのも、いかがなものかと考えてしまう。
なんか、卑怯な気がする。

理想は、importだけは書き換え、実装の方にはリポジトリ名の変更を感じさせない、というところか。


エイリアス、というものがあるらしい。

https://qiita.com/taji-taji/items/5a4f17bcf5b819954cc1#%E3%82%A8%E3%82%A4%E3%83%AA%E3%82%A2%E3%82%B9%E6%93%8D%E4%BD%9C

 

import分を書き換えた。

import "github.com/hashicorp/logutils"

↓↓

import logutils "github.com/hirokuma/logutilsutils"

go getかな?

$ go get
go: finding github.com/hirokuma/logutilsutils v1.0.0
go: downloading github.com/hirokuma/logutilsutils v1.0.0
go: extracting github.com/hirokuma/logutilsutils v1.0.0
go: local/helloworld/hel imports
        github.com/hirokuma/logutilsutils: github.com/hirokuma/logutilsutils@v1.0.0: parsing go.mod:
        module declares its path as: github.com/hashicorp/logutils
                 but was required as: github.com/hirokuma/logutilsutils

うーん、logutilsutils/go.modがhashicorpのままだからだろうか?
しかし、書き換えてもダメだ。

いや、ダメとか云々の前に、どこからhirokuma/logutilsutilsをダウンロードしているのだろうか?
v1.0.0が付いているのでgithubからtagを削除したのに、それでもまだpkg/mod/には@v1.0.0が付いているのだ。

 

$GOPATH/pkg/以下は全部削除しているし、$GOPATH以下にhashicorp関係のファイルがないところも確認した。
わからん、何を調べたらいいのか分からん。。。

 

苦し紛れに、local/helloworld/helにあるgo.modを削除するとうまくいった。
うまくいったというか、src/github.com/hirokuma/logutilsutilsを置いていたので、それを使うようになったらしい。
go.modには特に何も書いていなかったのだが、ファイルがあるだけで挙動が変わるのか。
GO111MODULE=onとかしてないから、そういうのも原因かもしれん。

2019/11/17

[golang]__func__的なものを出力したい

golangの練習で、いろいろ関数を書いている。
関数を呼び出していると何を実行しているのかわかりにくくなってきたので、logを出そうかと考えた。
が、別に関数の名前だけ表示すればいいや、とC/C++のプリプロセッサみたいなものを使って出力させようとした。

 

が、golangにはプリプロセッサのようなものは無いらしい。まあ、わからんでもない。プリプロセッサは強力すぎて訳がわからないことまでできてしまいがちだからなぁ。

スタックトレース的なものはあるので、あちこち参考にしながら私も書いてみた。

01: package dbg
02: 
03: import (
04: 	"fmt"
05: 	"runtime"
06: 	"strings"
07: )
08: 
09: //Trace trace log
10: func Trace() {
11: 	print("", "")
12: }
13: 
14: //Head function header
15: func Head() {
16: 	banner := strings.Repeat("*", 30)
17: 	print("\n\n"+banner+"\n", "\n"+banner)
18: }
19: 
20: func print(head string, tail string) {
21: 	upc, fname, fline, _ := runtime.Caller(2)
22: 	fmt.Printf("%s[%s:%d]%s()%s\n", head, fname, fline, runtime.FuncForPC(upc).Name(), tail)
23: }
  

関数の頭で呼んだときは派手派手にしたかったのでHead()とTrace()にわけた。
まあ、Traceっていっても名前を出すだけなんだけどね。

オーバーライドのようなものは無いみたいで、引数を構造体にしてデフォルト引数を実現するような技が出てきた。今回は使ってないがそういうのもあるということで。

あと、文字列なのでポインタ型で渡した方がいいのかと思ったが、string型はどうなのだろうか?

01: package dbg
02: 
03: import (
04: 	"fmt"
05: 	"runtime"
06: 	"strings"
07: )
08: 
09: var banner = strings.Repeat("*", 30)
10: var head = "\n\n" + banner + "\n"
11: var tail = "\n" + banner
12: 
13: //Trace trace log
14: func Trace() {
15: 	empty := ""
16: 	print(&empty, &empty)
17: }
18: 
19: //Head function header
20: func Head() {
21: 	print(&head, &tail)
22: }
23: 
24: func print(head *string, tail *string) {
25: 	upc, fname, fline, _ := runtime.Caller(2)
26: 	fmt.Printf("%s[%s:%d]%s()%s\n", *head, fname, fline, runtime.FuncForPC(upc).Name(), *tail)
27: }
  
emptyをconstにしようとしたけど失敗。よくわからん。。
スタックに場所を確保する程度の重荷だろうから、もうこのままでいいや。

[golang]久々のgolang (4) - 実行ファイル名

こういう構成でビルドした。
main()はmain.goにある。

image

$ cd $GOPATH/src/main
$ go mod init
$ go build
$ ls
ex.go  go.mod  go.sum  main  main.go

実行ファイル名がmainなのはいただけないな、とディレクトリ名を変更した。

$ rm main
$ cd ..
$ mv main gogoex

image

$ go build
$ ls
ex.go  go.mod  go.sum  main  main.go

あれ、mainのままになっているのはなぜだ?
実行ファイル名はmain()を含んでいるファイルのディレクトリ名になると思っていたのだが。

 

go.modの1行目を見ると、こうなっていた。

module main

これを、変更した。

module gogoex

そしてビルド。

$ rm main
$ go build
$ ls
ex.go  gogoex  go.mod  go.sum  main.go

ディレクトリ名よりもgo.modの方が強いのか!
というよりも、実行ファイル名の決定にgo.modが使われるのか。

 

Go Modulesの説明が長いので、理由を探す気力が湧かなかった。
go getなどで更新されるわけでもないので、わざとそうしているのだろうか?
やらかしそうな気がするので、注意しておこう。

ただ、単にモジュール名として使われるだけなら便利かもしれん。
ディレクトリはmain()が入っているのでmain/にしたい、とか。


と、単純に考えてディレクトリ構成を変更していたのだが、ビルドができなくなった。
直下にはgoファイルを置かず、中でディレクトリを作って分けたいのだが。。。

image

~/Golang/go/src/example$ go build
can't load package: package example: no Go files in /home/xxx/Golang/go/src/example

まあ、ないよね。

 

$ go build ./*

これだとエラーは出ずに終わるのだが、実行ファイルが生成されていないようだ。

 

$ go build ./main/main.go
$ ./main/command-line-arguments
I am masao.

なぜか"command-line-arguments"という実行ファイル名ができる。
そして実行できてしまう。

$ go build -o mmm ./main/main.go
$ ls
ex  main  mmm

gccと同じように-oは使えるようだ。

もしや、go.modの"module"はここで使われるものなのでは?

$ go mod init
$ vi go.mod
(適当にmoduleの名前を変更)
$ go build ./main/main.go
build command-line-arguments: cannot load example/ex: malformed module path "example/ex": missing dot in first path element

moduleの名前を変更しないと、main/command-line-argumentsができるが、変更するとエラーになる。

 

どこかで...という形式を使うのを見たことがある。

$ go install -v ./...
$ ls $GOPATH/bin
main

もしかしたらmain/go.modを置くといいのでは?とやってみたのだが、変わらん。
そもそも、./...とか関係あるのかどうかわからんし。

 

んー、適当じゃダメだな。


go build

https://golang.org/cmd/go/#hdr-Compile_packages_and_dependencies

-oはいいとして、ビルド対象の指定方法を知りたいのだ。
[packages]と書いてあるが、.goファイルのリストでもよいらしい。
-vするとコンパイルしたパッケージの名前が表示されるらしい。

$ go build -v ./main/
go build: build output "main" already exists and is a directory

なるほど、ディレクトリと同じ名前になるから生成できなかったというわけか。
golang-standards/project-layoutというものを見てみると、cmd/の中にアプリ名のディレクトリを作るというのが定番らしい。逆らう理由もないのでそうしてみよう。

image

$ go build -v cmd/gogoex/main.go
$ ls
cmd  ex  main

むう、まだディレクトリ名になってくれないのか・・・。

When compiling a single main package, build writes the resulting executable to an output file named after the first source file ('go build ed.go rx.go' writes 'ed' or 'ed.exe') or the source code directory ('go build unix/sam' writes 'sam' or 'sam.exe').

実行ファイル名は、goのファイル名からとるときとディレクトリ名から取るときがあるのか。

$ go build -v ./cmd/gogoex
example/cmd/gogoex
$ ls
cmd  ex  gogoex

なるほど、これでいいんだ。
最初の./を省略するとフルのパッケージ名として解釈しようとするので、相対パスの要領で"cmd/gogoex"とするとエラーになる。
ここなら、example/cmd/gogoex、と指定すれば良い。

 

go install

https://golang.org/cmd/go/#hdr-Compile_and_install_packages_and_dependencies

go buildは実行ファイルをその場に置いておくが、go buildは$GOPATH/bin/に移動させるようだ。

 

go clean

https://golang.org/cmd/go/#hdr-Remove_object_files_and_cached_files

cleanがよくわからん。
前回などは"go build"としてビルドしていて、それは"go clean"で実行ファイルが削除できていた。
でも、今回のようにパッケージ名を指定すると、どこのファイルも消してくれたように見えない。
"from package source directories"とあるから、$GOPATH/pkg/の方を消すのかな?
この程度のサンプルだとよくわからんな。


まあ、実行ファイル名関係についてはこのくらいでよかろう。
Makefileを使う場合もあるらしいし、また調べることになるだろう。

[golang]久々のgolang (3) - 他のpackage

前回golangのpackageのことを調べていたが、github.comなどのpackageについて調べ忘れていた。

 

例題として何かgit関係のリポジトリから取ってこよう。

https://godoc.org/github.com/hashicorp/logutils

ログ出力だったら、難しくないんじゃなかろうか。

local/helloworld/hel/main.go

01: package main
02: 
03: import "log"
04: import "os"
05: import "local/helloworld/hel/makkey"
06: import "github.com/hashicorp/logutils"
07: 
08: func main() {
09: 	filter := &logutils.LevelFilter{
10: 		Levels:   []logutils.LogLevel{"DEBUG", "WARN", "ERROR"},
11: 		MinLevel: logutils.LogLevel("WARN"),
12: 		Writer:   os.Stderr,
13: 	}
14: 	log.SetOutput(filter)
15: 	log.Print("[WARN]Hello, World!\n")
16: 	makkey.Callme(filter)
17: }
  

local/helloworld/hel/makkey/masao.go

01: package makkey
02: 
03: import "log"
04: import "github.com/hashicorp/logutils"
05: 
06: //Callme masao
07: func Callme(filter *logutils.LevelFilter) {
08: 	log.Print("[ERROR]I'm masao!\n")
09: }
  

 

$ go get -d -u github.com/hashicorp/logutils
$ go run local/helloworld/hel
2019/11/16 23:28:51 [WARN]Hello, World!
2019/11/16 23:28:51 [ERROR]I'm masao!

を見ながら、なんとなく動かせた。


go getでライブラリをダウンロードしたりインストールしたりするのを見ていたので使ってみたのだが、そもそもどうなんだろうか?
他にもglideやらdepやらでインストールすることがあったのだが、そもそもどれが何なのだろうか?

 

  • glideは非推奨になって、depを使ってほしいらしい(glide v0.13.0)
  • depはgolang v1.9以上らしい
  • go getは依存関係は解決してくれるが、パッケージ管理とは異なりそうな気がする
  • vgoという名前も出てくるが、Go Modulesの前身?

 

わけが分からんぞ!

たぶん、golang v1.13以降で、リポジトリにgo.modがあるなら、GO111MODULE=onなどしなくてもGo Modulesの動作をするんだろうと思う。
では、Go Modulesの動作とは何かというと、おそらく$GOPATH/pkg/mod/の下に依存するソースファイルをダウンロードすることだ(ビルドもするのだろう)。「go getがGOPATH modeとは異なる動作をする」といってるので、そうなのじゃなかろうか。

 

私が見たGo Modulesの使い方は、こんなのだった。

$ GO111MODULE=on go install xxx

これでやってるせいか知らないが、pkg/mod/の中に入ってるものはアクセス権があってsudoしないと削除できなかった。

 

仕事でやろうとしているリポジトリはgo.modがあったし、今はgolang v1.13なのでgo getしてみたものの、pkg/mod/にはダウンロードせず、src/github.com/などにだらだらダウンロードし始めてしまった(途中でプロセスを殺したので、最後までは知らない)。

$ GO111MODULE=on go get -u xxx

とすると、pkg/mod/の下にダウンロードしているようだ。src/の下は何も増えていない。

 

「golang v1.13でもGO111MODULE=autoがデフォルトなのはそのままで、ただし$GOPATH/src/の中にgo.modがあればmodule-aware modeになる」らしい。

module-aware modeというのが、私が思うGo Modulesっぽい動作なのだろう。そしてGO111MODULE=onすると最初からGo Modulesっぽい動作をするので、$GOPATH/src/にはダウンロードされないんだろう。

なので、golang v1.13でGO111MODULEは未設定のまま、メインのソースファイルを編集したいが依存するパッケージはそのまま使いたいのなら、本体はgit cloneなどで$GOPATH/src/以下に取得し、その中でgo installすればいいはずだ。go.modがあるなら$GOPATH/pkg/mod/以下に取得するし、無かったら$GOPATH/src/に取得するだろう。

 

私がめんどくさいと思っているのは、$GOPATH/src以下に取得するときなのだ。github.comだったとして、github.com/aaaa/bbbbというリポジトリを取得したいのだったら、

$ mkdir -p $GOPATH/src/github.com/aaaa
$ cd $GOPATH/src/github.com/aaaa
$ git clone https://github.com/aaaa/bbbb

とかしないといかんじゃないか。git cloneで引数を付けてディレクトリを指定してもいいけど、打ち込む文字数はそんなに変わらんし。それがgo getだとディレクトリの作成までやってくれるので使いたかったのだ。


src/local/helloworld/helにはgo.modがないので、go installとかgo getとかすると$GOPATH/src/以下にgithub.com/hashicorpのリポジトリをダウンロードしてくる。

これをsrc/に起きたくなかったら、go.modを書くのが良いだろう。
手動で書かなくても済みそうな気がするが、自動生成はできるのだろうか?

https://github.com/golang/go/wiki/Modules

できそうだ。

$ cd $GOPATH/src/local/helloworld/hel
$ go mod init local/helloworld/hel
$ go build

go mod initでgo.modができて、go buildすると更新されてgithub.com/hashicorp/logutilsがバージョン付きで追加されていた。logutilsもsrc/の下ではなく、pkg/mod/の下に取得されていた。
local/helloworld/hel/makkeyの方は書かれていなかったので、パスで見分けているのかもしれん。

2019/11/16

[golang]久々のgolang (2) - package

前回に引き続き、golangの勉強。versionは1.13.4。


まずはpackageというかライブラリというか、そういうやつ。

たぶん、種類としてはgo言語標準、ローカルで実装したもの、他から取得したもの、くらいになるんじゃなかろうか。

 

標準ライブラリ

https://golang.org/pkg/#stdlib

これが標準のものだろう。
下の方にOther packagesがあるが、これはメインのgoには入ってないがプロジェクトの一部ではあるらしい。"net"はstandardにもotherにもあるが、otherはgolang.org/x/net(import "goglang.org/x/net/bpf"など)とのこと。

 

ローカルの実装

JavaやC#のイメージだと、機能単位でpackageを分けて、あわよくば他でも使えるようにするだろう。
ライブラリと同じようなイメージかもしれん。

 

他から取得したもの

よく見るのは、これだ。githubなどで公開されているものを使うのだ。
git getとかで取得できていた気がする。

 

 

標準のやつは"fmt"で試せたことにして、まずはローカルの実装だろう。

image

こんな感じで、masao.goというファイルを作った。

local/helloworld/hel/main.go

01: package main
02: 
03: import "fmt"
04: 
05: func main() {
06: 	fmt.Printf("Hello, World!\n")
07: 	//masao.Callme()
08: 	Callme()
09: }
  

 

local/helloworld/hel/masao.go

01: package main
02: 
03: import "fmt"
04: 
05: //Callme masao
06: func Callme() {
07: 	fmt.Printf("I'm masao!\n")
08: }
  

これは、masaoをmainパッケージに置いたので、動いた。
Cなどと違って、別にincludeみたいなことをしなくて良いのだな。

 

では、masao.goを別パッケージにしたらどうなるかと、masaoというパッケージに変更した。

$ go install local/helloworld/hel
can't load package: package local/helloworld/hel: found packages main (main.go) and masao (masao.go) in /home/xxx/Golang/go/src/local/helloworld/hel

見つからないとかなんとかいう以前に、パッケージにmainとmasaoがあるからダメ、みたいなことを言われた。
Javaだとディレクトリ構成やファイル名がパッケージと関連していたが、golangはどうなんだろうか。

https://golang.org/doc/effective_go.html#package-names
Another convention is that the package name is the base name of its source directory;

ファイル名はいいとして、パッケージ名とディレクトリ構成は関連するのか。
ならば、パッケージ名はmakkeyにしてみよう。

image

local/helloworld/hel/main.go

01: package main
02: 
03: import "fmt"
04: import "local/helloworld/hel/makkey"
05: 
06: func main() {
07: 	fmt.Printf("Hello, World!\n")
08: 	makkey.Callme()
09: }
  

 

local/helloworld/hel/makkey/masao.go

01: package makkey
02: 
03: import "fmt"
04: 
05: //Callme masao
06: func Callme() {
07: 	fmt.Printf("I'm masao!\n")
08: }
  

 

これはコンパイルできたし、実行できた。

$ go install local/helloworld/hel
$ hel
Hello, World!
I'm masao!

 

ファイル名は影響しないようだ。
とはいえ、golangは構造体を使ってclassっぽいものを使えたと思うので、それに合わせることになるだろう。

[golang]久々のgolang (1)

いろいろあって、golangをやらねばならなくなりそうだ。
Pythonはスクリプト言語を覚えたいというモチベーションがあったのだけど、golangは仕事で使えればいいや、という程度だ。

まあ、仕事で使える程度、ってのはかなり高い要求なんだけどね。。。


まず、インストールから。Ubuntu18.04でやる。

これを書いている時点のstableバージョン(なのかな?)は、1.13.4。

https://github.com/golang/go/wiki/Ubuntu

PPAのを使って、apt installした。

$ go version
go version go1.13.4 linux/amd64

よかろう。


以前はGOPATHとかGOROOTとかの環境変数を設定していた気がする。
言語はやってなくてもgolangで作られたアプリはインストールしていたのだが、GOPATHしか設定した記憶が無い。
GOROOTは使わなくなったのだろうか?

GolangのGOPATHやGOROOTについて
https://tech.librastudio.co.jp/entry/index.php/2018/02/20/post-1792/

ほう、そうか。
GOROOTはインストール場所だったのか。aptでも有効だったのだろうか?まあ、もう遅いんだけど。

そして、デフォルトでGOPATH未設定の場合でもパスが自動で決められるらしい。
ただまあ、たくさんライブラリを使うようなアプリだったりすると、$GOPATH/srcの中がゴチャゴチャしてくるし、$GOPATH/binをPATHに追加してしまってよいものか悩むシーンも出てくる。
なので、あれこれ切り分けたいとか、golangあまり使わないのでそのときだけ設定したいとかあるだろうから、私はGOPATHは指定するようにしておこう。
pythonのvenvなんかを使い始めてわかったのだけど、GOPATHも環境を分ける方法の1つと考えて良いだろう。ストレージに余裕がある場合は、なるべく分けるようにしよう。

 

そういうわけで、今回は$HOME/Golangを作って、その中でやる。
こんなのをsourceコマンドで読み込んでおけばいいや。

01: #!/bin/bash
02: 
03: export GOPATH=`pwd`/go
04: export PATH=$PATH:$GOPATH/bin
  

では、helloworldしてみる。

$ cd Golang
$ source config.sh  #↑のスクリプト
$ mkdir -p go/src/hello
$ cd go/src/hello
$ vi main.go

================================================

package main

import "fmt"

func main() {
    fmt.Printf("Hello, World!\n")
}

================================================

$ go build
$ ./hello
Hello, World!
$

あれ、goって、$GOPATHの下にbin, pkg, srcができて、実行ファイルはbinにコピーされるんじゃないの?

Go言語の作業ディレクトリをドキュメントに従って作り直してみた
https://qiita.com/ttsuzo/items/45eb4fc269986df9122c

ああ、推奨構成みたいなものがあるのね。そしてgo installするとうまいことやってくれるようだ。

$ go install
$ ls ../../bin
hello

ほう。そしてどうでもいいことだが、go buildしてカレントディレクトリに作られていたhelloは削除されていた。
go installの引数は、カレントディレクトリかsrc/からのパスのようだ。

$ cd ..
$ pwd
/home/xxx/Golang/go/src
$ mkdir -p local/helloworld
$ mv hello local/helloworld/hel
$ go install local/helloworld/hel
$ ls ../bin
hel  hello

実行ファイル名はディレクトリ名がそのままになるのか。そして、名前が変わったとしても前回installしたものはそのまま残る、と。追跡はしていないようだ。

$ rm ../bin/*
$ go install local/helloworld/hel
$ go clean local/helloworld/hel
$ ls ../bin
hel

実行ファイルがカレントディレクトリにあったときはcleanすると削除してくれたのだが、binにあるとだめなのか。
helpで"Clean removes object files from package source directories."と書いているから、srcだけなのだろう。

$ go run local/helloworld/hel
Hello, World!

こうすると、バイナリファイルが残っていないけど実行ができている。
main.goがカレントディレクトリなら引数が不要かと思ったが、それはダメだった。


そろそろ、vscodeを使う。
src/で開くべきかgo/で開くべきか悩ましいが、まずはgoにしておこう。

それで気付いたのだが、Goのインデントはハードタブなのだろうか?
C言語でvscodeを使っているときはスペースだったと思うので、言語によって切り替えられているようだ。
https://golang.org/doc/を検索しても出てこないのだが、ネットではハードタブを使うような記述が出てくる。
スペースに置き換えて gofmt -w main.go するとハードタブになるから、少なくともコマンドとしてはハードタブがデフォルトのようだ。

 

vscodeにGoのextensionを入れたのだが、ツールのインストールを要求するのでOKOKとやってたところ、$HOME/Golang/goのbinやpkgにいろいろインストールし始めた。。。
せっかく仮想環境の一環だ、なんて思ってたのに台無しじゃないか。。。
go.toolsGopathにパスを書くと、そこをvscode用の$GOPATHとして使ってくれそうだ。

 

デバッガは、launch.jsonをデフォルトのまま使うことができた。
"${fileDirname}"になっているので、main.goを開いているならそのままでいいのだろう。しかし腰を据えてやるような状態だとmain.goなんかいちいち開いていられないだろう。
たぶんだが、main.goがあるディレクトリまでのフルパスを書けばよさそうだ。フルパスというのが、/home/から書かないとダメみたい。${workspaceFolder}から書き始めるのはよさそうだ。
これは、私が.bashrcなんかにGOPATHを書いてないとか、binにPATHを通してないとか、まだmain.goしかファイルがないとか、いろいろわかってないことが多いので保留にしておこう。

 

goとは関係ないが、vscodeのexplorerで"OPEN EDITORS"を表示したくない場合は

"explorer.openEditors.visible": 0

を追加すると良いそうだ。
あれがあると、ファイルを開くたびにツリーが下に下がっていくので、毎回隠していたのよねぇ。


デバッグできそうな環境ができたので、次からは言語の本体をやっていこう。
基本的なところだけわかれば、あとは仕事しながら覚えてもいいだろう。

2019/10/23

FLUKE 101

とうとうマルチメータを買った。
FLUKE 101だ。

image

ハンディサイズとは書いてあったが、本当に小さめだった。
今まで片手でずっしり持つタイプしか見たことがなかったので新鮮だ。
「かわいい」というやつか?

 

操作面はシンプル。
HOLDはいいとして、黄色いボタンは何だろうか。
抵抗レンジの通電ブザーとダイオードの切替と、Hzレンジの表示切替のようだ。

プローブのケーブルが長い。
まあ、短いよりはいいだろう。
あと、保護用のキャップが付いている。私は家でしか使わないけど、持って歩くときは無くさないように注意だな。

 

ダイアルの通り電流は計測できないが、普段は電圧と抵抗値くらいしか測らないので十分だろう。

2019/10/22

I2Cのその後・・・

あれからどうやってもI2Cがうまくいかない。
ゴチャゴチャとハンダ付けしているのがよくないので、取り付けていたユニバーサル基板から外そう。

 

image

最初はハンダ付けした面から取り外そうとしたのだが、両側からハンダ付けしている場合はほぼ無理なのよね。。。
もしかしたら物理的にハンダを吸い取るタイプの装置があればよかったのかもしれないが、私が持っているのはハンダ吸い取り線だ。
表面は吸い取れても、スルーホールの中までは無理だった。

 

では、と上から吸い取る方式に変更した。
こっちも似たようなものではあるが、ピンが飛び出している距離が短いので、ハンダが溶けている間に基板を引っ剥がせるかもしれない。

が、結局それも難しい。そりゃそうだ。

ピンが出ている距離を短くするため、今度はハンダごてで温めながら力を入れてピンを引き抜こうとした。
もちろん無理。
TXB0104の基板とユニバーサル基板の2箇所がハンダ付けされているのだ。そう簡単にはいかない。

 

で、最終的にはニッパでピンを折った。
折った後でハンダ吸い取りしながらピンを抜く。
その結果が写真だ。

いやあ、ボロボロですな。
温めすぎたせいか、TXB104の基板も多少溶けてしまっている。
ICとスルーホールとの接触が切れているところもあるので、そこは直接ピンとハンダ付けし直すしかないか。。。

 

争いは何も生まない、みたいな言葉があるが、基板からの部品引き剥がしも何も生まないと思った。

2019/10/21

[arduino]I2Cがうまく動かん

FeliCa LinkをArduinoから動かそうとした。
ブレッドボードで適当につないだときは動いたのだが、シールドとして同じように配線してみると、なぜか動かない。。。

 

FeliCa LinkとArduinoは電圧が違うので、レベルコンバータを間に入れている。

TXB0104
https://www.switch-science.com/catalog/1466/

これが、GPIOでHI/LOしているときは動いているのだが、I2C masterとして動かすと反応してくれないのだ。


まず、SDA, SCLのpinModeを見てみたい。

main.cppはここのようだ。
C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.21.0_x86__mdqgnx93n4wtt\hardware\arduino\avr\cores\arduino\main.cpp

たぶん、同じディレクトリないのwiring.cにあるinit()でピンの初期化をしている。
が、アセンブラだか組み込み命令だか知らんが、Arduinoの関数じゃないので放置だ。
たぶん、なんかうまいことやってるのだろう。

 

あきらめてWire::begin()を見てみる。
C:\Program Files\WindowsApps\ArduinoLLC.ArduinoIDE_1.8.21.0_x86__mdqgnx93n4wtt\hardware\arduino\avr\libraries\Wire\src\Wire.cpp

twi_init()を呼んでいるが、これは同じディレクトリにあるutility/twi.cだろう。
ここで、こうやっている。

  // activate internal pullups for twi.
  digitalWrite(SDA, 1);
  digitalWrite(SCL, 1);

pinMode()など出てこなかったので、main.cppから呼んでるinit()でうまいことやってるか、内部のプルアップ抵抗を変更するだけならこれでよいようになってるとか、そんなところなのか。

 

なので、変なことをしなければ、ArduinoのI2Cは内部でプルアップしてくれるのだと思う。
この状態でSDA-GND間をテスターで測ってみると、2Vくらいあった。1.8Vくらいかな。
微妙な電圧値だ。。。
だが、Wire.begin()の後でdigitalWrite(SDA, 0)とすると0Vになるので、これがプルアップ効果なのだろう。

 

レベルコンバータを使った例のサイトを見ると、ここはプルアップ抵抗をつないでいない。

https://learn.sparkfun.com/tutorials/txb0104-level-shifter-hookup-guide

ADXL345のページまで飛んだが、そっちはプルアップ抵抗を付けるような説明になっていたので、Arduinoの内蔵プルアップを使っているのだろう。
しかし、1.8V程度でやっていけるのだろうか。。。

 

あ、計測するのをテスターからオシロに変更したのだが、オシロだと5Vになってた。
もしかして私のテスター・・・値が不正確すぎ??
もう何十年も使ってるアナログテスターで、しかもそのときですら千円ちょっとで購入した気がするので、そろそろ引退してもよいのかもしれない。


5Vちゃんとあるなら、いいだろう。

しかし、だ。
これをTXB0104につなぐと、どっちも0Vになってしまうのだ。
SDA, SCLをpinMode()でOUTPUTにしてdigitalWrite()すると、ちゃんと制御できている。

image

 

というのが、今の悩みなのだ。
レベルコンバータを挟んでいるから、3.3V側でもプルアップしないとダメというだけなのだろうか。

いや、さっきまではプルアップ抵抗を付けていたのだけど、うまくいかないので全部外したのだ。
ただ、外してあれこれ試している途中で、変なところがショートしているのに気付いて、元に戻すかどうか考えているところなのだ。

そもそも、まだ3.3V側は何も取り付けていないのだ。オープンなのだ。
だからArduino側がそのまま突き抜けてくれてもいいと思うのだよ。
でもなぁ、私の今までの経験上、そういう適当な判断は間違っていることが多いのだった。。。

 

試しに、3.3V側のピンに適当な抵抗をつけてプルアップしてみた。
そうすると、ちゃんとHIになった。そして、プルアップを外してもHIが維持されている。
うーん、つまり、3.3Vにもプルアップがいるということか。

 

プルアップ抵抗として4.7kΩのをつないでみたのだが・・・スタートコンディションっぽい動作はするものの、一度立ち下がってから立ち上がる様子がない。

以前動いたときは1kΩだったので変更したが、これはこれで動きが変だ。
4.7kΩのときはLOに張り付いていたが、1kΩだとときどきHIになったりして、多少は動こうとしているように見える、というところだ。

 

うーん、なんかもう、ハンダ付けがうまくいってないとか、変なところがショートしてるとか、そういうのを心配するべきなんだろうか。。。。
実績が無ければ最初からやり直そうとするのだが、ブレッドボードの時に動いていたというのが気になってしまい、どうしても見直しをしてしまうのだよなぁ。。。

2019/10/19

SlimBlade Trackball

image

以前買ったOrbit Scroll Ring Trackballと、SlimBlade Trackballを並べてみた。

 

そう、実はずいぶん前にSlimBladeを購入していたのだ。
Orbitを購入したのは2017年10月17日らしい。ブログはあるのだが、画像が残っていなかった。

 

SlimBladeの購入は、かなり悩んだ。
値段が1万円以上するというのもあるし、トラックボールを2つ買ってどうするんだ、というのもあった。
でも、会社に家のOrbitを何度も持っていくのが面倒になり、どうせ買うなら大きい方がいいや、とあきらめて購入
あまり当てにしづらいAmazonのレビューを見たが、評価がずいぶん割れているようだったのも心配な要因だった。

 

数ヶ月使っていて、なんとなくレビューで言われている内容も意味が分かってきた。
今回は2つめのトラックボールという人の意見として読んでほしい。


まず、ボールが大きいのは良いことだ。
会社の方が作業時間が長いのでSlimBladeは会社に置いているのだが、家でOrbitを使うとボールの小ささがものすごく気になってしまう。
細かい制御をしないから気にならんだろうと思っていたのだが、思いのほかボールの大きさは重要だった。
Orbitだと指先でボールを回す感じだが、SlimBladeだと手の平で回すこともできる。その力強さが非常に助かるのだった。

 

ボタンが4つあるのも助かっている。
ボタンが2つだと、中クリックに相当するものを両ボタンクリックでまかなっていたのだが、それが1つのボタンで割り当てられるのだ。それでもまだボタンが1つあるという余裕。以外と中クリック使うのよねぇ。
デフォルトでは「戻る」になっていて、私はそのまま使っている。

 

評価が分かれるのはホイール動作だろう。
SlimBladeではボールを回すのだが、楽かどうかで言えばOrbitの方が楽だ。ボールを回すと、カーソルが動いたりするのよね。ぐりぐり回しているのにスクロールしてくれないと思ったらカーソルがずれている、というのはよくあることだ。
ただまあ、物理部品は壊れやすいということを考えると、それよりはいいのかもしれないな、と思ってしまうのだ。リングが故障したらたぶんまるごと買い替えるだろうから。

 

あと、ホイール音。
これはソフトウェアでSlimBladeが直接音を出している。つまり、通電状態じゃなければ音はしないのだ。
で、この音をやめる方法がない。何かスピーカーのようなものがついているだろうから取り外せばいいんだろうけど、そこまでやる気力は無い。

音が気になるかどうかは、個人差だろう。
今の職場は音楽を流していても良いようなところなので、SlimBladeのホイール音くらいなら気にならない。ただ、その音が気になるかもしれない、と思い始めると気になってしまう、というものだ。
静音マウスというものがあるくらいだから、音を切ることができるようになっておくとよいとは思う。

 

有線がいいのかどうかは、私には分からん。
ノートPCじゃないし、移動することもないので有線で充分なのだ。


こんなところか。

トラックボールだけじゃないけど、ディスプレイとかキーボードとかマウスのようなものって、実際に自分の環境である程度の時間を掛けて使わないとわからないことがしばしばある。
かなり個人の感性に依存してしまうものだから、私の感想もその程度に受け取ってもらえると助かる。

2019/10/14

[arm]Armv8-MのTrustZone

nRF9160で気になっていたのが、TrustZoneだ。
Cortex-AのTrustZoneを知らないので比較できないのだが、この記事によるとGCCから扱えるようだ。

https://www.lobaro.com/using-the-armv8-m-trustzone-with-gcc/


Introduction & Motivation

Cortex-M33プロセッサにはARM TrustZoneと呼ばれる機能がある。
この記事では、GNU ARM GCC compilerとCMSE(Cortex M Security Extensions)を使ってTrustZoneついての私見を述べる。

これはARMv8-MならnRF9160じゃなくてもいいし、GCC以外のコンパイラでもいい。

 

Secure & Non Secure CPU Operationg States

今まではThreadモード(通常モード)とHandlerモード(割込みモード)だけだったが、ARMv8-Mになるとsecure / non-secureという属性が加わる。

電源を入れたときはsecureモードで始まる。このときはメモリも周辺機能も全部使える。これは今までのM3やM4などと同じ。

TrustZoneの考え方は、セキュリティ関連のコードやデータをデバイス内の小さな特定領域に隠すということだ。その領域は"secure"と呼ばれ、そうじゃないところは"non-secure"と呼ばれる。メモリ、割込み、周辺機器をsecure/non-secureに分割するのはユーザが決める。電源ON時のコードはその分割をする処理から始めるのが普通だ。nRF9160のコード例(Secure Partition Managerと呼ぶらしい)では、FLASHの256KBをsecureに、768KBをnon-secureに割り当てている。

CPUは実行関係のレジスタ(スタックポインタとSystem Control Block)として2つのバンク(secure/non-secure)を持つ。今のCPU動作がsecureかnon-secureかというのは、どっちのバンクを使っているかということと同じ意味である。バンクのメカニズムはハードウェアで2回実装されている一部の周辺機器(SysTickなど)でも同様です【私:2回(twice)って何??】。secureなプログラムは常にどちら側にもアクセスできる。non-secureなスタックポインタとnon-secureなベクタアドレスの配置を設定した後、secureなブートコードはnon-secureなベクタテーブルの関数ポインタを使用する。そして、CPUはnon-secureなファームウェアとして動作し始める。

non-secureファームウェアは、secureファームウェアの存在を認識してはならない。TrustZoneがない時代と同じように普通に開発して、普通にnon-secureなメモリ領域とリンクするようにする。CPUがnon-secureに動き始めると、security exceptionが発生しない限りsecureなコード、データ、周辺機器にに直接アクセスできなくなる。non-secureコードからsecureな機能にアクセスする唯一の正当な方法は、secure側が提供することになる特別なゲートウェイ/エントリー/veneer機能を使うことである。では、ARM GCCとCMSE(Cortex M Security-Extensions)を使ってやってみよう。

 

Non-secure callable(NSC) memory regions and the secure gateway(SG) ASM instruction

non-secureからsecureな機能を呼び出せるようにするため、secureファームウェアのコードに"Non-secure callable(NSC)"といういくつかの小さなFLASH領域を設定しなくてはならない。nRF9160ではSPU(System Protection Unit。PDFのp.257参照)が担当している。

secureメモリの一部をNSCとして指定することにより、最初のOPコードとしてアセンブラの"SG(Secure Gateway)"命令を呼び出すことで、non-secureから呼び出せないという制限を緩和できる。SG命令を実行すると、プログラムはより制限されたsecureメモリ内にある=NSCではないメモリにあるsecure機能実装に分岐する。このようにしてnon-secureコードからsecure機能を呼び出すことで、まずNSCにジャンプし、続けてsecureメモリ内にある実際の機能にジャンプする。こういう過程を経ることにより、secureメモリへのアクセスは残りのsecureメモリから切り離される。詳しいことはこちらを読め:Reasons for introducing the NSC regions

 

Defining non-secure callable functions in secure firmware

幸いなことに、non-secureファームウェアからsecure機能を呼び出す過程はARMv8-Mコンパイラによってプログラマからは隠されている。non-secureファームウェアから呼び出すsecure機能のエントリーポイントはsecureファームウェアの定義をした後でcmse_nonsecure_entry属性をつける。

//some c file of secure firmware project defining veneer gateway functions
(ソース例は原文を見よう)

GCCでコンパイルするときにはオプションとして"-mcmse"がいる。

// GCC COMPILER flags used during secure side building
arm-none-eabi-gcc -o secureFirmware.elf -mcmse [...]

関数本体は通常secureメモリの.textセクションに配置される。が、SGと分岐する命令は.gnu.sgstubsセクション(GCCの場合。GCC以外は違う名前かもしれない)。

NSC領域の小さなredirect機能は、SGを実行して分岐する処理しかやらないので、"veneer"機能や"SG stubs"などと呼ばれる【私:blogでは"SG stups"と書いてあったけど、typoよね? それと、ここまでfunctionを「機能」と書いてきたけど、C言語の関数という意味で使われたりもする? redirect functionってなんだろう? non-secureからsecureに遷移するってこと?】。secureファームウェアのリンカスクリプトファイルは、non-secureファームウェアにジャンプする前にsecureブートプロセスの一部のNSCとして宣言するveneerコードをsecure領域に配置しなくてはならない。

// Linkerscript Section for TrustZone Secure Gateway veneers
(リンカスクリプトは原文を見よう)

【私:何が言いたいのかわからなかった。リンカスクリプトの見方が分かってない。】

 

Calling secure functions from non-secure firmware (NS->S)

通常、secureファームウェアはnon-secureファームウェアイメージから独立して開発&FLASH書込みされる。secureファームウェアの機能を使うために、secure側の関数を呼び出せるように書かれたヘッダファイル(.h)はnon-secureソースファイル側でincludeする。加えて、non-secureファームウェアはsecureメモリのNSC部分の前にveneerエントリーポイントスタブがどこにあるかを知っていないといけない【私:「NSC部分の前に」はbeforeだから時間的に前。FLASHのsecureとnon-secureの設定を行う前に決定していないといけない、という意味か?】。これは特殊なオブジェクトファイル(CMSE_importLib.o)とアドレス情報を必要としているnon-secureファームウェアをリンクすることで解決する【私:done、は、何とかしてくれる、くらいの意味だろうか?】。このオブジェクトファイルは以下のcmseリンカオプションを使用してsecureファームウェアがリンクされる間に生成されなくてはならない【私:CMSE_importLib.oはここで生成されているのだろうか?】。

// GCC LINKER flags used during SECURE side linking
arm-none-eabi-gcc -Xlinker --cmse-implib -Xlinker --out-implib=CMSE_importLib.o -Xlinker --sort-section=alignment [...]

オプション"-in-implib=CMSE_importLib.o"は、過去バージョン(TrustZone未対応)で既に定義された同じメモリに展開されるオブジェクトファイルを新しいバージョン(TrustZone対応)でも維持できるようにする【私:-in-implibではなく-out-implibの間違いでは?】。こうすることで(過去バージョンで作成済みのimport libにリンクされている可能性がある)non-secureイメージの更新をせずにsecureイメージに追加できるようになる。

.hファイルを使用してCMSE_importLib.oオブジェクトをリンクするために、non-secureファームウェアはControlCriticalIO()関数を呼び出す【私:the example aboveは、たぶん「原文を見よう」のコードだ。】。

// GCC LINKER flags used during non-secure side linking
arm-none-eabi-gcc -o non-secureFirmware.elf CMSE_importLib.o [...]

 

Calling non-secure (callback) functions from secure firmware (S->NS)

どのようにしてTrustZoneが2つの隔離されたファームウェアをgatewayやveneer機能で接続するのかを見てみよう。これはsecureファームウェアがnon-secureファームウェアから呼び出される機能を許可しているからだ。

もう1つの方向、すなわちsecureファームウェアからnon-secure関数を呼び出す場合、なんらかの方法でsecureコードにnon-secure関数のアドレスが与えられたならば、実行することができる。通常、これはsecure gateway functionが引数にnon-secureコールバックポインタを与えることで行われる。

(サンプルは原文を見よう)

"nonsecure_call()"属性は、潜在的なセキュリティリスクを割けるためにnon-secureコードにジャンプする前にバンクじゃないgeneral purposeレジスタをクリアするようsecureコンパイラに指示するために必要だ。そしてまた、ブランチアセンブラ命令の関数ポインタアドレスLSBをクリアする指示にも使われる。こういうことを行って、CPUはsecure状態からnon-secure状態に戻ってくる。

non-secureコールバックはvolatile値のように扱わねばならないので、non-secure割込みによって変更される可能性があるため、関数ポインタは"cb"にコピーが必要となる【私:cbはサンプルコードの中にあるfuncptr_ns型の変数か】。コールバックを実行する前に、ポインタがnon-secureメモリのみを挿しているのかチェックしなくてはならない。secureメモリを挿しているなら、それはセキュリティリスクとなりうる【私:ポインタ変数が4byteだとして、それが全部non-secure側にあるかどうかをチェックするのだろうか?】。

 

Hints and further reading

secure側のリンカスクリプトはveneer関数のセクションを考慮し、non-secure側のリンカスクリプトはsecureファームウェアのメモリ領域を反映しなくてはならない、ということを覚えておこう。archiveファイル(*.a)がnon-secureの呼び出し可能な関数を含んでいるなら、secureファームウェアのリンカオプションとして"whole-archive"を付けなくてはならない。この方法でのみCMSE import libが必要とするveneer関数に含めることができる。

 


けっこうがんばって読んでみたのだが、どうだろうか?
というか・・・私やっぱり英語力ないな。。。
ただ、この人のはGoogle翻訳などできれいに和訳されやすかった。がんばったのは、Google翻訳に通す前に自分でやってみたということについてだ。自信がないときはその後でGoogle翻訳して「ああやっぱり間違ってた」となるのだがね。

 

 

さて、整理しよう。

【NSC】
Non-Secure Callableの略。
non-secure状態からsecure状態に遷移するときに使うsecure側の領域。NSC memory regionsやNSC regionなどという使い方。

【SG】
Secure Gatewayの略。
non-secure状態からsecure状態に遷移したいときに使うアセンブラ命令。

 

まず、Defining non-secure callable functions in secure firmwareのところ。
ControlCriticalIO()という関数をcmse_nonsecure_entry属性つきで作る。
これはnon-secureファームウェアから呼び出されることになるのだが、その前にSGオペコードを呼び出したりなどをやってくれるのだろう。
これをsecureFirmware.elfという名前で生成している。-mcmseオプション付きだとsecure用になるのか。

 

次に出てきたのはリンカスクリプトで、.gnu.sgstubsが載っている。
これは自作が必要なのかもしれない。

 

次はCalling secure functions from non-secure firmwareのところ。
--Xlinkerを使ってリンカに指示している。オプションは--cmse-implib, --out-implib, --sort-section。
--out-implibはファイル名を付けているので、これが生成されるのだろう。

そして最後にCMSE_importLib.oと一緒にリンクしてnon-secureFirmware.elfを生成している。

 

最後の話で、callback_fnをcbにコピーするところがよくわからなかった。
C言語だから、引数は値渡しだよな? ポインタだから、アドレス値を渡しているはずだ。
じゃあ、コピーしなくてもいいんじゃないのかと思ったが、レジスタじゃなくてスタックで渡された場合だろうか。
うーん、答えが出ない。。

 

あと、secureとnon-secureにメモリのパーティションを分ける話があったが、FLASHはいいとして、SRAMの方はどうなってるのだろうか?
nRF9160のSPMを読むと、SRAMも分けるようだ。分けるというか、強制的に分けられるというべきか。だからSRAMについての記述がないのか。

 

nRFのFLASHって、自分で書き換えができたんだっけ?
OTAがあるのは覚えているが、BondingなんかはAPIを使ってしかやってないので、今ひとつ・・・いや全然記憶にない。


むー、もっと調査が必要か。

しかしまあ、しばらく触っていないうちにずいぶん状況が変わってきたな。
特にセキュリティ周り。
nRF51822だとBLEで手一杯だったので考慮しなかったが、nRF9160はTrustZoneがいるくらいのレベルが要求されているのだろう。まあ、nRF9160はBLEじゃなくてインターネット接続と直接接続される前提になっているからかもしれないが。

今までインターネットとは距離を置くことでセキュリティのことは放置してきたのだが、そういう時代ではなくなったのだな。。。

2019/09/28

gitのrebase

gitを使っている。

gitの説明って、だいたいコマンドラインで行われている。
コマンドを覚えるのが面倒なのでツールで済ませてしまいたいのだが、ずっと使っていてわかった。
なんだかんだで、コマンドラインでやらないと細かいことがやりづらいのだ。

いつか「gitの困ったときだけ使うGUIツール」みたいなのを作ろうかと考えていたのだけど、コマンドラインには叶わんのだよなぁ。。。

 

さて、gitを自分だけで使う分にはそこまで気を配らないのだが、チームで使うとなるとそうもいかん。
気にするのは、こんなところだろうか。

  • 自分に変なことが起きないようにする
  • 相手に変なことが起きないようにする

まあ、他に何があるんだっていわれそうだが、まずはこの2点に気を配るだろう。

 

GitHubでgitを使っているので、本体のリポジトリをforkして、branchを作って作業して、pull requestして本体にマージ、という流れにしている。
作業しているbranchは自分のものだから、好き勝手にいじっても問題が無い。
これで「相手に変なことが起きないようにする」は解決だ。
Pull Requestしてコンフリクトすることもあるだろうけど、そこはそこで解決すればいいだけのことだ。

 

ああ、「相手に」という意味では、commit履歴というものがある。
branchをつくって、今日の分、みたいな感じでcommitしていると、本体にマージしたときもその履歴が残ってしまう。
まあ、Pull RequestのマージでSquashというやり方もあるのだろうけど、1つにまとめていいかどうかPull Requestする人が悩むこともあるだろう。
そうならないように、Pull Requestする段階できれいな履歴にしておきたい。

 

で、ここからが本題だ。

いままで、私はbranchで作業したcommit履歴をまとめるために、別のbranchを作ってsquashさせていた。
これでよいときもあるのだが、単にこことここだけまとめたい、という場合もある。

そうなると、たぶんrebaseを使うはずだ。
rebaseすると、pushするときにforceで行うことになるが、まあ自分のbranchだから問題なかろう。
一度Pull Requestして修正するときも、rebaseしてpush forceしてしまえば履歴がきれいだし。


git rebase HEAD~2 と指定すると、直近のcommit履歴2つが出てくる。
上から古い順番で並んでいる。

ここでpickのところをsquashとかsとか書き換えると、commitがまとめられる。
では、sにした行の上にまとめられるのか、下にまとめられるのか。

試そう。

$ git log --oneline
741e4b2 (HEAD -> master) second
48697ff first

$ git rebase master
Current branch master is up to date.

あれ?? ダメなの??
じゃあ、branchを作って。。。

$ git checkout –b rb
$ 編集
$ git add xxxx
$ git commit
$ 編集
$ git add xxxx
$ git commit
$ git log --oneline
12d8146 (HEAD -> rb) forth
3ef2f57 third
741e4b2 (master) second
48697ff first

$ git rebase HEAD~2
Current branch rb is up to date.

うーん、rebase、つまりre-baseで、基点を変更するコマンドだ。
だから、masterが最新で、そこから延びただけだったら使えないのだろうか?

$ git rebase –i HEAD~2

あ、出てきた。

pick 3ef2f57 third
pick 12d8146 forth

これを、こうする。

s 3ef2f57 third
pick 12d8146 forth

You can fix this with 'git rebase --edit-todo' and then run 'git rebase --continue'.
Or you can abort the rebase with 'git rebase --abort'.

怒られた。

pick 3ef2f57 third
s 12d8146 forth

これはOKで、続けてcommit logの入力を促す画面になる。

$ git log --oneline
354f949 (HEAD -> rb) third and forth
741e4b2 (master) second
48697ff first

 

つまり、上にまとめられる、と覚えておけば良かろうか。
よくないのかもしれないが、私にはそれで事足りそうだ。

2019/09/14

[thingy:91]いろいろ調べる

勢いでThingy:91を買ったものの、詳細を全然知らない。
ユーザーズガイドで調べよう。


構成

nRF9160とnRF52840が載っている。

nRF9160 : LTE-M/NB-IoTモデム

  • Cortex-M33
  • FLASH : 1MB
  • RAM : 256KB

nRF52840 : BLE / NFC

  • Cortex-M4F
  • FLASH : 1MB
  • RAM : 256KB
  • USB

 

なるほど、だからSWDのスイッチがあるのか。
firmwareも、モデムとApplicationに分かれていたが、nRF9160とnRF52840だろう。

ちなみに、Applicationのzipを解答すると、hexファイルたちとzipファイルが入っていた。
zipファイルはモデムのfirmwareだ。

firmwareの焼き方もnrfjprogでやる方法が書かれているのだが、ファイルは<hex>としか書かれていない。
全部焼けば良いのだろうか?
ちょっと怖いので、もう少し後にしよう。


user-programmable LED

たぶん、一番目だって光っているLEDのことだろう。
どういう光り方をしているのか気になる。

  • オレンジ点滅 : 電源ON直後?
  • シアン点滅 : SIMで接続済み?
  • 紫点滅 : GPS検索中
  • 緑点滅 : GPS確定

 

うーん。
いま、青点滅しているように見える。
これがシアンか?
白色のように点滅しているときもあるが、これが紫だろうか。

nRF ConnectでLTEを見ているのだが、アンテナ4本中1本しか立っていない。
室内で窓から遠いので、弱いのか。
だったら、GPSはとても無理だろう。

 

ちなみに、ここでは「点滅」と書いたが、英語ではbreathingになっている。
点滅というよりも、ぼわぼわした明滅だな。


書かれているのは、このくらいだ。

あとはハードウェアの細かいところとか。

[thingy:91]やはりJ-Linkじゃないとだめなのか

nRF Connectのv3.2.0を立ち上げると、Programmerというものがあった。

image

 

こいつならfirmwareを焼くことができる!と思ったのだが・・・

image

 

LTE Monitorは使えるから、Thingy:91をUSB接続してCOMポート経由でアクセスすることはできている。
ということは、やはりJ-Linkじゃないとダメなのか・・・。

 

まだnrfjprogで焼くという手段は残っているので、あきらめるのはもう少し先だ。

[thingy91]LPC-Link2が使えそうだ

さて、私が持っているJ-Link LITE CortexMはThingy:91につなげそうにない。
ならば、J-Link Baseを購入するしかないか。。。

 

とJ-Link Baseの情報を探していると、こちらのサイトが見つかった。

超便利 最強デバッガ J-Link
http://idken.net/posts/2017-08-31-jlink/

この記事の下の方に「各種評価ボードのデバッガをJ-Link OB(On Board)化する」という章がある。
評価ボードをJ-Link OBなるものにできるとか。
そういえば、昔NXPのLPC-Link2を買った記憶がある。

 

https://www.segger.com/products/debug-probes/j-link/models/other-j-links/lpc-link-2/

書いてある通りに、LPCScryptをダウンロードしてインストールし、Firmwareのbinファイルをダウンロードし、インストールしたディレクトリのprobe_firmware/LPCLink2のbinファイルを置き換え、scripts/program_JLink.cmdを実行した。
LCP-Link2はJP1を外してUSBに接続すれば良い。
指示通りにやると焼けた。

 

あとは、Thingy:91とLPC-Link2のJ7を接続し、nrfjprogを実行。

nrfjprog --memrd 0 --n 1024 -f nrf91

おー、でてきたでてきた。

 

さて、firmwareを焼いてみたいものの、どれを焼くと良いのだろうか?
mfwnrf9160101.zipとthingy91fwv022.zipがダウンロードできて、前者はModem Firmware and modem DFU tool (nRF9160 LTE-M/NB-IoT/GPS modem)、後者は単にFirmware for Thingy:91と紹介されている。

展開すると、HEXやらBINが複数あるのだ。
アップデートの説明に飛んだが、よくわからん。

ユーザーズガイドのPDFを読むか。

[thingy91]LTE Deviceの登録

Thingy:91には手持ちのJ-Link LITEが使えなさそうだったので、先にeSIMのアクティベートをやることにした。

https://www.nordicsemi.com/Software-and-Tools/Prototyping-platforms/Nordic-Thingy-91/GetStarted

YouTubeで手順を紹介してくれているようだが、英語が聞き取れないのでUnboxing step by stepの方を読む。

 

 

nRF Cloudのサイトを開いて、私はNordic Devのアカウントを持っていたのでそれでSIGN IN。
プラスアイコンをクリックして、LTE deviceを選択。
SIMの表面に3行の文字列があるが、上2行がICCID。PUKはeSIMの台紙に波線っぽいスクラッチがあるので削ると出てくる。

それを入力して、チェックボックスをONにしてボタンをクリック(チェックボックスに気付かず、ずっと失敗していた・・・)。
うまくいくと住所などの入力になるので、適当に打ち込む。

 

その後に、IMEIとPINを入力してデバイスを登録するらしいのだが・・・うまくいかない。

・・・あれ? Thingy:91のLEDが青点滅から緑点滅に変わっている?
もう一度IMEIとPINを入力してボタンを押すとうまくいった。

青点滅の時

image

 

緑点滅のとき

image

 

単に時間が足りてなかったとかか。

 

もしかしたら日本向けにFirmwareのアップデートがいるのかと思ってあせった。
どうやらFirmwareのアップデートにはJTAGデバッガが必要っぽいからねぇ。

J-Linkの限界

Thingy:91も届いたことだし、JLinkでつないでみよう。
J-Linkといっても、J-Link LITEだがね。

最初はnrfjprogのバージョンが古くてfamilyオプションのnrf91に対応できなかった。
バージョンを上げてつないでみると。。。

 

image

 

えっ?
J-Linkってそんな制限があるの??

 

Firmwareの更新で済めば良いのだが・・・ダメだった。。。

image

 

J-Linkのサポートするデバイスとしては、nRF9610も入っている。

https://www.segger.com/downloads/supported-devices.php

ということは、J-LinkじゃなくてJ-Link LITEがサポートしていないのだろうか。
Thingy:91にはSWD Selectというスイッチがあって、nRF91とnRF52が選べるようだ(今はnRF91)が、せっかくだからnRF91で使えるようになっておきたいじゃないか。

先にeSIMのアクティベートをやるか。

Nordic Thingy:91到着

NordicのThingy:91が届いた。

箱は小さい。

image

 

SIMが入ってるんだ!
eSIMで、フルサイズのように見えるけど内側に切れ目のようなものが入っていて、nano SIMサイズまで小さくできるようだ。この方式はいいですな。

image

image

 

 

一応、搭載されているnRF9160の技適を確認。
よし。

image 

2019/09/07

Zephyr v2.0.0

Zephyrのv2.0.0がリリースされたようだ。

https://github.com/zephyrproject-rtos/zephyr/releases/tag/zephyr-v2.0.0

rc2まで動かしていたのだが、CCCD定義用マクロの引数の数が変わっていたのだけ気付いた。

そんな感じで、v1とは互換がないところがあるかもしれないので、アップデートの際はご注意を。

2019/09/01

[zephyr?]%lluがうまくいかない

Zephyrでuint64_tを扱うことがあった。

64bitの値を文字列にして渡したい。
あまり考えず、

char str[20];
snprintf(str, sizeof(str), “%” PRIu64, val64);

みたいな感じで書いた。

そうするとですな、文字列で”%lu”になってしまうのだ。

 

gccは、gcc-arm-none-eabi-8-2019-q3-updateを使っている。
ターゲットはnRF51822なので、Cortex-M0だ。

ここら辺を見ると%lluのようだから、最初のlだけ消えている・・・?
%luはうまくいくので、全部がダメなわけではないのだよなぁ。
コンパイラかと思っていたのだが、sprintf.cがあるのでZephyrの方かもしれない。

 

CONFIGを見ていると、CONFIG_MINIMAL_LIBC_LL_PRINTFというのがあった。
これをyesにすると、64bitも扱えるようになった。
まあ、普通はいらないよねぇ。。。今回も64bitで扱うけど、実際は32bitも使わないし。。。

2019/08/31

[nordic][zephyr]Advertisingするチャネルを制御したいが、わからんかった

わからんかったシリーズだ。

 

ZephyrでBLEの簡単なことはできるようになったのだが、どんな感じで無線が飛んでいるのか見てみたいときもあるだろう。

そういうときはパケットスニファが便利だ。
私はTIのスニファを使っているのだが、接続後の無線を見たい場合は、チャネルを選んでAdvertisingから接続を待ち受けている。
そして、別のチャネルで接続開始された場合は切断して、またやり直す。

と、非常に効率が悪い。
もっといいやり方もあるのかもしれんのだが、使いこなせていない。

 

それで、私はAdvertisingするチャネルを制限させる、という方法を使っていた。

hiro99ma blog: [nrf51]特定CHへのAdvertisingを止める(デバッグ)
https://hiro99ma.blogspot.com/2015/11/nrf51chadvertising.html

ZephyrとはいえNordicの部分はNordicがサポートしているようなので、同じような手法が使えるんではなかろうか。


上記の記事は、S110 SoftDevice v8.0.0のときだった。
もう4年も前のことで、手元にあったS132(SDK15)を見てみると、ch_37_off、みたいなメンバがいなくなっていた。

今は配列になっているようだ。

typedef uint8_t ble_gap_ch_mask_t[5];

8bit * 5 = 40ということで、1bitが1chを指しているようである。
[0]のbit0がch0と同じなのかな?

 

 

zephyrのv2.0.0-rc1をchannel_maskでgrepすると、残念ながらたくさん出てきた。
さらに残念なことに、たぶん全部PPI関係のようだ。

あきらめて「37」で検索すると、これはこれでたくさん出てくる。。。
が、ようやくそれっぽいものを見つけた。

modules/lib/openthread/third_party/NordicSemiconductor/softdevice/s140/headers/ble_gap.h

なんだ、nRF SDKと同じファイル名ではないか。。。
そしてchannel_maskだったので、単にさっき見逃しただけのようである。


しかし・・・もともとnRF SDKでもchannel_maskはユーザアプリから触れる変数ではなかった。
ble_advertising_start()ならアクセスできるので、そこを書き換えていたのだ。

そして、もっと根本的な問題として、このファイルは使われているのだろうか?というものがある。
openthreadとなっているけど、zephyrのGitHubではなく、west updateで取得したものなのだ。

channel_maskを持っているble_gap_adv_params_tでgrepしたが、そのヘッダファイルしか出てこなかった。
残念だ。。。

 

ならば、advertisingの開始辺りを見てみればよいかと思ったが、よくわからん。
nRF SDKだとGAPだったのだが、subsys/bluetooth/host/hci_core.cあたりにあるようだ。
たぶんset_advertise_enable()だと思っている。あるいは手前のbt_le_adv_start_internal()か。

 

だが、ソースをぱっと見ても追えそうな気がしなかった。
残念だが、今回はあきらめよう。

[win10]WSLをexplorer.exeでアクセスできるのは1903からか?

ZephyrのビルドなどはWSLでも大丈夫だし、そのまま焼くこともできる、と安心していた。

しかし・・・別の環境でやってみると、そうはいかなかった。
explorer.exeで\\wsl$が開かないのだ。

名前がUNCで隠しフォルダなんかにアクセスする時みたいな名前なので「そんな機能知らなかった~」くらいに思っていたのだが、実は新機能だったのか。。。

 

ちなみに、explorerを使うとこう見える。

image

ユーザ名がhirokumaだから、それが/homeの下に見えている。
ネットワーク経由でアクセスするから、WSLとかそういうのは関係ないんだなー、賢いなー、とちょっと感激した私であった。

2019/08/25

nrfjprog.exeをWSLから実行できそうだ

いま、zephyrでビルドしたHEXファイルをnRF51822に焼くとき、west flashを使っている。
jlink用に設定しているので、nrfjprog.exeが呼ばれているのだと思う。

Windowsでzephyrをやっているのは、ほぼこのwest flashのためだ。
試していないが、WSLだとUSBが使えなかったと思うので、nRF Command Line ToolsのLinux版をインストールしても動かんだろう。
nrfjprogだけWindowsでやればいいんだろうけど、それは敗北だろう。

幸い、westをたたくくらいしかやることはないので、WindowsだろうとWSLだろうとそんなに問題は無く、毎回lsなどと打ってしまってエラーになるのがしゃくに障るだけだ。

 

そこで思いついたのが、WSLからWindowsのnrfjprog.exeをたたく、という方法だ。
これだとコマンドはWindows側で実行されるだろうから、USBも動くはずだ。

nrfjprog.exeのシンボリックリンクを作ってnrfjprogという名前を付けたのだけど、うまくいかなかった。
代わりにbashのシェルスクリプトの中でnrfjprog.exeを実行するようにして、ファイル名をnrfjprogにするとよさそうだった。

 

最初は、HEXファイルが見つからないと怒られた。
Windows風のパスじゃないとダメなのかとwslpathでやってみたが、それでもダメ。
試していて分かったが、実際にファイルのある場所がWindows側だったためか、そのディレクトリにシンボリックリンクした先のファイルを指定したからかわからないが、ともかくそんな理由で見つからなかったようだ。

作業ディレクトリをWSL側にしたところ、wsl flashがそのまま動いた。


Windows側のフォルダに置いておくと、他の開発作業と同じ管理ができるから楽だったのだが、WSL側にあったとしても最近はUNCの\\WSL$でアクセスできるようになっていたので、クイックアクセスか何かでピン留めしておけばよかろう。

2019/08/24

[zephyr][ble]serviceを作る (3)

LEDサービスを作った。

https://github.com/hirokuma/zephyr_ble_led/tree/135c5c0c40e53450e402f4b42bccc39a6ef8d63c

characteristicは1つで、0以外を書き込むとLEDオン、0だったらLEDオフ。
これだけだ。

 

main.cはsamplesのperipheral_hrなどとほとんど同じだろう。
接続したときにLEDサービスのinitを呼び出している。あと、notifyがなくなった。

 

LEDサービスのinitでGPIOの初期化をしている。
これは今ひとつだな。接続し直すたびに呼び出されてしまう。

write_ct()でWRITE時の処理をさせている。
関数名はそのまま使ったのだけど、この_ctはたぶんCurrent Timeの略だな。。。
readがないので値は保存しなくても良いのだけど、一応やっておいた。

 

サービスの登録だが、特に関数を呼び出している様子が無い。
これはたぶん、BT_GATT_SERVICE_DEFINEマクロがうまいことやっているのだろう。
staticが付けられないのが残念だが、それによって簡略化できているのだろう。

 

実装はこんな感じで、難しいところがない。
SRAMもそんなに消費していない。

Memory region         Used Size  Region Size  %age Used
           FLASH:       84851 B       256 KB     32.37%
            SRAM:       15120 B        16 KB     92.29%
        IDT_LIST:         136 B         2 KB      6.64%

 

どっちかといえば、UUIDってどういう決め方だったかとか、そういう調査の方に時間がかかった。
nRFgo Studioで作ったときの習慣で、場所でいえば0000xxxx-0000-1000-8000-00805F9B34FBのxxxxだけをServiceとCharacteristicで変更しているのだけど、それぞれ新規でUUIDを生成した方がいいんだろうか?

まあ、要調査だな。

[zephyr][ble]serviceを作る (2)

menuconfigの続き。

 

menuconfigでSaveすると、ファイルはbuildの下に.configとして保存されるようだ。
このファイルは、—prestineすると消えてしまうようである。

なので、これをprj.confとして保存してしまえばいいや、と思ったが、CONFIGがずらずら並んでいると気が滅入る。
どうやって減らそうかと思ったら、advancedということでminimal configをSaveするメニューがあった。

# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)
CONFIG_GPIO=y
CONFIG_SOC_SERIES_NRF51X=y
CONFIG_ARM=y
CONFIG_SERIAL=y
CONFIG_UART_0_NRF_UART=y
CONFIG_ENTROPY_NRF5_THR_THRESHOLD=4
CONFIG_ENTROPY_NRF5_ISR_THRESHOLD=12
CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y
CONFIG_CLOCK_CONTROL_NRF_K32SRC_250PPM=y
CONFIG_BT=y

今の設定で出力させたのだが、THRESHOLDの設定を変更したかどうか記憶にない。。まあいいや。

 

samples/bluetooth/peripheral_hrと比べて、いくつか追加した。

# Generated by Kconfiglib (https://github.com/ulfalizer/Kconfiglib)
CONFIG_GPIO=y
CONFIG_SOC_SERIES_NRF51X=y
CONFIG_ARM=y
CONFIG_SERIAL=y
CONFIG_UART_0_NRF_UART=y
CONFIG_ENTROPY_NRF5_THR_THRESHOLD=4
CONFIG_ENTROPY_NRF5_ISR_THRESHOLD=12
CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y
CONFIG_CLOCK_CONTROL_NRF_K32SRC_250PPM=y
CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_GATT_DIS=y
CONFIG_BT_GATT_DIS_MODEL="LED control"
CONFIG_BT_GATT_DIS_MANUF="hiro99ma"
CONFIG_BT_SMP=y
CONFIG_BT_DEVICE_NAME="BLE Test"

標準の項目については大半がCONFIGの設定だけで済ませられそうだ。
こりゃ、便利だわ。

Memory region         Used Size  Region Size  %age Used
           FLASH:       76267 B       256 KB     29.09%
            SRAM:       12988 B        16 KB     79.27%
        IDT_LIST:         136 B         2 KB      6.64%

まだまだ大丈夫だ。


Device Information Serviceを組み込んだので、これだけでも何か動いていいはずだ。
samples/bluetooth/peripheral_hrからBASとHRSを削除すればいいだろう。

 

main.cを見て気付いたが、BASとHRSっぽいのって、ほぼnotify()だけではないか。。。
こんなにコードが少なくて済むのか。
まあ、自分でserviceを作ってないからだとはいえ、助かるな。

 

Advertisingする設定が気になる。
peripheral_hrではこんな設定になっていた。

static const struct bt_data ad[] = {
	BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)),
	BT_DATA_BYTES(BT_DATA_UUID16_ALL, 0x0d, 0x18, 0x0f, 0x18, 0x05, 0x18),
};

UUIDはいつも128bitだったような気がするが、ここは16bitなのか。
じゃあ、16bitの設定はどこで? 128bitはどうやって??

 

やはりコードだけじゃ無理だな。

https://docs.zephyrproject.org/latest/guides/bluetooth/bluetooth-dev.html#initialization

説明はないが、ここではadが2byte分しかないのでわかりやすい。
0xaa, 0xfeはGoogleだろう。

https://www.bluetooth.com/ja-jp/specifications/assigned-numbers/16-bit-uuids-for-members/

image

BT_DATA_UUID16_ALLは、ALLとついているから列挙しているのだろう。
serviceなんかで、こういう値があった気がするのだが。。。

https://www.bluetooth.com/ja-jp/specifications/gatt/services/

そういえば、GenericだかGeneralだかがあったな。Device Information Serviceもそうだし。

 

Core仕様書が出てきた。
ここではv4.2を見ている。たぶんv5.1でも同じ章番号だろう。

Vol 3 – Part B – 2.5.1 UUID

https://www.bluetooth.com/specifications/assigned-numbers/service-discovery/

Serviceはここから探せそうだ。

https://www.bluetooth.com/specifications/gatt/services/

0x18があるのでGenericなServiceの番号かと思ったのだけど、なんか違う気がする。
Current Time Serviceは使って無さそうだから。
あまり深く考えず、AdvertisingだからAdvertisingのところを調べるべきだろう。


Advertisingは、GAP(Generic Access Profile)だ。
GAPは、Vol.3 – Part Cだ。

https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile/

BT_DATA_UUID16_ALLは0x03なので、«Complete List of 16-bit Service Class UUIDs»を表しているのだろう。
0x09の«Complete Local Name»はBT_DATA_NAME_COMPLETEだから、COMPLETEを使ってくれれば悩まなかったのに。。。

ともかく、Service Classの16bit UUIDを列挙しているということだろう。
あれ、じゃあさっきの考えは間違ってなかったのか?

0x180d = Heart Rate
0x180f = Battery
0x1805 = Current Time

普通はこういう情報に現在時刻が必要よね?ということで付けたのだろうか。
じゃあ、最初に「Googleだろう」って書いたのは間違っているのかな。

 

まあいい。
今回は、Device Information Serviceだけだから、0x180Aでよかろう。
ビルドして焼いてみる。

image

image

むう、あっさり動いた。


さて、ここで出てきたUUIDのF1:76:C0:98:07:47はなんだろう?
MACなんだろうけど、どうやって指定するんだっけ。

6 byte mac address oft nrf52832 - Nordic DevZone
https://devzone.nordicsemi.com/f/nordic-q-a/31062/6-byte-mac-address-oft-nrf52832

ああ、FICRか。
nRF52832のデータだけど、こんな感じでDEVICEADDR[]が入っているようだ。

https://infocenter.nordicsemi.com/index.jsp?topic=%2Fcom.nordic.infocenter.nrf52832.ps.v1.1%2Fficr.html

nRF51822だと少し違うのかな?

nRF51822 - ID - Nordic DevZone
https://devzone.nordicsemi.com/f/nordic-q-a/6452/nrf51822---id

たぶん書き換えられないんだろう。

2019/08/20

[zephyr][ble]serviceを作る (1)

nRF51822(FLASH: 256kB, SRAM: 16kB)に、Zephyr OSを載せて、BLEのPeripheral serviceを作って動かしたい。

もちろん、characteristicなんかも作るが、とにかくZephyrでBLEを動かしたいのだ。


師匠(?)の記事を読もう。

Zephyr RTOS Programming - Qiita
https://qiita.com/deden/items/c7e6fddf7dffb3af5f06#mpu6050

最初は、LEDをON/OFFするだけのserviceでよいだろう。
service類の作り方が分かれば、あとはデバイスを動かすだけになるはず。

 

ソースコードの前に、prj.confだ。

https://github.com/github-deden/zephyr_ble_mpu6050/blob/a97351f3165452286fb4b4a265eb879906db57ae/peripheral_mpu6050/prj.conf

CONFIGの種類が分からないと、削れるかどうかも分からんな。

Configuration Options — Zephyr Project Documentation
https://docs.zephyrproject.org/latest/reference/kconfig/index.html

やめてー! おおすぎー!

 

・・・まずは、新規プロジェクトを作るところからやろう。


適当なところに作業ディレクトリを作り、CMakeLists.txtとかmain.cとかを置いていけば良いらしい。

https://docs.zephyrproject.org/latest/application/index.html#overview

RustだとHello Worldを自動的に作ってくれるが、そこまではやってくれないらしい。
なので、ここはsamplesのhello_worldをコピーする。
ビルドも確認。

Memory region         Used Size  Region Size  %age Used
            FLASH:       10756 B       256 KB      4.10%
             SRAM:        2356 B        16 KB     14.38%
         IDT_LIST:          56 B         2 KB      2.73%

むう、これだけで15%消費するのか。。。まあ、ほぼOSのみと思っておこう。


では、あきらめてCONFIGを見ようかと思ったが、そういえばLinux Kernelならmenuconfigがあった。
似たようなものがあるんじゃなかろうか?

Zephyr News, 14 May 2018 | Foundries.io
https://foundries.io/insights/2018/05/14/zephyr-news-20180514/

pip3でscripts/requirements.txtをインストールすると使えるのか?

やってみたのだが、hub==2.0がないとかでエラーになってしまった。。。
masterにはhubがなかったので、v2.0.0-rc1で入っていただけなのか。
その行を削除するとインストールは正常に終わった。

 

だが、menuconfig的なものが無い。
”python menuconfig”で探すと、kconfiglibというものがでてきた。

https://pypi.org/project/kconfiglib/

Zephyrという文字も出てきているので、これを使っているのだろう。
が、これはライブラリであって、ツールではないよな。。

https://docs.zephyrproject.org/latest/application/index.html?highlight=menuconfig#overriding-the-default-configuration

ああ・・・westなのか。

west build –t menuconfig

で出てきた。

image

あとは、ここからじわじわ眺めていくか。

とりあえずBluetoothだけ有効にしてビルドした。

Memory region         Used Size  Region Size  %age Used
           FLASH:       33632 B       256 KB     12.83%
            SRAM:        9496 B        16 KB     57.96%
        IDT_LIST:         136 B         2 KB      6.64%

ははは、もう半分以上使われてしまった。
でも、まだ余裕があるな。
リンクも何もしていないので、これからということか。

2019/08/19

[vscode]Cortex-M disassembly

昨日、nRF51822をvscodeのCortex Debugでデバッグし、ブレークポイント位置がソースコードと微妙に違うという話になった。

inlineとかあるから、アセンブラのコードも見えると良いのだがやり方が分からん、というところで終わった。

 

が、Cortex Debugの機能にあった。

image

たぶんデフォルトがAutoになっているので、Forceを選択。

image

出た!

出たんだが、

  • mixed表示じゃないので、読み解くのがめんどくさい。
  • Cソース側で打ったブレークポイントが見えない。Disassembly側でぶれーくポイントの設定はできるし、止まる。
  • CソースとDisassemblyを両方表示しても、Disassembly側しかステップカーソルが移動しない。

など、ちょっと微妙だ。
まあ、gcc/gdbの範囲でしか対応できないとは思うが、もうちょっと表示が豊かになるとうれしい。

 

レジスタの表示もできるのだが、16進数で表示してくれないのもちょっと困る。
あからさまにアドレスが入るようなレジスタは16進数になっているのだが、汎用レジスタは10進数のようだ。

これからに期待ですな。

2019/08/18

Zephyr (10)

夏休みが終わる・・・集中Zephyr学習もたぶん今回が最後だろう。
あとは、動かしながら試すまでだ。

 

最後に、デバッグの仕方を調べておこう。

Eclipse Debugging
https://docs.zephyrproject.org/latest/application/index.html#eclipse-debugging

えー、eclipseなのかー。
いや、eclipseが悪いわけではないのだけど、最近はvscodeをよく使っているので、これもvscodeでやりたいのだが。

 

まあ、eclipseを使えるということは、vscodeも使えるはずだ。
つまり、vscodeでCortex-M0のデバッグができればよいというだけのことだ。


こういうextensionがあった。

Cortex-Debug - Visual Studio Marketplace
https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug

インストールして、settings.jsonにJLinkのGDB ServerCLの実行パスを設定しておくとよさそうだ。
8.3形式じゃないとダメな気がするので、短くしておこう。というか、スペースがない名前にしたいだけなんだけどね。

"cortex-debug.JLinkGDBServerPath": "c:\\PROGRA~2\\SEGGER\\JLINK\\JLINKG~2.EXE"

launch.jsonはこうした。

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Cortex Debug",
            "cwd": "${workspaceRoot}",
            "executable": "./build/zephyr/zephyr.elf",
            "request": "launch",
            "type": "cortex-debug",
            "servertype": "jlink",
            "device": "nrf51",
            "interface": "swd",
            "ipAddress": null,
            "serialNumber": null,
            "armToolchainPath": "c:\\Winappli\\gcc-arm-none-eabi-8-2019-q3-update-win32\\bin"
        }
    ]
}

 

これで、blinkyを動かしたのだが、一応デバッグできているようだ。
「一応」がつくのは、ブレークポイントの位置に止まってくれないからだ。

image

ずれているわけではなく、gpio_pin_write()には止まってくれないようだ。
最適化の関係か?

だったらアセンブラコードとあわせて見るとわかるのだけど、出し方が分からん。
残念だが、今回はこれで終わろう。

Zephyr (9)

Zephyrで、自分用のボード設定を作ろう!

私が使っているのはnRF51822のrevision 2。CEAAなので、FLASHが256kB、SRAMが16kBだ。
GPIOは開放されているので、こっちで好きに使ってよい。

HR serviceを使ったサンプルですらSRAMの使用量は93%近くあるので、余裕を持って作りたいなら32kB以上はSRAMがほしいところだ。


新しいボードをサポートしたい場合はテンプレートを使ってくれ、と書いてある。が、これはドキュメントのテンプレートであってボード設定のテンプレートではない。

https://docs.zephyrproject.org/latest/boards/index.html

今回は、BLE Nanoに近い構成で、J-LINKだけPCA10028の設定を使いたいというだけなので、BLE Nanoをコピーして作れば良かろう。

Linuxの場合は、defconfigをコピーして.configにしてからmenuconfigでカスタマイズしていたのだが、Zephyrも似たようなツールがあるのだろうか?
・・・いかんいかん、先走っちゃいかんな。
まずはボードの設定を作ろう。

 

ファイルは6つ。

board.cmake
Kconfig.board
Kconfig.defconfig
XXX.dts
XXX.yaml
XXX_defconfig

XXXはボード名のようだ。
他にもファイルが置けるようだが、まあ今回はよかろう。

https://docs.zephyrproject.org/latest/application/index.html#boards

 

ファイルの中身を見ただけだが、だいたいこんなところだろうか。

board.cmake : west flashなどで使いそうな設定
Kconfig.board : menuconfigで使いそうな設定
Kconfig.defconfig : 自分のボードが有効なときにmenuconfigで使いそうな設定
XXX.dts : Device Tree Script、だったか。ボード/デバイスの設定。
XXX.yaml : マイコンの種別、RAMのサイズのような設定
XXX_defconfig : CONFIGのデフォルト値

 

今回のnRF51822はBVMCN5102を使っているので、XXXはnrf51_bvmcn51にしよう。
QFAAとかQFACとか設定があるのだが、dtsiを見るとFLASHとRAMのサイズで分けているだけのように見える。

51822_QFAA : FLASH=256, SRAM=16
51822_QFAB : FLASH=128, SRAM=16
51822_QFAC : FLASH=256, SRAM=32

となると、yamlファイルに書いてあるRAMサイズは人間用なのだろうか?
まあいいや。

そして、board.cmakeはPCA10028の設定を使う。
内蔵RCを使うようにもしておこう。
これくらいしか変更点が思いつかない。


peripheral_hrをビルドした。

Memory region         Used Size  Region Size  %age Used
           FLASH:      109535 B       256 KB     41.78%
            SRAM:       17712 B        16 KB    108.11%
        IDT_LIST:         152 B         2 KB      7.42%

あれー。
nrf51_blenanoを変更しただけなのに。。。
念のためボードをnrf51_blenanoにしてビルドすると、こっちもRAMオーバーした。
なんだこりゃ?
昨日はなんだったのだ??

WSL版でも同じようになったから、そうなんだろう。
CONFIG_SERIALやCONFIG_CONSOLEをnoにしていったが、さほど効果無し。
zephyrがmasterブランチだったので、v2.0.0-rc1タグにしたが、変わらん。

 

うーん、今日、west build系がなぜかうまくいかなかったのでbuild/ごと削除したのだが、それが原因だろうか。原因というか、昨日はキャッシュが効いたままになってて、なんか外れた状態になっていたのか?

 

zephr_prebuilt.mapを見てみる。大きいのはこの辺か。

bss        0x1810
noinit     0x2551
datas     0x05cb

なんか、ちょこちょこlogという名前が出てくる。
そういえば、prj.confから削った気がする。

CONFIG_BT_DEBUG_LOG=n

CONFIG_PRINTKなんかがnだったので消えるんだろうと思っていたが、prj.confに入っているからリンクされるのか。
prj.confを書き換え、buildディレクトリごと削除して、west build。

>west build -b nrf51_blenano samples\bluetooth\peripheral_hr –pristine

Memory region         Used Size  Region Size  %age Used
            FLASH:       86591 B       256 KB     33.03%
             SRAM:       15364 B        16 KB     93.77%
         IDT_LIST:         152 B         2 KB      7.42%

効くねぇ。

では、BVMCN51でビルドし直す。

Memory region         Used Size  Region Size  %age Used
           FLASH:       85667 B       256 KB     32.68%
            SRAM:       15328 B        16 KB     93.55%
        IDT_LIST:         152 B         2 KB      7.42%

微妙な差は、CONFIG_SERIALやCONFIG_CONSOLEをnoにしているからか?
BLE Nanoと同じ設定にしてビルドし直す。

Memory region         Used Size  Region Size  %age Used
            FLASH:       86591 B       256 KB     33.03%
             SRAM:       15364 B        16 KB     93.77%
         IDT_LIST:         152 B         2 KB      7.42%

同じになった。
内蔵RCを使ったとしても引数が変わるだけだから、その差は無いみたいだ。

CONFIG_COMPILER_OPT=”-O3”を設定するとこうなった。

Memory region         Used Size  Region Size  %age Used
            FLASH:      134582 B       256 KB     51.34%
             SRAM:       15380 B        16 KB     93.87%
        IDT_LIST:         152 B         2 KB      7.42%

CONFIG_SIZE_OPTIMIZATIONS=yでは変わらず。デフォルトがそうなのか。

 

consoleは使わないがUARTは使いたいので、CONFIG_SERIAL=yにしておこう。

Memory region         Used Size  Region Size  %age Used
            FLASH:       86383 B       256 KB     32.95%
             SRAM:       15348 B        16 KB     93.68%
         IDT_LIST:         152 B         2 KB      7.42%

ちなみに、LOG_BACKEND_RTTとUSE_SEGGER_RTTをyesにするとRAMがあふれてしまった。


動いてそうなので、GitHubに置いておこう。

https://github.com/hirokuma/zephyr_boards_arm_nrf51_bvmcn51/tree/89f64c97200c7477008342182a447dfbb1e8cc7c

west flashで焼けるのが便利ですな。

2019/08/17

Zephyr (8)

では、BOARDはble nanoにしたまま、prj.confで内蔵RCを使うように設定してビルドする。

Memory region         Used Size  Region Size  %age Used
            FLASH:       85823 B       256 KB     32.74%
             SRAM:       15108 B        16 KB     92.21%
         IDT_LIST:         152 B         2 KB      7.42%

ちょっと増えたが、まあよかろう。44byteだし、そのくらいは何かあるんだろう。
nrfjprogで焼くと、動いた。
あの苦労は何だったのか。。。


west flashだが、runnerを選択してjlinkを使うことができるらしい。

https://docs.zephyrproject.org/latest/guides/west/build-flash-debug.html#choosing-a-runner

やってみたのだが、BLE NanoはDAPなのでサポートしてない、みたいなメッセージが出てきた。
ということは、自分用のボード設定を作ってしまえばできるんじゃなかろうか。

次回はそれだな。

Zephyr (7)

そろそろ、BLEのサンプルを動かそう。


その前に。。

NordicSemiconductorのGitHubに何かないか見ていたら、nrfxというリポジトリがあった。

Standalone drivers for peripherals present in Nordic SoCs

もしかしてSoftDeviceの部分をオープンソースにしたのか??と思ったけど、Peripheral部分だけのようだ。
nRF5 SDKから切り出したというところか。
MDKのことも書いてあるので、自分のところで出したSDKだけだと利用者が増えないので、切り出しやすいところを出して、他の環境でも使えるようにしているのだろうか。


あと、ログも。
RTTが使えるかどうかで効率はずいぶん変わってくる。

https://docs.zephyrproject.org/latest/samples/subsys/logging/logger/README.html

loggerのサンプルを動かしたが、RTT Viewerには出てくれない。
ファイルを見てみると、prj.confの他にprj_rtt.confというものもあった。

CONFIG_LOG_BACKEND_RTT=y
CONFIG_USE_SEGGER_RTT=y

差分はこの2行の追加だけ。

CMakeLists.txtに

set(CONF_FILE "prj_rtt.conf")

を追加(末尾ではダメだったので、minimumの次くらいに追加)してビルドして焼き直すと、RTT Viewerに現れた。

image

printk()はそのまま、時間が出ているところはログ関数、というところだろうか。

ログ出力するだけのサンプルかと思ったけど、スレッド使ったりして、簡単に読めるものではなかった。
main()ないし。

ともかく、RTTを有効にすればRTTに出力できることは分かった。

 

違うCONFファイルを使いたい場合は、CMakeLists.txtに書かずとも、westのオプションでよいようだ。

https://docs.zephyrproject.org/latest/application/index.html#basics


では、BLEのサンプルを動かそう。
自分で書くのは、ずいぶん後になるだろう。

Bluetooth: Peripheral HR
https://docs.zephyrproject.org/latest/samples/bluetooth/peripheral_hr/README.html

私が使っているnRF51822のボードには外部RCがないので、RTT Viewerを使いたいのもひっくるめてprj.confに追記した。

CONFIG_LOG_BACKEND_RTT=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_CLOCK_CONTROL_NRF5_K32SRC_RC=y
CONFIG_CLOCK_CONTROL_NRF5_K32SRC_250PPM=y

 

>west build samples\bluetooth\peripheral_hr --pristine

うーん、CLOCK_CONTROL_NRF5_K32SRC_RCなど知らない、といわれてしまった。
Nordicのブログに書いてあったのだが、時代が変わったのだろうか。

今はNRF5ではなくNRFだけでになったようだ。

https://github.com/zephyrproject-rtos/zephyr/blob/v2.0.0-rc1/drivers/clock_control/Kconfig.nrf#L30

では、修正を。

CONFIG_LOG_BACKEND_RTT=y
CONFIG_USE_SEGGER_RTT=y
CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC=y
CONFIG_CLOCK_CONTROL_NRF_K32SRC_250PPM=y

ビルドはできたが、焼いても動かない。
blinkyの点滅を先頭に組み込んだが、それも動いていなさそうだ。
うーん。

 

あれ。

Memory region         Used Size  Region Size  %age Used
           FLASH:      110415 B       256 KB     42.12%
             SRAM:       20144 B        32 KB     61.47%
         IDT_LIST:         152 B         2 KB      7.42%

SRAMが61.47%の使用率・・・。
私が使っているnRF51822はrevision 2なのだが、revision 2のSRAMは16kB・・・。

image

嘘・・・私のnRF51822、SRAM少なすぎ?

 

main.cを削っていったが、16kB以内にすることができなかった。
あとちょっとなのだけど、これで動いたとしてもアプリがほとんど書けないということになるので、やる気が出ない。。

 

nRF52を使おうとしたら、BLE Nanoが出てきた。
うちにあるのは古いやつなので、これもたぶん16kBだろう。
こっちはSupport Boardなので、一応試しておこう。

Memory region         Used Size  Region Size  %age Used
           FLASH:       84495 B       256 KB     32.23%
            SRAM:       15064 B        16 KB     91.94%
        IDT_LIST:         152 B         2 KB      7.42%

入るんだ・・・。
私のCONFIGの削り具合が足りてなかったということだろうか。

そして、BLE NanoはMbedっぽくなっていて、DAP対応?しているため、west flashで焼くことができる。
焼き終わると立ち上がって、Advertisingしだした。

image

nRF ToolboxでHRMを使うと、ちゃんとバッテリーとHRが見える。

 

ということは、私が使ってるnRF51822だってできるはずだ。
次はそれを試そう。

Zephyr (6)

ここまでエミュレータで動かしただけだったので、そろそろ実機で動かしたいところだ。

動かしたいところなのだが・・・夏休みを終えて作業場に戻ってきてしまった。
となると、作業場のPCにもZephyrの環境を作りたい。
今までのPCは何だったかというと、移動用のPCだったのだ。

作業用PCもWindowsなのだが、同じようにWSLにインストールするのも面白味がない。
それに、WSLだとUSBが使えないので、ウェストフラッシュ!(west flushのこと)が使えないのだ。
だからWindows Nativeのインストールをしよう。

 

というわけで、今回はまたZephyr v2.0.0-rc1をインストールする話だ。 

https://docs.zephyrproject.org/latest/getting_started/index.html

pythonのことはスルーして、required toolsのインストールを行う。

https://docs.zephyrproject.org/latest/getting_started/installation_win.html

前回は、ここのOption 2になるはずだ。
今回はOption 1。recommendedだから、すんなりいくだろう。

PowerShellでもよいらしいが、私はcmdしか知らん。
そして、chocolateyというツールを使うらしい。思い出せないが、他の環境を作るときにこの手のツールを使ったことがある。Windows用のパッケージマネージャーの1つだ。
sudo …じゃなくてAdministratorでインストールする方を選択。そうじゃない方はめんどくさそうだったから。

何も考えずにchocoを使ってインストールしたのだけど、Noteを見ると、別に手動でインストールしてもよいらしい。
だが、やってしまったのだ。Option1の5と6を実行すると、WindowsのPATHに追加されていた。

image

まあ、インストールの説明って、どうしてもそうなるよな。
私も最近までインストールの資料を作っていて、細かい説明を書くのが面倒・・というか無理になってきたので、Ubuntuのaptを使った説明しか書かない、みたいなことをやっていたのだ。

image

binの中を見てみたけれど、gitなんかはコマンドが入ってないな。pythonもない。
ただ、pythonをcmd.exeで実行すると、Microsoft Storeのアプリが立ち上がってPythonアプリのインストールが出てくる。
いや、gitもpythonも、もうインストールされているのよ?
そこは考慮してくれないの??
いま気付いたけど、cmakeはProgram Filesにインストールしたの???

再起動して分かったが、cmakeもgitもProgram Filesにインストールされていて、コントロールパネルの「プログラムと機能」にも出ていた。 
「プログラムと機能」にCMakeが出ていたので「昔インストールしてたやつだろう」と思って削除したら消えていたので分かった。。。


あれこれやって、gitにもPATHを通して、west initからやり直す。
そして、”Zephyr (2)”の要領でZEPHYR_TOOLCHAIN_VARIANT=hostにして、ボードはqemu_x86にする。

そうすると、「指定されたファイルがが見つかりません」になった。
おそらくdts.cmakeの${CMAKE_C_COMPILER}だろう。
Windowsの場合、標準のコンパイラなどないから、gccなどをインストールするのだろう。

 

でも、そうするとWSLでいいやん、という気分になるので、QEMUを使うのは止めよう。

set ZEPHYR_TOOLCHAIN_VARIANT=gnuarmemb
set BOARD=reel_board
set GNUARMEMB_TOOLCHAIN_PATH=C:\Winappli\gcc-arm-none-eabi-8-2019-q3-update-win32

こんなのをzephyrrc.cmdというファイルに保存して、c:\Users\xxxxの直下に置いた。west initなどしてダウンロードしたzephyr-env.cmdは、%userprofile%\zephyrrc.cmdを読み込むようになっているのだ。
west buildは、通った。

 

そして!
ようやくwest flashところまでやってきた。

D:\Prog\Zephyr\zephyr>west flash
-- west flash: rebuilding
ninja: no work to do.
-- west flash: using runner pyocd
-- runners.pyocd: Flashing file: D:/Prog/Zephyr/zephyr/build/zephyr/zephyr.hex
0000405:WARNING:common:STLink and CMSIS-DAPv2 probes are not supported because no libusb library was found.
No connected debug probes
0000417:CRITICAL:__main__:uncaught exception:
Traceback (most recent call last):
  File "c:\python37\lib\site-packages\pyocd\__main__.py", line 338, in run
    self._COMMANDS[self._args.cmd](self)
  File "c:\python37\lib\site-packages\pyocd\__main__.py", line 468, in do_flash
    with session:
  File "c:\python37\lib\site-packages\pyocd\core\session.py", line 284, in __enter__
    assert self._probe is not None
AssertionError
ERROR: command exited with status 1: pyocd flash -e sector -t nrf52 -f 4000000 D:/Prog/Zephyr/zephyr/build/zephyr/zephyr.hex

私としてはpyocdじゃなくてJ-Linkを使ってやりたいのだがね。

Nordic nRF5x Segger J-Link
https://docs.zephyrproject.org/latest/guides/tools/nordic_segger.html

残念ながら、west flashではできないようだ。
あれ・・・私がWSLを使わないでセットアップした理由はなんだったっけ。。。


ともかく、実機に焼いて動かす。
書いてあるとおりにHEXを焼いて、RTT Viewerを起動。
何も出ない。

 

そもそも、printk()が自動的にRTTを使ってくれるのかどうかわかっていない。
単にUARTに吐き出すだけかもしれない。
もう少しわかりやすく、LEDの点滅をさせよう。

https://docs.zephyrproject.org/latest/samples/basic/blinky/README.html

west build samples\basic\blinky --pristine
nrfjprog --eraseall -f nrf51
nrfjprog --program build\zephyr\zephyr.hex -f nrf51
nrfjprog --reset -f nrf51

なんか出た。
使ったボードはPCA10028じゃないけど、LED0のポートはGPIOとして使えるようになっていたので、そこにつなげた。

image

 

せっかくなので、オシロで間隔を測った。
若干、1秒よりも短いが、まあ仕方ないか。

image

 

あっさり動いたように書いたが、最初は別のGPIOを使おうとして動かなかったのだ。
ポート番号さえあってりゃいいんだろう、と思っていたけど、nRF51はGPIO0.xxという割り当てなので、GPIO0のところをdevice_get_binding()でやってやらんといかんらしい。

ここら辺になると、そろそろZephyrでの書き方というものを調べねばならぬだろう。