ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【python】livereloadを使って画面を自動更新するWebページを作ってみる

 最近pythonでの簡単なサーバの作り方をよく調べています。
 その中で画面を自動更新できるライブラリである「livereload」というものを知ったので、今回はそれについての備忘録になります。

 詳しい情報は参考資料に挙げているページ制作者様のGithubなどを参照してください。


 では、始めます。


1:livereloadについて
 livereloadは対象ファイルが更新された場合に処理を実行させ、その処理が終わった後にWebページのreloadが行われるようにするためのライブラリです。

 日本語で説明すると少しごちゃごちゃしていますが、要は以下のような処理ができるようになります。

  1. 対象ファイルが更新される
  2. 更新時の処理を実行
  3. Webページ更新

 例えば「1つページしか存在しないようなWebページサーバで定期的に何かデータを受信してその値を画面に表示させる」というようなものを作りたい場合、このlivereloadを使えば比較的簡単に実装できます。


2:livereloadのインストール
 インストールは簡単で以下のpipコマンドでインストールできます。

pip install livereload


3:livereloadの使い方
 livereloadの使い方は簡単で、以下のように書きます。

from livereload import Server # livereloadライブラリ読み込み

def reload():
  # 画面更新前に行いたい処理の関数
  pass

server = Server()
# 引数filepathには監視対象のファイルパス、funcには監視対象ファイルが更新された時に実行する関数を設定
server.watch(filepath="target.txt", func=reload)
# 引数hostにはホスト名、portにはポート番号、rootには表示するHTMLファイルを設定
server.serve(host="ホスト名", port=4000, root="index.html")

 上記の内容を見ればわかるかと思いますが、livereloadのserver.watchのfilepath引数でlivereloadで監視するファイルを指定します。そしてfunc引数には画面更新前に実行させたい関数を指定します。
 その後server.serveでホスト名とポート、表示させたいHTMLファイルを指定して実行することで、livereloadのサーバを起動させることができます。


4:livereloadを使ったサンプル
 では実際にlivereloadのサンプルを作って実行してみます。

 以下の「template.html」と「page_livereload.py」のファイルを作成し、同じフォルダ内(同じ階層)に格納してください。

・template.html

<html lang="ja">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="Cache-Control" content="no-cache">
    <title>livereloadのテスト</title>
</head>
<body>
画面更新回数:$count
</body>
</html>

・page_livereload.py

# -*- coding: utf-8 -*-
import os
import platform
import socket
import threading
import time
from string import Template

from livereload import Server # livereloadライブラリ読み込み


# 画面を自動更新するクラス
class PageLiveReload:
  TEMPLATE_PATH     = "template.html"
  UPDATE_FILE_PATH  = "target.txt"
  DISPLAY_HTML_PATH = "index.html"
  PORT_NUM          = 4000

  _ipaddress         = ""
  _template          = None
  _target_path       = ""
  _display_html_path = ""
  _update_count      = 0 # 画面更新カウント数


  def __init__(self):
    # コンストラクタ(何もしない)
    pass


  def get_ipadress(self):
    # IPアドレスを取得する関数
    ipaddress = ""
    if platform.system() == "Windows":
        # Windowsの場合のIPアドレス取得
        host = socket.gethostname()
        ipaddress = socket.gethostbyname(host)
    elif platform.system() == "Linux":
        # Linuxの場合のIPアドレス取得
        connect_interface = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        connect_interface.connect(("8.8.8.8", 80))
        ipaddress = connect_interface.getsockname()[0]
        connect_interface.close()
    return ipaddress


  def setup(self):
    # 本ファイルまでのディレクトリのパスを取得
    base_dir = os.path.dirname(os.path.abspath(__file__))
    # テンプレートとなるhtmlを読み込む
    template_html_path = os.path.join(base_dir, self.TEMPLATE_PATH)
    self._template = Template(open(template_html_path, "r", encoding="utf-8").read())
    # 画面更新判断用ファイルパスの設定
    self._target_path = os.path.join(base_dir, self.UPDATE_FILE_PATH)
    # 画面表示させるHTMLファイルパスの設定
    self._display_html_path = os.path.join(base_dir, self.DISPLAY_HTML_PATH)
    # IPアドレスを取得
    self._ipaddress = self.get_ipadress()
    # 更新判断用ファイルを作成
    with open(self._target_path, mode='w', encoding="utf-8") as f:
      f.write("")
    # HTMLを初期化(作成)
    self.update_html()


  def main(self):
    server = Server()
    # 引数filepathには監視対象のファイルパス、funcには監視対象ファイルが更新された時に実行する関数を設定
    server.watch(filepath=self._target_path, func=self.reload)
    # Webページのアドレスをprintで表示
    print("http://" + self._ipaddress + ":" + str(self.PORT_NUM))
    # 引数hostにはホスト名、portにはポート番号、rootには表示するHTMLファイルを設定
    server.serve(host=self._ipaddress, port=self.PORT_NUM, root=self._display_html_path)


  def reload(self):
    # 監視対象ファイルが更新された時に実行する関数(この関数実行後にページがreloadされる)

    # カウントアップ
    self._update_count += 1
    # HTML更新
    self.update_html()


  def update_html(self):
    # テンプレートから表示させるHTMLの内容を取得
    html_content = self._template.safe_substitute(count=str(self._update_count))
    # HTMLファイルに書き込み
    with open(self._display_html_path, mode='w', encoding="utf-8") as f:
      f.write(html_content)


if __name__ == '__main__':
  # 準備
  page_reload = PageLiveReload()
  page_reload.setup()

  # マルチスレッドでlivereloadのサーバを実行
  web_reload_thread = threading.Thread(target=page_reload.main, daemon=True)
  web_reload_thread.start()

  # 無限ループ
  while True:
    time.sleep(10)

    # 10秒毎に画面を更新させる
    with open(page_reload._target_path, mode='w', encoding="utf-8") as f:
      f.write("")

 内容を少し解説すると、templare.htmlをテンプレートとして読み込み、livereloadで画面更新する際にカウントアップしたものを表示させるということをやっています。
 また画面の更新は「if __name__ == '__main__':」内に書いてあるようにおよそ10秒毎にしています。

 この2つのファイルを同じ階層に配置できたら、その階層まで移動して以下のコマンドで実行します。

python page_livereload.py

 実行するとURLがprint表示されるのでそのページをブラウザで開きます。

 ブラウザを開くと以下のように画面更新回数が表示されます。

 ブラウザを開いた状態で何もせずに待っていると、およそ10秒毎に画面が更新されて数値がカウントアップされていくようになっているはずです。


5:livereloadを使う際の注意点
 livereloadは便利ですが、注意点があります。

 それは以下のissueに書いてあるように「短い時間に監視対象ファイルが更新された場合、画面更新が無視される」というものです。

 ソースコードを見ると3秒以内に監視対象ファイルが更新された場合はlivereloadによる処理は実行されないようです。

 なので、更新タイミングが短くなることが予想される場合には今回のサンプルのように別途10秒毎に画面を更新するなどの対策が必要になります。

 また自分の環境だけかもしれませんが、Raspberry Pi OS(64bit)の環境でこのlivereloadを使おうとするとエラーが出て使えませんでした。
 Raspberry Pi OS(32bit)だと使えたので、32bitと64bitの違いで使えたり使えなかったりする可能性もあるので注意してください。

 以上がlivereloadを使って画面を自動更新する方法になります。

 限定的といえば限定的かもしれませんが、ちょっとしたWebページ用サーバであれば割と簡単にかけるこの方法でやってもよいのではないかと思います。


・参考資料

【python】PCのIPアドレスを取得する

 過去に簡単なAPIサーバを作る記事を書きました。

 この記事ではホスト名をlocalhostにしていましたが、ローカルネットワーク上のサーバのAPIを叩いて確認したい場合などがあります。
 その場合はホスト名にIPアドレスを設定すると思いますが、一々調べてソースコード内の定数を書き換えるのは面倒です。
 実行しているPCのIPアドレスを自動的に取得するようにしていれば一々書き直す必要がなくなって楽になると思ったので、今回は実行しているPCのIPアドレスを取得する方法の備忘録になります。

 詳しくは参考資料に挙げているページ様を参照してください。


 では、始めます。


・実行しているPCのIPアドレスを取得する方法
 結論から書きますが、以下の方法で取得できます。

・get_ipaddress.py

# -*- coding: utf-8 -*-
import platform
import socket


# 実行PCのIPアドレスを取得する関数
def get_my_ipadress():
    ipaddress = ""

    if platform.system() == "Windows":
        # Windowsの場合のIPアドレス取得
        host = socket.gethostname()
        ipaddress = socket.gethostbyname(host)
    elif platform.system() == "Linux":
        # Linuxの場合のIPアドレス取得
        connect_interface = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        connect_interface.connect(("8.8.8.8", 80))
        ipaddress = connect_interface.getsockname()[0]
        connect_interface.close()

    return ipaddress


if __name__ == '__main__':
   ipaddress = get_my_ipadress()
   print(ipaddress)

 ソースの中身を見ればわかると思いますが、WindowsLinuxでそれぞれ取得方法が違います。

 これを実行すると「192.168.XXX.XXX」のような形のIPアドレスを取得することができます。


 以上が実行中のPCのIPアドレスを取得する方法になります。

 ソースコードを書いてみれば単純ですが、ちょっとしたテストサーバをRaspberry Piに立てる時などには使うことになりそうです。


・参考資料

【Raspberry Pi】pip installを使えるようにする

 以前Raspberry Pi 5の環境構築に関する記事を書きました。

 その後色々調べていると、どうやらデフォルトの状態ではpipでのインストールができないということを知ったので、それを解決するための備忘録になります。

 詳しくは参考資料に挙げているページ様を参照してください。


 では、始めます。


0:デフォルトの状態でpip installをしてみる
 Raspberry Pi 5にRaspberry Pi OS(64bit)を入れた状態で、以下のようにpipからOpenCVを入れるコマンドを実行してみます。

pip install opencv-pyhton

 すると以下のようなエラーが出てインストールが実行されません。

$ pip install opencv-pyhton
error: externally-managed-environment

× This environment is externally managed
╰─> To install Python packages system-wide, try apt install
    python3-xyz, where xyz is the package you are trying to
    install.

    If you wish to install a non-Debian-packaged Python package,
    create a virtual environment using python3 -m venv path/to/venv.
    Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make
    sure you have python3-full installed.

    For more information visit http://rptl.io/venv

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.

 これはどうやらpip 23から導入された「PEP668」というのが原因らしく、要は「仮想環境以外でpip installをできないようにする」対応だそうです。
 とりあえず現状そのままのコマンドだと動かないので、使えるようにしていきます。


1:ちゃんとした対応方法
 正式?な対応方法を最初に書いておきます。

 まず最初にpip installするプロジェクトのディレクトリを作成します。ディレクトリ名はなんでもよいですがここでは「test_pip」として話を進めます。

mkdir test_pip

 pip installするプロジェクトディレクトリを作成したら、そのディレクトリに移動します。

cd test_pip

 対象ディレクトリに移動したら、以下のコマンドを実行します。「'test_pip'」の部分は対象ディレクトリの名前に各自修正してください。

python -m venv --system-site-packages --clear --prompt 'test_pip' --upgrade-deps $(pwd)/.venv

 このコマンドは1分ほど時間がかかるので、終了するまで待ちます。

 終了すると対象ディレクトリ内に「.env/bin/」のフォルダができています。その中にpipやpythonなどが入っているかを以下のコマンドで確認します。

ls .venv/bin/

 自分の場合は実行すると以下のようにちゃんとpipとpythonが入っていました。

$ ls .venv/bin/
Activate.ps1  activate  activate.csh  activate.fish  pip  pip3  pip3.11  python  python3  python3.11

 これでプロジェクトのディレクトリ内の仮想環境の準備ができたので、以下のコマンドで仮想環境を適応させます。

source .venv/bin/activate

 仮想環境を適応させると、以下のように「(プロジェクト名)」がユーザ名などの前につくようになります。

(test_pip) $

 この状態で「command -v python」で仮想環境に入っているpythonを確認すると、仮想環境下でのpythonが使われていることがわかります。

/test_pip/.venv/bin/python

 この状態であれば「pip install」コマンドや「pip install -r requirements.txt」も実行できるようになっています。

 仮想環境から抜ける場合は以下のコマンドで抜けることができます。

deactivate

 つまりは「source .venv/bin/activate」コマンドで仮想環境を読み込み、その状態でpip installを実行し、deactivateで仮想環境から抜けるという形になります。


2:手っ取り早く解決する方法
 1の方法が一番望ましい方法だと思いますが、一応強制的にpip installできるオプションがあります。

 以下のようにコマンドの末尾に「--break-system-packages」を付けるだけです。

pip install [パッケージ名] --break-system-packages

 ちなみにuninstallする時も同じオプションを付けてあげれば実行できます。

pip uninstall [パッケージ名] --break-system-packages

 ただしこれは強制的に実行しているので、あまり推奨はされないようです。


 以上がpip installを使えるようにする方法になります。


 今回はRaspberry Pi 5での方法でしたが、pip 23以降のバージョンでは起こり得る可能性があるので知っておくと良いかもしれません。


・参考資料

【Raspberry Pi】Raspberry Pi 5を触ってみる

 つい先日の2/13に日本でもRaspberry Pi 5が発売されました。

 以下はスイッチサイエンスでの商品ページになりますが、この他にもKSYやDigiKeyなどでも売られています。

 発売したばかりなので品切れとなっているところもありますが、世界的に見ると一時期のような品薄状態ではなく安定して供給されているようなので、1カ月もすれば在庫は復活しそうな感じはします。

 そんなRaspberry Pi 5を手に入れることができたので、さっそくRaspberry Pi OSを書き込んで使っていきたいと思います。

 過去に何度もRaspberry Piに関する環境構築の記事を書いてきていますが、最近は色々と便利になったり変わってきたりしているのと今から初めてRaspberry Piに触るという人の参考になるかもしれないので改めて丁寧に記事にしようと思った次第です。

 ちなみにWindows PCでの手順となるので注意してください。


 では、始めます。


0:準備するもの
 Raspberry Pi本体だけを買っても何もできないので、以下のものを準備します。

 今回は「Raspberry Pi 5 8GB」を使用します。

  • 電源

 今回は「27W出力のUSB-Cタイプの電源」を使用します。Raspberry Pi 5を使う場合、5V-5Aが公式に推奨されているのでこの出力が出せるものを選んでください。また電源はUSB-Cなのでそれに合っているものを選んでください。

  • ケース

 今回は「ヒートシンクと一体となっているケース」を使用します。正直なんでもよいのですが、Raspberry Pi 4の時点でヒートシンクは必須という感覚でしたが、5になってより消費電力が上がっているためより発熱に注意する必要があります。大きいヒートシンクのものや、ケースにファンがついているものなどを選んだ方が良いかと思います。

  • Micro SDカード

 OSを書き込むためのMicro SDカードです。新品のものでない場合、予めフォーマットしておく必要があります。また用途によりますが、容量は最低16GB程度あれば良いかと思います。特に速度や信頼性を求めないのであればDAISOなどに売っているMicro SDカードでも問題ありません。

 Micro SDカードにOSを書き込まないといけないため、SDカードライターも必要になります。

  • Micro HDMI to HDMIケーブル(または変換アダプタ)

 Raspberry Pi 5の場合、通常のHDMIケーブルではなくMicro HDMIになっています。なので変換アダプタを買うか片方がMicro HDMIになっているケーブルを購入してください。

  • マウス、キーボード、モニター

 その他にもマウスやキーボード、モニターも用意する必要があります。

 全部揃えるとかなりお金がかかりますが、頑張って準備しましょう。


1:Micro SDカードにRaspberry Pi OSを書き込む
 まずはWindows PCで以下のページを開きます。

 開いたページを少し下にスクロールすると、以下のようにRaspberry Pi Imagerのダウンロードボタンが出てくるので「Download for Windows」をクリックします。

 ダウンロードしたインストーラをダブルクリックで起動させます。

 ダブルクリックして起動させると許可を求められるので許可をすると、以下のような画面になるので「Install」ボタンをクリックします。

 インストールが始まるのでしばらく待ちます。

 完了画面で「Run Raspberry Pi Imager」にチェックを入れて「Finish」ボタンをクリックします。

 自動的にRaspberry Pi Imagerが立ち上がります。

 この状態でOSを書き込みたいMicro SDカードをSDカードライターにセットしてWindows PCに繋ぎます。

 Micro SDカードの書き込み準備ができたらRaspberry Pi Imagerの「デバイスを選択」をクリックします。

 一覧が出てくるので適切なものを選択します。今回はRaspberry Pi 5向けのOSを準備したいので「Raspberry Pi 5」を選択します。

 次に「OSを選択」をクリックします。

 一覧が出てくるのでこれも適切なものを選択します。今回はRaspberry Pi 5 8GB向けなので「Raspberry Pi OS (64-bit)」を選択します。本体メモリが8GB以外はフルにメモリを使いきれないため、Raspberry Pi OS (32-bit)を選択しても問題ないかと思います。

 次に「ストレージを選択」をクリックします。

 一覧が出てくるので書き込みたいMicro SDカードのドライブを選択します。

「次へ」をクリックします。

 以下のダイアログが表示されるので「設定を編集する」をクリックします。

 ユーザ名とパスワードの設定、WiFiの設定、ロケール設定などを予め設定できます。特にWiFiを設定する場合はWiFiを使う国のところを必ず日本の「JP」に設定しておいてください。他のタブでもSSHの有効化なども設定できます。

 設定が終わったら「保存」ボタンをクリックします。

 ダイアログに戻るので「はい」をクリックします。

 書き込みの警告ダイアログが出ますが「はい」をクリックします。

 書き込み処理が始まるので、終わるまで待ちます。

 
 Micro SDカードへの書き込みが正常終了すると、以下のように表示されるので「続ける」をクリックします。

 これでもうMicro SDカードをPCから取り外しても大丈夫なので取り外し、Raspberry Pi Imagerも終了させます。


2:Micro SDカードをRaspberry Piに挿して起動させる
 OSを書き込んだMicro SDカードの準備ができたら、そのMicro SDカードをRaspberry Piに挿します。

 挿したらHDMIケーブルやマウス、キーボードなどを繋いで準備をし、電源を入れます。

 初回起動時は少し時間がかかりますが、起動すると以下のような画面が表示されます。


3:各種ソフトを最新化する
 Windows PCの初回起動時にWindows Updateが走るように、Raspberry Pi OSもデフォルトのソフトを最新化させた方が良いです。

 デスクトップ画面の左上にある「Terminal」アイコンをクリックすると、以下のようにWindowsコマンドプロンプトのような画面が出てきます。

 このTerminalで以下の2つのコマンドを実行します。

sudo apt update
sudo apt -y upgrade

 様々なソフトのダウンロードとインストールが始まるのでかなり時間がかかりますが、気長に待ちます。

 正常に終了すれば最新化が完了となります。


3:メニューなどの日本語化
 デフォルトのままだとメニューなどが英語なので、日本語化します。

 デスクトップ画面の左上にあるRaspberry Piのマークのアイコンをクリックし「Preferences」→「Raspberry Pi Configration」をクリックします。

 以下の画面が開くので一番右にある「Localisation」のタブをクリックします。

 Localisationが開くので「Set Locale」をクリックします。

 デフォルトだと英語設定になっているため、以下のように日本語のものを選択し、文字コードUTF-8にして「OK」ボタンをクリックします。

 次に「Set Timezone」をクリックします。

 OS書き込み時にちゃんと設定していれば以下のようになっているはずなので確認し、「OK」ボタンをクリックします。

 次に「Set Keyboard」をクリックします。

 各自のキーボードに合わせてください。日本語配列のキーボードであれば以下のデフォルト設定で問題ないと思います。確認できたら「OK」ボタンをクリックします。

 次に「Set WLAN Country」をクリックします。

 こちらもOS書き込み時にちゃんと設定できていれば「JP Japan」となっているはずなので、確認して「OK」ボタンをクリックします。

 設定が終わったら「OK」ボタンをクリックします。

 設定変更で再起動するかを尋ねられるので「Yes」ボタンをクリックして再起動させます。

 再起動後、各種メニューが日本語表示になっています。


4:日本語入力を可能にする
 3の手順で日本語入力が可能になっている場合がありますが、なっていない場合は日本語入力できるようにする必要があります。

 Terminalを開いて以下のコマンドを実行します。

sudo apt install fcitx-mozc

 実行した後にRaspberry Piを再起動させると「半角/全角」キーで切り替えてテキストファイルなどに日本語を入力することができます。

Raspberry Pi OSのデフォルトブラウザに設定されているchromiumでは、テキストファイルなどに入力した日本語をコピペすることはできますが、直接入力ができないようになっています。なのでデフォルトで入っている別ブラウザのFirefoxに変更するか、chromiumの更新で入力できるようになるまで待つか、特殊な方法で入力できるようにするかしか現在は手段がないようです。


 以上が改めて1からRaspberry Pi 5の最低限の環境構築をやった内容になります。

 まだ最低限の環境しか整えていませんが、明らかに体感できるぐらいRaspberry Pi 4から性能アップしています。ただし消費電力も上がっており、CPU部分の発熱がかなりすごく指で触ってかなり熱いと感じる程度だったので、発熱対策は必須となるようです。
 とはいえ、小さい作業用PCとしても使える程度の性能にはなっているので、このRaspberry Pi 5を使って色々なものを作っていきたいです。

・参考資料

【Arduino】Seeed Studio XIAO ESP32C3で色センサーを使ってみる

 かなり前の記事でXIAO ESP32C3の環境構築の記事を書きました。

 小さいし高性能なので久々に引っ張り出して何かやってみようと思っていたところ、S11059-02DTという色センサーを見つけました。
 今回はXIAO ESP32C3でその色センサーを使ってみた備忘録になります。


 では、始めます。


1:準備するもの
 今回準備したものは以下の通りです。  


2:回路図
 以下のような回路図を作ります。今回の色センサーはI2Cを使うため、SDAとSCLを使っています。またこの時色センサーの真上に赤色LEDが来るように配置してください。


3:サンプルスケッチの実行
 回路図ができたら以下のサンプルスケッチを作成し、XIAO ESP32C3に書き込んでみてください。

・XIAO_ESP32C3_color_sensor.ino

/**
 * デジタルカラーセンサモジュール(S11059-02DT)のテスト
 */
#include <Wire.h>

// カラーセンサのアドレス
#define SENSOR_ADDRESS 0x2A
// カラーセンサのコントロール
#define CONTROL_MSB 0x00
// カラーセンサの出力バイト数
#define OUTPUT_BYTES_NUM 0x03
// カラーセンサのADCリセット(HIGHゲイン)
#define ADC_RESET 0x89
// カラーセンサのバスリリース(HIGHゲイン)
#define BUS_RELEASE 0x09

const int LED_PIN = D10;


void setup() {
  Serial.begin(115200);
  delay(1000);

  Serial.println("setup start!");
  pinMode(LED_PIN, OUTPUT);

  // I2Cの準備
  setupI2c();
  // LED点灯
  digitalWrite(LED_PIN, HIGH);

  Serial.println("setup end!");
}

bool is_on = true;
int count = 0;

void loop() {
  // 色センサーの値を取得して表示
  getSensorVal();
  count++;
  delay(500);

  if (count % 10 == 0) {
    is_on = !is_on;
    digitalWrite(LED_PIN, is_on);
    Serial.print("LED: ");
    Serial.println(is_on);
    count = 0;
  }
}

// 色センサのI2Cの準備をする関数
void setupI2c() {
  // I2C開始
  Wire.begin(SDA, SCL);

  Wire.beginTransmission(SENSOR_ADDRESS);
  Wire.write(CONTROL_MSB); // コントロールバイトを指定
  Wire.write(ADC_RESET);   // ADCリセット、スリープ解除
  Wire.endTransmission();

  Wire.beginTransmission(SENSOR_ADDRESS);
  Wire.write(CONTROL_MSB); // コントロールバイトを指定
  Wire.write(BUS_RELEASE); // ADCリセット解除、バスリリース
  Wire.endTransmission();
}

// 色センサの各値を取得して表示する関数
void getSensorVal() {
  int high_byte = 0;
  int low_byte = 0;
  int red = 0;
  int green = 0;
  int blue = 0;
  int IR = 0;

  Wire.beginTransmission(SENSOR_ADDRESS);
  Wire.write(OUTPUT_BYTES_NUM); //出力データバイト(ここでは8byte)を設定
  Wire.endTransmission();

  // 8Byteのデータ要求
  Wire.requestFrom(SENSOR_ADDRESS, 8);

  if (Wire.available()) {
    // 赤色のhigh_byteとlow_byteを読み込み結合して値を取得
    high_byte = Wire.read();
    low_byte = Wire.read();
    red = high_byte << 8|low_byte;
  
    // 緑色のhigh_byteとlow_byteを読み込み結合して値を取得
    high_byte = Wire.read();
    low_byte = Wire.read();
    green = high_byte << 8|low_byte;

    // 青色のhigh_byteとlow_byteを読み込み結合して値を取得
    high_byte = Wire.read();
    low_byte = Wire.read();
    blue = high_byte << 8|low_byte;

    // 赤外線のhigh_byteとlow_byteを読み込み結合して値を取得
    high_byte = Wire.read();
    low_byte = Wire.read();
    IR = high_byte << 8|low_byte;
  }

  Wire.endTransmission();

  // 取得した各値をSerialに表示
  Serial.print("r: ");
  Serial.print(red);
  Serial.print(" g: ");
  Serial.print(green);
  Serial.print(" b: ");
  Serial.print(blue);
  Serial.print(" IR: ");
  Serial.print(IR);
  Serial.println("");
}

 スケッチについての詳細はデータシートや参考資料に挙げているページ様を参照していただきたいのですが、このS11059-02DTの色センサーにはHIGHゲインモードとLOWゲインモードという2種類のモードがあります。

 これらモードは受光部分のセンサの数が違っており、LOWゲインは4セル分しか使わず、HIGHゲインは40セル分をフルで使うという違いがあります。
 LOWゲインとHIGHゲインはサンプルスケッチでいう定数の「ADC_RESET」と「BUS_RELEASE」の値を書き換えることで切り替えることができます。

 一度LOWゲインでも試しましたが、光を全然捉えられていないのか、色センサーから読み取る値がほとんど変化しないという感じだったため、サンプルスケッチではHIGHゲインの方を採用しています。

 実際に書き込んで動作させてみると、D10に繋いだ赤色のLEDが5秒間隔程度で点滅します。

 またシリアルモニタで確認してみると、以下のようにr, g, b, IRの4つの項目のセンサの値が表示されます。

15:19:57.146 -> LED: 0
15:19:57.146 -> r: 39 g: 57 b: 41 IR: 8
15:19:57.661 -> r: 48 g: 58 b: 34 IR: 7
15:19:58.146 -> r: 39 g: 59 b: 42 IR: 7
15:19:58.646 -> r: 49 g: 72 b: 42 IR: 7
15:19:59.146 -> r: 49 g: 59 b: 34 IR: 6
15:19:59.657 -> r: 49 g: 72 b: 42 IR: 7
15:20:00.172 -> r: 49 g: 59 b: 33 IR: 6
15:20:00.654 -> r: 39 g: 55 b: 39 IR: 8
15:20:01.138 -> r: 44 g: 68 b: 45 IR: 7
15:20:01.639 -> r: 52 g: 68 b: 38 IR: 6

 ちゃんと色センサーの部分に赤色LEDの光が当たっている場合、赤色LEDが点灯している時にはrの値が高くなり、消灯している時にはrの値が低くなっていることがわかるかと思います。


 以上がXIAO ESP32C3でS11059-02DTの色センサーを使ってみた内容になります。

 どの程度正確に色が取得できているかはわかりませんが、なんとなく赤が強め、青が強めなどある程度の色によって処理を変えることは可能だと思います。
 また今回はXIAO ESP32C3を使いましたが、I2Cを使えるものであれば別のマイコンボードでも問題なく使えると思うので、割といろいろなことに応用できそうです。


・参考資料

【Raspberry Pi Pico】Raspberry Pi PicoでMicro SDカードの読み書きを行う

 以前の記事でESP32でMicro SDカードを扱う記事を書きました。

 Raspberry Pi PicoでもMicro SDカードの読み書きができることがわかったので、今回はその備忘録となります。
 詳しく参考資料に挙げているページ様を参照してください。


 では、始めます。


1:準備するもの
 今回準備したものは以下の通りです。Micro SDカードについてはデータが破損する可能性があるので、壊れてもよいようなものを使う方が安心です。


2:回路図
 以下のように回路を作ります。


3:サンプルスケッチの実行
 回路とライブラリの準備ができたら、以下のサンプルスケッチを作成します。RP2040のボードがインストールされていれば、サンプルスケッチで使うライブラリも入っているはずです。

・pico_sd_test.ino

#include <SPI.h>
#include <SD.h>

const int _MISO = 4;
const int _MOSI = 7;
const int _CS = 5;
const int _SCK = 6;
const String TARGET_PATH = "example.txt";

File target_file;

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

  // 一応waitを入れる
  delay(3000);

  Serial.println("Initializing SD card...");

  // SPIのピン設定
  SPI.setRX(_MISO);
  SPI.setTX(_MOSI);
  SPI.setSCK(_SCK);

  // SDカードの接続判定
  if (!SD.begin(_CS)) {
    // 接続できなかったら終了
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");

  if (SD.exists(TARGET_PATH)) {
    // SDカードにすでにファイルがある場合はprintのみ
    Serial.println(TARGET_PATH + " exists.");
  } else {
    // ファイルがない場合は新規作成
    Serial.println("Creating " + TARGET_PATH + "...");
    target_file = SD.open(TARGET_PATH, FILE_WRITE);
    target_file.close();
  }

  // ファイルを書き込みモードでopen(openは1つのファイルしかできないため、この前には必ずcloseしておくこと)
  target_file = SD.open(TARGET_PATH, FILE_WRITE);
  Serial.println("Writing: testing 1, 2, 3.");
  // ファイル書き込みしてclose
  target_file.println("testing 1, 2, 3.");
  target_file.close();

  // ファイルを読み込みモードでopen
  target_file = SD.open(TARGET_PATH, FILE_READ);

  if (target_file) {
    Serial.println(TARGET_PATH + ":");
    // ファイルが存在していた場合は中身を表示
    while (target_file.available()) {
      Serial.write(target_file.read());
    }
    target_file.close();
    // ファイル削除
    SD.remove(TARGET_PATH);
  } else {
    Serial.println(TARGET_PATH + " is not exists!!");
  }

  Serial.println("end!!");
}

void loop() {
  // 何もしない
}

 スケッチの内容を見ればわかるかとは思いますが「Micro SDカードにアクセスしてファイルがなければ新規作成をして書き込み、そのファイルを読み込んで中身を表示した後にファイル自体を削除」という動きをしています。

 上記のスケッチをRaspberry Pi Picoに書き込み実行すると、シリアルモニタには以下のように表示されます。

15:40:18.060 -> Initializing SD card...
15:40:18.608 -> initialization done.
15:40:18.608 -> Creating example.txt...
15:40:18.608 -> Writing: testing 1, 2, 3.
15:40:18.686 -> example.txt:
15:40:18.686 -> testing 1, 2, 3.
15:40:18.732 -> end!!


 以上がRaspberry Pi PicoでMicro SDカードの読み書きを行う方法になります。

 SDカードへのアクセスが必要になってくる時もあるので、知っておいて損はないかと思います。


・参考資料

【html/Googleドライブ】Googleドライブで公開設定にした動画をhtmlのvideoタグで表示する

 Googleドライブに動画をアップロードし、公開設定にしたそのURLにアクセスすると動画が再生されます。
 動画として読み込めるならhtmlのvideoタグで再生できないものか、と気になって調べたところできるようだったので今回はその備忘録です。

 基本的には参考資料に挙げているページ様そのままなので、詳しくはそちらを参照してください。


 では、始めます。


1:videoタグについて
 htmlのvideoタグを使うことで動画を再生できます。

 動画と再生ボタンなどをただ表示させたいだけの場合は、srcに動画ファイルのパスとcontrolsオプションを記述するのみで再生できます。

<!-- 記述例(htmlファイルと同じ階層にtest.mp4がある場合) -->
<video src="./test.mp4" controls></video>

 この他にも色々とオプションはありますが、詳しくは参考資料にも挙げている以下のページ様を参照してください。


2:Googleドライブで公開設定にした動画をvideoタグで表示する方法
 まずはvideoタグで表示させたい動画を公開設定にします。やり方は各自で調べるようお願いします。

 Googleドライブ上の対象動画ファイルを右クリックして「共有」→「共有」をクリックします。

 一般的なアクセスが「リンクを知っている全員」の公開設定になっていることを確認し、左下の「リンクをコピー」ボタンをクリックしてリンクをコピーします。

 コピーしたリンクをエディタに貼り付けると以下のような形式になっています。

// ファイルID部分は英数字記号の文字列
https://drive.google.com/file/d/[ファイルID]/view?usp=sharing

 このリンクをvideoタグで読み込めるよう、以下のように書き換えます。

https://drive.google.com/uc?id=[ファイルID]

 書き換えたものをvideoタグのsrcの部分に記述すれば表示できるようになります。


3:サンプルコード
 では実際に表示させてみます。以下の内容をコピペしてvideo_test.htmlという名前で保存してください。

・video_test.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="Cache-Control" content="no-cache">
  <title>video test</title>
</head>
<body>
  <video
    src="https://drive.google.com/uc?id=1zeK4Sv8HUd9N9QilLvSmzbsr__sMYnXO"
    width="50%"
    height="50%"
    controls
    playsinline
    loop>
  </video>
</body>
</html>

 video_test.htmlを保存できたら、インターネットにつながっている状態でvideo_test.htmlをブラウザで開いてみてください。
 ブラウザで開くと、少し動画が読みこまれる表示が出た後、以下のように動画再生ができるようになります。ちなみにここで指定している動画は過去に本ブログで公開した動画です。


 以上がGoogleドライブで公開設定にした動画をhtmlのvideoタグで表示する方法になります。

 Googleドライブで公開設定にした動画を一覧化してhtmlで表示するなども可能なので、色々と使い道があるかもしれません。


・参考資料