CAN サーバプログラム – CANメッセージをどうやって拾うか – の1

Analog3 のミッションコントロールとは何者なのか?まだ全体の仕様書を書いたわけではないのでざっくりしていますけれども、大まかにこんな感じのモジュールです

Analog3の設計について熱く語りたいところですがそれはこの記事の本題と違うので、ざっくりと、ユーザから要求を受けつつ他のモジュールと通信して全体のシステムを統括するひとがミッションコントロールです。

ミッションコントロールは現実にこの課題をどうさばくのか。作りは普通にサーバソフトウェアなんですが、普通と違うというか少なくとも今まで僕がやったことがないのが、ネットワークの口がイーサネットだけでなくて CAN があるということです。今のところプラットフォームは Linux で、イーサネットの入出力ならソケットが使えますが、また CAN でも探すとデバイスドライバがあるようでそれを使えばソケットと似たような方法で入出力できるはずですが、そうなると OS への依存が深まってしまいます。どこかの時点で Linux から離れて移植したくなる可能性も高いので、ここは自前の極力ユーザ領域を使った CAN インタフェースで頑張りたいところです。そしてそれをサーバの出入り口の体裁に持っていくのが最初の難関です。

プログラミングとしては素直にデバイスドライバを使った方が楽だと思いますけれどもこの記事では CAN に限らずソフトウェアに「待ち」と「処理」をさせる手法について一般的な話をしたいと思います。

CAN バスにメッセージが流れたらミッションコントロールはどうそれを拾って処理するのでしょうか?一番最初のコードでは受け取った CAN メッセージはこんな風に伝搬されていました。

メッセージは MCP2515 CAN コントローラに端を発して(もっというと CAN トランシーバに…ですがきりがない)、最終的にはミッションコントロールのメッセージ処理部に到達します。この記事ではサーバがメッセージを受け取る手法に話題を絞るので、MCP2515 からコールバックまでの段階段階がどうなっているかについての詳細は以下の CAN インタフェースのコードを読んでください、あるいは「解説してちょうだい」とコメントを入れてくだされば頑張って別記事書こうと思います。

https://github.com/naokiiwakami/can-controller/tree/main

まあとにかく受け取ったメッセージはミッションコントロールにはキューの形で届くわけですが、この図で一か所変なところがあります。それはキューからメッセージを拾うところで、矢印の向きが逆転しています。これはどういうことなのかというと、ミッションコントロールのメッセージ処理部は新規にメッセージが来ても通知を受け取る手段がないので、ひたすらキューにメッセージが入っていないか見続けているということです。実際のメッセージ処理部のコードはこんなです。

fn main() {
    let can_controller = CanController::new();
    let message_handler = ModuleManager::new(&can_controller);
    loop {
        if let Some(message) = can_controller.get_message() {
            message_handler.handle_message(message);
        }
    }
}
Rust

get_message() 関数はキューの内容をチェックして、メッセージがあってもなくてもすぐに戻ってきます。そういうわけで、メインプログラムは、ループの中でメッセージがあるかひたすらチェックを続けメッセージがあれば処理を行います。

これはポーリングという技法でマイクロコントローラでは常套手段で実際動作上問題はないのですが、一つ、特に普通のマイクロコントローラよりも高速なラズベリーパイでは CPU への負担が問題になります。下のスクリーンショットは大きなプロセスが走っていない時に見た htop です。

アイドル状態

ホストはラズベリーパイ4で、CPU が4つありますが、何もメーターに現れてきていません。こういう時 CPU は何をしているのかというと、アイドル状態という不活発な状態に入って低消費電力になることに徹しています。では、上記のループぶん回りバージョンのミッションコントロールを走らせるとどうなるか?それが下のスクリーンショット

メインプログラムでループをぶん回すと

CPU のうち一個が 100% になっています。する仕事がなくてもぐるぐるポーリングをしているので CPU はアイドル状態に入れないわけです。この状態で同じホストで複数のプログラムを実行するとそれはそれで問題になるのですがそれは置いておいて、「使ってるプログラムは一個だから CPU を占有していてもいいんじゃないの?誰にも迷惑かからんし」という考え方もないことはないのですが、ここで心配なのは、「ラズパイは熱に弱い」ということなのです。人間だって走ったら発熱するし、なんか熱くなりそうじゃん?ということで調べてみました。

調べ方は簡単、以下の bash スクリプトは 10秒おきにラズパイの内部温度の記録を取って CSV 形式で吐き出します。

#!/bin/bash

echo "time(sec),temperature"
time=0
while true; do
    echo "${time},$(vcgencmd measure_temp | cut -b6-9)"
    sleep 10
    time=$((time+10))
done
Bash

このプログラムを走らせつつミッションコントロールを起動してみました。さあ結果はどうだったでしょう?

プログラムの起動は測定開始から 2 分後。3分間走らせて 5 分の時点で停止しました。

これはあからさまです。CPU ぶんまわしポーリングはコンピュータを発熱させるのですね。経験上ラズパイがあったまるとろくなことが起こらないのでこのやり方はやめておくことにしました。

さて記事が長くなってしまったのでここで一区切りします。

続きは、普段は CPU をアイドル状態に入れつつ到着したメッセージをどうさばくかの話を書きたいのですが、記事を書くのに少し時間がかかりそうです。

つづく

Comments

No comments yet. Why don’t you start the discussion?

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

This site uses Akismet to reduce spam. Learn how your comment data is processed.