ラズパイ用チャットアプリ再構成

チャット機能は以下の記事の通り独立モジュールとして開発していましたが、

https://isehara-3lv.sakura.ne.jp/blog/2023/04/28/ラズパイにチャット機能を追加/

mifareカードアプリと分ける必要もなくなったので統合しました。ソースコードのリンクは以下になります。

https://github.com/chateight/myfare-copy

修正箇所は、router.goの以下の2行と、myfareのブラウザ画面からのリンクボタン追加とmain.goからの呼び出し(以下のコード切り出し)の追加。

r.Static(“/static”, “./go_chat/view/static”)
r.LoadHTMLGlob(“./go_chat/view/*.html”)
Static/LoadHTMLGlob()のリンクディレクトリ呼び出しは、Run()を起動したmain.goが基準になるのでそこからの相対ディレクトリに変更、関連処理のmain.goからの呼び出しを移動しただけです。
<main.go部分>
func main() {
	// to call card reader function()
	go uidSerial.SerialMain()

	// chat data base create and make table to store chat messages
	sql_db.DbCreate()
	// start chat service
	go chat.Run()
	
	// start myfare card service
	wevServer()
}

 

admin

GolangでHTML escape処理

HTML escapeはほとんどのケースで入口、つまりHTMLでFORM入力された文字列のチェック、でのJavaScriptの仕事です。もしGolangがフロントエンドの役割を果たす時にはhtmlパッケージにある、

func EscapeString(s string) string

関数が使えます。もしunescapeするならば、

func UnescapeString(s string) string

になります。docのリンクは、

https://pkg.go.dev/html

 

admin

 

ラズパイにチャット機能を追加

ラズベリーパイでMyfareカードのアプリを動かすのと同時にWi-Fi内でのチャット機能も追加しました。

https://www.cetus-media.info/article/2021/line-chat/

雛形は上のリンクですが、いくつかカスタマイズしています。

一番大きな部分は、チャット内容をSQLite3に保存して、新たな参加者が入ったらそれまでの履歴を表示するようにしたことでしょうか。

普通に提供されているHTML renderingはテキストしか対応出来ないので、タグが使えるようにginのWriter.WriteStringでHTMLページを組み立ててroom.htmlリクエストに対応しています。

<router.goの該当部分抜き出し>

	r.GET("/room/:name", func(c *gin.Context) {
		chattername := c.Query("name")
		pmsg := chatbuild(chattername)
		c.Writer.WriteString(pmsg)		// to genrate initial chat page instead of the static html

全部のソースは、

https://github.com/chateight/golang/tree/master/go_chat

になります。ginとmelody使っているので、制御そのものはシンプルに出来ています。

画面はこんな感じです、ブラウザはsafariはJavascriptのサポートレベルがイマイチ(チャット画面に移動時、投稿の最新にスクロールしない)なのでchromeがおすすめです。

 

admin

Goのビルド時間を条件同じにして計測してみた

以下の記事で、Intel MacとM1 Macでビルド時間が違うことに気づいたので、条件を同じにして比較。

https://isehara-3lv.sakura.ne.jp/blog/2023/04/23/dockerコンテナからイメージを作成する/

<時間計測の条件>

一度ビルドを完了した状態で、実行ファイルを削除して再度ビルドに要する時間を計測(実際行っていることはリンケージですね)

スクリプトファイルで、

% go build main.go

の前後のタイムスタンプで計測

M1 Mac native build time -> 2 seconds

 % ./build.sh 
2023年 4月24日 月曜日 13時12分41秒 JST
2023年 4月24日 月曜日 13時12分43秒 JST


M1 Mac Docker container build time -> 11 seconds

# ./build.sh
Mon Apr 24 05:00:53 BST 2023
Mon Apr 24 05:01:04 BST 2023


Intel mac Docker container build time -> 61 seconds

# ./build.sh
Mon Apr 24 04:18:53 BST 2023
Mon Apr 24 04:19:54 BST 2023


Raspberry Pi model B+ native build time -> 118 seconds

$ ./build.sh
Mon 24 Apr 13:05:53 JST 2023
Mon 24 Apr 13:07:51 JST 2023

 

Intel/M1 Macは他のタスクではほぼ同じパーフォーマンスなので、Intel CPUではarm CPUのエミュレーションが明らかに速度が遅いようで、M1 MacでのDockerだけがクロスビルドの許容内、規模にもよりますが。

 

admin

Wi-Fi無しのラズパイにWi-Fi機能追加

元々Linux用のUSBドングルでまともなものは無いのは知っているから、Wi-Fi機能のない初代に近いモデルにWi-Fi機能を追加するのはおそらくこれが一番手っ取り早い。

TP-LINKの携帯用のWi-Fiルーターを天板に両面テープで貼り付けただけ。元々このモデルはルーター/中継機/クライアントの三つのモードがあるので、クライアントモードを設定すれば良い。

 

電源にtype-b2本のUSB必要だけど、見た目一体感はありそう、ちょいダサいけど。

 

admin

Dockerコンテナからイメージを作成する

誤ってコンテナ削除すると、それまでに適用したアップデートが全て消えるので、コンテナからイメージを作成する方法。

コンテナを停止、docker psで見えない状態、してからたとえば以下のようなコマンドを実行すればrasp32_image_goという名前のイメージが作成されます。作成後はrunでコンテナ(ここではrasp32_go)を作成して内容を確認しておけば大丈夫です。

% docker commit rasp32 rasp32_image_go

<image>

<container>

rasp32コンテナに適用した変更を保存するために、rasp32_image_goというイメージを作成しています。簡単ですね、

必要ならDockerhubにアップロードしておけば共有やバックアップができます。個人で使う分にはDockerhubはバックアップでしかないですが。

共同作業をする対象ならば、historyが見えなくなるので使いづらいでしょうが、個人で使う分にはDcokerfileを使わなくてもこれで十分だろうと思う。

 

admin

DockerでラズパイのGolang build環境を作る

https://isehara-3lv.sakura.ne.jp/blog/2023/04/19/golangアプリは単純にクロスビルドしても動かない(db/

の対応としてとりあえずラズパイ自身でbuildさせましたが、ラズパイModel B+でbuildに必要な時間は、実行ファイルのタイムスタンプからおよそ二時間。これでは実用的ではないので代替え方法を考えるけれども、一番楽そうなのはDockerを使うことでしょう。

自分の環境を汚染させないとか、イメージの配布とかではなく、ラズパイの環境を作るための使用(DockerはM1 Macにインストール、ターゲットのコンテナはraspbian32 OS)です。

多少以前の記事になりますが、以下を参考に実行。raspbianイメージとかは最新版、と言っても2020年度が最新ですが。

https://www.koatech.info/blog/raspbian-on-docker/

・イメージ取得と作成

% wget http://ftp.jaist.ac.jp/pub/raspberrypi/raspios_lite_armhf/root.tar.xz

% docker image import ./root.tar.xz raspbian-stretch-lite:2020

・以下のコマンドでwarningが出る、とりあえずは無視して大丈夫な様子(QEMUは元々Intel CPU用だからか、Apple siliconでも今のところ何とかなってるけど)

% docker run --rm --privileged multiarch/qemu-user-static --reset -p yes

WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested

exec /register: exec format error

・コンテナ作成と起動(名前指定)

% docker run -it --name rasp32 raspbian-stretch-lite:2020 bash

・作成しているコンテナを起動するなら

% docker exec -it rasp32 bash

・Golang install

# wget https://golang.org/dl/go1.20.1.linux-armv6l.tar.gz

# sudo tar -C /usr/local -xzf go1.20.1.linux-armv6l.tar.gz

# /usr/local/go/bin/go version (パスが通っていない状態)

・Dockerのコンテナからファイルをクライアントにコピー(例)

% docker cp 21f2c6ce6eb8:/home/pi/hello .

・ディレクトリ指定すればその中全部をコピー(クライアントからコンテナへ)

% docker cp myfare 21f2c6ce6eb8:/home/pi/

コンテナ上のMacの該当ディレクトリを丸ごとコピー(gomodなど含めて)してきてbuild、できた実行ファイルをscpでラズパイに転送して起動するとちゃんと実行できました。実行ファイルの動作確認、最初は定番のハローワールドで確認しています。

エミュレーション(QEMU)なので、速度はMacでbuildするのに比較すると遥かに遅い、それでも二分ぐらいで終わっているからやはりラズパイで実行するよりはほぼ百倍速いから実用的です。

実行ファイルのサイズが微妙に違うのはライブラリの版数違いか、main1のほうがコンテナでbuildしたもの。

コンテナ上のraspbian、

ラズパイのbuild環境としてはおそらくデフォルトだと思う。カスタマイズしたコンテナを作る時にDcokerfileを記述しておけば、再現性確保できます。

 

admin

UTMのUbuntu起動しなくなった、

M1 Macbook AirでインストールしているArm版Ubuntu、アップデートが正常に終了しなくなった後で、起動しなくなった。新たにインストールし直ししか道はなさそう。

まあUTMの限界といえばそうなのかもしれないから、VMWaraのAppli silicon native版待ちかな。個人使用無償の継続期待が前提ですが。

 

admin

/etc/rc.localからsystemdになってました

ラズパイでmyfareアプリを起動時に立ち上げしようと思ってrc.localに記述しても起動しません。実は最近のLinuxのバージョンではrc.localは単にipアドレスの表示をするだけになっていて、アプリの起動はsystemdを使えということのようです。しかしsystemdに設定したつもりでも起動するとエラーになります。実行ファイルと同じディレクトリに配置しているファイルが見つからないと言われます。

$ sudo systemctl status myfare.service
● myfare.service - myfare
     Loaded: loaded (/etc/systemd/system/myfare.service; enabled; vendor preset>
     Active: failed (Result: exit-code) since Wed 2023-04-19 11:57:00 BST; 9s a>
    Process: 1805 ExecStart=/home/pi/myfare/main (code=exited, status=1/FAILURE)
   Main PID: 1805 (code=exited, status=1/FAILURE)
        CPU: 121ms

Apr 19 11:57:00 rasp-b systemd[1]: Started myfare.
Apr 19 11:57:00 rasp-b main[1805]: open uid.json: no such file or directory

事例検索して行き着いたのが、WorkingDirectory設定。

[Unit]
Description=myfare

[Service]
WorkingDirectory=/home/pi/myfare/
Type=simple
ExecStart=/home/pi/myfare/main

[Install]
WantedBy = multi-user.target

つまりこれを指定しないと、実行ファイルからファイルを見つけられなくなります。

systemdの記述方法はネットにたくさんありますが、ここに行き着くのに一時間以上。大本のマニュアル見た方が早かったかと思いますが、ともかくも以下の一連のコマンドの手順で自動起動できました。


サービスファイルを記述して、daemonのリロード
$ sudo systemctl daemon-reload
起動確認
$ sudo systemctl start myfare.service
正常に起動していることを確認
$ sudo systemctl status myfare.service
起動時にサービスを有効化
$ sudo systemctl enable myfare.service

 

admin

 

Golangアプリは単純にクロスビルドしても動かない(DBドライバがcgo使ってる)

Macで開発したアプリをラズパイで動かそうとしましたが、そのままでは動かない。なぜならgormもdbドライバーもcgoを使っている、つまりターゲットのgccを用意してそれを指定しないといけないから。

とりあえず動かすだけなら、すごく時間はかかりますがラズパイでビルド、2時間ぐらい放置してたらビルド完了してました。

実行ファイルを起動すると、Macよりは多少レスポンスは遅いのですがちゃんと動作しています。

<layout.html>

これだけはws://mbair.local:8080/wsをラズパイに変更が必要です。

 window.onload = function () {
  socket = new WebSocket("ws://mbair.local:8080/ws");
  socket.onopen = function () {
    append_message("system", "Socket Connected");
  };
  socket.onmessage = function (event) {
    append_message("server", event.data);
  };

  const send = function (){
    socket.send("")
  }
  setInterval(send, 500);

};

クロス環境をどうするかですが、Dockerがおすすめのようなので、それでやってみます。

メモリ使用状況は、こんな感じです、クライアント一台だけで、

すぐにもう一台増やすと、およそ200KBぐらい増えていますが、この程度では普通には十分です。

 

admin