ラズパイで動くアプリを開発したい
↑をやろうとしたとき2つのやり方があると思います
-
ラズパイ上で開発
sshで接続してvimなどテキストエディタで開発
コードを書いたらすぐに実行できる
使い慣れた開発環境を使いづらい
-
PC上で開発
使い慣れた開発環境、エディタを使える
コードをすぐに実行できない、ラズパイに送って必要であればbuildしてなどが必要
そんなときにbalenaを使ってみましょうー
###balenaとは
- ヨーロッパのスタートアップ(だったはず)
- IoTのデバイス管理とアプリ開発サービス
- dockerベースのソリューション
- ローカルで書いたコードをcloud上にpushするとdocker buildしてデバイスにdeployしてくれる
- アプリ10個まで無料(だったばず)
###開発の流れ
- cloud上でアプリを作る
- OSのimageファイルがダウンロードされる
- ダウンロードしたimageファイルをsdに焼いてラズパイを起動
- cloud上にラズパイが登録される
- 登録されたラズパイにコードをローカルからpush
初めての人は登録したら、Create applicationをクリックします。
登録はgoogleやgithubのアカウントでできます。
デバイスのタイプでラズパイを選択して名前をタイプして、applicationを作成します。
Add deviveを選択します。
Download balenaOSをクリックすると、imgファイルのダウンロードが始まります。
wifi + Ethernetを選択すると、SSIDとパスワードを設定済みのimgファイルを作成することもできます。
今回はEthernet onlyで行います。
ダウンロードしたらファイルを解凍して、
imgファイルを microsdに書き込みます。
書き込みが完了したらsdカードをラズパイに挿して電源を入れます。
しばらくすると balenaの画面上でラズパイが認識されます。
クリックするとlogやブラウザ上から接続できるコンソールがあります。
リブートやアプリの再起動も可能です。
これでアプリを作成する準備ができました。
documentのGet startedで使われている Balena Seed Projects、ようはひな型プロジェクトを使います。
今回は go を選択しました。
https://www.balena.io/docs/learn/more/examples/seed-projects/
https://www.balena.io/docs/learn/getting-started/raspberrypi3/go/
※github
https://github.com/balena-io-examples/balena-go-hello-world
↑これをlocalにcloneします。
C:\Users\k-satou\go\src\balena>git clone https://github.com/balena-io-examples/balena-go-hello-world.git
Cloning into 'balena-go-hello-world'...
remote: Enumerating objects: 56, done.
remote: Total 56 (delta 0), reused 0 (delta 0), pack-reused 56
Unpacking objects: 100% (56/56), done.
C:\Users\k-satou\go\src\balena>
appフォルダにあるmain.goファイルのソースはこんな感じです。
hello worldを返すhttpサーバですね。
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello, world (from go!)\n"))
})
addr := ":80"
fmt.Println("Example app listening on port ", addr)
log.Fatal(http.ListenAndServe(addr, nil))
}
ではこれをラズパイで動かしたいわけですが、pushするためにlocal PCにbalena-cliをインストールしておきます。
お使いのOSに合わせてインストールしてください。
https://github.com/balena-io/balena-cli/releases
インストールしたら適時PATHに通して、balena loginと打って自分のアカウントでloginします。
C:\work>balena login
_ _
| |__ __ _ | | ____ _ __ __ _
| '_ \ / _` || | / __ \| '_ \ / _` |
| |_) | (_) || || ___/| | | || (_) |
|_.__/ \__,_||_| \____/|_| |_| \__,_|
Logging in to balena-cloud.com
? How would you like to login? Credentials
? Email: xxx@xxx.com
? Password: [hidden]
Successfully logged in as: g__xxx
Find out about the available commands by running:
$ balena help
If you need help, or just want to say hi, don't hesitate in reaching out
through our discussion and support forums at https://forums.balena.io
For bug reports or feature requests, have a look at the GitHub issues or
create a new one at: https://github.com/balena-io/balena-cli/issues/
C:\work>
login成功したら balena apps と打つと自分の作成したアプリのlistが表示されます。
C:\work>balena apps
ID APP NAME DEVICE TYPE ONLINE DEVICES DEVICE COUNT
1648225 raspberry raspberrypi3 0 1
1706473 sample-app raspberrypi3 1 1
C:\work>
これで先ほどcloneしたアプリをpushできる準備が完了したので、pushしてみます。
git cloneしたフォルダに移動して、balena push <アプリ名> を実行します。
するとローカルのファイルがbalena cloud上に転送されてdocker buildが走ります。
C:\Users\k-satou\go\src\balena\balena-go-hello-world>balena push sample-app
[Info] Starting build for sample-app, user g__610
[Info] Dashboard link: https://dashboard.balena-cloud.com/apps/1706473/devices
[Info] Building on arm01
[Info] Pulling previous images for caching purposes...
[Success] Successfully pulled cache images
[main] Step 1/7 : FROM balenalib/raspberrypi3-golang:latest-build AS build
[main] ---> 9a43177841ca
[main] Step 2/7 : WORKDIR /go/src/github.com/balena-io-projects/app
[main] ---> Running in 00561cfde90d
[main] Removing intermediate container 00561cfde90d
[main] ---> 5dbdd5316d49
[main] Step 3/7 : COPY /app ./
[main] ---> 6f05e3a48c17
[main] Step 4/7 : RUN go build
[main] ---> Running in 8a4652f7c303
[main] Here are a few details about this Docker image (For more information please visit https://www.balena.io/docs/reference/base-images/base-images/):
[main] Architecture: ARM v7
[main] OS: Debian Buster
[main] Variant: build variant
[main] Default variable(s): UDEV=off
[main] The following software stack is preinstalled:
[main] Go v1.14.4
[main] Extra features:
[main] - Easy way to install packages with `install_packages <package-name>` command
[main] - Run anywhere with cross-build feature (for ARM only)
[main] - Keep the container idling with `balena-idle` command
[main] - Show base image details with `balena-info` command
[main] Removing intermediate container 8a4652f7c303
[main] ---> 9d859b2ad7eb
[main] Step 5/7 : FROM balenalib/raspberrypi3-debian:stretch
[main] ---> 28821f571ef5
[main] Step 6/7 : COPY --from=build /go/src/github.com/balena-io-projects/app/ .
[main] ---> dcd37d1557d0
[main] Step 7/7 : CMD ./app
[main] ---> Running in c468272913eb
[main] Removing intermediate container c468272913eb
[main] ---> fe8cabee2906
[main] Successfully built fe8cabee2906
[Info] Uploading images
[Success] Successfully uploaded images
[Info] Built on arm01
[Success] Release successfully created!
[Info] Release: 0c7a4625c2cf80962b4441a0ab8ff54b (id: 1464860)
[Info] ┌─────────┬────────────┬────────────┐
[Info] │ Service │ Image Size │ Build Time │
[Info] ├─────────┼────────────┼────────────┤
[Info] │ main │ 134.59 MB │ 49 seconds │
[Info] └─────────┴────────────┴────────────┘
[Info] Build finished in 1 minute, 11 seconds
\
\
\\
\\
>\/7
_.-(6' \
(=___._/` \
) \ |
/ / |
/ > /
j < _\
_.-' : ``.
\ r=._\ `.
<`\\_ \ .`-.
\ r-7 `-. ._ ' . `\
\`, `-.`7 7) )
\/ \| \' / `-._
|| .'
\\ (
>\ >
,.-' >.'
<.'_.''
<'
C:\Users\k-satou\go\src\balena\balena-go-hello-world>
docker build に成功すると管理画面上でラズパイへのアプリのダウンロード、docker pullが行われます。
statusがOnlineになったら、アプリが動いているかラズパイのIPアドレスに curlコマンドを送ってみます。
ちゃんとレスポンスが返ってきました
C:\Users\k-satou\go\src\balena\balena-go-hello-world>curl 192.168.0.238
hello, world (from go!)
C:\Users\k-satou\go\src\balena\balena-go-hello-world>
あとはmain.goにコードを書いて balena push すれば簡単にローカルでアプリが開発できます。
というわけでコードを変えてみます。
microbitとBLEでペアリングして温度を読み取ってみます。
micro:bitにはこのページを参考に作成したプログラムを書き込んでおきます。
https://sanuki-tech.net/micro-bit/bluetooth/temperature/
別のラズパイでスキャンして microbitのアドレスと温度の値を取得するUUIDを確認しておきます。
こちらの記事を参考にbluepyを入れたラズパイで確認しました。
https://qiita.com/FiveOne/items/ea04b74271da0f382ed6
pi@raspberrypi:ble $ sudo python3 scan.py
-----------------------------------
address : d8:a4:8d:c5:c7:1d
addrType : random
RSSI : -46
Adv data :
( 1) Flags : 06
( 9) Complete Local Name : BBC micro:bit [gatip]
pi@raspberrypi:ble $ sudo python3 getHandle.py d8:a4:8d:c5:c7:1d
------------------------------------------
UUID : 00002a00-0000-1000-8000-00805f9b34fb
Handle 0003 : READ WRITE
------------------------------------------
UUID : 00002a01-0000-1000-8000-00805f9b34fb
Handle 0005 : READ
------------------------------------------
UUID : 00002a04-0000-1000-8000-00805f9b34fb
Handle 0007 : READ
------------------------------------------
UUID : 00002a05-0000-1000-8000-00805f9b34fb
Handle 000A : INDICATE
------------------------------------------
UUID : e95d93b1-251d-470a-a062-fa1922dfa9a8
Handle 000E : READ WRITE
------------------------------------------
UUID : e97d3b10-251d-470a-a062-fa1922dfa9a8
Handle 0011 : WRITE NO RESPONSE NOTIFY
------------------------------------------
UUID : 00002a24-0000-1000-8000-00805f9b34fb
Handle 0015 : READ
------------------------------------------
UUID : 00002a25-0000-1000-8000-00805f9b34fb
Handle 0017 : READ
------------------------------------------
UUID : 00002a26-0000-1000-8000-00805f9b34fb
Handle 0019 : READ
------------------------------------------
UUID : e95d9775-251d-470a-a062-fa1922dfa9a8
Handle 001C : READ NOTIFY
------------------------------------------
UUID : e95d5404-251d-470a-a062-fa1922dfa9a8
Handle 001F : WRITE NO RESPONSE WRITE
------------------------------------------
UUID : e95d23c4-251d-470a-a062-fa1922dfa9a8
Handle 0021 : WRITE
------------------------------------------
UUID : e95db84c-251d-470a-a062-fa1922dfa9a8
Handle 0023 : READ NOTIFY
------------------------------------------
UUID : e95dca4b-251d-470a-a062-fa1922dfa9a8
Handle 0027 : READ NOTIFY
------------------------------------------
UUID : e95dfb24-251d-470a-a062-fa1922dfa9a8
Handle 002A : READ WRITE
------------------------------------------
UUID : e95d7b77-251d-470a-a062-fa1922dfa9a8
Handle 002D : READ WRITE
------------------------------------------
UUID : e95d93ee-251d-470a-a062-fa1922dfa9a8
Handle 002F : WRITE
------------------------------------------
UUID : e95d0d2d-251d-470a-a062-fa1922dfa9a8
Handle 0031 : READ WRITE
------------------------------------------
UUID : e95d9250-251d-470a-a062-fa1922dfa9a8
Handle 0034 : READ NOTIFY
------------------------------------------
UUID : e95d1b25-251d-470a-a062-fa1922dfa9a8
Handle 0037 : READ WRITE
pi@raspberrypi:ble $
goでBLE通信を行うためにgo-bleライブラリを利用して、sambleコードを参考に以下のようなコードにしました。
https://github.com/go-ble/ble
nameとUUIDに先ほどスキャンして調べたアドレスをセットしておきます。
package main
import (
"context"
"log"
"strings"
"time"
"os"
"os/signal"
"github.com/go-ble/ble"
"github.com/go-ble/ble/examples/lib/dev"
)
func main() {
name := "d8:a4:8d:c5:c7:1d"
sd := 5*time.Second
ondo_uuid := "e95d6100251d470aa062fa1922dfa9a8"
d, err := dev.NewDevice("raspberry")
if err != nil {
log.Fatalf("can't device : %s", err)
}
ble.SetDefaultDevice(d)
filter := func(a ble.Advertisement) bool {
return strings.ToUpper(a.Addr().String()) == strings.ToUpper(name)
}
log.Printf("Scannig for %s...\n", sd)
ctx := ble.WithSigHandler(context.WithTimeout(context.Background(), sd))
cln, err := ble.Connect(ctx, filter)
if err != nil {
log.Fatalf("can't connect : %s", err)
}
done := make(chan struct{})
go func() {
<-cln.Disconnected()
log.Printf("%s is disconnected \n", cln.Addr())
close(done)
}()
log.Printf("Discovering profile...\n")
p, err := cln.DiscoverProfile(true)
if err != nil {
log.Fatalf("can't discover profile: %s", err)
}
var chara *ble.Characteristic
for _, s := range p.Services {
if ondo_uuid == s.UUID.String() {
chara = s.Characteristics[0]
}
}
read, err := cln.ReadCharacteristic(chara)
if err != nil {
log.Fatalf("read characterristic is err: %s", err)
}
for {
log.Println("read data: ", read)
time.Sleep(3 * time.Second)
}
quit := make(chan os.Signal)
signal.Notify(quit, os.Interrupt)
go func() {
for sig := range quit {
log.Println("Signal catch: ", sig)
close(quit)
os.Exit(130)
}
}()
log.Printf("Disconnecting %s ...", cln.Addr())
cln.CancelConnection()
<-done
}
Dockerfileにライブラリをgo getするように追加します。
FROM balenalib/%%BALENA_MACHINE_NAME%%-golang:latest-build AS build
WORKDIR /go/src/github.com/balena-io-projects/app
RUN go get github.com/go-ble/ble && go get github.com/mgutz/logxi/v1 && go get golang.org/x/sys/unix
COPY /app ./
RUN CGO_ENABLED=0 go build
FROM balenalib/%%BALENA_MACHINE_NAME%%-debian:stretch
COPY --from=build /go/src/github.com/balena-io-projects/app/ .
CMD ./app
ではこれをbalena pushしてみます。
C:\Users\k-satou\go\src\balena\balena-go-hello-world>balena push sample-app
[Info] Starting build for sample-app, user g__610
[Info] Dashboard link: https://dashboard.balena-cloud.com/apps/1706473/devices
[Info] Building on arm01
[Info] Pulling previous images for caching purposes...
[Success] Successfully pulled cache images
[main] Step 1/7 : FROM balenalib/raspberrypi3-golang:latest-build AS build
[main] ---> 9a43177841ca
[main] Step 2/7 : WORKDIR /go/src/github.com/balena-io-projects/app
[main] ---> Running in 00561cfde90d
[main] Removing intermediate container 00561cfde90d
[main] ---> 5dbdd5316d49
[main] Step 3/7 : COPY /app ./
[main] ---> 6f05e3a48c17
[main] Step 4/7 : RUN go build
[main] ---> Running in 8a4652f7c303
[main] Here are a few details about this Docker image (For more information please visit https://www.balena.io/docs/reference/base-images/base-images/):
[main] Architecture: ARM v7
[main] OS: Debian Buster
[main] Variant: build variant
[main] Default variable(s): UDEV=off
[main] The following software stack is preinstalled:
[main] Go v1.14.4
[main] Extra features:
[main] - Easy way to install packages with `install_packages <package-name>` command
[main] - Run anywhere with cross-build feature (for ARM only)
[main] - Keep the container idling with `balena-idle` command
[main] - Show base image details with `balena-info` command
[main] Removing intermediate container 8a4652f7c303
[main] ---> 9d859b2ad7eb
[main] Step 5/7 : FROM balenalib/raspberrypi3-debian:stretch
[main] ---> 28821f571ef5
[main] Step 6/7 : COPY --from=build /go/src/github.com/balena-io-projects/app/ .
[main] ---> dcd37d1557d0
[main] Step 7/7 : CMD ./app
[main] ---> Running in c468272913eb
[main] Removing intermediate container c468272913eb
[main] ---> fe8cabee2906
[main] Successfully built fe8cabee2906
[Info] Uploading images
[Success] Successfully uploaded images
[Info] Built on arm01
[Success] Release successfully created!
[Info] Release: 0c7a4625c2cf80962b4441a0ab8ff54b (id: 1464860)
[Info] ┌─────────┬────────────┬────────────┐
[Info] │ Service │ Image Size │ Build Time │
[Info] ├─────────┼────────────┼────────────┤
[Info] │ main │ 134.59 MB │ 49 seconds │
[Info] └─────────┴────────────┴────────────┘
[Info] Build finished in 1 minute, 11 seconds
\
\
\\
\\
>\/7
_.-(6' \
(=___._/` \
) \ |
/ / |
/ > /
j < _\
_.-' : ``.
\ r=._\ `.
<`\\_ \ .`-.
\ r-7 `-. ._ ' . `\
\`, `-.`7 7) )
\/ \| \' / `-._
|| .'
\\ (
>\ >
,.-' >.'
<.'_.''
<'
/ Packaging the project source...[Warn] CRLF (Windows) line endings detected in file: C:\Users\k-satou\go\src\balena\balena-ble\Dockerfile.template
[Warn] CRLF (Windows) line endings detected in file: C:\Users\k-satou\go\src\balena\balena-ble\.gitignore
[Warn] CRLF (Windows) line endings detected in file: C:\Users\k-satou\go\src\balena\balena-ble\README.md
[Warn] CRLF (Windows) line endings detected in file: C:\Users\k-satou\go\src\balena\balena-ble\LICENSE
[Info] Starting build for sample-app, user g__610
[Info] Dashboard link: https://dashboard.balena-cloud.com/apps/1706473/devices
[Info] Building on arm03
[Info] Pulling previous images for caching purposes...
[Success] Successfully pulled cache images
[main] Step 1/8 : FROM balenalib/raspberrypi3-golang:latest-build AS build
[main] ---> 9a43177841ca
[main] Step 2/8 : WORKDIR /go/src/github.com/balena-io-projects/app
[main] Using cache
[main] ---> 75fe37fd7909
[main] Step 3/8 : RUN go get github.com/go-ble/ble && go get github.com/mgutz/logxi/v1 && go get golang.org/x/sys/unix
[main] ---> Running in 8327007ce2e9
[main] Here are a few details about this Docker image (For more information please visit https://www.balena.io/docs/reference/base-images/base-images/):
[main] Architecture: ARM v7
[main] OS: Debian Buster
[main] Variant: build variant
[main] Default variable(s): UDEV=off
[main] The following software stack is preinstalled:
[main] Go v1.14.4
[main] Extra features:
[main] - Easy way to install packages with `install_packages <package-name>` command
[main] - Run anywhere with cross-build feature (for ARM only)
[main] - Keep the container idling with `balena-idle` command
[main] - Show base image details with `balena-info` command
[main] Removing intermediate container 8327007ce2e9
[main] ---> 9cb3a5decba6
[main] Step 4/8 : COPY /app ./
[main] ---> 4be0eea56236
[main] Step 5/8 : RUN CGO_ENABLED=0 go build
[main] ---> Running in db32f2e9a080
[main] Removing intermediate container db32f2e9a080
[main] ---> 215778d493a4
[main] Step 6/8 : FROM balenalib/raspberrypi3-debian:stretch
[main] ---> 28821f571ef5
[main] Step 7/8 : COPY --from=build /go/src/github.com/balena-io-projects/app/ .
[main] ---> 0d20d73a9424
[main] Step 8/8 : CMD ./app
[main] ---> Running in ecde7b0d4c05
[main] Removing intermediate container ecde7b0d4c05
[main] ---> 53a772a2b1ed
[main] Successfully built 53a772a2b1ed
[Info] Generating image deltas from release 0c7a4625c2cf80962b4441a0ab8ff54b (id: 1464860)
[Success] Successfully generated image deltas
[Info] Uploading images
[Success] Successfully uploaded images
[Info] Built on arm03
[Success] Release successfully created!
[Info] Release: 5a7247943dfa1359b49fa6676e16129b (id: 1464893)
[Info] ┌─────────┬────────────┬────────────┬────────────┐
[Info] │ Service │ Image Size │ Delta Size │ Build Time │
[Info] ├─────────┼────────────┼────────────┼────────────┤
[Info] │ main │ 131.63 MB │ 3.08 MB │ 46 seconds │
[Info] └─────────┴────────────┴────────────┴────────────┘
[Info] Build finished in 2 minutes,
\
\
\\
\\
>\/7
_.-(6' \
(=___._/` \
) \ |
/ / |
/ > /
j < _\
_.-' : ``.
\ r=._\ `.
<`\\_ \ .`-.
\ r-7 `-. ._ ' . `\
\`, `-.`7 7) )
\/ \| \' / `-._
|| .'
\\ (
>\ >
,.-' >.'
<.'_.''
<'
[Warn] Windows-format line endings were detected in some files. Consider using the `--convert-eol` option.
C:\Users\k-satou\go\src\balena\balena-ble>
先ほどまでアプリに代わり新しいアプリがインストールされました。
microbitとペアリングして正常に温度を読み取れているようです。
というわけでこのようにbalena cloudを利用することでIoTアプリの開発がスムーズに行えるかと思います。
ぜひ試してみてはいかがでしょうか。