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にするための設定が待っている。つづく・・・

LEGO GBC Controller

 LEGOによる玉ころがし(ピタゴラ的)のGBC用のコントローラを自作してみました。こういったLEGO装置を作るにはモータの制御を細かくしたいのですが、既存のモノはなかなかないので作ってみました。    詳しくは ProtoPedia にアップしています。詳しくはProtoPed...