スタックチャンを作ってみた

注意

本記事はスタックチャンを作ってみるまでにかかったことを、作ってみて面白かったので記事にしたものです。探りながら進めたので、現状の最良の方法とは限りません。

スタックチャンのまとめた情報としてScrapboxが作られています。そちらの方が最新の可能性があります

scrapbox.io

それを承知の上で参照してください。

スタックチャンとは

スタックチャンとは、M5Stackを使ったシンプルなロボットを作るムーブメントです。この記事が最初のようです。

protopedia.net

会社のSlackの電子工作同好会チャンネルで、スタックチャンを作ってみたいよね、という話になり、私もいつかは作ってみたいと思っていたため、今回作ってみることにしました。

スタックチャンのファームウェア

スタックチャンのファームウェアは、スタックチャンだと名乗ればスタックチャンのファームウェアだと言えるのだとおもいますが、主に以下の2つがメジャーなようでした。

ししかわさん作成の、ModdableフレームワークJavaScriptを使ったファームウェアフレームワーク)ベースのもの

github.com

@robo8080作成の、ArduinoベースのAIスタックチャン

github.com

主にAIスタックチャンを作ってみたいと思い、作ってみました。まだModdableフレームワークのものはビルドしていないのですが、いずれはしてみたいと思っています。

スタックチャンに必要なもの

スタックチャン自体は、ロボットの概念であるため、M5Stack製品を使った一部の機能を持ったものであっても、スタックチャンだと思えばスタックチャンとなると思われます。

一般的に、サーボを使って動き、ディスプレイを顔に見立てるスタックチャンを作るには、以下のものが必要です。他のものでも可能と思われますが、私の方で確認できたものを挙げています。

  • M5Stack: M5Stack BASIC(マイクなし)、M5Stack Core2
  • サーボモータ SG-90
  • 3Dプリントケース
  • サーボ制御用PCB
  • (任意)リチウムイオン電池(ししかわさん版で利用)、M5GO Bottom2(Core2用バッテリーボトム、タカヲさん版で利用)

一次情報

github.com

3Dプリントケースと、PCBは対応しているモノを使います。

よく使われているのは、以下の2点のようで、どちらもオープンソースハードウェアとして公開していただいています。

ししかわさん作成のm5-pantiltは、使用部品も多く、リチウムイオン電池を接続することもできます。ただし、MBUSを用いるため、Core2においてMBusの小さなPCBに付いているマイクが利用できなくなってしまいます。

タカヲさんの方は非常に部品点数も少なくシンプルです。充電池としてM5GO Bottom2を使います。ただ、USB給電であればM5GO Bottom2がなくても良いようです。

加えて、タカヲさんの方は、PCB、ケースとも販売されており、気軽に入手することができます。

Stack-chan_Takao_Base(完成品) — スイッチサイエンス

Stack-chan_Takao_Base(完成品)www.switch-science.com

タカヲさんのBoothショップ

mongonta.booth.pm

サーボモータ SG-90 は、秋月電子で購入することができます。Amazonやアリエクで購入できるものは互換品であることが多く、私の家も転がっていたSG-90ではケースにハマらないことが発生しました。秋月電子で購入するようにした方が良いようです。

akizukidenshi.com

わたしとしては、どちらが良いのかわからんかったため、両方とも作ってみることにしました。

現在では、作ってみるだけならば入手しやすくシンプルなタカヲさん版をお勧めします。

ししかわさん版を作ってみる

PCBをJLCPCBに発注し、LCSCで部品を注文しました。

MBusピンヘッダに関しては、M5Stack用のものを使用しました(他で入手できた同様のSMDタイプのピンヘッダはプラスチックの部分がM5Stackのものより厚いものしか入手できず、割高ですがM5Stackのものを使用しました)。

M5Stack用2 x 15ピンヘッダ・ソケットセット [A001]www.switch-science.com

ただし、このPCBではうまくいかないことがあり、以下の変更をしました。

  • MBusのピンソケット J2 を、結局使わないので、外す
  • スライドスイッチSW1の位置がケースにあわないので、少し下にずらす

また、実装時に以下を変更しました。

  • Port.A J6のJST_PH_S4B(スルーホール水平ソケット)ソケットが、ケースに干渉したため、JST_PH_B4B(スルーホール垂直ソケット)に変更
  • サーボ接続用のJ3、J4の、JST_EH_B3B(スルーホール垂直ソケット)がサーボの部分に干渉してすぐ抜けた、L字2.54ピンヘッダ1x3(プラスチックが長い方ではなく、短い方に付いているもの)に変更。

変更後のPCB

github.com

ケースは自宅の3Dプリンタ(AnkerM5C)で印刷しました。

他に用意した部品として、以下があります。

組み立て方などは一次情報を参考にしました。

https://github.com/stack-chan/stack-chan/blob/dev/v1.0/case/README_ja.md

github.com

これで組み立てることができました。

サーボの動きを確認する

サーボが動くかどうか検証するのに、タカヲさんがテストファームウェアを作成されています。PlatformIOという普段から使っているVS Codeマイコンファームウェア開発環境を使われているので、さくっと使うことができました。

github.com

まず、これで動くことを確認しました。これで、サーボが正しく動くことを確認できました。

AIスタックチャンを入れてみる

まずはM5Stack BASICで作っていたのですが、その次にマイクの付いたM5Stack Core2でおしゃべりできるAIスタックチャンを作ってみることにしました。

AIスタックチャンのファームウェアは以下にあります。こちらもPlatformIOが使われています。

github.com

音声発話にVOICEVOXの無料で使わせていただけるWeb版のキーの発行方法や、ChatGPTについてもドキュメントで触れられており、簡単に使うことができました。

github.com

WiFiAPIキーの設定はSDCard経由でもできますが、私の場合は直接ファームウェアに埋め込むようにしています(SDカードの準備が面倒なため)

// #define USE_SDCARD
#define WIFI_SSID "SET YOUR WIFI SSID"
#define WIFI_PASS "SET YOUR WIFI PASS"
#define OPENAI_APIKEY "SET YOUR OPENAI APIKEY"
#define VOICEVOX_APIKEY "SET YOUR VOICEVOX APIKEY"
#define STT_APIKEY "SET YOUR STT APIKEY"

https://github.com/robo8080/AI_StackChan2/blob/dfd8ba2709f59b13cacd442aa8c4a27fb3a241de/M5Unified_AI_StackChan/src/main.cpp#L41-L46

また、サーボのピンは直接指定するようにしました。

#define SERVO_PIN_X 27  //Core2 PORT A
#define SERVO_PIN_Y 19

https://github.com/robo8080/AI_StackChan2/blob/dfd8ba2709f59b13cacd442aa8c4a27fb3a241de/M5Unified_AI_StackChan/src/main.cpp#L48-L59

しかし、AIスタックチャンで喋るようになりましたが、どうもマイクが聞き取れません。一時、胴体無しで動かしていたときに、マイクが動いていたため、マイクが壊れてしまったかと思いました。これをXにポストしたところ、拾っていただき、m5-pantiltを使うとマイクが使えないことを教えていただきました。マイクはMBusに付けるPCBに含まれているため、それを外してしまうと使えないということでした。

なお、マイクがなくてもHTTP経由でスタックチャンに話しかけることができます。この機能を使って遊んでいました。

http://192.168.xxx.xxx/chat?text=こんにちは

そこで、用意していたタカヲさん基板でAIスタックチャンを作り直してみることにしました。

タカヲさん版ケースとPCBを使ってみる

PCBはJLCPCBで注文し、部品はLCSCで注文しました。

LCSCでUSBソケットを注文しましたが、上記部品は若干フットプリントが異なりますが、無理矢理さしたら刺せました。USBソケットを除き、入手しやすい部品で構成され、スルーホール部品も使えるようになっているので、やりやすいです。後から気づきましたが、USBソケットは背面から電源を挿したい場合のみ必要で、M5Stack側面USBでも大丈夫そうで、無くても良さそうです。

スライドスイッチは秋月で買えるものを利用しました(LCSCでジャストのものを見つけられなかった)。

組み立て方は、ブログに詳細に書かれているため、そちらを確認して組み立てました。

raspberrypi.mongonta.com

ファームウェアの設定も、PORT_Aをコメントアウトして、PORT_Cを使うようにするだけでした。

//#define PORT_A
#define PORT_C
#if defined (PORT_A)
  #define SERVO_PIN_X 33  //Core2 PORT A
  #define SERVO_PIN_Y 32
#elif defined (PORT_C)
  #define SERVO_PIN_X 14  //Core2 PORT C (INTERNAL UART2)
  #define SERVO_PIN_Y 13
#endif

また、タカヲさん版は「M5GO Bottom2とよばれていた」と書かれていたため、M5GO Bottom2(大容量バッテリーボトム)が必須なのかと思いましたが、なくてもUSB給電のみとなりますが使えるようです。試しに、ボトムを外してPort.Aと、PCBを繋いでみて、ファームウェア#define PORT_Aを有効かさせて、動かすことができました。

ウェイクワードを「スタックチャン」に設定して、「スタックチャン。日本のタクシーを呼べるアプリには何がある?」と質問して答えさせることができました。

M5StackCoreS3SEを使ってみようとしたが、まだうまくいっていない

M5StackシリーズのESP32-S3を使った強化版であるM5StackCoreS3が出ており、最近CoreS3からカメラ等の一部の機能を除いてお安くしたM5StackCoreS3SEというのが出ています。1万円越えのCoreS3に比べて、CoreS3SEは6500円ほどなので、非常に価格的にも魅力的です。

こちらでAIスタックチャンを動かそうと、以下のことをしてみたのですが、結果として、マイクのみ動かすことができませんでした。時間ができたらちょっと追求してみます。

  • PlatformIOでenv:esp32-s3-devkitc-1のenvに切り替える
  • platfomio.iniのlib_depsに書かれているライブラリm5stack/M5Unifiedのバージョンをm5stack/M5Unified@0.1.16に指定する。
  • m5-pantiltを使うようにSERVO_PIN_X、SERVO_PIN_Yのピンを変更する

なお、タカヲさんに残ったm5-pantileを使ってM5StackCoreS3SEを使うと完全体になるよと、そそのかしていただいたので、トライしてみていました。

M5StackCoreS3S3を既に持っていたので、すぐトライすることができました(トラックパッド化検証用のつもりだった)。

StackChanが増えた!!!

これで、わが家にAIスタックチャンが加わりました。BASIC、Core2、CoreS3と3体のスタックチャンがいるお家になりました。

今後のスタックチャンへの展望

この後スタックチャンでやりたいことと言えば以下のことがあります。

  • 薄紫色のフィラメントを買ったので、その色のボディを作る
  • VOICEVOXが無償Web版を使わせていただいているのが、ありがたいですが、申し訳ない感じがするので、自宅サーバ内にVOICEVOXを建てて、利用するようにする。
  • ChatGPT APIやVOICEVOXへの操作を、M5Stack内でやると応用が若干難しいので、自宅サーバに逃がして、応用が利くようにする。
  • 会社で、スタックチャン制作会をやる

アナログ電圧計を使って、ヘッドレスPCのCPU使用率表示を作る

tl;dr

  • アナログ電圧計は、PWMでお手軽にアナログメーターを作ることができるよ。その記事を見つけて、非常に惹かれたよ。
  • ESP32-C3をWebサーバとして、送られてきた値をPWMに流すファームウェアを作ったよ。
  • GoでCPU使用率をHTTPで送るプログラムを作って、使ってみたよ。ヘッドレスLinuxPCとして使っているOrangePi5+のCPU使用率を表示できる様にしたよ。
  • LEDとかで光らせても良かったけど、自分には不要と感じたので、CPUとメモリ使用率を表示して完成としたよ。

アナログ電圧計をPCのCPUモニタとして使う記事

Hackadayでアナログ電圧計をCPUモニタとして使う記事を見つけました(ina_aniさんの紹介で知りました)。

hackaday.com

www.youtube.com

これを見て、非常に簡単で面白いアイディアだと思いました。実際に針が動くのは面白く、加えてアナログ電圧計であればPWMがあれば簡単に制御できます。

自分はMacを使いながら、ほとんどの開発はVS CodeのリモートSSH機能を使って、OrangePi5+上のUbuntuで開発しています。その時、CPUやメモリ使用率はターミナル上で表示させることはできますが、結構煩雑です。ですので、これを表示できるようにしたいという需要がありました。

アーキテクチャ

以下のような仕組みにするように考えました。

  • PCとマイコンの間はWiFiを使い、HTTP通信で送るようにする
  • ESP32-C3を使い、WebServerを立てる。受け取った値を、PWMとして出力して、表示する
  • PC上で、CPU使用率をHTTPで送るプログラムを動かす

PCとマイコン間は、USBで接続しても良いのですが、設置の簡単さからWiFiを使うことにしました。ESP32であれば、簡単にArduinoでWebServerを立てることができます。REST APIとして作ることにしました。以下のように送ることになります。なお、自宅内ネットワークのため、認証等はありません。

POST http://192.168.1.105/cpu

{"value": 50}

私はIoT機器の電子工作にはESP32-C3を良く使っています。ESP32-C3-WROOM-02は秋月で310円と、非常に安価に購入できます。今回もESP32-C3を使うようにしました。

今回は簡単な回路であり、加えて複数個作る予定もないため、PCBを制作せず、ユニバーサル基板で実装することにしました。回路図は以下に置いてあります。

KiCanvas 回路図

https://kicanvas.org/?github=https%3A%2F%2Fgithub.com%2F74th%2Fanalog-cpu-meter%2Fblob%2Fmain%2Fsemantics%2Fanalog-cpu-meter.kicad_sch

アナログ電圧計を用意する

アナログ電圧計には、表示上限の電圧があり、マイコンのIOの3.3Vで操作する都合、3Vの電圧計を使うことにしました。1個400円ほどで購入できました。

アナログ電圧計 長方形アナログボルトパネルメーターゲージ 10 スタイルボルト T - AliExpress

https://ja.aliexpress.com/item/1005006415730772.html

ESP32-C3で、出力するPWMを受け付けるWebServerを作る

ESP32-C3でWebServerを作るのは、Arduinoを使えば簡単です。加えてJSONを処理できるArduinoJsonを使えば、JSONの読み取りも比較的簡単にできます。

// src/main.cpp
#include <Arduino.h>
#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include <ssid.h>

const char *ssid = WIFI_SSID;
const char *pass = WIFI_PASSWORD;
WebServer server(80);

void handleCPU()
{
    const char *path = "cpu";

    if (server.method() != HTTP_POST)
    {
        server.send(405, "application/json", "{\"message\": \"Method Not Allowed.\"}");
        return;
    }
    Serial.printf("POST /%s\r\n", path);

    String requestBody = server.arg("plain");
    Serial.printf("request body: %s\r\n", requestBody.c_str());

    DynamicJsonDocument doc(1024);
    DeserializationError error = deserializeJson(doc, requestBody);

    if (error)
    {
        Serial.println("failed to deserialize");
        Serial.printf("POST /%s 400\r\n", path);
        server.send(400, "application/json", "{\"message\": \"Bad Request.\"}");
        return;
    }

    int32_t value = doc["value"].as<int32_t>();

    if (value < 0 || 100 < value)
    {
        Serial.printf("POST /%s 400\r\n", path);
        server.send(400, "application/json", "{\"message\": \"Bad Request. Unexpected value.\"}");
        return;
    }

    Serial.printf("input value: %d\n", value);

    // ここにPWM制御を記述する

    Serial.printf("POST /%s 200\r\n", path);
    server.send(200, "text/json", "{\"message\": \"Success.\"}");
}

void setup()
{
    Serial.begin(9600);

    // WiFiの接続
    WiFi.begin(ssid, pass);
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(500);
    }
    Serial.printf("IP:%s \r\n", WiFi.localIP());

    // Webサーバの開始
    server.on("/", handleRoot);
    server.on("/cpu", HTTP_POST, handleCPU);
    server.onNotFound(handleNotFound);
    server.begin();
}

void loop()
{
    server.handleClient();
}

なお、このssid.hはWiFiSSIDを記述したファイルです。いつもこのファイルを作っておき、.gitignoreに加えています。ArduinoIDEではなく、PlatformIOを使うと、Arduino開発であっても、普通のC++の開発として利用できます。

// src/ssid.h
#define WIFI_SSID "__WIFI_SSID__"
#define WIFI_PASSWORD "__WIFI_PASSWORD__"

PWMも、以下のような形でできます。

// src/main.cpp
#define METER_PIN_1 GPIO_NUM_0
#define METER_PIN_2 GPIO_NUM_1
#define METER_PWM_CH_1 0
#define METER_PWM_CH_2 1

void setup()
{
    // 略

    // PWMの設定
    pinMode(METER_PIN_1, OUTPUT);
    ledcSetup(METER_PWM_CH_1, 1000, 8);
    ledcAttachPin(METER_PIN_1, METER_PWM_CH_1);
    ledcWrite(METER_PWM_CH_1, 0);
    pinMode(METER_PIN_2, OUTPUT);
    ledcSetup(METER_PWM_CH_2, 1000, 8);
    ledcAttachPin(METER_PIN_2, METER_PWM_CH_2);
    ledcWrite(METER_PWM_CH_2, 0);
}

void handleCPU()
{
    // 略

    // 3.3Vを上限とすると、若干低くなったため、3.1Vとして計算
    float_t analogValue = 256 * 3 / 3.1 * value / 100;
    uint16_t pwmValue = (uint16_t)analogValue;

    Serial.printf("pwm value: %d\n", pwmValue);

    ledcWrite(pwm_ch, analogValue);

    // 略
}

ソースコードは以下に置いてあります。

github.com

CPU使用率を送るプログラム

CPU使用率を送るプログラムはGoを使って記述しました。

マルチプラットフォームに対応したCPU・メモリ使用率を取得する便利なライブラリがあり、こちらを利用しました。

github.com

ソースコードは以下に置いてあります。

github.com

これをUbuntuのsystemd上で自動起動するようにしました。

[Unit]
Description=CPU Usage Sender Service
After=network.target

[Service]
Type=simple
User=nnyn
Group=nnyn
ExecStart=/usr/local/bin/send-cpu-usage -h http://192.168.1.105 -i 1000ms
ExecStop=/bin/kill ${MAINPID}
Restart=always

[Install]
WantedBy=multi-user.target
sudo cp ./send-cpu-usage /usr/local/bin/
sudo cp systemd/send-cpu-usage.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable send-cpu-usage.service
sudo systemctl start send-cpu-usage.service
sudo systemctl status send-cpu-usage.service

とりあえずブレッドボードで運用

まずはブレッドボードで実装して運用することにしました。使っていると、欲しい機能が見えてくると思ったためです。元の記事にあるように光らせたらカッコ良いなど、やりたくなるのではと思っていました。

しかし、2週間運用してみて、常に視界に入るものなので、光る必要はないなという結論に至りました。3Dプリンタでケースを作ることも考えたのですが、モニタの上にちょこんとアナログメーターがあるだけで良いと思い、ケースも作らないことにしました。ユニバーサル基板用の簡易ケースだけ作るつもりです。

運用してみた感想は、VS Codeにつなぎに行くとき、結構CPU使うなーとか、Python実装している時保存する度にmypy、black、pylintが動くのかキュキュっとCPUが触れるので、わりと実装時もCPU使うなーとか、そのくらいでした。

ユニバーサル基板に移す

ユニバーサル基板で実装し、稼働させました。ツイート中の動画で、ESP32のファームウェアをビルドをしてCPUが触れるのを確認できます。

まとめ

アナログでカッコ良いCPU・メモリ使用率モニタを、ESP32-C3を使って作ることができました。 開発サーバとして使っているヘッドレスPCであっても、CPU使用率が常に見えるので良い感じです。

アナログ電圧計はPWMで簡単に扱うことができるので、今後もおしゃれにしたいときに組み込んでいきたいと思います。

RP2040の手はんだ実装汎用サポート治具を作った

tl;dr

  • RP2040は0.4mmピッチのはんだづけが必要なMCUだよ。実装前の位置決めが、実装の成功を分けるポイントだと感じているよ。
  • 位置決めをサポートする治具をかつてつくったけれど、PCBごとにサポート治具を作るのは面倒だけど、汎用治具を作ったよ。
  • 実際にキーボード基板に適用したけれど、実装が凄く楽になったよ。

RP2040を手はんだ実装しよう

RP2040は、Raspberry Pi Picoに使われているマイコンで、最近電子工作では多く使われています。 QMK FirmwareがRP2040に対応したのもあって、私自身が作る自作キーボードはRP2040を実装するものになっています。

一方基板に組み込むには、RP2040は0.4mmピッチのICとなっており、かなり実装難度が高い物になっています。 私自身はこの実装難度にトライするのが面白く、「手はんだ実装指南ガイド」を作成し、さらに「手はんだ実装挑戦開発ボードキット」販売するほどです。

docs.google.com

74th.booth.pm

74th.booth.pm

この指南ガイドでは、マスキングテープで固定し、ルーペで拡大して確認するように求めています。 この方法でほぼ失敗することは私はなくなりましたが、なかなかに気力を消費します。

以前位置合わせ治具を作っていたが、無駄が多いと感じて止めてしまった

一番最初にRP2040にトライしていたときは固定補助具を開発して利用していました(既に2年前)。

この作戦は非常にうまくいったのですが、「RP2040を使った基板を作る度にこれを作らねばならないのか?」という無駄をかんじてしまい、手はんだで頑張るテクニックを磨く方に行きました。

RP2040を使った自作キーボードキットを作る

RP2040の手はんだ技術を身につけたことに満足した私は、自分の作る自作キーボードキットにおいて、RP2040を直接実装することを考えました。その方が、開発ボードを使うよりもコストが安く済みます。そして、RP2040の実装自体は私の方でやっておけば、購入者にとってはダイオードとソケットさえ実装できれば良く、開発ボードを使ったキットと苦労は変わらないのではと言う結論に至りました。

自作キーボード専門の即売会イベント、キーケットにて、RP2040を使った自作キーボードキット3種を販売しました。3種とも、RP2040を私の方で事前にてはんだしたキットになります。

74th.booth.pm

74th.booth.pm

74th.booth.pm

74th.hateblo.jp

この時、20個ほどRP2040を実装する必要がありました。

この時もやはりRP2040のはんだづけが一番苦労しました。そしてかなり神経を使い疲れました。

途中から、はんだごてでの実装よりも、プリヒーターを使った方が楽という結論に至り、その方針に変更しました。すると、位置合わせが割と楽になりました。

そして、持っていくキットの作成が完了しました。

プリヒーターを使えば良いのですが、もっと簡単にできないかと考えるようになりました。

汎用位置合わせ治具を作る

以前行った位置合わせ治具の作戦は良かったとして、これを毎回作り直さなくても使える様にならないかと思いました。

そこで、汎用位置合わせ治具のアイディアがでてきました。

これは、予め位置合わせにピンヘッダを通す穴を多数開けておき、その穴のうち最低2個を使うように合わせたPCBを作成すれば、どのようなサイズのPCBでも位置合わせに活用できるものです。

位置合わせ治具PCB

位置合わせ治具と、RP2040のまわりに穴の空いたPCB

RP2040のまわりに穴の空いたPCB

ピンヘッダを貫通させて位置を合わせる

実装を終えて取り外す

これにより、位置合わせを慎重に行う必要がなくなりました。新たなRP2040を使ったPCBを作ったとしても、このいくつか対応した穴を開けておけば、利用できます。

キーケットで完売してしまった自作キーボードキットの第2ロットは、この仕様にしたところ、非常に扱いが楽になりました。

Boothにて販売中

こちらの汎用治具は、Boothにて販売しております。

74th.booth.pm

販売しておりますが、KiCadファイルはこちらで公開しておりますので、良ければ是非使ってみてください。

github.com