はじめに
割り込みのタスク、よく分からない仕様、突然の障害通知...
エンジニアは常日頃様々なストレスに悩まされています。この仕事は何か癒しがないとやってられないでしょう。たぶん。
もし会社に猫が居て膝の上で寝てくれたりすれば24時間働けそうなものですが、そういうわけにもいきません。
ということで作ることにしました。自分で仮想上の猫を創造して、常にデスクで見れるようにしたら小さな癒しとして活躍してくれるのでは?!
完成したもの
とりあえず形になればいいやということで、無骨な感じですがキャットタワーを作りました。
こういう時に3Dプリンターがあると助かりますね。
仕事中にふと目をやると、自由気ままに歩き回ったり、寝たりしています。かわいい。
こだわりポイント
- 物理的な「タワー」感: OLEDディスプレイを2枚使い、猫が1階と2階をランダムに行き来します。
- Slack駆動ごはん: 現代のエンジニアはSlackに住んでいるので、Slackからコマンドを叩くことでご飯をあげられるようにしました。
- カスタム可能: マイコンに直接はんだづけをしていないので、必要な部品があればあとから取り付けて、ねこの生活空間の改善が可能です。
構成
・Slackでコマンドを打つと、目の前の物理デバイスが反応してほしい
・IPアドレスに左右されず、WiFiに接続出来るならどこでも使える
・ご飯をあげるとき、そこまでラグは考慮しない
・専用のサーバーを用意したくない(ラズパイはちょっと面倒)
上記の要件から、以下のような構成になりました。
Slack (Slash Command) → GAS (通訳) → Blynk (IoTハブ) → ESP32 (猫本体)
- ESP32 (XIAO ESP32C3): 猫の挙動を制御
- Blynk: 面倒なサーバー構築なしでデバイスをリモート操作できるサービス。IoTデバイスを作成するにはかなりオススメ。
- Google Apps Script (GAS): SlackとBlynkを繋ぐだけ。
- Slack: 給餌インターフェース。
ハードウェア
筐体は3Dプリンタで自作しました。キャットタワーをイメージしています。
適当にCADで。

使用パーツ
- Seeed XIAO ESP32C3: 親指サイズのマイコン。Wi-Fiが使えて安い。最高。
-
0.96インチ OLED (SSD1306) x 2: 安価で小さいディスプレイ。しっかり光るので問題なし

これって経費おりますか???
注意点:I2Cアドレスの衝突
SSD1306はデフォルトのアドレスが 0x78 です。これを2つ繋ぐとアドレスが被ってしまい、2画面とも同じ描画になってしまいます。
今回はOLEDの裏面にあるチップ抵抗をはんだごてで移植できるOLEDなので、1台だけアドレスを 0x7A に変更しました。これで個別に制御できます。米粒より小さいチップ抵抗を安いはんだごてだけで外して再度取り付けるのはなかなか大変ですね...

ソフトウェア
ねこの挙動などはgithubにあるので、詳しく知りたい方はご覧ください。
弊社ではIDEにCursorを使用しており、今回もほとんどAIに書いてもらいました。
https://github.com/AraiRyusei/digital_cat
ドット絵職人になる
まずは猫のグラフィックです。Piskel というエディタでポチポチとドットを打ちました。
これを image2cpp でC言語の配列(Bitmap)に変換します。この瞬間、絵がデータになります。ねこを表示するにはビットマップデータで扱う必要があるので、生成してできたものをコピーして使います。

Blynkによる遠隔操作の受け口
ESP32側で複雑なサーバーを立てるのは大変なので、Blynk というIoTプラットフォームを利用しました。
ライブラリを使えば、「V0ピンに信号が来たら関数を実行する」という処理が数行で書けます。
// Blynk (V0) から給餌トリガーを受け取る
BLYNK_WRITE(V0) {
int v = param.asInt();
if (v == 1) {
isFeeding = true;
isSleeping = false; // 起こす
feedStartMs = millis();
// 1階なら強制ジャンプで2階へ
if (currentScreen == 0 && !isJumping) {
isJumping = true;
jumpFromScreen = 0;
jumpToScreen = 1;
jumpStartMs = millis();
}
// 押しボタンを自動で戻す(Blynk側にスイッチUIの場合)
Blynk.virtualWrite(V0, 0);
Serial.println("Feed via Blynk V0");
}
}
インフラ連携設定 (Blynk + GAS + Slack)
クラウド側の連携設定です。
Step 1. Blynkの設定 (IoTハブ)
まずはスマホやPCからデバイスを操作するための受け口を作ります。
-
Templateの作成:
Blynk Consoleにアクセスし、Developer Zone>Templatesから新規テンプレートを作成。


-
Datastreamの設定:
テンプレート内のDatastreamsタブでNew Datastream>Virtual Pinを選択。



- Name: Feed
- Pin: V0 (これがESP32側のコードとリンクします)
- Data Type: Integer
-
Deviceの作成:
DevicesメニューからNew Device>From Templateで先ほどのテンプレートを選択。
ここで表示される Template ID, Device Name, Auth Token の3つをコピーして、ESP32のコードに貼り付けます。




Step 2. GASの設定 (通訳)
SlackとBlynkを直結できない理由は2つあります。
- 通信方式の違い: SlackはPOSTでデータを送りますが、Blynk APIはGETリクエストを推奨しています。
- Slackの3秒ルール: Slackはコマンド送信後、3秒以内にレスポンスがないとエラー表示を出します。
そこで、GASに「Blynkへの通知」と「Slackへの即時レスポンス」を任せます。
script.google.com で新規プロジェクトを作成し、以下のコードを書いて、デプロイします。
function doPost(e) {
// 1. Blynk君に「スイッチ押して」とこっそり頼む
var token = "ここにBlynkのAuthTokenを入れる";
var blynkUrl = "[https://blynk.cloud/external/api/update?token=](https://blynk.cloud/external/api/update?token=)" + token + "&V0=1";
try {
UrlFetchApp.fetch(blynkUrl); // GETリクエスト
} catch(err) {
// エラーは握りつぶす(強い心)
}
// 2. Slack様に「やりました!」と即答する (JSON形式)
var slackResponse = {
"response_type": "in_channel",
"text": "みゃう"
};
return ContentService.createTextOutput(JSON.stringify(slackResponse))
.setMimeType(ContentService.MimeType.JSON);
}
デプロイ時の最重要ポイント
右上の「デプロイ」→「新しいデプロイ」を選択し、以下のように設定してください。ここを間違えるとSlackからアクセスできません。
- 種類の選択: ウェブアプリ
- 次のユーザーとして実行: 自分
- アクセスできるユーザー: 全員 (ここが「自分のみ」だと動きません!)
デプロイ後に発行される ウェブアプリのURL をコピーしておきます。

Step 3. Slackの設定 (インターフェース)
最後にSlackアプリを作成します。
-
Slack API から
Create New App>From scratchを選択。


-
次は左メニューの
Slash Commands>Create New Commandを選択。


-
Command:
/feed_cat(好きなコマンド名) - Request URL: 先ほどコピーしたGASのURL
- Short Description: 猫にご飯をあげます
-
Command:
-
保存したら
Install Appから自分のワークスペースにインストールします。
いざ、給餌!
仕事でバグを踏んでイライラした時、Slackを開いてこう打ち込みます。
すると…
Slack「みゃう」
実機(カチャカチャ…)お皿にご飯が山盛り!!

……癒やされる。
まとめ
- XIAO ESP32C3 は小さく値段もお手頃でよい。Type-Cだし、もちろん技適もあるので安心。
- Blynk + GAS の組み合わせは、固定IPもサーバー構築も不要でSlack連携できる最強のソリューション。
- デスクに動くものがあると、殺伐とした開発環境が少しだけ明るくなった(気がする)。
ここまでご覧くださった方、ありがとうございました!
また気が向いたら変なものを作ろうと思います。




