2022年10月9日日曜日

fly.io に Python Flask アプリをデプロイ

はじめに

PaaS である fly.io で Python Flaskアプリ (DB は Postgres を利用) をデプロイしてみたのでまとめています。

なお、「fly.io に Python Flask アプリをデプロイするためのローカルの作業及び開発環境を作成」にて作成した Docker コンテナ内で作業します。

fly.io サイトでアカウント登録

https://fly.io/ でアカウントを登録しておきます。

作業用の Docker コンテナを起動

fly.io に Python Flask アプリをデプロイするためのローカルの作業及び開発環境を作成」にて作成した Docker コンテナを起動

$ docker start --interactive fly-work

woker ユーザーに su して作業ディレクトリに移動

# su - worker
$ cd ~/work/

この状態で以下の 2つのファイルが存在します。

$ tree -a ./
./
├── requirements.txt
└── testapp.py

0 directories, 2 files

flyctl をインストール

fly.io のコマンドラインツール flyctl をインストール

$ curl -L https://fly.io/install.sh | sh

環境変数を .bashrc に記載

$ echo 'export FLYCTL_INSTALL="/home/worker/.fly"' >> ~/.bashrc
$ echo 'export PATH="$FLYCTL_INSTALL/bin:$PATH"' >> ~/.bashrc

環境変数を反映

$ source ~/.bashrc

fly.io にログイン

$ flyctl auth login
  • GUI 環境が無いので以下のメッセージが出ます。

    failed opening browser. Copy the url (https://fly.io/app/auth/cli/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx) into a browser and continue
    Opening https://fly.io/app/auth/cli/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...
    

ブラウザでメッセージ中の URL https://fly.io/app/auth/cli/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx を開き、未ログインの場合はログインし、「Continue as [登録したメールアドレス]」をクリック

作業環境で以下のメッセージとなり、flyctl でのログイン完了

successfully logged in as xxxxxxxx@xxxx.xxx

Python Flask アプリをローンチ

flyctl コマンドでローンチを実施します。しかし、ローカルに Docker イメージを作成する環境が無いためか、この段階ではデプロイまでは実施されません。

Fly アプリをローンチ

$ flyctl launch
  • アプリ名を聞いてくるので入力

    ? App Name (leave blank to use an auto-generated name): myflyapp
    
  • リージョンを聞いてくるので Tokyo を選択してみました

    ? Select region:  [Use arrows to move, type to filter]
    ... 省略 ...
    > nrt (Tokyo, Japan)
    
  • Postgres DB をセットアップするか聞いてきますが、別で作成しているので、ここではセットアップしません

    ? Would you like to set up a Postgresql database now? No
    
  • 最後のこのメッセージが表示され、作成された Procfile を実際にあわせて編集する必要があるとのことです

    We have generated a simple Procfile for you. Modify it to fit your needs and run "fly deploy" to deploy your application.
    

二つのアプリが設定されました

$ flyctl apps list
NAME                OWNER           STATUS          PLATFORM        LATEST DEPLOY
fly-builder-XXXX    personal        suspended       machines
myflyapp            personal        pending
  • myflyapp

    • flyctl launch で設定したアプリ
  • fly-builder-XXXX

    • デプロイ時に Docker Image を作成するためのリモートビルダーアプリ
    • ローカルで Docker イメージを作成して Fly サイトへ push するのがデフォルトの動作のようですが、Docker デーモンがローカルに無い場合はローカルで Docker イメージを作成できないので Fly サイトにリモートビルダーアプリが作成され、これが Docker イメージを作成するとのことです。

また、Procfile と fly.toml が作成されました。

$ tree -a
.
├── Procfile
├── fly.toml
├── requirements.txt
└── testapp.py

0 directories, 4 files

fly.toml は fly アプリの設定ファイルとのことです。

Procfile の中身を確認

$ cat Procfile
# Modify this Procfile to fit your needs
web: gunicorn server:app

Procfile を以下のように書き換え

$ cat Procfile
# Modify this Procfile to fit your needs
web: gunicorn testapp:app --log-file=-
  • server:app の部分を実際の Python スクリプトに合わせて testapp:app に書き換え

  • testapp.py スクリプトの中の Flask オブジェクトが app = Flask(__name__) となっているので testapp:app と指定します

  • ログを取得できるように --log-file=- を追加

WSGI サーバーとして gunicorn を使用するので requirements.txt に追加

$ cat requirements.txt
gunicorn
Flask
Flask-SQLAlchemy
psycopg2-binary

Python Flask アプリが使用する Postgres DB 用の fly アプリを作成

Postgres DB 用の fly アプリを作成

$ flyctl postgres create
  • アプリ名を聞いてくるので入力

    ? Choose an app name (leave blank to generate one): mydbapp
    
  • リージョンを聞いてくるので Tokyo を選択してみました

    ? Select regions:  [Use arrows to move, type to filter]
    ... 省略 ...
    > Tokyo, Japan (nrt)
    
  • 設定を聞いてくるので以下を選択してみました。(これ以外の選択だと有料プランが必要になるかも)

    ? Select configuration:  [Use arrows to move, type to filter]
    > Development - Single node, 1x shared CPU, 256MB RAM, 1GB disk
    
  • DB への接続情報が表示されました。後で参照できるよう、どこかセキュアな場所に保存しておきます。(二度と確認することができないとのことです)

    Postgres cluster mydbapp created
      Username:    postgres
      Password:    XXXXXXXXXXXXXXXXXXXXXXX
      Hostname:    mydbapp.internal
      Proxy Port:  5432
      Postgres Port: 5433
    

Postgres DB アプリ mydbapp が作成され起動していることを確認

$ flyctl apps list
NAME                OWNER           STATUS          PLATFORM        LATEST DEPLOY
fly-builder-XXXX    personal        suspended       machines
mydbapp             personal        running         nomad           2m49s ago
myflyapp            personal        pending

flyctl を使って Postgres DB に接続

$ flyctl postgres connect --app mydbapp
  • 通常の psql ツールで接続されているようです。

  • ユーザー名は指定していませんが、postgres ユーザーで接続されました。パスワードは不要でした。

  • データベースを確認

    postgres=# \l
                                        List of databases
      Name    |   Owner    | Encoding |  Collate   |   Ctype    |     Access privileges
    -----------+------------+----------+------------+------------+---------------------------
    postgres  | flypgadmin | UTF8     | en_US.utf8 | en_US.utf8 |
    template0 | flypgadmin | UTF8     | en_US.utf8 | en_US.utf8 | =c/flypgadmin            +
              |            |          |            |            | flypgadmin=CTc/flypgadmin
    template1 | flypgadmin | UTF8     | en_US.utf8 | en_US.utf8 | =c/flypgadmin            +
              |            |          |            |            | flypgadmin=CTc/flypgadmin
    (3 rows)
    
    • 通常の以下のデータベースのみ作成されていました。
      • postgres
      • template0
      • template1
  • 接続先のデータベース名も postgres データベースでした。

    postgres=# select current_database();
    current_database
    ------------------
    postgres
    (1 row)
    
  • 終了

    postgres=# \q
    

ローカルの psql ツールやローカルの Python スクリプトから Postgres DB に接続するには flyctl proxy を使うとのことです。

  • flyctl proxy 起動

    $ flyctl proxy 15432:5432 --app mydbapp
    Proxying local port 15432 to remote [mydbapp.internal]:5432
    
    • ローカルでリッスンする proxy ポートは 15432
    • リモートの Postgres DB ポートは 5432
  • 例えば、ローカルの psql ツールで接続する場合

    $ psql postgres://postgres:XXXXXXXXXXXXXXXXXXXXXXX@localhost:15432/
    
    • パスワード XXXXXXXXXXXXXXXXXXXXXXX は上記で控えたものを使用
  • psql 終了

    postgres=# \q
    
  • flyctl proxy も Ctrl-C で終了

Python Flask アプリと Postgres DB アプリを紐づけてテーブル作成とデータ挿入

Python Flask アプリ myflyapp が Postgres DB アプリ mydbapp に接続できるよう、両者を紐づけ

$ flyctl postgres attach \
  --app myflyapp \
  mydbapp

Postgres cluster mydbapp is now attached to myflyapp
The following secret was added to myflyapp:
  DATABASE_URL=postgres://myflyapp:XXXXXXXX@top2.nearest.of.mydbapp.internal:5432/myflyapp
  • myflyapp と mydbapp が紐づけられました
  • myflyapp に上記の環境変数 DATABASE_URL が設定されました
  • mydbapp に以下が作成されています
    • myflyapp データベース
    • myflyapp ユーザー (パスワード XXXXXXXX)

ローカルの psql ツールで mydbapp に接続するための flyctl proxy 起動

$ flyctl proxy 15432:5432 --app mydbapp

Python Flask アプリ testapp.py では Flask-SQLAlchemy を使用しているため、テーブル作成やデータ挿入等は Python を使って testapp.py で作成した SQLAlchemy オブジェクトを使用します。

Python 起動

$ DATABASE_URL=postgres://myflyapp:XXXXXXXX@localhost:15432/myflyapp \
python3
  • 後に testapp.py をモジュールとしてインポートしますが、環境変数として DATABASE_URL を参照するので設定して Python を起動してます。

  • testapp.py をモジュールとしてインポート

    >>> import testapp
    
  • テーブル作成

    >>> testapp.db.create_all()
    
  • 作成された test_record テーブルにデータ挿入

    >>> record = testapp.TestRecord()
    >>> record.test_col = 'Hello World'
    
    >>> testapp.db.session.add(record)
    >>> testapp.db.session.commit()
    
  • Python 終了

    >>> exit()
    

testdb データベースが作成されていることを確認

$ psql \
postgres://myflyapp:XXXXXXXX@localhost:15432/myflyapp \
--command '\l'

   Name    |   Owner    | Encoding |  Collate   |   Ctype    |     Access privileges
-----------+------------+----------+------------+------------+---------------------------
 myflyapp  | flypgadmin | UTF8     | en_US.utf8 | en_US.utf8 |
 postgres  | flypgadmin | UTF8     | en_US.utf8 | en_US.utf8 |
 template0 | flypgadmin | UTF8     | en_US.utf8 | en_US.utf8 | =c/flypgadmin            +
           |            |          |            |            | flypgadmin=CTc/flypgadmin
 template1 | flypgadmin | UTF8     | en_US.utf8 | en_US.utf8 | =c/flypgadmin            +
           |            |          |            |            | flypgadmin=CTc/flypgadmin
(4 rows)

testapp.py の TestRecord オブジェクトに対応する test_record テーブルが testdb データベースに作成されていることを確認

$ psql \
postgres://myflyapp:XXXXXXXX@localhost:15432/myflyapp \
--command '\d'

            List of relations
 Schema |    Name     | Type  |  Owner
--------+-------------+-------+----------
 public | test_record | table | myflyapp
(1 row)

test_record テーブルにデータが挿入されていることを確認

$ psql \
postgres://myflyapp:XXXXXXXX@localhost:15432/myflyapp \
--command 'select * from test_record'

  test_col
-------------
 Hello World
(1 row)
  • flyctl proxy を Ctrl-C で終了

Python Flask アプリをデプロイ

Python Flask アプリ myflyapp をデプロイ

$ flyctl deploy

割り当てられた IP を確認

$ fly ips list
VERSION IP                      TYPE    REGION  CREATED AT
v4      XXX.XXX.XXX.XXX         public  global  5m19s ago
v6      XXXX:XXXX:X::XXXX       public  global  5m16s ago

myflyapp アプリのステータスを確認

$ flyctl status
App
  Name     = myflyapp
  Owner    = personal
  Version  = 0
  Status   = running
  Hostname = myflyapp.fly.dev
  Platform = nomad

Deployment Status
  ID          = XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
  Version     = v0
  Status      = successful
  Description = Deployment completed successfully
  Instances   = 1 desired, 1 placed, 1 healthy, 0 unhealthy

Instances
ID              PROCESS VERSION REGION  DESIRED STATUS  HEALTH CHECKS           RESTARTS        CREATED
XXXXXXXX        app     0       nrt     run     running 1 total, 1 passing      0               1m31s ago
  • 特に、Hostname = myflyapp.fly.dev より、外部のブラウザから Python Flask アプリ myflyapp にアクセスするための URL が http://myflyapp.fly.dev/ であることが確認できる

  • また、flyctl open を実行すると作業環境は GUI が無いのでブラウザの起動に失敗しますが、表示されるメッセージからもアクセスする URL を確認することができる

    $ flyctl open
    opening http://myflyapp.fly.dev ...
    Error failed opening http://myflyapp.fly.dev: exec: "xdg-open": executable file not found in $PATH
    

ブラウザで http://myflyapp.fly.dev/ でアクセス

  • myflyapp アプリが mydbapp から取得した Hello World が表示されれば正常に動作しています。

参考

2022年10月8日土曜日

fly.io に Python Flask アプリをデプロイするためのローカルの作業及び開発環境を作成

はじめに

PaaS である fly.io で Python Flaskアプリ (DB は Postgres を利用) をデプロイするためのローカルの作業及び開発のための環境を Docker コンテナで構築してみたので以下にまとめています。

なお本記事はローカル環境の構築までについて記載しており、その先の fly.io の操作や fly.io へのアプリのデプロイについては別途記事にする予定です。

Docker コンテナ準備

ベースとなる ubuntu:22.04 docker イメージをローカルに取り込み

$ docker image pull ubuntu:22.04

コンテナを作成して起動

$ docker run \
  --publish 5001:5000 \
  --name fly-work \
  --interactive \
  --tty \
  ubuntu:22.04
  • コンテナ内で実行する開発環境の Python Flask アプリは 5000番ポートをリッスンするので --publish 5001:5000 によりホスト側の 5001番ポートに紐づけます。
  • 後述する通り、ホストのブラウザーからコンテナ内の Python Flask アプリにアクセスする場合は URL として http://localhost:5001/ を指定することになります。

なお次回以降の PC 起動時等、停止しているコンテナを開始する場合は上述の docker run ではなく、以下の通り docker start となります。

$ docker start --interactive fly-work

コンテナ内のシェルが起動するので、まずは作業用の worker ユーザーを作成しパスワードも worker に設定

# useradd \
  --create-home \
  --groups sudo \
  --shell /bin/bash \
  worker

# echo 'worker:worker' | chpasswd

worker ユーザーで個別にインストールが必要なツールをインストール

# apt update && apt -y install sudo net-tools tree

worker ユーザーに su

# su - worker

以降はこのコンテナ内の worker ユーザーでの作業となります。

Ubuntu の基本的な設定

パッケージを最新化

$ sudo apt update && sudo apt -y upgrade

よく使うコマンドを使えるようにするための基本的なパッケージをインストール

$ sudo apt install -y \
  ubuntu-minimal \
  ubuntu-standard \
  ubuntu-server
  • geographic area を聞いてくるので Asia - Tokyo を指定
  • Encoding を聞いてくるので UTF-8 を指定
  • character set を聞いてくるので Guess optimal character set を指定

以降のパッケージインストールで Failed to retrieve available kernel versions. 等のメッセージが出ないようにするためのおまじない

$ sudo apt purge -y needrestart

不要なパッケージを削除

$ sudo apt autoremove -y

日本語環境の設定

$ sudo apt install -y \
  language-pack-ja-base \
  language-pack-ja \
  locales

$ sudo locale-gen ja_JP.UTF-8
$ echo "export LANG=ja_JP.UTF-8" >> ~/.bashrc
$ source ~/.bashrc

ロケールの確認

$ locale
LANG=ja_JP.UTF-8
LANGUAGE=
LC_CTYPE="ja_JP.UTF-8"
LC_NUMERIC="ja_JP.UTF-8"
LC_TIME="ja_JP.UTF-8"
LC_COLLATE="ja_JP.UTF-8"
LC_MONETARY="ja_JP.UTF-8"
LC_MESSAGES="ja_JP.UTF-8"
LC_PAPER="ja_JP.UTF-8"
LC_NAME="ja_JP.UTF-8"
LC_ADDRESS="ja_JP.UTF-8"
LC_TELEPHONE="ja_JP.UTF-8"
LC_MEASUREMENT="ja_JP.UTF-8"
LC_IDENTIFICATION="ja_JP.UTF-8"
LC_ALL=

Postgres DB をインストール

Python Flask アプリをローカルの開発環境で実行する場合にアプリがアクセスする Postgres をインストールします。

Postgres DB をインストール

$ sudo apt install -y postgresql

Postgres DB を起動

$ sudo service postgresql start

$ sudo service postgresql status
14/main (port 5432): online

postgres ユーザーのパスワードを変更

$ sudo su - postgres --command "
    psql --command \"
      alter
        user postgres
        with encrypted password 'pgpass';
    \"
  "

なお、直接 psql コマンドで接続する場合は以下のいずれでもいけました。

  • $ sudo su - postgres --command "psql --dbname postgres"
  • $ psql postgres://postgres:pgpass@localhost/postgres

Python Flask のサンプルアプリを起動

Ubuntu パッケージをインストール

$ sudo apt install -y python3-pip

アプリ用のディレクトリを作成して移動

$ mkdir ~/work
$ cd ~/work/

requirements.txt を配置

$ cat <<EOF > requirements.txt
Flask
Flask-SQLAlchemy
psycopg2-binary
EOF

Python パッケージをインストール

$ sudo pip install -r requirements.txt

スクリプトを配置

$ cat <<EOF > testapp.py
#!/usr/bin/env python3

import os
import re
from flask import (
  Flask,
  Response
)
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = re.sub(
  '^postgres://',
  'postgresql://',
  os.environ['DATABASE_URL']
)

app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

db = SQLAlchemy(app)

class TestRecord(db.Model):
  test_col = db.Column(db.Text, primary_key=True)

@app.route('/')
def root():
  query = db.session.query(TestRecord)
  record = query.one()

  return Response(record.test_col)
EOF

アプリがアクセスする Postgres のデータベースを作成

$ sudo su - postgres --command "
    psql --command \"
      create database testdb;
    \"
  "

テーブルを作成しデータをインサート

$ DATABASE_URL=postgres://postgres:pgpass@localhost/testdb python3

>>> from testapp import db, TestRecord

>>> db.create_all()

>>> test_record = TestRecord()
>>> test_record.test_col = 'hello world'

>>> db.session.add(test_record)
>>> db.session.commit()

>>> exit()

アプリを起動

$ DATABASE_URL=postgres://postgres:pgpass@localhost/testdb FLASK_APP=testapp flask run --host=0.0.0.0

ブラウザで http://localhost:5001/ にアクセス

参考

2022年1月28日金曜日

Heroku で Python Flask で Postgres へ接続 (ローカル開発環境は Docker コンテナ)

Heroku の Python Flask アプリの環境で Postgres への接続を試してみました。

一つの Flask アプリでコードを書き換えずにローカル環境で実行した場合はローカルの Postgres、ネット上の Heroku 環境で実行した場合は Heroku の Postgres に接続できることを確認しています。

ローカルの開発環境は Docker コンテナを一つ用意し、この中に Heroku CLI、Postgres を入れています。

Docker イメージ作成

Heroku CLI, Postgres 用の Docker イメージは Docker Hub の Postgres をベースにして dockerfile でごにょごにょして Heroku CLI のインストール等を行います。

dockerfile は以下の通りです。

FROM postgres

RUN apt update && apt -y upgrade
RUN apt update && apt -y upgrade

RUN apt install -y \
  sudo \
  curl \
  wget \
  unzip \
  git \
  byobu \
  python3-pip \
  libpq-dev

RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*

RUN curl https://cli-assets.heroku.com/install.sh | sh

RUN useradd \
  --create-home \
  --groups sudo \
  --shell /bin/bash \
  worker
RUN echo 'worker:worker' | chpasswd
RUN su - worker -c 'mkdir /home/worker/Desktop'

Docker イメージをビルド

$ docker build --tag heroku-postgres:1 ./

Docker コンテナを開始

$ docker run \
  -p 5000:5000 \
  --name test-heroku-postgres \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -d \
  heroku-postgres:1
  • -p 5000:5000」で Heroku ローカル開発環境 (heroku local web) で公開する 5000 番ポートに外部のブラウザからアクセスできるようにしてます。

  • コンテナ開始時に Postgres が初期化されるようなので「-e POSTGRES_PASSWORD=mysecretpassword」環境変数でパスワードを指定しています。ローカルのとりあえずの環境なのでパスワードそのまま書いていますが、気になる場合は https://hub.docker.com/_/postgres の「Docker Secrets」あたりを読むとパスワードをファイルに書いてそのファイル名を指定することもできるようです。(試していないので本当にできるかわかりません)

  • -d」を指定しているのでシェルのコマンドプロンプトにすぐ戻り、「$ docker ps -a」で確認するとバックグラウンドでコンテナが動いていることが確認できます。

  • PC 再起動時等、コンテナが停止している場合は以下でコンテナを起動できます。

    $ docker container start test-heroku-postgres
    

起動しているコンテナで bash を実行して worker ユーザーになります。

$ docker exec -it test-heroku-postgres /bin/bash
# su - worker
$ cd Desktop/

以下、コンテナの中の bash で実行しています。

Flask アプリのスクリプトをダウンロード

Postgres に接続するテスト用の Python Flask スクリプトを Gist (https://gist.github.com/nezuppo/c36bbb4c1f81da348ba74e8ccf3001bf) に貼り付けています。

Gist からこのスクリプトをダウンロード

$ wget https://gist.github.com/nezuppo/c36bbb4c1f81da348ba74e8ccf3001bf/archive/e1df36533ba102fd1494e06d23398f6f3d7b891e.zip
  • 別途新規で git init をする必要があるため、git clone ではなく wget で zip ファイルをダウンロードしています。

ダウンロードした zip を解凍してディレクトリに cd

$ unzip e1df36533ba102fd1494e06d23398f6f3d7b891e.zip
$ cd c36bbb4c1f81da348ba74e8ccf3001bf-e1df36533ba102fd1494e06d23398f6f3d7b891e/

ローカル開発環境で Flask アプリからローカルの Postgres に接続

localdb データベース作成

$ createdb -U postgres localdb

postgres ユーザーで localdb データベースを指定して psql 起動

$ psql -U postgres localdb

psql 内で testtable を作成してデータを挿入

localdb=# create table testtable(aaa text);
localdb=# insert into testtable values('test local');
localdb=# select * from testtable;
    aaa
------------
 test local
(1 row)

localdb=# exit

Python の必要なパッケージをインストール

$ sudo pip install -r requirements.txt

環境変数 DATABASE_URL で Postgres への接続情報を指定して heroku ローカル開発環境を起動

$ DATABASE_URL=postgresql://postgres@localhost/localdb \
  heroku local web

ブラウザで http://localhost:5000/ を開くと Flask アプリが Postgres に接続し、インサートされた「test local」をひっぱってきてブラウザに表示されます。

終了は ctrl-c です。

ネット上の Heroku サイトの Flask アプリから Heroku 上の Postgres に接続

git の初期設定

$ git config --global user.email "worker@localdomain"
$ git config --global user.name "worker"

git を初期化

$ git init

.gitignore 作成

$ echo "__pycache__/" > .gitignore

ファイルを git に追加して commit

$ git add .
$ git commit -m "first commit"

Heroku にログイン

$ heroku login

Heroku アプリを作成

$ heroku create

Heroku Postgres をアドオン

$ heroku addons:create heroku-postgresql:hobby-dev
  • 最初に git init しているので --app で Heroku アプリ名を指定する必要はありません

Heroku のアプリに Postgres がアドオンされたことを確認

$ heroku addons

Heroku Postges への接続 URL の確認

$ heroku config

Heroku Postgres に関する情報の確認

$ heroku pg

Heroku アプリにアドオンした Postgres (ローカルではなくリモート) に psql で接続

$ heroku pg:psql

psql 内で testtable を作成してデータを挿入

DATABASE=> create table testtable(aaa text);
DATABASE=> insert into testtable values('test remote');
DATABASE=> select * from testtable;
     aaa
-------------
 test remote
(1 row)

DATABASE=> exit

リモートが Heroku であることを確認

$ git remote
heroku

リモートに git push して Heroku アプリを初期化

$ git push heroku master

ブラウザでアクセスする URL を確認

$ heroku open
  • 本来はブラウザを立ち上げるためのコマンドだが、Docker コンテナが CUI 環境のためブラウザが起動されず URL が表示されるので、これをブラウザにコピペして開く
  • Postgres にインサートした ‘test remote’ が表示される

2022年1月17日月曜日

Docker の Ubuntu でよく使うコマンドやツールが一通りインストールされたイメージを作成

WSL の Ubuntu には sudo、wget、byobu 等の普段良く使うツールがあらかじめインストールされていますが、Docker のデフォルトの Ubuntu イメージは最小限必要な構成になっているため、これらはインストールされていません。

必要なパッケージをひとつひとつインストールするように dockerfile を書くのも大変なので、まとめてインストールできるよう調べてみました。

環境

  • Docker の Ubuntu イメージ: ubuntu:20.04

結論

いろいろ検討した結果、dockerfile は以下の通りです。

$ cat dockerfile
FROM ubuntu:20.04

RUN apt update && apt -y upgrade
RUN apt update && apt -y upgrade

ARG DEBIAN_FRONTEND=noninteractive
RUN apt install -y ubuntu-minimal
RUN apt install -y ubuntu-standard
RUN apt install -y ubuntu-server

RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*

以下、この dockerfile を作成するために検討した内容です。

WSL のパッケージを確認

上述の通り、WSL の Ubuntu には sudo、wget、byobu 等の普段良く使うツールがインストールされているので、これらを依存関係として持っている大元のパッケージを確認します。以下、WSL にインストールした Ubuntu で実施しています。

まずはインストールされているパッケージ一覧を取得するため、`dpkg -l’ の出力を確認

$ dpkg -l
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                           Version                               Architecture Description
+++-==============================-=====================================-============-=================================================================>
ii  accountsservice                0.6.55-0ubuntu12~20.04.4              amd64        query and manipulate user account information
ii  adduser                        3.118ubuntu2                          all          add and remove users and groups
ii  alsa-topology-conf             1.2.2-1                               all          ALSA topology configuration files
... (省略) ...

というように、最初の 5行目まではヘッダ情報で、6行目以降の「ii」に続く 2項目がインストールされているパッケージのようなので、これを抜き取りパッケージ一覧を作ります。

$ dpkg -l | tail -n +6 | awk '{print $2}'
accountsservice
adduser
alsa-topology-conf
... (省略) ...

これらすべてのパッケージの依存関係一覧を取得します。

$ dpkg -l | tail -n +6 | awk '{print $2}'| xargs -I{} apt depends {} 2>/dev/null | less

出力の中から、sudo、wget、byobu 等に依存関係を持っているパッケージを見てみると、ubuntu-minimal、ubuntu-standard、ubuntu-server パッケージによく使うコマンドやツールが一通り依存関係に含まれていることがわかりました。

Docker の Ubuntu イメージに ubuntu-minimal、ubuntu-standard、ubuntu-server パッケージをインストールしようとすると予期しないエラーその 1が発生

本題とはそれますが、予期しないエラーが出ました。

まず、ubuntu-minimal、ubuntu-standard、ubuntu-server パッケージをインストールする dockerfile は以下のようににしてみました。

$ cat dockerfile
FROM ubuntu:20.04

RUN apt update && apt -y upgrade

RUN apt install -y ubuntu-minimal
RUN apt install -y ubuntu-standard
RUN apt install -y ubuntu-server

この docker ファイルでイメージを build すると、予期しないエラーが出ました。

$ docker build --tag test-with-tools:1 ./
... (省略) ...
#5 144.7 E: Failed to fetch http://archive.ubuntu.com/ubuntu/pool/main/s/systemd/systemd-timesyncd_245.4-4ubuntu3.14_amd64.deb  404  Not Found [IP: 91.189.88.142 80]
... (省略) ...
executor failed running [/bin/sh -c apt install -y ubuntu-minimal]: exit code: 100

例えば、「http://archive.ubuntu.com/ubuntu/pool/main/s/systemd/systemd-timesyncd_245.4-4ubuntu3.14_amd64.deb」がダウンロード出来ないとのことです。

ブラウザでこの URL を開いてみると「Not Found」と出ました。サーバーは動いているが、指定したファイルが存在しないようです。

そこで、「systemd-timesyncd_245.4-4ubuntu3.14_amd64.deb」が置かれているディレクトリ「http://archive.ubuntu.com/ubuntu/pool/main/s/systemd/」をブラウザで開いてみると

systemd-timesyncd_245.4-4ubuntu3.15_amd64.deb」は置かれているが
systemd-timesyncd_245.4-4ubuntu3.14_amd64.deb」は置かれていないことがわかります。

古いバージョンの「systemd-timesyncd_245.4-4ubuntu3.14_amd64.deb」をダウンロードしようとしてエラーになっているようです。

dockerfile を見てみると「RUN apt update && apt -y upgrade」というように古いリポジトリ情報で apt update し、その後 apt upgrade しているので、このタイミングでリポジトリ情報が新しいものに更新されていると想定されます。なので、もう一度 update すれば良いのではないかと思われます。(ついでに upgrade も実施)

この時点で dockerfile は以下のようになってます。

$ cat dockerfile
FROM ubuntu:20.04

RUN apt update && apt -y upgrade
RUN apt update && apt -y upgrade

RUN apt install -y ubuntu-minimal
RUN apt install -y ubuntu-standard
RUN apt install -y ubuntu-server

無事 Docker イメージのビルドが進みました。(が、以下の予期しないエラーその 2が発生しました)

Docker の Ubuntu イメージに ubuntu-minimal、ubuntu-standard、ubuntu-server パッケージをインストールしようとすると予期しないエラーその 2が発生

以下のエラーです。

#6 242.1 Errors were encountered while processing:
#6 242.1  console-setup
#6 242.1  ubuntu-minimal
#6 242.1 E: Sub-process /usr/bin/dpkg returned an error code (1)
------
executor failed running [/bin/sh -c apt install -y ubuntu-minimal]: exit code: 100

ubuntu-minimal と依存関係がある console-setup をインストールしうようとしていてエラーになっているっぽいです。

更にメッセージを見てみると、

#6 241.9 If you don't use a framebuffer, the choices that start with "." will reduce the
#6 241.9 number of available colors on the console.
#6 241.9
#6 241.9   1. . Arabic
#6 241.9   2. # Armenian
#6 241.9   3. # Cyrillic - KOI8-R and KOI8-U
... (省略) ...
#6 241.9   21. . Combined - Latin; Slavic Cyrillic; Greek
#6 241.9   22. . Combined - Latin; Slavic and non-Slavic Cyrillic
#6 241.9   23. Guess optimal character set
#6 241.9
#6 241.9 Character set to support:

と出ているので、コンソールから入力を求められているが、スクリプトでの処理のため入力できずエラーとなったようです。

ググると環境変数で「DEBIAN_FRONTEND=noninteractive」を指定すると良いようなので dockerfile は以下のようにしました。

コンテナの中に環境変数 DEBIAN_FRONTEND を残したくないので「ENV DEBIAN_FRONTEND=noninteractive」ではなく、「ARG DEBIAN_FRONTEND=noninteractive」としてます。

$ cat dockerfile
FROM ubuntu:20.04

RUN apt update && apt -y upgrade
RUN apt update && apt -y upgrade

ARG DEBIAN_FRONTEND=noninteractive
RUN apt install -y ubuntu-minimal
RUN apt install -y ubuntu-standard
RUN apt install -y ubuntu-server

apt インストールのキャッシュを削除してイメージのサイズを少しでも減らす

ググると apt install した時のキャッシュを削除してイメージのサイズを小さくすると良いとのことなので dockerfile に追加しました。

$ cat dockerfile
FROM ubuntu:20.04

RUN apt update && apt -y upgrade
RUN apt update && apt -y upgrade

ARG DEBIAN_FRONTEND=noninteractive
RUN apt install -y ubuntu-minimal
RUN apt install -y ubuntu-standard
RUN apt install -y ubuntu-server

RUN apt-get clean
RUN rm -rf /var/lib/apt/lists/*

参考

2022年1月10日月曜日

WSL Ubuntu で Heroku with Python

Ubuntu への Heroku CLI のインストール、サンプル Python アプリのデプロイ、アプリ開発のためのローカル Heroku 環境の構築等は Heroku サイトのドキュメント「Getting Started on Heroku with Python」を参考にできますが、WSL Ubuntu に適用しようとするとこの通りに進めるだけではうまくいかなかったので、Heroku のドキュメントを実施するにあたり別途必要なことをここにまとめておきます。

環境

  • Windows11 Home
  • WSL2 Ubuntu 20.04.3

pip の代わりに pipenv を使うための準備

Heroku のドキュメントではローカル開発環境でのサンプルアプリ実行に必要な Python パッケージのインストールに pip を使っています。pip でも問題なく動作するかと思われますが、Python パッケージ環境を肥大化させたくないので pip の代わりに今回は pipenv を使います。

Heroku のドキュメントに書かれたことを実施する前にまずは以下の手順で pipenv 環境の準備をします。

pipenv をインストール

$ sudo apt install pipenv

作業用ディレクトリを作成して移動

$ mkdir /some/where/work
$ cd /some/where/work/

pipenv プロジェクトを作成し pipenv shell を起動

$ pipenv --python $(which python3)
$ pipenv shell

以降、全て pipenv shell で実施

Heroku CLI のインストールに snap を使えない

Ubuntu への Heroku CLI のインストールは「$ sudo snap install heroku --classic」を実行するとのことですが、WSL では snap を使うことは難しいようなのでインストール用のシェルスクリプトをダウンロードして実行。

$ wget https://cli-assets.heroku.com/install-ubuntu.sh
$ chmod +x install-ubuntu.sh
$ ./install-ubuntu.sh

アプリ開発のためのローカル Heroku 環境の構築に pipenv を使う

サンプルアプリに必要なパッケージをインストール。これを実施しないと、後述の pipenv install でエラーが出ます。

$ sudo apt install libpq-dev

このサンプルアプリを実行するために必要な python パッケージは requirements.txt で定義されています。

$ cat requirements.txt
django
gunicorn
django-heroku

ローカルにこれらの Python パッケージをインストール。今回は前述の通り pip の代わりに pipenv を使っています。

$ pipenv install -r requirements.txt

ローカル環境にアプリの変更を適用する時も pip ではなく pipenv

ドキュメントではローカル環境に Python の request パッケージをインストールするのに $ pip install requests としていますがこれも pipenv でやります。

$ pipenv install requests

2021年9月20日月曜日

github pages のドキュメントを sphinx で作成

github pages のドキュメントを sphinx で作成する方法はググるといろいろあるようですが、今回は以下を参考にしてみました。

github ではリポジトリの gh-pages ブランチを github pages として公開することができるので、main ブランチの sphinx の build/html/ に同リポジトリの gh-pages ブランチをサブモジュールとして割り当てます。


環境

  • Ubuntu 20.04.3 LTS
  • Sphinx 4.2.0

github でリポジトリを用意

github で新しくリポジトリを新規作成しました。リポジトリ名は test としています。

ローカルの Ubuntu にリポジトリをクローン

$ cd ~
$ git clone https://github.com/nezuppo/test.git

gh-pages ブランチを作成

git の test リポジトリのディレクトリに移動

$ cd ~/test/

–orphan オプションを付けて何も commit されていない gh-pages ブランチを作成してチェックアウト

$ git checkout --orphan gh-pages
Switched to a new branch 'gh-pages'

一応 git-branch で確認すると orphan だからなのか gh-pages ブランチは出てこず、以下のような出力となりました。

$ git branch -a
  main
  remotes/origin/HEAD -> origin/main
  remotes/origin/main

ファイルは main ブランチと同じのものがありましたが、commit 前の状態でした。

$ ls -la
total 16
drwxr-xr-x 3 test test 4096 Sep 19 22:46 .
drwxr-xr-x 8 test test 4096 Sep 19 22:55 ..
drwxr-xr-x 8 test test 4096 Sep 19 23:16 .git
-rw-r--r-- 1 test test    6 Sep 19 22:46 README.md
$ git status
On branch gh-pages

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   README.md

まずは空の状態にしました。

$ rm README.md
$ git rm --cached README.md
rm 'README.md'
$ ls -la
total 12
drwxr-xr-x 3 test test 4096 Sep 19 23:20 .
drwxr-xr-x 8 test test 4096 Sep 19 22:55 ..
drwxr-xr-x 8 test test 4096 Sep 19 23:21 .git
$ git status
On branch gh-pages

No commits yet

nothing to commit (create/copy files and use "git add" to track)

空の状態だと github に push できないので .nojekyll ファイルを追加し push までしました。なお、github pages でデフォルトで有効になっている Jekyll ツールが sphinx ドキュメントとの相性が悪いようなので .nojekyll ファイルで無効にする必要があるとのことです。

$ touch .nojekyll
$ git add .nojekyll
$ git commit
$ git push --set-upstream origin gh-pages

この状態で github の test リポジトリの [Settings] - [Pages] で確認すると Source が gh-pages リポジトリの / (root) となっていました。


main ブランチ配下に gh-pages ブランチをサブモジュールとして割り当て

main ブランチの doc/ に sphinx を展開し、html ファイル一式が置かれる doc/build/html/ に gh-pages ブランチをサブモジュールとして割り当てます。これにより gh-pages ブランチ の / (root) に sphinx ドキュメントの html ファイル一式が置かれ github pages で公開されるようになります。

main ブランチの / (root) に移動

$ cd ~/test

$ git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.

サブモジュール用のディレクトリを作成

$ mkdir -p doc/build

gh-pages ブランチをサブモジュールとして割り当て

$ git submodule add -b gh-pages https://github.com/nezuppo/test.git doc/build/html

doc/ に sphinx を展開

doc/ に移動

$ cd doc

sphinx を展開。以下のように source と build ディレクトリは分割させました。

$ sphinx-quickstart
(省略)
> Separate source and build directories (y/n) [n]: y
(省略)

この状態で make clean すると build 配下がサブモジュール化された状態も含め全て削除されてしまい都合が悪いので Makefile を以下のように修正。ついでに make clean と make html、make help 以外は使えないようにしました。

$ diff -U -1 Makefile{.org,}
--- Makefile.org        2021-09-20 00:14:36.590000000 +0900
+++ Makefile    2021-09-20 00:15:43.500000000 +0900
@@ -1,20 +1,25 @@
 # Minimal makefile for Sphinx documentation
 #

 # You can set these variables from the command line, and also
 # from the environment for the first two.
 SPHINXOPTS    ?=
 SPHINXBUILD   ?= sphinx-build
 SOURCEDIR     = source
 BUILDDIR      = build

 # Put it first so that "make" without argument is like "make help".
 help:
        @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

 .PHONY: help Makefile

+clean:
+       rm -rf $(BUILDDIR)/html/*
+       rm -rf $(BUILDDIR)/html/.buildinfo
+       rm -rf $(BUILDDIR)/doctrees
+
 # Catch-all target: route all unknown targets to Sphinx using the new
 # "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
-%: Makefile
+html:
        @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

また、sphix のテーマは bizstyle が好みなの以下のようにで変更しました。

$ diff -U 0 source/conf.py{.org,}
--- source/conf.py.org  2021-09-20 00:21:20.030000000 +0900
+++ source/conf.py      2021-09-20 00:21:58.500000000 +0900
@@ -54 +54 @@
-html_theme = 'alabaster'
+html_theme = 'bizstyle'

html ドキュメントを生成

$ make html

build/html/ 配下に github pages で公開する html ドキュメントが生成されました。

$ ls -la build/html/
total 48
drwxr-xr-x 4 test test 4096 Sep 20 00:23 .
drwxr-xr-x 4 test test 4096 Sep 20 00:23 ..
-rw-r--r-- 1 test test  230 Sep 20 00:23 .buildinfo
-rw-r--r-- 1 test test   45 Sep 19 23:59 .git
-rw-r--r-- 1 test test    0 Sep 19 23:59 .nojekyll
drwxr-xr-x 2 test test 4096 Sep 20 00:23 _sources
drwxr-xr-x 2 test test 4096 Sep 20 00:23 _static
-rw-r--r-- 1 test test 3070 Sep 20 00:23 genindex.html
-rw-r--r-- 1 test test 4484 Sep 20 00:23 index.html
-rw-r--r-- 1 test test  269 Sep 20 00:23 objects.inv
-rw-r--r-- 1 test test 3437 Sep 20 00:23 search.html
-rw-r--r-- 1 test test  598 Sep 20 00:23 searchindex.js

gh-pages ブランチを commit

main ブランチより先に gh-pages ブランチを commit する必要があるようです。

gh-pages ブランチを割り当てたサブモジュールディレクトリに移動

$ cd ~/test/doc/build/html/

ブランチが gh-pages であることを確認

$ git branch
* gh-pages
  main

git インデックスに追加

$ git add *
$ git add .buildinfo

commit

$ git commit

git status でコミット漏れがないことを確認して push

$ git status
On branch gh-pages
Your branch is ahead of 'origin/gh-pages' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
$ git push

main ブランチを commit

gh-pages ブランチの次に main ブランチを commit します。

test リポジトリの / (root) に移動

$ cd ~/test

ブランチが main であることを確認

$ git branch
  gh-pages
* main

git インデックスに追加

$ git add doc/build/html/
$ git add doc/

commit

$ git commit

git status でコミット漏れがないことを確認して push

$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean
$ git push

github pages を確認

これで sphinx ドキュメントの html ファイル一式を github pages で公開することができました。


sphinx ドキュメントを更新する場合

以下の順序で実施します。

  1. doc/source/ 配下のドキュメントを更新
  2. doc/ で make html
  3. 上記の [gh-pages ブランチを commit] を実施
  4. 上記の [main ブランチを commit]

新たにリポジトリを clone する場合

gh-pages リポジトリをサブモジュールとして割り当てられた test リポジトリを新たに clone する場合は以下の順序で実施します。

  1. test リポジトリを clone
  2. サブモジュールを update
    $ cd test/
    $ git submodule update --init
    
  3. サブモジュールのディレクトリに移動して gh-pages ブランチにチェックアウト
    $ cd doc/build/html/
    $ git checkout gh-pages
    

参考

2021年7月18日日曜日

find コマンドでディレクトリを除外して列挙

はじめに

find コマンドでディレクトリを除外して列挙しようと思って調べてみたら

$ find /etc ! -type d

でよさそうなので、本当にいけてるか調べてみました。

まずは対象ディレクトリ配下のファイルやディレクトリの数を確認

対象ディレクトリとして /etc でやってみます。

$ find /etc
/etc
/etc/services
/etc/lsb-release
(以下省略)

普通に find できます。が。。。

$ find /etc > /dev/null
find: ‘/etc/polkit-1/localauthority: Permission denied
find: ‘/etc/ssl/private: Permission denied

というように標準エラー出力を確認すると、一般ユーザーではアクセスできないものがあるようなので以下、sudo つけてやります。

これらがファイルとかディレクトリとかを確認するために ls に渡します。

$ sudo find /etc | xargs sudo ls -lad
drwxr-xr-x 93 root root        4096 Jul 18 09:55 /etc
-rw-------  1 root root           0 Jun  4 06:39 /etc/.pwd.lock
drwxr-xr-x  3 root root        4096 Jun  4 06:40 /etc/NetworkManager
(以下省略)

drwxr-xr-x の先頭の文字でファイルとかディレクトリとか判断できます。 (この場合は d なのディレクトリ)

cut コマンドで先頭の文字だけ切り出します。

$ sudo find /etc | xargs sudo ls -lad | cut -b 1
d
-
d
d
-(以下省略)

ソートをかけて uniq コマンドでカウントします。

$ sudo find /etc | xargs sudo ls -lad | cut -b 1 | sort | uniq -c
    728 -
    213 d
    628 l

/etc 配下にはファイル (-) が 728個、ディレクトリ (d) が 213個、シンボリックリンク (l) が 628個あることがわかりました。

最終的にファイル名やディレクトリ名にスペースが含まれる場合でも対応できるように以下のようにします。(find に -print0、xargs に -0 を付けてます)

$ sudo find /etc -print0 | xargs -0 sudo ls -lad | cut -b 1 | sort | uniq -c
    728 -
    213 d
    628 l

find に ! -type d を付けてディレクトリが除外されているか確認

ディレクトリが除外され、その他 (今回の場合はファイルとシンボリックリンク) が期待通り列挙されているようです。

$ sudo find /etc ! -type d -print0 | xargs -0 sudo ls -lad | cut -b 1 | sort | uniq -c
    728 -
    628 l

念のため、!-type d だけに効くように () を付けます。

$ sudo find /etc \( ! -type d \) -print0 | xargs -0 sudo ls -lad | cut -b 1 | sort | uniq -c
    728 -
    628 l

結論

$ find /etc ! -type d

でディレクトリを除外して列挙できるみたいです。

2020年10月2日金曜日

meishi2 の QMK ファームウェアのビルドをラズパイのコマンドラインでやってみた

名刺サイズ自作キーボード meishi2 を組み立てた後そのままにしていたので、引っ張り出してきて QMK ファームウェアをビルドして書き込んでみました。ラズパイのコマンドラインでやっています。ほぼ「 meishi2 keyboard ビルドガイド」に記載されていることをそのままやっています。

環境

QMK ファームウェアをビルドする母艦はラズパイを使いました。

  • Raspberry Pi 4B Model B 4GB Memory
  • Raspberry Pi OS Version: August 2020 (Raspbian GNU/Linux 10 buster)

QMK ファームウェアのビルド環境を構築

QMK ソースコードをダウンロード

$ git clone https://github.com/qmk/qmk_firmware.git

QMK ビルドに必要な依存を解決

$ cd qmk_firmware/
util/qmk_install.sh
  • 主に QMK ビルドに必要な Raspberry Pi OS の deb パッケージがインストールされるようです。

meishi2 をラズパイに接続

ログを見ながら meishi2 をラズパイに接続してみるのでまずはシスログをリアルタイムで確認

$ tail -f /var/log/syslog

meishi2 とラズパイを USB ケーブルで接続すると LED が点灯し、ログには以下のメッセージやその他いろいろ出てきました。

Sep 25 12:25:43 raspi4 kernel: [  409.663764] usb 1-1.1: Product: meishi2

QMK をビルドして meishi2 に書き込み

とりあえずやってみたらエラーになりました。

$ make meishi2:default:avrdude
QMK Firmware 0.10.17
WARNING: Some git submodules are out of date or modified.
 Please consider running make git-submodule.

Making meishi2 with keymap default and target avrdude

tmk_core/protocol/lufa.mk:14: lib/lufa/LUFA/makefile: No such file or directory
make[1]: *** No rule to make target 'lib/lufa/LUFA/makefile'.  Stop.
Make finished with errors
make: *** [Makefile:584: meishi2:default:avrdude] Error 1

メッセージに従い以下実施

$ make git-submodule

再度以下実行したらうまくいきました。

$ make meishi2:default:avrdude

途中で以下のメッセージが出ました。

Detecting USB port, reset your controller now

meishi2 基板上の pro micro の横のタクトスイッチ (シルク印刷で SW5 と記載) を押すと meishi2 のリセットがかかり書き込みが完了しました。この時にラズパイ上で meishi2 と接続されている USB デバイスファイルが /dev/ttyACM0 であると自動認識したようです。

meishi2 の pro micro の LDE は緑 x 1、赤 x 2 が点灯している状態になったので USB ケーブルの切断/再接続をしたところ緑 x 1 のみの点灯状態となりました。

これで meishi2 が使えるようになったようです。

動作確認

meishi2 には 4つのキーがあり、今回の QMK の初期設定で左から順番に以下の内容が設定されていました。

  • 左 CTRL + Z (やり直し)
  • 左 CTRL + X (切り取り)
  • 左 CTRL + C (コピー)
  • 左 CTRL + V (貼り付け)

meishi2 を PC と USB ケーブルで接続するとうまくキーボードとして認識されたようで、エディタ上で上記 4ボタンが期待通りに動きました。

参考

2020年6月19日金曜日

ラズパイで PlatformIO でコマンドラインで M5StickC を Lチカ

ラズパイのコマンドラインで PlatformIO を使って M5StickC を Lチカしてみました。


環境

  • Raspberry Pi 4 model B
  • Raspbian GNU/Linux 10 (buster)
  • M5StickC

pipenv プロジェクト作成

pipenv プロジェクトのディレクトリを作成して移動

$ mkdir /some/where/m5stickc-lflash
$ cd /some/where/m5stickc-lflash/

pipenv プロジェクト作成

$ pipenv --python $(which python3)

pipenv shell 起動

$ pipenv shell

以下、全て pipenv シェルで実行


Platformio インストール

インストール

$ pipenv install platformio

バージョン確認

$ platformio --version
PlatformIO, version 4.3.4

M5StickC のボード ID を確認

PlatformIO での M5StickC のボード ID を確認しました。

$ platformio boards
--- 省略 ---
Platform: espressif32
================================================================================
ID                               MCU    Frequency    Flash    RAM     Name
-------------------------------  -----  -----------  -------  ------  --------------------------------
--- 省略 ---
m5stack-core-esp32               ESP32  240MHz       4MB      320KB   M5Stack Core ESP32
m5stack-fire                     ESP32  240MHz       16MB     6.25MB  M5Stack FIRE
m5stack-grey                     ESP32  240MHz       16MB     520KB   M5Stack GREY ESP32
m5stick-c                        ESP32  240MHz       4MB      320KB   M5Stick-C
--- 省略 ---

ボード ID は m5stick-c でよさそうです。


PlatformIO プロジェクト作成

プロジェクトのディレクトリを作成して移動

$ mkdir pio_project
$ cd pio_project/

プロジェクト初期化

$ platformio init --board=m5stick-c
  • 上で調べてた M5StickC のボード ID を指定しました。

シリアルポートデバイスを確認

M5StickC を USB 接続したラズパイのシリアルポートデバイスを確認しました。

M5StickC をラズパイの USB に接続する時の /var/log/messages で確認することもできますが、PlatformIO をインストールしたのでこれで確認してみました。

M5StickC をラズパイの USB に接続している状態で以下のコマンドを打ちました。

$ platformio device list
/dev/ttyUSB0
------------
Hardware ID: USB VID:PID=0403:6001 SER=7552CE3E93 LOCATION=1-1.3
Description: M5 Serial Converter

/dev/ttyAMA0
------------
Hardware ID: fe201000.serial
Description: ttyAMA0

/dev/ttyUSB0 で良いようです。


Lチカプログラムを用意

led-flash.ino ファイルを src/ に置きました。

$ cat src/led-flash.ino

#define LED_PIN 10

#define LED_ON  LOW
#define LED_OFF HIGH


void setup() {
    pinMode(LED_PIN, OUTPUT);
}

void loop() {
    digitalWrite(LED_PIN, LED_ON);
    delay(100);

    digitalWrite(LED_PIN, LED_OFF);
    delay(900);
}
  • M5StickC の LED は LOW が ON、HIGH が OFF とのことです。

プログラムをビルド

以下のコマンドでビルド

$ platformio run

エラー無くビルドできました。ターゲットに upload を指定していないのでビルドされただけで M5SticC には書き込まれていません。

.pio/ 配下にビルドされたファイルが出来ていました。

$ ls -la .pio/build/m5stick-c/
合計 28712
drwxr-xr-x 4 pi pi     4096  6月 19 21:38 .
drwxr-xr-x 3 pi pi     4096  6月 19 21:32 ..
-rw-r--r-- 1 pi pi   149000  6月 19 21:38 .sconsign37.db.dblite
drwxr-xr-x 3 pi pi     4096  6月 19 21:32 FrameworkArduino
-rw-r--r-- 1 pi pi   211040  6月 19 21:32 firmware.bin
-rwxr-xr-x 1 pi pi  4265992  6月 19 21:32 firmware.elf
-rw-r--r-- 1 pi pi 24743628  6月 19 21:32 libFrameworkArduino.a
-rw-r--r-- 1 pi pi        8  6月 19 21:32 libFrameworkArduinoVariant.a
-rw-r--r-- 1 pi pi     3072  6月 19 21:32 partitions.bin
drwxr-xr-x 2 pi pi     4096  6月 19 21:38 src

再度ビルドして M5StickC に書き込み

$ platformio run --upload-port /dev/ttyUSB0 --target upload
  • 上で確認したシリアルポートデバイスを指定しました。
  • --target upload によりビルド後 M5StickC に書き込まれました。

M5StickC で Lチカできました。


参考

ラズパイで esptool でコマンドラインで M5Stick のフラッシュをバックアップ&リストア

ラズパイのコマンドラインで esptool を使って M5StickC のフラッシュをバックアップ、リストアしてみました。

ほぼ https://lang-ship.com/blog/work/esp32-esptool-py/ に書かれていることをそのままやっています。

環境

  • Raspberry Pi 4 model B
  • Raspbian GNU/Linux 10 (buster)
  • M5StickC

M5StickC を接続したラズパイのシリアルポートデバイスを確認

ラズパイで $ tail -f /var/log/messages をしながら M5StickC に付属の USB ケーブルでラズパイの USB に接続すると以下のメッセージが出ました。

Jun 18 23:33:44 raspi4 kernel: [19742.992308] usb 1-1.2: FTDI USB Serial Device converter now attached to ttyUSB0

ttyUSB0 と出ているのでシリアルポートデバイスは /dev/ttyUSB0 であることがわかりました。

デバイスファイルも存在していました。

$ ls -l /dev/ttyUSB0
crw-rw---- 1 root dialout 188, 0  6月 18 23:33 /dev/ttyUSB0

pipenv プロジェクト作成

pipenv インストール

$ sudo apt install pipenv

pipenv プロジェクト用のディレクトリを作成して移動

$ mkdir /some/where/m5stickc
$ cd /some/where/m5stickc/

pipenv プロジェクト作成

$ pipenv --python $(which python3)

pipenv shell 起動

$ pipenv shell

以下、全て pipenv シェルで実行

M5StcikC のフラッシュをバックアップ

esptool をインストール

$ pipenv install esptool

esptool.py --help で確認すると出てくる flash_id : Read SPI flash manufacturer and device ID を実行。シリアルポートは上で確認した /dev/ttyUSB0 を指定しました。

$ esptool.py --port /dev/ttyUSB0 flash_id
esptool.py v2.8
Serial port /dev/ttyUSB0
Connecting......
Detecting chip type... ESP32
Chip is ESP32-PICO-D4 (revision 1)
Features: WiFi, BT, Dual Core, 240MHz, Embedded Flash, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
--- 省略 ---
Manufacturer: c8
Device: 4016
Detected flash size: 4MB
Hard resetting via RTS pin...

いろいろ情報が表示され、フラッシュのサイズが 4MB であることがわかりました。

フラッシュをバックアップ

$ esptool.py --port /dev/ttyUSB0 read_flash 0x00000 0x400000 ./image-factory-default-4M.bin
  • 先頭アドレス 0x00000 から上で確認したフラッシュのサイズ 0x400000 (10進数で ‭4,194,304‬) バイトを読み出して指定したファイルに保存しました。

保存したファイルのサイズはフラッシュのサイズと同じ 4MB でした。

$ ls -lh image-factory-default-4M.bin
-rw-r--r-- 1 pi pi 4.0M  6月 17 23:18 image-factory-default-4M.bin

バックアップしたフラッシュをベリファイ

$ esptool.py --port /dev/ttyUSB0 verify_flash --diff yes 0x0000 ./image-factory-default-4M.bin
--- 省略 ---
Auto-detected Flash size: 4MB
Verifying 0x400000 (4194304) bytes @ 0x00000000 in flash against ./image-factory-default-4M.bin...
-- verify FAILED: 279 differences, first @ 0x0000b02b
   0000b02b 00 a8
   0000b02c 00 aa
   0000b02d a0 fa
   --- 省略 ---
   0000b81b 00 ff
   0000b81c 01 ff
   0000b81d 00 ff

A fatal error occurred: Verify failed.

ベリファイがエラーになりました。。。

「起動するたびにデータを書き換えているプログラムが存在しており、その場合にはベリファイチェックがNG」とのことで、工場出荷時のフラッシュだったのですがこれに該当しているのかもしれません。

フラッシュをリストア

ベリファイはエラーになりましたが、このバックアップからリストアしてみました。

まずはフラッシュを消去

$ esptool.py --port /dev/ttyUSB0 erase_flash

バックアップしたフラッシュをリストア

$ esptool.py --port /dev/ttyUSB0 write_flash 0x0000 ./image-factory-default-4M.bin
  • 先頭アドレス 0x0000 を指定すると良いようです

うまくリストアできていました。

2020年5月14日木曜日

Grafana で Raspberry Pi の CPU、GPU 温度をグラフ表示



前回のブログ [Grafana で Raspberry Pi のメモリ使用率などリソース状態を可視化してみた] で CPU 使用率やメモリ使用率等のリソースを Grafana のダッシュボードで確認できるようにしましたが、下のキャプチャ画面のように CPU と GPU 温度に関しては現在の状態 (温度) の確認はできますが、過去の状態をグラフで確認することはできないようです。

CPU、GPU の現在の温度

今回は Grafana にダッシュボードを追加して、Raspberry Pi の CPU、GPU 温度のグラフを表示してみました。

環境

InfluxDB に保存されている CPU、GPU の温度データを確認

InfluxDB に保存されている CPU 温度のデータは実際の温度を 1,000倍した値が保存されており、GPU 温度はそのままの値が保存されているので確認してみました。

InfluxDB shell 起動

$ influx

telegraf データベースを指定

> use telegraf

CPU 温度データを確認

> select * from cpu_temperature limit 3
name: cpu_temperature
time                host  value
----                ----  -----
1587525500000000000 raspi 50464
1587525510000000000 raspi 50464
1587525520000000000 raspi 50464

CPU 温度は 50464 という値になっていますが、これは Telegraf で CPU データを取得するときに /sys/class/thermal/thermal_zone0/temp を参照しているためと思われます。実際は値を 1000 で割った 50.464度ですね。

ちなみに手動だと以下のように現時点の値を確認できました。

$ cat /sys/class/thermal/thermal_zone0/temp
54230

GPU 温度は以下のように、実際の温度が値として保存されていました。

> select * from gpu_temperature limit 3
name: gpu_temperature
time                host  value
----                ----  -----
1587525501000000000 raspi 50.5
1587525510000000000 raspi 49.9
1587525520000000000 raspi 50.5

CUP 温度とは異なり GPU 温度は以下のコマンドで取得しているためだと思われます。

$ /opt/vc/bin/vcgencmd measure_temp
temp=53.7'C

ダッシュボード追加

ブラウザで Grafana の GUI を開いて以下実行しました。

  • [Create (+ アイコン)] - [Dashboard]

  • CPU 温度を追加するために [Add Query] をクリック

  • 以下、Query のフォームで

    • [From] の [select measurement] をクリックして [cpu_temperature] を選択
    • [Select] の [field (value)]、[mean ()] に続く [+] をクリックして [Math] - [math] を選択
      • [math (/100)] が追加されたので [(/100)] をクリックして [(/1000)] に変更 (上で確認した通り、IfluxDB 上では 1,000倍された値なので 1,000 で割りました)
  • 画面右側の方の [Query Inspector] をクリックしてクエリを確認すると select 文でも / 1000 されていることが確認できました。

    SELECT mean("value") / 1000 FROM "cpu_temperature" WHERE time >= now() - 6h GROUP BY time(20s) fill(null)
    
  • 次に GPU 温度を追加するために [Add Query] をクリック

  • 追加された Query のフォームで

    • [From] の [select measurement] をクリックして [cpu_temperature] を選択
    • CPU 温度とは異なり、GPU 温度は InfluxDB に実温度が保存されている (1,000倍されていない) ので 1,000 で割る必要はありません
  • 画面上部のフロッピーアイコンの [Save dashboard] をクリックし、適宜ダッシュボード名等を入れて保存

CPU、GPU 温度のグラフを表示できました。

2020年5月11日月曜日

試しに Markdown で Sphinx ドキュメント作成



Sphinx はドキュメントを作成するためのツールであり reStructuredText を採用しています。

reStructuredText は Markdown と同様な軽量マークアップ言語のひとつで実際に使ってみるとなかなか使いやすいですが、Markdown と比べると Sphinx 以外では汎用的に採用されておりません。

そこで Sphinx で reStructuredText ではなく Markdown を使ってドキュメントを作成できるようにしてみました。

下にも書いてますが、何かワーニングが出ましたが一応 Markdown でドキュメントを書いて html に変換できました。

環境

  • Ubuntu 18.04.4 LTS (Windows10 WSL)

インストール

pipenv で Sphinx をインストールするのでまずは pipenv をインストール

$ sudo apt install pipenv

sphinx インストール用のディレクトリを作成して移動

$ mkdir /some/where/sphinx
$ cd /some/where/sphinx/

virtual environment 作成

$ pipenv --python $(which python3)

Sphinx インストール

$ pipenv install sphinx

Markdownパーサー recommonmark をインストール

$ pipenv install recommonmark

pipenv シェル起動

Sphinx をインストールした virtual environment でシェルを起動しました。

virtual environment に起動

$ cd /some/where/sphinx/

pipenv シェル起動

$ pipenv shell

Sphinx ドキュメントプロジェクト作成

上に描いた「pipenv シェル起動」を実行した状態で以下をやりました。

プロジェクト用のディレクトリを作成して移動

$ mkdir /some/where/document
$ cd /some/where/document/

プロジェクトファイル作成

$ sphinx-quickstart

いろいろ聞いてくるので以下のように入力してみました。

> Separate source and build directories (y/n) [n]: n
> Project name: test-markdown
> Author name(s): Nezuppo
> Project release []:
> Project language [en]: ja

以下のうようにディレクトリとファイルが作成されました。

$ tree -aF
.
├── Makefile
├── _build/
├── _static/
├── _templates/
├── conf.py
├── index.rst
└── make.bat

3 directories, 4 files

Markdown が使えるようにプロジェクトを設定

まずは conf.py を修正するのでバックアップをとる

$ cp conf.py{,.org}

以下のようになるよう、conf.py を修正。なお本題とは関係ありませんが好みで html_theme を bizstyle に変更しました。

$ diff -u conf.py{.org,}
--- conf.py.org 2020-05-10 14:51:51.652409000 +0900
+++ conf.py     2020-05-10 15:30:52.760240100 +0900
@@ -28,6 +28,7 @@
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
 extensions = [
+    'recommonmark'
 ]

 # Add any paths that contain templates here, relative to this directory.
@@ -51,9 +52,17 @@
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
 #
-html_theme = 'alabaster'
+html_theme = 'bizstyle'

 # Add any paths that contain custom static files (such as style sheets) here,
 # relative to this directory. They are copied after the builtin static files,
 # so a file named "default.css" will overwrite the builtin "default.css".
 html_static_path = ['_static']
+
+
+
+source_suffix = {
+    '.rst': 'restructuredtext',
+    '.txt': 'markdown',
+    '.md' : 'markdown',
+}

Markdown でドキュメント作成

index.rst のファイル名を index.md に変更

$ mv index.rst index.md

この時点で index.md の中身はデフォルトで用意されたサンプルの reStructuredText なので、不要であることを確認して中身を削除し Markdown に書き換え

html に変換

$ make html

以下のワーニングが出ました。。。

/home/worker/.local/share/virtualenvs/sphinx-5rYzJHjd/lib/python3.6/site-packages/recommonmark/parser.py:75: UserWarning: Container node skipped: type=document
  warn("Container node skipped: type={0}".format(mdnode.t))

が、とりあえず _build/html が作成され、ブラウザで index.html を確認できました。

参考

2020年4月24日金曜日

Grafana で Raspberry Pi のメモリ使用率などリソース状態を可視化してみた



Grafana Labs でラズパイ用の Grafana ダッシュボード Raspberry Pi Monitoring が公開されているので試してみました。
CPU 使用率やメモリ使用率等だけでなく、CPU や GPU の温度も確認できるようです。

Raspbian の標準リポジトリから Grafana をインストールするとログイン画面が真っ白で何も表示されなかったので、Grafana サイトのリポジトリからインストールしました。
なお、Raspberry Pi Monitoring はデータソースに InfluxDB、メトリックスコレクタに Telegraf を使うことが前提のようなのでこれらもインストールしました。

環境

  • Raspberry Pi 3 Model B
  • Raspbian GNU/Linux 10 (buster)

Ansible インストール

Ansible の playBook で Grafana, InfluxDB, Telegraf をインストールしてみるので、まずは Ansible をインストールしました。

$ sudo apt install ansible

Ansible playbook ファイル作成

pbook-grafana.yaml を作成

- hosts: localhost
  vars:
    distrib_id: debian
    distrib_codename: buster
  connection: local
  gather_facts: no
  become: true
  handlers:
    - name: telegraf-restarted
      service:
        name: telegraf
        state: restarted
  tasks:
    # influxdb, telegraf 共通
    - name: influxdb, telegraf apt_key
      apt_key:
        url: https://repos.influxdata.com/influxdb.key
        state: present
    - name: influxdb, telegraf apt_repository
      apt_repository:
        repo: deb https://repos.influxdata.com/{{ distrib_id }} {{ distrib_codename }} stable
        state: present
        filename: influxdb
    - name: influxdb, telegraf apt update
      apt:
        update_cache: yes

    # influxdb
    - name: apt install influxdb
      apt:
        update_cache: no
        state: present
        pkg:
          - influxdb
    - name: service influxdb start
      service:
        name: influxdb
        state: started

    # telegraf
    - name: apt install telegraf
      apt:
        update_cache: no
        state: present
        pkg:
          - telegraf
    - name: service telegraf start
      service:
        name: telegraf
        state: started
    - name: telegraf user
      user:
        # GPU 温度取得 (vcgencmd measure_temp) のために必要
        name: telegraf
        groups: video
    - name: telegraf blockinfile
      blockinfile:
        dest: /etc/telegraf/telegraf.d/raspi.conf
        create: yes
        block: |
          [[inputs.net]]

          [[inputs.netstat]]

          [[inputs.file]]
            files = ["/sys/class/thermal/thermal_zone0/temp"]
            name_override = "cpu_temperature"
            data_format = "value"
            data_type = "integer"

          [[inputs.exec]]
            commands = ["/opt/vc/bin/vcgencmd measure_temp"]
            name_override = "gpu_temperature"
            data_format = "grok"
            grok_patterns = ["%{NUMBER:value:float}"]
      notify:
        telegraf-restarted

    # grafana
    - name: grafana apt_key
      apt_key:
        url: https://packages.grafana.com/gpg.key
        state: present
    - name: grafana apt_repository
      apt_repository:
        repo: deb https://packages.grafana.com/oss/deb stable main
        state: present
        filename: grafana
    - name: grafana apt update
      apt:
        update_cache: yes
    - name: apt install grafana
      apt:
        update_cache: no
        state: present
        pkg:
          - grafana
    - name: service grafana-server start
      service:
        name: grafana-server
        state: started
    - name: grafana systemd
      systemd:
        name: grafana-server.service
        state: started
        enabled: yes

Ansible でインストール

$ ansible-playbook -i localhost, pbook-grafana.yaml

インストール後の InfluxDB データベース確認

InfluxDB shell 起動

$ influx
Connected to http://localhost:8086 version 1.8.0
InfluxDB shell version: 1.8.0
>

データベース確認

> show databases
name: databases
name
----
_internal
telegraf
  • telegraf データベースが作成されていました。

telegraf データベースを選択

> use telegraf
Using database telegraf

telegraf データベースの measurements を確認

> show measurements
name: measurements
name
----
cpu
cpu_temperature
disk
diskio
gpu_temperature
kernel
mem
net
netstat
processes
swap
system

試しに gpu_temperature のデータを確認

> select * from gpu_temperature limit 3;
name: gpu_temperature
time                host  value
----                ----  -----
1587525501000000000 raspi 50.5
1587525510000000000 raspi 49.9
1587525520000000000 raspi 50.5
  • 取得した GPU の温度が格納されているようです。

InfluxDB shell 終了

> quit

ブラウザでアクセスできるようになるまで待つ

ansible を実行すると grafana-server の起動まで完了しますが、初めて起動する時はしばらく (5分程度) は 3000番ポートが開かれていないためブラウザでアクセスできません

3000番ポートが開かれている場合

$ netstat -na | grep 3000
tcp6       0      0 :::3000                 :::*                    LISTEN

3000番ポートが開かれていない場合

$ netstat -na | grep 3000
$

3000番ポートが開かれていない場合でもログが流れているので起動していることがわかります。

$ sudo tail -f /var/log/grafana/grafana.log

3000番ポートが開かれるのを待って次に進みます。

ブラウザで Grafana にログイン

  • URL: http://[grafana-server の IP]:3000/login
  • 初期ログイン情報
    • ID: admin
    • PW: admin
  • 案内に従いパスワード変更

データソースに InfluxDB を登録

  • [Confuguration (歯車アイコン)] - [Data Sources]
  • [Add data source]
  • influxDB を選択
  • Name: InfluxDB (任意)
  • Name の [Default] を ON
  • url: http://localhost:8086/
  • Access: Server (dfault)
  • Whitelisted Cookies: (空)
  • Auth は全てデフォルト通り無効
  • Database: telegraf
  • User, Password, HTTP Method は空白
  • Min time interval も空白
  • [Save & Test]
    • Data source is working と表示された

Raspberry Pi Monitoring ダッシュボートを Import

ダッシュボード ID (今回は 10578) を指定するとインポートできるようですが、今回の環境ではなぜかうまくいかなかったので、サイトから json ファイルをダウンロードしてインポートやってます。

ダッシュボードの JSON ファイル取得

ダッシュボードを登録

  • [Create (+ アイコン)] - [Import]
  • [Upload .json file] で上でダウンロードした raspberry-pi-monitoring_rev1.json をアップロード
  • 以下入力
    • Name: Raspberry Pi Monitoring (多分任意)
    • Folder: General
    • Unique identifier (uid): value set (初期値)
    • influxdb: InfluxDB
    • [Import]

ダッシュボードで Raspberry Pi のリソースを見ることができるようになりました。

grafana-dboard

参考

2020年3月25日水曜日

JupyterLab で画像の穴のずれ具合を確認

from PIL import Image, ImageDraw
import numpy as np

水平になるように画像を回転

im = Image.open('IMG_20200302_210756-2.png')

im = im.rotate(-1.34)

draw = ImageDraw.Draw(im)
size = im.size
y = 2245
draw.line((0, y, size[0], y), fill=(255, 0, 0), width=10)
im

必要な部分の画像を切り取り

im_crop = im.crop((1050, 1350, 1600, 1900))
im_crop

丸の位置とサイズ確認

im_crop = im.crop((1050, 1350, 1600, 1900))
draw_crop = ImageDraw.Draw(im_crop)

width = 5
diff = 10

# 外丸
outer_center = np.array([292, 275])
outer_rad = 213

outer_color = (255, 0, 0)
draw_crop.ellipse([tuple(outer_center - outer_rad), tuple(outer_center + outer_rad)], outline=outer_color, width=width)
draw_crop.line(
    [
        tuple(outer_center - diff),
        tuple(outer_center + diff)
    ],
    fill=outer_color,
    width=width
)
draw_crop.line(
    [
        (
            outer_center[0] + diff,
            outer_center[1] - diff
        ),
        (
            outer_center[0] - diff,
            outer_center[1] + diff
        )
    ],
    fill=outer_color,
    width=width
)
im_crop

# 内丸
inner_center = np.array([315, 245])
inner_rad = 83

inner_color = (0, 0, 255)
draw_crop.ellipse([tuple(inner_center - inner_rad), tuple(inner_center + inner_rad)], outline=inner_color, width=width)
draw_crop.line(
    [
        tuple(inner_center - diff),
        tuple(inner_center + diff)
    ],
    fill=inner_color,
    width=width
)
draw_crop.line(
    [
        (
            inner_center[0] + diff,
            inner_center[1] - diff
        ),
        (
            inner_center[0] - diff,
            inner_center[1] + diff
        )
    ],
    fill=inner_color,
    width=width
)
im_crop

中心のずれ (ピクセル)

center_diff_pix = outer_center - inner_center
center_diff_pix
array([-23,  30])

中心のずれ (mm) => 内円の半径 1.5mm で計算

center_diff_mm = center_diff_pix * 1.5 / inner_rad
center_diff_mm
array([-0.41566265,  0.54216867])

2020年3月7日土曜日

Ansible で Ansible をバージョンアップ

2020年3月6日現在、PyPI の Ansible リポジトリ で確認すると Ansible の最新バージョンは 2.9.6 のようです。

一方、Raspbian Buster の apt コマンドで標準リポジトリからインストールする Ansible のバージョンは 2.7.7 で少々古いです。

また、Ansible の replace モジュールドキュメント を読むと As of Ansible 2.7.10, the combined use of before and after works properly. と書いてあり、裏を返すと replace モジュールの after と before を組み合わせて使う場合に 2.7.10 より前のバージョンでは適切に動かないようです。

今回はまずは Raspbian Buster の標準リポジトリから少々古い Ansible をインストールして、この Ansible を使って新しめの Ansible にバージョンアップしてみました。

■ 環境

  • Raspberry Pi 3 Model B
  • Raspbian GNU/Linux 10 (buster)

■ Raspbian Buster の標準リポジトリから少々古い Ansible をインストール

Ansible インストール

$ sudo apt udpate
$ sudo apt install ansible

バージョンを確認すると 2.7.7 でした。

$ ansible --version
ansible 2.7.7
config file = /etc/ansible/ansible.cfg
configured module search path = ['/home/pi/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python3/dist-packages/ansible
executable location = /usr/bin/ansible
python version = 3.7.3 (default, Dec 20 2019, 18:57:59) [GCC 8.3.0]

■ Ansible で Ansible をバージョンアップ

今回は上の手順でインストールした古めの Ansible を使って、新しめのバージョンが用意されていると思われる Ubuntu 18.04 LTS 用 PPA リポジトリの Ansible にバージョンアップしてみました。

Playbook ファイルを準備

ansible-playbook 実行

$ ansible-playbook -i localhost, pbook-update-ansible.yaml

Ansible が 2.9.4 にバージョンアップされました。

$ ansible --version
ansible 2.9.4
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/pi/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.16 (default, Oct 10 2019, 22:02:15) [GCC 8.3.0]

■ 参考

2019年12月7日土曜日

3DTouch の画像ファイルから Fritzing パーツ作成

■はじめに

Fritzing で 3DTouch の画像からパーツを作成してみました。

今回は Fritzing に最初から用意されているピンソケットパーツのブレッドボードビューのみを 3DTouch の SVG 画像に変更して作成しているので、作成したパーツのブレッドボードビュー以外のアイコンや回路図、PCB ビューの画像はピンヘッダーのもののままです。

■環境

  • Windows 10
  • Inkscape 0.92
  • Fritzing 0.9.4

■部品として使用する 3DTouch の画像を準備

[Geeetech のサイト] から 3DTocuch の画像をダウンロードし、https://howpon.com/2044 を参考にGIMP を使って以下のように 3DTouch を切り抜いた画像を用意しました。

本体が白基調で背景も白で見にくいですが、とりあえずこれでいきます。

■3DTouch の画像を SVG ファイルに変換

Fitzing のパーツに登録できるように画像ファイルを Inkscape を使って SVG 形式に変換しました。

https://www.youtube.com/watch?time_continue=147&v=0RDrw8WvYNQ&feature=emb_logo」 を参考にしてやってみました。

Inkscape で画像を開きました。開く時のオプションはデフォルトのまま。

  • 画像のインポート形式: 埋め込み
  • 画像 DPI: ファイルから
  • 画像のレンダリングモード: なし (自動)

ドックでビットマップのトレースオブジェクトを作成

メニューで [オブジェクト] - [objects] を選択し [objects]ドック を表示

画像を選択し、メニューで [パス] - [ビットマップのトレース]

[ビットマップのトレース] 画面で [更新] をクリックすると荒い感じのプレビューが表示されました。

パラメーターをいじりながら [更新] を押してプレビューを確認して、以下の設定でいくことにして [OK] を押しました。

objects ドックでビットマップのトレースオブジェクトが挿入されていることがわかります。g4386 がビットマップのトレースオブジェクト、image3721 が最初に開いた画像オブジェクトです。

[ビットマップのトレース] 画面の右上の [X] を押し閉じました。

最初に開いた画像オブジェクト image3721 は不要のようなので削除

ビットマップのトレースオブジェクトのサイズを変更

メニューで [オブジェクト] - [変形] を選択し [変形]ドック を表示

[変形 ドック] の [拡大縮小] タブで [比率を維持して拡大縮小] をチェック

3DTouch の高さはだいたい 50mm くらいなので [高さ] に 50 を入力して単位を % -> mm に変更して [適用]

ビットマップのトレースオブジェクトのサイズをインチ単位で確認

Fritzing のグリッド間隔はデフォルトで 0.1インチ単位なので Inkscape でもグリッド間隔やドキュメントサイズは 0.1インチの倍数にした方がずれが発生せず都合が良いようです。

そのため、まずはビットマップのトレースオブジェクトのサイズをインチ単位で確認しました。

ビットマップのトレースオブジェクトを選択

メニューで [オブジェクト] - [変形] で [変形]ドックを表示し

[拡大縮小]タブで単位に [in] (インチ) を選択するとインチ単位で幅と高さを確認できます。今回は以下のようでした。

  • 幅: 1.134インチ
  • 高さ: 1.969インチ

SVG ドキュメントのサイズを変更

上で述べたようにドキュメントサイズは 0.1インチの倍数にすると Fritzing に取り込んだときに都合が良いようです。

今回はビットマップのトレースオブジェクトのサイズが 1.134 x 1.969 なのでこれより大きい 0.1インチの倍数で 1.2 x 2.0 インチとします。

メニューで [ファイル] - [ドキュメントのプロパティ]

[ページ]タブの [カスタムサイズ] 欄で単位に [in] (インチ) を選択

幅に 1.2、高さに 2.0 を指定

SVG ドキュメントの中央にビットマップのトレースオブジェクトを移動

オブジェクトを選択してメニューで [オブジェクト] - [整列と配置] を選択し [整列と配置]ドックを表示

整列の [基準] に [ページ] を選択

下図の[中心を垂直軸に合わせる]アイコンと [水平軸の中心に揃える]アイコンをを押して中央に移動

SVG ドキュメントのグリッドを設定

メニューで [ファイル] - [ドキュメントのプロパティ]

[グリッド]タブで [矩形グリッド] を選択して [新規]

[グリッドの単位] で [in] (インチ) を選択

[X方向の間隔]、[Y方向の間隔] のどちらも 0.1 (インチ) を指定

コネクタピンを置きたい位置にグリッド点が来るように [開始位置X]、[開始位置Y] を指定

コネクタピンを配置

コネクタピン用の 1mm x 1mm の矩形を作成。色はお手本と同じ黄色にしてみました。

以下のように配置。[オブジェクトの中心をスナップ] をオンにすると XY グリッド線の交点にコネクタピンの中心を設定できました。

※ 図ではグリッドが長方形になっていますが、上の記述に従うと実際のグリッドは正方形になります。

コネクタピンのオブジェクト ID を設定

コネクタピンオブジェクトを選択し右クリックで [オブジェクトのプロパティ]

5個のコネクタピンの ID をそれぞれ以下の通り設定

  • connector0pin
  • connector1pin
  • connector2pin
  • connector3pin
  • connector4pin

これにしておくと、後工程で Fritzing に取り込んだ時にコネクタピンとして認識してくれるようです。

メニューの [編集] - [XML エディター] で念のため対象のコネクタピンオブジェクトの ID が設定されていること確認

プレーン SVG 形式で保存

Fritzing にインポートするために プレーン SVG 形式で保存する必要があるようです。

メニューで [ファイル] - [名前を付けて保存]

[ファイルの種類] で [プレーン SVG] を選択

[保存] を押して保存

■Fritzing で 3DTouch パーツを作成

元にするパーツを配置

Fritzing 用の 3DTouch パーツを作成するにあたり、[Generic female header - 2 pins]パーツを元とするので、まずはこのパーツを配置しました。

Fritzing を起動し、[ブレッドボード]レイヤを表示

[Core Parts] の [Generic female header - 2 pins] を [ブレッドボード]レイヤに Drag & Drop で配置

オブジェクトを選択し、[インスペクター] の [pins] を 5に変更するとオブジェクトのピン数も 5個に増えました。

パーツエディターを起動

オブジェクトを選択

メニューで [パーツ] - [Edit (new parts editor)] を選択しパーツエディターを起動

ブレッドボードビュー用の SVG をロード

[ブレッドボード]タブを表示

メニューで [ファイル] - [Load image for view]

上で保存した SVG ファイルを指定

ブレッドボードビューのパーツ画像が 3DTouch になりました

5個のピンはしっかりとグリッド上にあり、[コネクター]ドックの pin1 ~ pin5 にもチェックが入っていて画像上の 5個のピンと対応できていました。

パーツを保存

メニューで [ファイル] - [Save as new part]

[Filename prefix] に [3DTouch] と入力

作成された 3DTouch パーツを確認

パーツエディターを閉じて Fritzing 本体に戻ると、[My Parts] に 3DTouch パーツが追加されていました。

ただし、アイコン画像 (その他、回路図や PCB の画像も) は変更していないのでピンソケットの画像のままとなっていました。

ブレッドボードビューに Drag & Drop すると 3DTouch パーツが配置され、ワイヤーケーブルもコネクタに配線できました。

■参考

2019年8月31日土曜日

PlatformIO Core で Marlin 1.1.9 コンパイル

はじめに

Arduino IDE ではなく、PlatformIO Core を使って 3D プリンターのファームウェア Marlin をコンパイルしてみました。

自宅の 3D プリンターは Alunar M508 です。この 3D プリンター用に修正した Marlin 1.1.9 ソースコードは Arduino IDE ではすんなりコンパイルできたのですが、PlatformIO では更に一か所修正しないといけなかったのでブログ記事にしました。

なお、PlatfromIO Core なので全てコマンドラインでの操作です。

環境

  • Ubuntu 18.04.3 (Windows10 WSL)
  • 3D プリンター Alunar M508
  • Marlin 1.1.9

PlatformIO Core インストール

Python3 のバージョン確認

$ python3 --version
Python 3.6.8

Python 3.6 で pipenv プロジェクト作成

$ mkdir myproject/
$ cd myproject/
$ pipenv --python 3.6

PlatformIO Core インストール

$ pipenv install platformio

プロジェクトの virtualenv をアクティベート

$ pipenv shell

PlatformIO のバージョン確認

$ platformio --version
PlatformIO, version 4.0.3

以降、全てアクティベートした virtualenv で実行しました。

自宅の 3D プリンター Alunar M508 用の Marlin 1.1.9 をダウンロード

自宅の 3D プリンター Alunar M508 用に修正した Marlin 1.1.9 は以前のブログ 「 3D プリンター Alunar M508 の Marlin を 1.1.9 にバージョンアップするためにソースコードを修正 」 に書いた通りで、このソースコードを GitHub からダウンロードしました。

$ wget https://github.com/nezuppo/Marlin-for-my-alunar-m508/archive/1.1.9r1.tar.gz
$ tar xvzf 1.1.9r1.tar.gz

Alunar M508 用の Marlin 1.1.9 ソースコードディレクトリができました。

$ ls -lad Marlin-for-my-alunar-m508-1.1.9r1/
drwxrwxr-x 1 worker worker 4096 Aug 16 15:19 Marlin-for-my-alunar-m508-1.1.9r1/

ソースコードディレクトリ に移動

$ cd Marlin-for-my-alunar-m508-1.1.9r1/

以降、このディレクトリで作業しました。

ディレクトリの中を確認しました。

$ ls -la
total 132
drwxrwxr-x 1 worker worker  4096 Aug 16 15:19 .
drwxrwxrwx 1 worker worker  4096 Aug 31 20:46 ..
-rw-rw-r-- 1 worker worker   347 Aug 16 15:19 .gitattributes
drwxrwxr-x 1 worker worker  4096 Aug 16 15:19 .github
-rwxrwxr-x 1 worker worker  1958 Aug 16 15:19 .gitignore
-rw-rw-r-- 1 worker worker 14163 Aug 16 15:19 .travis.yml
-rw-rw-r-- 1 worker worker 35144 Aug 16 15:19 LICENSE
drwxrwxr-x 1 worker worker  4096 Aug 16 15:19 Marlin
-rw-rw-r-- 1 worker worker  8539 Aug 16 15:19 README.md
drwxrwxr-x 1 worker worker  4096 Aug 16 15:19 buildroot
-rw-rw-r-- 1 worker worker  4130 Aug 16 15:19 platformio.ini
-rw-rw-r-- 1 worker worker 10974 Aug 16 15:19 process-palette.json
  • PlatformIO 用の設定ファイル platformio.ini もありました。

PlatformIO 用の環境名確認

本家 Marlin のドキュメント 「 Installing Marlin (CLI) 」 を参考にしました。

自宅の Alunar M508 のマザーボードは MKS_BASE で、Marlin/Configuration.h でも定義してます。

$ grep '#define MOTHERBOARD' Marlin/Configuration.h
  #define MOTHERBOARD BOARD_MKS_BASE

Marlin/pins.h の MKS_BASE の該当箇所確認

$ cat Marlin/pins.h
... snip ...
#elif MB(MKS_BASE)
  #include "pins_MKS_BASE.h"          // ATmega1280, ATmega2560                     env:megaatmega1280 env:megaatmega2560
... snip ...

env:megaatmega1280 env:megaatmega2560 となっているので、環境名は megaatmega1280 か megaatmega2560 のどちらかなのですが、Arduino ID でコンパイルした時はプロセッサを ATmega2560 (Mega 2560) にしてうまくいったので、PlatformIO 用の環境名は megaatmega2560 でよさそうです。

Marlin を PlatformIO でコンパイル (まずはエラーになるところを確認)

カレントディレクトリは platformio.ini があるディレクトリです。

$ ls -la
total 132
drwxrwxr-x 1 worker worker  4096 Aug 16 15:19 .
drwxrwxrwx 1 worker worker  4096 Aug 31 20:46 ..
-rw-rw-r-- 1 worker worker   347 Aug 16 15:19 .gitattributes
drwxrwxr-x 1 worker worker  4096 Aug 16 15:19 .github
-rwxrwxr-x 1 worker worker  1958 Aug 16 15:19 .gitignore
-rw-rw-r-- 1 worker worker 14163 Aug 16 15:19 .travis.yml
-rw-rw-r-- 1 worker worker 35144 Aug 16 15:19 LICENSE
drwxrwxr-x 1 worker worker  4096 Aug 16 15:19 Marlin
-rw-rw-r-- 1 worker worker  8539 Aug 16 15:19 README.md
drwxrwxr-x 1 worker worker  4096 Aug 16 15:19 buildroot
-rw-rw-r-- 1 worker worker  4130 Aug 16 15:19 platformio.ini
-rw-rw-r-- 1 worker worker 10974 Aug 16 15:19 process-palette.json

環境名に megaatmega2560 を指定してコンパイルしたら以下のようにエラーになりました。

$ platformio run -e megaatmega2560
... snip ...
Marlin/ultralcd_impl_HD44780.h:194:29: fatal error: LiquidCrystal.h: No such file or directory

***********************************************************************
* Looking for LiquidCrystal.h dependency? Check our library registry!
*
* CLI  > platformio lib search "header:LiquidCrystal.h"
* Web  > https://platformio.org/lib/search?query=header:LiquidCrystal.h
*
***********************************************************************
... snip ...

Marlin/ultralcd_impl_HD44780.h の 194行目で LiquidCrystal.h が無いって怒ってました。

書かれている通りに ライブラリのレジストリを確認して LiquidCrystal.h の依存関係を確認してみました。

$ platformio lib search "header:LiquidCrystal.h"
$ platformio lib search "header:LiquidCrystal.h"
Found 2 libraries:

LiquidCrystal
=============
#ID: 136
LiquidCrystal Library is faster and extensable, compatible with the original LiquidCrystal library

Keywords: lcd, hd44780
Compatible frameworks: Arduino
Compatible platforms: Atmel AVR, Espressif 32, Espressif 8266
Authors: F Malpartida

LiquidCrystal
=============
#ID: 887
Allows communication with alphanumerical liquid crystal displays (LCDs).

Keywords: display
Compatible frameworks: Arduino
Compatible platforms: Atmel AVR, Atmel SAM, Espressif 32, Espressif 8266, Intel ARC32, Microchip PIC32, Nordic nRF51, Nordic nRF52, ST STM32, Teensy, TI MSP430
Authors: Arduino, Adafruit

LiquidCrystal.h は LiquidCrystal ライブラリに含まれているようです。 ただし同名のライブラリが二つ (ID:136 と ID:887) があるようで、どちらが正解なのかわかりませんでした。

ソースコードを修正して再度コンパイル

適当にググるとこのエラーの場合は platformio.ini を以下のように修正すると良いと書かれていました。

$ diff -u platformio.ini{.org,}
--- platformio.ini.org  2019-08-31 21:42:42.054394900 +0900
+++ platformio.ini      2019-08-31 21:43:28.900397600 +0900
@@ -28,7 +28,7 @@
   -ggdb
 lib_deps =
   https://github.com/MarlinFirmware/U8glib-HAL/archive/dev.zip
-  LiquidCrystal_I2C@1.1.2
+  LiquidCrystal@1.3.4
   TMC2130Stepper
   https://github.com/teemuatlut/TMC2208Stepper/archive/v0.1.1.zip
   Adafruit NeoPixel@1.1.3

GitHub 上の本家 Marlin の bugfix-2.0.x ブランチ (2aef83ddcdd3317c3191e79c3be3fc06969f7768) の platformio.ini 」 もこれになっているので良さそうです。

上に書いたとおり、エラーが出た LiquidCrystal.h は LiquidCrystal ライブラリに含まれている点も合致します。

この platformio.ini で再度コンパイル

$ platformio run -e megaatmega2560
... snip ...
Building .pioenvs/megaatmega2560/firmware.hex
Memory Usage -> http://bit.ly/pio-memory-usage
DATA:    [=====     ]  49.5% (used 4053 bytes from 8192 bytes)
PROGRAM: [====      ]  41.5% (used 105502 bytes from 253952 bytes)
============================================ [SUCCESS] Took 135.49 seconds ============================================

Environment           Status    Duration
--------------------  --------  ------------
megaatmega2560        SUCCESS   00:02:15.491
... snip ...

無事コンパイルできました。

メッセージに出ている通り、Alunar M508 の Marlin ファームウェア書き換えのための新しいイメージが以下のように出来ていました。

$ ls -la .pioenvs/megaatmega2560/firmware.hex
-rw-rw-rw- 1 worker worker 296769 Aug 31 21:57 .pioenvs/megaatmega2560/firmware.hex

参考

Alunar M508 の Marlin ファームウェア書き換えは Raspberry Pi で以下のようにやりました。

$ avrdude -p m2560 -c avrispmkII -P /dev/ttyACM0 -D -U flash:w:/some/where/firmware.hex:i

参考にしたサイト