2023-09-13

リアルRPGシステム第1弾「宝箱と魔法のリング」

リアルRPGシステムとは?

ロールプレイングゲーム中では不思議な現象に出会うことも多いでしょう。でも、これは所詮は画面の中の話。元々誰かが作ったプログラムなんだから、そんなんできて当たり前!と思っていたんじゃつまらない!リアルな世界でもRPGに見られる現象を再現してみようということで、RPGに見られるよくある様々なアイテムを、ギミックをも含めリアルで実現してみようというプロジェクトを始めることにしました。

まずは第1弾!(つづきはあるのか???)

ゲーム画面は「ドラゴンクエストXI(スクエアエニックス)」と「ゼルダの伝説TOK(任天堂)」より

アイテム#1「宝箱」

どんなRPGでも必ず出てくる宝箱。これってさー、結構いろいろなイベントをクリアして条件揃わないと開かないモノもあったりして、そこんとこってどうやって判別して開けてるのかファンタジーw ということで、リモートで開閉する宝箱を作ってみた。

外観はLEGOで作成、後ろから見るとつまみがついていて、実は手動でも開きますw(意味あんのか?)、あくまでもメンテのため。
開けるとキャンディが入っていたりして子供たちは大はしゃぎ間違いなし!(アダルトなあなたも?)
これじゃタダの箱じゃんというあなた!裏から見るといろいろからくりが用意されているのがわかる。

M5StampPicoのESP32マイコンでBLEもしくはWebから遠隔で制御できちゃいます。フタの開閉は360°サーボ、スピーカも内蔵しているので効果音も再生できます。電源は後ろについているTypeC端子から供給できます。外から供給もできますが、宝箱内部にかなりのスペースがあるのでモバイルバッテリー内蔵駆動もできます。

動作させると鍵穴が光りますが、カギは刺せません!あしからずw内部ではM5StampPicoのRGB LEDから光ファイバーで鍵穴を光らせています。

 

大事なことを忘れていました。制御についてはワイヤレス通信で実現させるために、BLEかWebで行います。BLEは、LEGOマインドストームのハブ間通信プロトコルを使用しているので、LEGOからも制御可能です。
 Webの方は、URLをたたけば開いたり閉まったりできるようにしてあるので、他の機器からも制御可能です。鍵穴のLEDの明るさも変えられます。ただし、ローカルアクセスのみです。双方とも開けたら約10秒後には自動的に閉まり始めるので、アイテムはさっさと取りましょう!

アイテム#2 魔法のリング

せっかく宝箱を作ったんだから、それを開けるための何かを用意しないといけません!先日、10数年ぶりに大阪の日本橋の共立さんに行く機会があったので、なにげに店内を散策していると・・・

これはっ!!10年くらい前から欲しかったNFCの指輪ではないか!昔はめっちゃ高くて手に入りにくかった覚えしかなかったんだが、数百円とはいい時代になったもんだということで、3種類の大きさすべてをゲット!

これをリーダーで読んでUID判別して宝箱にBLEで送ればいいだけじゃんということで、魔法のリング読み取りシステムはサクッと作る。


マイコンはなんでもいいのでM5AtomS3 Liteに、リーダーも激安のRFID2ユニットで決まりね!

認識したかがわからないと困るので、AtomS3の裏にスピーカーを直差し。とりあえず、3種類の指輪の区別はピピピの回数でということに。

ここで問題なのが、どうやって読み取らせるか???RFID2ユニットに指輪を近づけたらいいじゃん!なんて考えているのは素人で、あくまでもリアルRPGシステムなので、ここは何か意味のあるモノにかざすみたいなのが欲しい。ということで、RPGではおなじみ?の石版(らしきもの?)をフォトショで作成。これが一番手間がかかったかも・・・


 石版といいながらスチレンボードだったりしてめちゃ軽い。AtomS3Liteはバッテリーを持っていないので、モバイルバッテリーを貼り付ける。と。。。なんと、小さいモバイルバッテリーがなく、石版からはみ出てしまうという失態をやらかしたので、ばらしたモノを裏に貼り付けることにする。

NFCリングの感度がイマイチなのでスチレンボードを掘って埋め込んである。

ということで、動作状況を動画で撮ったのでどうぞ!

 

 ついでに「M5Stack Japan Creativity Contest 2023」に締め切り間際の滑り込み応募しました。

 

2023-04-28

Grove Base Hat for Raspberry Pi の設定でハマったので備忘録

ゼミ生にセンサ情報を収集するのにラズパイでIoT化させていて学生がハマっていたので、実際に動作させる方法を調べた結果を備忘録として残しておく。 今回のターゲットはこのボード(Grove Base Hat for Raspberry Pi for Zero)

(追記2023/10/24) なお、ラズパイOSのバージョンは11(bullseye)以降で、古いOSは当てはまらないので注意

(追記2023/11/10) 現在売られているのはボード上のチップ(MM32)とアドレス(0x08)が変更されているバージョンなので注意が必要

・ハマった点
Python用のgrove.pyをインストールして使用しようとしたが、このBase Hatが見つからないと言われ、ライブラリが使用できない
 
・問題点
Wikiにも書いてあるのだが、バージョン違いでI2Cアドレスが変わっているので変更する必要があるのに、そのままインストールしてしまった
 
 
実はこのwikiは、同じBase Hatではあるのだが、フル規格のラズパイ用の方のもの(↓写真)で、Zero用の方には書いていないため、ゼミ生を含め、気がつかない人も多いのかもしれない。
とはいえ、使用するライブラリ自体は「grove.py」という同じモノなので、なんだかなぁという感覚は残るものの、Hatを認識しないというのだから、I2Cアドレスは一番にチェックしなければならないところだともいえるので、うちのゼミ生の経験不足といわれても仕方ない。。。
 
・解決法
解決法の前に、このライブラリのインストールの手順をおさらいしておく。
1.gitでライブラリのクローンをローカルに作成
     $ git clone https://github.com/Seeed-Studio/grove.py
2.pip3でインストール
     $ cd grove.py
     $ sudo pip3 install .
3.動作確認(D5にLEDを接続)
     $ python3 grove/grove_led.py 16
ここで、3の動作確認でHatがないと怒られるのである。
そこで、先ほどのwikiの注意を確認すると、adc.pyにI2Cアドレスの指定があるので、変えろと書いてある。念のため、このHatを購入したスイッチサイエンスのサイトを確認してみる。
 
 
やはり、I2Cアドレスは0x04となっている。 (追記2023/11/10 現在売られているのはMM32チップでアドレスが0x08に変更されているものなので、以下の変更は必要ない。)
で、肝心のadc.pyの方はというと、、、
 

0x08になってるじゃん!動くわけないじゃん!ということで、これをサクッと4に変更して、再度pip3でインストールをかける。
んでもって動作確認すると無事動きましたとさ。となるわけである。
 
以上の説明だけでサクッと解決できる人は、きっとこんなブログなんて必要のない人であろうことから、もう少し手順を説明しておく。
1.クローンはそのまま
2.pip3をかける前に、このgrove.pyフォルダにあるadc.pyをエディタで編集して、上記の08を04に変更して保存した上で、pip3をかける
3.動作確認がうまくいくことを確認して終了となる。
 
しかし、こんなところでつまずいていて肝心のIoTセンササーバの構築は大丈夫か???
 
(追記2023/5/9) なんとせっかくのオンボードのADCコントローラを制御するライブラリがオワコンになっていて使えないという情けない展開に。。。ということで、このボードを使用する時はアナログ端子は使えないので、デジタルかI2Cでどうぞということになる。まぁGroveのコネクタを使えるという意味では、このボードの存在意義はないわけではないが、なんだかなぁという感じ。。。これ以上の追求は時間がまたあるときにということで。
(追記2023/10/24) 久しぶりにWikiを見ると、ライブラリがアップデートされているのか、ポート番号さえ直せば使えるようになっていた。とりあえず、ここのやり方でボードの有無チェックについてはクリアできそうである。ただ、Wikiの一番最初に書いてあるPWMブザーのサンプルはライブラリのファイル不足で実行できないという残念な状態になっているので、ここでめげてしまわないように!ちなみにLED点灯やADC関係、サーボモータは動作することを確認しておいた。
 (追記2023/11/10) このたび、追加購入したものはチップとアドレスが変更されたバージョンのモノだった。今後は、通常のインストール方法で動作が可能なようだ。どちらのバージョンかは、i2cdetect -y 1で08が表示されていれば新バージョン、そうでなければ旧バージョンということになる。なお、旧バージョンの場合、i2cdetectで04は表示されない。

2022-09-09

LEGO MindStorms Robot Inventorキットのハブ間通信のESP32によるエミュレーションライブラリ

LEGO MindStorms は、プロトタイピングや教育用にも非常によいツールであり、20年以上前のRIS時代から利用しているが、2022年6月にやっと日本でも発売になったRobot Inventorキットでは、以前にも増してセンサーやモーターのコネクタが独自形状で、自作のセンサーやアクチュエータを接続することは、かなりハードルが高いものになっている。


このキットではPythonやビジュアルプログラミングツールで動作をプログラミングできるのだが、これによって、プログラムを実行するHubと呼ばれるメインユニット同士のHub間通信ができるようになっており、BLEによるシグナル名とデータの組み合わせによる無線通信が可能になっている。

これをESP32などのBluetooth内蔵マイコンで送受信できれば、いろいろ夢が広がりそうということで、いろいろ探してみたところ、以下のサイトで解析した結果が公開されていた。

LEGO MINDSTORMS Robot Inventor's Hub to Hub Communication Hacks 

実のところ、このサイトの情報には誤りがあって(もしかしたら、この解析のあとにLEGO社の仕様が変わったのかもしれないが)、LEGO社の製造者IDのエンディアンが逆になっていて、この情報のままでは通信できなかった。それに気がつくまでは、結構七転八倒したわけだが、最終的にはBLEパケットモニタでモニタリングしてやっと気がついたということで、めでたく通信が可能となったわけである。

なんちゅうことはない、Advertisementデータをブロードキャストして、製造者IDとシグナル名が一致するデータを勝手に受信するだけという仕組みである。そういう意味では、送受信の確実性はないのが玉に瑕なのだが、お手軽さには勝てないので、早速実装することに。

以下に、ESP32のM5StickCPlusやM5StampC3で実行確認済みのライブラリを貼っておくので、このアイデアというかソースコードとかは、自由に使ってもらって構わない。

まさかとは思うが、金を取っていろいろやるものに組み込む人はいないとは思っているのだが、勝手にLEGO社の製造者IDを使っているので、その辺はかなりグレーである。その辺のトラブルには巻き込まれたくないので、使用は各自の目の届く範囲に収めておくこと。

今回は調子に乗って、作品を紹介する動画まで作ってみた。参考までに貼っておく。




みなさんが、これで一歩踏み込んだLEGO MindStormsライフを送れることを祈る!

コロナ禍なのでCO2センサの情報をLEGOに送って何かおもろいからくり作りたいなぁ・・・・

// LEGO Hub2Hub Communication Emulator for ESP32
//  Copyright (C) 2022 Kikyoya (Karakuri Studio).
//
// Tested M5StickCPlus, M5StampC3
//
/* 使用法(usage)
Setup()
  void initHub2Hub(String dev="Hub2Hub LEGO"); dev:デバイス名(省略可)

loop()
Sending: Advertising to LEGO HUB
  void sendHub2HubData(String key,String data,int seq=H2H_AUTOSEQ,int period_ms=300);
  key & dataは必須、seqとperiod_msは省略可
  LEGOではkeyはシグナル名と表記されている。日本語を使う場合UTF-8で記述
  dataは22バイトまで(NULL文字入れて23バイト)
  Seqは、送るデータが変わるたびに違う値に更新。省略またはH2H_AUTOSEQで自動更新
  period_msは、データ送信する期間。この間複数回にわたって送信する

Recieving: Receive HUB to HUB data
  受信パケットをスキャンしてKeyに一致するものがあるかどうか判別
    bool scanHub2Hub(String key);
  受信したデータを返す
    String getHub2HubData();
    受信した文字列を返す、受信なしの場合は””が返る 
*/
#include <BLEDevice.h> // Bluetooth Low Energy 
#include <esp_rom_crc.h>  // CRC32 Lib

#define H2H_AUTOSEQ -1
const uint16_t ManuID = 0x0397; // Manufacturer ID(LEGO)

// key string to CRC32(little endian)
uint32_t calCRC(String key){
  return esp_rom_crc32_le(0,(uint8_t*)key.c_str(), key.length());
}

#pragma pack(1)
union aPacket{
  char aData[32];
  struct {
    byte len;
    byte adtype;
    uint16_t manuid;
    byte seq;
    uint32_t key;
    char str[23];
  };
  aPacket(String k,String d,byte s){
    len = d.length()+8;
    adtype = 0xff;
    manuid = ManuID;    // LEGO 0x0397
    seq = s;
    key = calCRC(k);
    strcpy(str,d.c_str());
  };
};
#pragma pack()

void sendHub2HubData(String key,String data,int seq=H2H_AUTOSEQ,int period_ms=300)
{
    static byte _seq=1;
    if(seq==H2H_AUTOSEQ) seq=_seq++;   // 引数なし(=H2H_AUTOSEQ)でseqは自動更新
    BLEAdvertising *pAdv = BLEDevice::getAdvertising(); // get adv. object
    BLEAdvertisementData oAdvData = BLEAdvertisementData(); // get Adv Data
    data.remove(22); // limited 22bytes(23文字以降は削除)

    aPacket ad = aPacket(key,data,seq); // Key & data
    
    pAdv->setAdvertisementType(ADV_TYPE_SCAN_IND);
    oAdvData.addData(ad.aData);
//  for(int i=0;i<32;i++) Serial.printf("%02x ",ad.aData[i]); // for Debug
    pAdv->setAdvertisementData(oAdvData);

    pAdv->start();  // Start Advertising
    delay(period_ms);
    pAdv->stop();   // stop Advertising
}

bool bRec=false;
uint32_t KeyCRC;
String Hub2HubStr;

// Scan call back object
class Hub2HubCB: public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice dev){
    if(dev.haveManufacturerData()){ // data available
        static int r_seq = -1; 
        std::string data = dev.getManufacturerData();
        int manu_id = data[1] << 8 | data[0];
        int seq = data[2];
        uint32_t *hash = (uint32_t*)&(data[3]); // LE
        if((manu_id==ManuID)  // check ManuID & Key
            &&(KeyCRC==*hash)
            &&(seq!=r_seq)){  // check seq no
          Hub2HubStr=String((char*)&data[7]);
          r_seq=seq;
          bRec=true;
        }
        dev.getScan()->stop();   // abort scanning
    }
  }
};
// scan Hub to Hub Packet
bool scanHub2Hub(String k)
{
  KeyCRC=calCRC(k);
  bRec=false;
  BLEDevice::getScan()->start(1);
  return bRec;
}
// get Hub to Hub Data
String getHub2HubData()
{
    if(bRec){
      bRec=false;
      return Hub2HubStr;
    }
    return "";
}
// init Hub to Hub Lib
void initHub2Hub(String dev="Hub2Hub LEGO")
{
    BLEDevice::init(dev.c_str());   // initialize device
// set BLE Scan Callback(コールバック登録)
    BLEDevice::getScan()->setAdvertisedDeviceCallbacks(new Hub2HubCB());
}

2021-02-15

ラズパイ設定メモ

 ゼミでは、様々な形でラズパイを使用することがあるが、それぞれの設定がまちまちで引き継ぎなどに苦労している。そこで、統一したやり方で設定するために、標準の設定をメモとして残しておくことにした。

ゼミで使用しているラズパイはこれだけの種類がある。

3B、4B、ZW、AIY Voice Kit、AIY Vision Kit

AIYシリーズのOSはGoogleさんからDLしたものを使用するとして、ドノーマルなラズパイは基本的に同様の設定をすればSDを差し替えて使用できそうなので、後々の管理が楽になると思われる。もちろん、AIYシリーズもSDを作成した後の設定は同じように行うこととする。

また、設定に先立ち、USB電源、マウスやキーボード、HDMIディスプレイ、必要に応じてUSBやHDMIの変換コネクタは必要なので、書いていないからいらないというわけではない。

 OSイメージ(SDカード)の作成

マイクロSDは8GB以上あればOKらしいが、最近では100均での16GBを500円で売っている世の中なので16GB以上を標準とする。
昔はOSイメージをSDに書き込む作業が初心者には難しかったらしいが、RasPi ImagerでSDを作成→セットして電源ON→即緑LED点灯、点滅で起動OK
ここで、赤LED点滅→おかしいのでUSBケーブルなどを確認
実際にあった経験として、USB電源のパワーに問題ないのに起動後に「雷マーク」(電圧低下)が出るのでチェックしたら、100均の充電用USBケーブルに問題があったようで、多くの電流を流せなくて低下したようだった。電源ケーブルには注意!

初期起動設定

  • ロケール→Japanなど日本語に設定
  • パスワード→(ゼミ共通設定に)
  • Display、Wifiは適宜
  • 初期アップデートは実行→かなり時間がかかる→リブート
    以前はコマンドで初期アップデートをかけていたが、今は自動でやってくれるらしい

起動後の設定

 メニューからRasPiの設定で最低限必要なもの
  • システム→ホスト名
    (他のマシンと区別するために必要であとで必要になるので必ずメモる)
  • ディスプレイ→接続するディスプレイに合わせて設定
  • インタフェース→SSHを有効に→以降はSSHを中心に操作
  • 以前はオプション扱いだった「日本語化」と「mDNS」はすでに導入済み

SSHによるリモート操作

  • コマンドプロンプトでpi@マシン名.localでSSH接続(Win10では.local不要かも)例:>ssh pi@karakuripi.local

ラズパイをシャットダウン後、電源を切ってよいかを簡単に確認する設定

ラズパイは、確実にシャットダウンして電源が切れる状態にしないと、むやみに電源を切ることはできない。ラズパイ上の緑色の ACT LEDは、デフォルトでは SDカードへのアクセス時に点滅するが、それをハートビート点滅に変更することで確認しやすくする。

下記の設定をする事で、緑色の ACT LEDの動作がハートビート動作になり 1秒程度の一定間隔で点滅動作をする。シャットダウンしてラズパイの CPUの動作が停止するとハートビート動作も停止するので緑色の ACT LEDは点灯したままとなる。これによって CPUの動作の停止を確認出来るのでコンセントからの給電をオフにできる

$ sudo nano /boot/config.txt
 で /boot/config.txtに下記を追加して再起動
dtparam=act_led_trigger=heartbeat

ちなみにシャットダウンしたあとにGPIO3をGNDに接続すると起動する。

ミニHDMIモニターに対応(必要なら)

秋月で売っているHDMIモニター(↓こんなやつ)に対応させるためには、解像度を調整しないといけないので、以下の設定を必要に応じて実行する。

ラズパイ用として売っているLCDは解像度がHDであったりしてデフォルトの設定があったりするのだが、こいつは1366x768と結構特殊なので、設定を追加する必要がある。

$ sudo nano /boot/config.txt
 で /boot/config.txtに下記を編集して再起動

hdmi_group=2
#hdmi_mode=85   #1920x1080 for Full HD Monitor 通常はこれ
hdmi_mode=81   #1366x768 for Mini HDMI Monitor

シャットダウンボタンの設置

マウスやキーボードはおろか、画面も無いようなラズパイでシャットダウンのためだけにリモート接続してシャットダウンするのは非常に面倒である。そこで、シャットダウン用のスイッチをつけて長押しによるシャットダウンを実現させる。

さて、スイッチをつけるにしてもどこにつけるかが問題で、ネットで検索するといろんなGPIOにつけている例を見かけるのだが、ここではGPIO23につけることにする。

実は、このG23はAIYシリーズの2機種とも(↓)上についているボタンに接続されており、最初からスイッチが付いているので都合がよい。


ほかのHAT類をいくつか調べてみたが、結構このG23は空いていることが多かったので採用することにした。こことGNDの間にスイッチを接続するのだが、マザーボード用のコネクタの付いたスイッチ(↓)が便利で、写真のような2ピンなら下図のようにG23と左のGND、3ピンならG23と2つ右のGNDへ刺せば簡単に実現できる。

このスイッチ長押しでシャットダウンボタンにするための設定は下記のとおり。
  • ローカルにあるシャットダウンボタンスクリプトなど(下記)をラズパイにコピー
    >scp .\shutdownbutton.py pi@karakuripi.local:~
    >scp .\shutdownbutton.service pi@karakuripi.local:~
ラズパイ上でsystemdのサービスを登録
  • ~/shutdownbutton.pyのスクリプトを755(実行権限付加)に
    $chmod 755 ~/shutdownbutton.py
  • ~/shutdownbutton.serviceを/etc/systemd/system/へコピー
    $sudo cp ~/shutdownbutton.service /etc/systemd/system/
  • 設定ファイルをリロード
    $sudo systemctl daemon-reload
  • サービスを起動時に有効化
    $sudo systemctl enable shutdownbutton.service
  • サービスを開始
    $sudo systemctl start shutdownbutton.service
  • 正しく動作しているか確認
    $sudo systemctl status shutdownbutton.service

shutdownbutton.py

#!/usr/bin/python
# coding:utf-8
#
import time
import RPi.GPIO as GPIO
import os

GPIO.setmode(GPIO.BCM)

#GPIO23pinを入力モードとし、pull up設定とします
GPIO.setup(23,GPIO.IN,pull_up_down=GPIO.PUD_UP)

while True:
	GPIO.wait_for_edge(23, GPIO.FALLING)
	sw_counter = 0

	while True:
		sw_status = GPIO.input(23)
		if sw_status == 0:
			sw_counter = sw_counter + 1
			if sw_counter >= 100:
				os.system("sudo shutdown -h now")
				break
		else:
			break
		time.sleep(0.01)

shutdownbutton.service

[Unit]
Description=Shutdown raspberry pi by GPIO button input
Wants=network.target

[Service]
ExecStart=/home/pi/shutdownbutton.py
Restart=on-failure
RestartSec=10s

[Install]
WantedBy=multi-user.target

これでとりあえず最低限の設定はできたことにする。

次はLAMPにするための設定が待っている。つづく・・・

リアルRPGシステム第1弾「宝箱と魔法のリング」

リアルRPGシステムとは? ロールプレイングゲーム中では不思議な現象に出会うことも多いでしょう。でも、これは所詮は画面の中の話。元々誰かが作ったプログラムなんだから、そんなんできて当たり前!と思っていたんじゃつまらない!リアルな世界でもRPGに見られる現象を再現してみようというこ...