kuroの覚え書き

96の個人的覚え書き

Milk-V Duo SをDebianで運用する


買ったまま放置していたMilk-V Duo Sである。
一応、公式のBuildrootファームウェアをSDに入れて起動はしてみていたがBuildrootではaptもyumも使えず、不自由なのでdebianを入れることにした。
Resource Download Summary | Milk-V
このサイトからimgファイルをダウンロードしてくればいいのだが、それなりに色々と面倒が待っている。
duos-e_sd.img.lz4がDuo S用のファイルなので、これをリンクからダウンロードする。

次に、まず母艦がMacなのでlz4形式の圧縮ファイルを解凍するため、

brew install lz4

でアーカイブユーティリティをインストールするところから。homebrewが入っていなかったらそこから始めないとならない。

lz4 -d duos-e_sd.img.lz4

によって解凍したらbalena etcherでSDに書き込む。このあたりは他のSBCなどと変わらない。
さて、SDをスロットに差し込んでUSB-Cポートで電源を投入すれば起動するのではあるが、このままではログインする手段がない。ethernet
ポートに振られたIPアドレスもわからないし、WiFiは装備されていない。一応ルーターの接続ユーティリティーでIPアドレスを特定するとかの手段は使えるかもしれない。(一応デフォルトではDHCPでアドレスを自動取得しているらしい。)
手っ取り早いのはシリアルコンソールでログインすることかな。

ということでUSBシリアル変換でつなぐ。GND,TxD,RxDをつなぎ、電源は別途USBポートから供給する。ボーレートは115200なので

% sudo cu -l /dev/tty.usbserial-21130 -s 115200

のようにして接続する。シリアル変換を繋いで上記コマンドで通信接続してからからUSB電源を繋げば起動画面の文字列スクロールが見える。
プロンプトが出たらrootでログインする。デフォルトパスワードはrv。risk-Vだからだろう。rootパスワードは変更しておくべき。
一応一般ユーザを作っておく。

% adduser kuro
password:

% sudo grep -E '^%sudo|^%wheel' /etc/sudoers
%sudo   ALL=(ALL:ALL) ALL

% sudo usermod -aG sudo kuro

debianなのでsudoが使えるグループはsudoとなっているため作ったユーザをsudoグループに入れておく。
これでようやく一般ユーザでログインしてsudoも使えるようになった。

次に、毎回シリアルログインするのは大変なので、USB-Cポート(OTGポート)でMacと繋いでログインできるようにしておきたい。
Macから

networksetup -listallhardwareports

としてもUSB経由のネットワークポートらしきものは何も見えていない。

Duo S側で

$ ls /sys/class/udc/
4340000.usb

$ find /sys/kernel/config/usb_gadget -maxdepth 4 2>/dev/null
/sys/kernel/config/usb_gadget
/sys/kernel/config/usb_gadget/g0
/sys/kernel/config/usb_gadget/g0/os_desc
/sys/kernel/config/usb_gadget/g0/os_desc/qw_sign
/sys/kernel/config/usb_gadget/g0/os_desc/b_vendor_code
/sys/kernel/config/usb_gadget/g0/os_desc/use
/sys/kernel/config/usb_gadget/g0/strings
/sys/kernel/config/usb_gadget/g0/strings/0x409
/sys/kernel/config/usb_gadget/g0/strings/0x409/serialnumber
/sys/kernel/config/usb_gadget/g0/strings/0x409/product
/sys/kernel/config/usb_gadget/g0/strings/0x409/manufacturer
/sys/kernel/config/usb_gadget/g0/configs
/sys/kernel/config/usb_gadget/g0/configs/c.1
/sys/kernel/config/usb_gadget/g0/configs/c.1/rndis.usb0
/sys/kernel/config/usb_gadget/g0/configs/c.1/strings
/sys/kernel/config/usb_gadget/g0/configs/c.1/bmAttributes
/sys/kernel/config/usb_gadget/g0/configs/c.1/MaxPower
/sys/kernel/config/usb_gadget/g0/functions
/sys/kernel/config/usb_gadget/g0/functions/rndis.usb0
/sys/kernel/config/usb_gadget/g0/functions/rndis.usb0/os_desc
/sys/kernel/config/usb_gadget/g0/functions/rndis.usb0/protocol
/sys/kernel/config/usb_gadget/g0/functions/rndis.usb0/subclass
/sys/kernel/config/usb_gadget/g0/functions/rndis.usb0/class
/sys/kernel/config/usb_gadget/g0/functions/rndis.usb0/ifname
/sys/kernel/config/usb_gadget/g0/functions/rndis.usb0/qmult
/sys/kernel/config/usb_gadget/g0/functions/rndis.usb0/host_addr
/sys/kernel/config/usb_gadget/g0/functions/rndis.usb0/dev_addr
/sys/kernel/config/usb_gadget/g0/max_speed
/sys/kernel/config/usb_gadget/g0/UDC
/sys/kernel/config/usb_gadget/g0/bcdUSB
/sys/kernel/config/usb_gadget/g0/bcdDevice
/sys/kernel/config/usb_gadget/g0/idProduct
/sys/kernel/config/usb_gadget/g0/idVendor
/sys/kernel/config/usb_gadget/g0/bMaxPacketSize0
/sys/kernel/config/usb_gadget/g0/bDeviceProtocol
/sys/kernel/config/usb_gadget/g0/bDeviceSubClass
/sys/kernel/config/usb_gadget/g0/bDeviceClass

事前情報通りrndis.usb0というのが見える。RNDISが最近のMacOSでは使えなくなっているらしいので、これを違うプロトコル(NCM)にする必要がある。

sudo mv /boot/usb.rndis /boot/usb.rndis.bak
sudo touch /boot/usb.ncm

/bootにあるusb.rndisをリネームして使えなくして、usb.ncmという空のファイルを作っておく。

sudo /etc/init.d/S03usbdev stop
sudo /etc/init.d/S03usbdev start

もしくはリブートすればOK
このUSBポートにはデフォルトで
10.85.232.1
というアドレスが振ってあるのでMacの方を
10.85.232.2
のように固定してやればUSBで繋いでログインできるようになるはず。

% networksetup -listallhardwareports  

Hardware Port: licheervnano
Device: en12
Ethernet Address:

のようなポートができているはずなので

sudo networksetup -setmanual licheervnano 10.85.232.2 255.255.255.0 10.85.232.1

のように設定する。

これでMacのターミナルから

ssh kuro@10.85.232.1

でログインできるようになった。
一旦ログインできてしまえばethernetの方のアドレス設定なども不自由なくなるはずだ。
デフォルトではDHCPとなっているのを固定アドレスにするなら

sudo nano /etc/network/interfaces.d/end0

allow-hotplug end0
iface end0 inet static
    address 192.168.40.51/24
    gateway 192.168.40.1
    dns-nameservers 8.8.8.8 1.1.1.1

反映

sudo ifdown end0
sudo ip addr flush dev end0
sudo ifup end0

これでほぼ不自由ないlinux boxになったはずだ。

まあしかし日本国内ではそれなりの値段だしRaspberry Piのほうがなんだかんだで使いやすそうな感じだな。TPUがあってエッジAIとしては有利かもしれないがマイクロサーバ的用途ならRaspberry Pi 2Wにイーサネットポートをつけたほうが手っ取り早そう。MilkV Duo 256Mのメモリが512Mでお値段据え置きだったらいいのに。

IMX219搭載 CMOSカメラモジュールをRaspberry Pi5で使う


IMX219搭載 CMOSカメラモジュール(M12/CSマウント)ssci.to
このSeeedのCSIカメラをRaspberry Pi5に2台繋いでタイムラプスを撮るために使いたい。
IMX219センサー搭載
CSマウント(M12マウントにも付け替え可能)で8メガピクセル
高解像度:3280 x 2640ピクセル

このような謎の説明があるが、実際にはRP5でも認識して画像を撮ることができる。
ただし自動ではカメラを認識しないので、/boot/firmware/config.txtの中で

camera_auto_detect=0
dtoverlay=imx219,cam0
dtoverlay=imx219

としておく

しかし、どういうわけか、撮れる画像は周辺が赤被りして、赤外線カメラで撮ったみたいな像になる。単純にホワイトバランスをいじっても
真ん中と周辺で全然色味が違ってしまう。

ちなみに使用したレンズはELPのUSBカメラに付属していた3.6-10mm, 1/1.8" F1.4のCS2マウントレンズである。

オートホワイトバランスを切って白い紙を撮すとこんな感じ。

rpicam-still --camera 0 --awbgains 1,1 -o white_standard.jpg

どうやら原因はラズパイカメラ(純正)の補正値とこのモジュールの色補正がマッチしていないためらしい。
/usr/share/libcamera/ipa/rpi/pisp/imx219.json
このファイルに色補正マッピングが仕込まれているらしい。

なので、これをホームディレクトリに一旦コピーし、設定値をいじって、

rpicam-still --camera 0 --awbgains 1,1 --tuning-file ~/imx219_mod.json  -o white_modified.jpg

のように明示的に指定して撮影をするといいようだ。

まずは色補正をキャンセルしてみる。

python3 - <<'PY'
import json

src = "/usr/share/libcamera/ipa/rpi/pisp/imx219.json"
dst = "/home/kuro/imx219_no_alsc.json"

with open(src) as f:
    data = json.load(f)

before = len(data["algorithms"])

data["algorithms"] = [
    x for x in data["algorithms"]
    if "rpi.alsc" not in x
]

after = len(data["algorithms"])

with open(dst, "w") as f:
    json.dump(data, f, indent=4)

print(f"Algorithms: {before} -> {after}")
print(f"Written to: {dst}")
PY

そして撮影してみると

rpicam-still --camera 0 -n --tuning-file ~/imx219_no_alsc.json --awbgains 1,1 -o white_no_alsc.jpg


周辺部分の赤みが抑えられた。しかしまだこのままホワイトバランス調整だけでは均一にはならないだろう。
あと明るさも全体的に落ちる傾向が出るので。Luminance補正は残すことにする。

python3 - <<'PY'
import json

src = "/usr/share/libcamera/ipa/rpi/pisp/imx219.json"
dst = "/home/kkuro/imx219_luma_only.json"

with open(src) as f:
    data = json.load(f)

for algo in data["algorithms"]:
    if "rpi.alsc" in algo:
        alsc = algo["rpi.alsc"]
        alsc.pop("calibrations_Cr", None)
        alsc.pop("calibrations_Cb", None)

with open(dst, "w") as f:
    json.dump(data, f, indent=4)

print(dst)
PY

これで明るさはそこそこ回復したがやはりこれでも色むらが出てるな。
ということで白い紙を撮影した画像を使って積極的なキャリブレーションファイルを作成する。

まずRAWつきで白画面の画像を作る

mkdir -p ~/camera_tuning/alsc

cd ~/camera_tuning/alsc

rpicam-still --camera 0 \
    -n \
    --raw \
    --shutter 10000 \
    --gain 1 \
    --awbgains 1,1 \
    -o white_01.jpg

白飛びしているとキャリブレーションに問題が出るので、明るさを自動ではなく指定して撮影する。暗すぎたら徐々に明るくしていく
最終的には

    • shutter 250000

で撮った。

作業用のpython環境を作って

sudo apt update
sudo apt install -y libgl1 libglib2.0-0 python3-venv

python3 -m venv ~/ctt-env
source ~/ctt-env/bin/activate
pip install --upgrade pip
pip install rpi-ctt

cp white_01.dng \
   ~/camera_tuning/cam0_alsc/alsc_5000k_0.dng
cp /usr/share/libcamera/ipa/rpi/pisp/imx219.json \
   ~/camera_tuning/imx219_seeed_cam0.json

source ~/ctt-env/bin/activate

ctt --alsc-only \
    -i ~/camera_tuning/cam0_alsc \
    --update ~/camera_tuning/imx219_seeed_cam0.json

これで補正値が登録されたはずなので

rpicam-still --camera 0 -n \
  --tuning-file ~/camera_tuning/imx219_seeed_cam0.json \[f:id:k-kuro:20260824134303j:plain]
  --awbgains 1,1 \
  -o white_ctt.jpg


かなり均一な色に補正できた。あとはホワイトバランスを調節すればほぼ問題ない色味に補正できるはず。


いい感じ。

TTL-RS485

RS232に比べて長い距離を安定して通信できる通信規格であるRS485をTTLに変換してマイコンやラズパイで通信する。
max485などの変換ICを使うといいのだが、アマゾンで中華製基板が売ってるので試しに買ってみた。
ただし使い方の説明など一切ないので、ネット検索しながら試してみるしかなさそうだ。

どうやら構成としては

こんな感じで接続するといいらしい。

MilkV DUO 256M

Lichee Zero-dockをいじっていたが、製品をどんどん更新して旧機種のサポートをあっという間に切ってしまうというSiPEEDという中華メーカーの悪いところがやはり出ている。Debianの更新が出荷時点からほとんど止まっていて、アップデート、アップグレードが全然できないので、アプリやライブラリの追加もできず、いかんともしがたい事態に陥る。ネットワークに繋ぐまでの手間が相当あって、やっと繋がったと思ったら、そういう状況が見えてくるので始末が悪い。

ということでやっぱりSiPEEDのボードにリソースを割くのは無駄と早々に撤退することにした。暇があったらソースからカーネルビルドとかするのも悪くはないが、未来のないボードにその投資はねえ。

で、今が旬のMilkVにさっさと移る。

Duo256M | Milk-V

せっかくなのでIOボード(Milk-V Duo USB&Ethernet IOB)も合わせて入手。ラズパイ4互換のボードデザインとなっており、USB-A(2.0) x 4, USB-C x 1, RJ-45 x 1, シリアルのヘッダ, リセットボタンがついている。ボード上には他にも何やらチップを乗せるランドやピンヘッダが立てられそうなホールが並んでいるが、メーカーサイトには直接的な説明はない。

14ピンの未実装ヘッダは
ピン機能(U11)
1: CAN_L
2: CAN_L
3: CAN_H
4: CAN_H
5: GND
6: GND
7: RXD232(RS232 RX)
8: RXD232(RS232 RX)
9: TXD232(RS232 TX)
10: TXD232(RS232 TX)
11: RS485_B_D-
12: RS485_B_D+
13: RS485_A_D+b
14: RS485_A_D-
ということらしい。

ただしRS485トランシーバーは実装されていないのでそのまま結線すれば使えるというものではない。GNDは繋がっていたけど。

MilkV自体にはUSBやシリアル以外にもGPIOがあるのだが、それらをIOB上で取り出すのは難しいらしいので、足の長いピンソケットをつけて、ソケットの足をIOBのソケットに刺して使用することにする。足の長いピンヘッダでもいいのだけど、ピンヘッダよりソケットのほうがブレッドボードとジャンパピンでつなぎやすいからラズパイのようなピンヘッダよりソケットのほうが都合がいいと思う。
 

RJ-45や電源USBとの接合にはポゴピンが使われているので、足は長い目でちょっと浮いているくらいがちょうどいいようだ。

さて、組み立てもできたことだし、OSを準備。

公式のGithubにカスタムイメージが用意されている
Releases · milkv-duo/duo-buildroot-sdk · GitHub

ひとまず公式をインストールしてみることにする。
milkv-duo256m-sd-v1.1.4.img.zip
をダウンロードしbalenaEtcherを使ってSDカードに書き込む。

そして、SDスロットにSDカードを刺してUSBをMacと接続する。赤いLEDが点灯し、しばらくすると青いLEDが点滅を始める。たしかテストプログラムとしてLED点滅のプログラムが自動起動すると何処かに書いてあったような。

そして、USB経由でsshできると言われているので、

ssh root@192.168.42.1

これでログインできるはず・・・・あれ?タイムアウトした。

どうやらIOBにつなぐとUSBのモードが変わってしまい、sshできなくなるらしいぞ。仕方がないのでUSB引っこ抜く。ログインできないからシャットダウンもできないし。
改めて単体に戻してUSBをつなぐと、LED点滅は同じ。sshしてみるとやっぱり繋がらない・・・

% ping 192.168.42.1 -c 5
PING 192.168.42.1 (192.168.42.1): 56 data bytes
64 bytes from 192.168.42.1: icmp_seq=0 ttl=64 time=1.241 ms
64 bytes from 192.168.42.1: icmp_seq=1 ttl=64 time=1.426 ms
64 bytes from 192.168.42.1: icmp_seq=2 ttl=64 time=0.973 ms
64 bytes from 192.168.42.1: icmp_seq=3 ttl=64 time=1.435 ms
64 bytes from 192.168.42.1: icmp_seq=4 ttl=64 time=1.454 ms

--- 192.168.42.1 ping statistics ---
5 packets transmitted, 5 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.973/1.306/1.454/0.183 ms

pingを打ってみたらちゃんと帰ってきたので繋がっているはずなんだけど
改めて

 % ssh root@192.168.42.1 
root@192.168.42.1's password: 
[root@milkv-duo]~#

きたきた。pingを打ってからsshしないとならないのか?いやそういうわけでもなさそうだ、一旦接続を切って再起動したら一発目でsshできたし。ちなみにshutdown -h nowでは終了できない。haltで終わるしかないらしい。ちなみにパスワードの初期設定はmilkv

さて、次はIOBに繋いでアクセスしたい。
RJ-45にイーサネットケーブルを接続し、イーサネット経由でsshすればいいのだが、一つ問題があって、IPアドレスDHCPで割り当てられてしまい、単体ではどんなアドレスになったかわからないため、sshしようがないのだ。しかたがないのでルータの管理画面を開き、DHCPで新たに割り振られたアドレスを探してsshしてみるしかないのだ。

もしくは自分のネット空間を把握しているなら固定アドレスを設定しておくという手もある。
とりあえずIOBから取り外し、USBでssh接続して

nano /etc/network/interfaces

そして

auto eth0
iface eth0 inet static
    address 192.168.0.50
    netmask 255.255.255.0
    gateway 192.168.0.1
    dns-nameservers 8.8.8.8 1.1.1.1

のように書き足す。
そのうえでhaltし、IOBに刺し直してケーブルを繋いでやれば、無事イーサネットから

ssh root@192.168.0.50
root@192.168.40.50's password: 

[root@milkv-duo]~# 

とログインできる。

いやあLichee ZeroーDockに比べて格段に余裕だわ。

# cat /etc/os-release
NAME=Buildroot
VERSION=20241122-1405
ID=buildroot
VERSION_ID=2021.05
PRETTY_NAME="Buildroot 2021.05"

ただし、基本組み込みLinuxなのでaptやdnfでアップデートができるというわけではないので、そこんとこは学習が必要。

というわけでDebianを入れてみる。マシンパワー的に辛いかもという気もするけど。
Resource Download Summary | Milk-V
こちらにMilkVで使えるイメージが配布されている。Ubuntuもあるみたいだけど、とりあえずはDebianにしておく。
ダウンロード〜解凍〜balenaEtcherで書き込み、は同じ流れ。
解凍にはlz4が必要なのでbrewでインストールし

lz4 -d duo256_sd.img.lz4

サクッと起動したら・・・USBからIOBあるなしにかかわらず接続できない。IOBのLANからは一応可能。ルーターを見て該当アドレスを割り出したら、sshでアクセス。

% ssh debian@192.168.0.50

debian@192.168.0.50's password: 
Linux duo256 5.10.4-20240329-1+ #1 PREEMPT Tue May 7 08:14:28 UTC 2024 riscv64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Sun Jan 25 06:31:10 2026 from 192.168.40.16
debian@duo256:~$

sshキーのやり取りのあと無事ログインできた。
Debianはrootではsshできないようになっているのでユーザーはdebianを使う。デフォルトのパスワードはrootもdebianもrvだ。

このままだとやはりDHCPで起動ごとにアドレスがわからなくなるため、固定IP化しておく。LAN以外のアクセス方法がないと、別のネットワークに持っていったときに再設定でツボにはまるわけだが、ひとまずそこは目を瞑る。

debian@duo256:~$ su-
root@duo256:~# nano /etc/systemd/network/end0.network

rootになって設定ファイルを作る。debianの有線LANのデバイス名はend0らしいので

[Match]
Name=end0

[Network]
Address=192.168.0.50/24
Gateway=192.168.0.1
DNS=8.8.8.8

のようにしておく

root@duo256:~# systemctl enable systemd-networkd
root@duo256:~# systemctl restart systemd-networkd
root@duo256:~# systemctl disable networking
root@duo256:~# systemctl stop networking

root@duo256:~# reboot

これでイーサネットアドレスが固定されたはず。
sshでログインできるしアップデートなども不自由なく行える。

これでほぼラズパイと同等の使い勝手となったのではないか。HDMI出力がないという点だけはいざというとき不便なのではないかと思うが。

Lichee Zero-Dockをネットワークに繋ぐ

と、とりあえず起動できるようになったLichee Zero-Dockだが、このままではシリアルケーブルで繋がった状態でインターネットに繋がっていないので、アップデートやソフトウェアのインストールなどできないので、ネットに繋ぐことを目指す。
せっかくRJ-45があるのにそれを使えるようにするためのライブラリをインストールするためのネット接続ができないというジレンマ。

ということで、まずは無線LANによるネット接続を確立する。
ただしこのボードには無線LANはついていない。簡単な方法はUSBのWIFIドングルをUSBに接続することだ。このボードのmicro USBポートはデフォルトでUSBホストとなっているので、ここにOTGケーブルを介してUSBドングルを接続できる。とりあえずtp-Linkのものをつけてみたら認識した。

# ip a

3: wlan0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc mq state DOWN group default qlen 1000

なので設定すれば使えるようになるわけだ。
wifiの設定の雛形を生成

root@LicheePi:~# wpa_passphrase "${your ssid}" ${your password}
network={
	ssid="${your ssid}"
	#psk="${your password}"
	psk=${key}
}

このkeyなどを設定ファイルに記入しなければならないが、シリアルターミナルでのファイル編集はなかなかに手強い。

root@LicheePi:~# ls -al /etc/wpa_supplicant
total 60
drwxr-xr-x  2 root root  4096 Jan  1  1970 .
drwxr-xr-x 78 root root  4096 Jan  1  1970 ..
-rw-------  1 root root 12288 Jan  1  1970 .wpa_supplicant.conf.swp
-rwxr-xr-x  1 root root   937 Aug  2  2016 action_wpa.sh
-rwxr-xr-x  1 root root 25892 Aug  2  2016 functions.sh
-rwxr-xr-x  1 root root  4696 Aug  2  2016 ifupdown.sh

/etc/wpa_supplicant/wpa_supplicant.confがないのでファイルを作って、中身を入れる

root@LicheePi:~# touch /etc/wpa_supplicant/wpa_supplicant.conf
root@LicheePi:~# echo 'ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=netdev'  
>> /etc/wpa_supplicant/wpa_supplicant.conf
root@LicheePi:~# echo 'update_config=1' >> /etc/wpa_supplicant/wpa_supplicant.co 
nf
root@LicheePi:~# echo 'country=JP' >> /etc/wpa_supplicant/wpa_supplicant.conf
root@LicheePi:~# echo 'ssid="${your ssid}"' >> /etc/wpa_supplicant/wpa_supplicant.conf
root@LicheePi:~# echo '#psk="${your password}"' >> /etc/wpa_supplicant/wpa_supplicant.conf
root@LicheePi:~# echo 'psk=${key}' >> /etc/wpa_supplicant/wpa_supplicant.conf

そして、

root@LicheePi:~# cat /etc/network/interfaces
# interfaces(5) file used by ifup(8) and ifdown(8)
# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d
auto eth0
allow-hotplug eth0
iface eth0 inet dhcp

こうなっているはずなので

root@LicheePi:~# echo "auto wlan0" >> /etc/network/interfaces
root@LicheePi:~# echo "iface wlan0 inet dhcp" >> /etc/network/interfaces
root@LicheePi:~# echo "wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf" >> /etc 
/network/interfaces
root@LicheePi:~# reboot

これで再起動するとWIFIに接続するはずである。
apt update
apt upgrade
でシステムをアップデートする。

更に同じネットワーク上のMacからsshでアクセスできるようになっているはず。

ここまでくれば次は有線LANの設定に入れる。

Lichee Zero-Dock

秋月電子で在庫がたくさんあって値段が安いLinuxの走るシングルボードコンピュータ(SBC)はRaspberry Pi以外にもたくさんあるが、それぞれ特徴があって、目的にあったボードが選べる、ような気がするが、どっちかというと一長一短、決定打にかける、結局ラズパイでいいんじゃないのか?という状態である。ただまあ、ラズパイは近年スペックがどんどん上がり、それに伴って消費電力も高くなってきてしまっており、目的によっては使いづらくなってきている。電源の不安定性が運用を妨げる原因となりかねない。値段もそこそこ高くなってきているし。

そんな中で低価格、有線LANインターフェースあり、SDカードにOSをインストールして起動、IOピンでセンサーなどの接続や、シリアル通信ができる、というなかなかニッチな要求を満たすボードとしてLichee Zero-Dockというのをチョイスした。小洒落たケースに入っている。

このボード、SiPEED https://sipeed.com という中国メーカーが結構前から展開しているSBCである。このLicheeシリーズの他にもMaixというカメラAIボードやFPGAボードなどもある。カメラは以前格安で手に入れたことがあって使ったことがあるのだが、イマイチ使い勝手が良くなく、御蔵入りしていたりする。

さて、入手したボードにはmicro USB端子がついていて、おそらく電源はここから取れるんだろうな、と思う。とりあえずUSBを繋いだらなにか反応があるかと思ったんだが、繋いだからと言ってLEDが点灯するでもなく、USB経由でシリアル接続されるとかもない。

とりあえずSDカードにOSをインストールするしかないかね。

OSはおそらく公式サイトからダウンロードできるんだろうなと思って、ひとまずネットを検索して先人の情報を漁る。
続けるブログ: Lichee Zero-Dockを使ってみる
Sipeed Lichee Zeroを買ったのでとりあえずセットアップしてみた #Linux - Qiita
Licheepi Zero-Doker 今更セットアップ - toromukun’s blog
そこで記載されていたダウンロードサイトに接続したんだが、なんかリンク切れしてるぞ・・・嫌な予感。

結局灯台下暗しで秋月の商品ページにあるスペックシートをダウンロードしてみたところ、そこにメーカーサポートサイト情報が出ていた。
https://akizukidenshi.com/goodsaffix/Sipeed_Lichee_Zero-Dock_Datasheet_EN_V1.0.pdf

ついでにハードウェアスペックも書いておこう。
Main features:
・CPU : Allwinner V3S ; ARM Cortex-A7 architecture ; Basic frequence 1.2Ghz
・ RAM : SIP 64MB DDR2
・ Memory: On-board TFcard slot / Flash SOP8 pads (Dual system boot way)
・ Display: General 40P RGB LCD FPC 0.5mm Connector ; Support resolution of 272x480 ,
480x800 , 1024x600 , etc.
・ Interfaces:SDIO , UART , SPI , I²C , OTG USB , PWM , CSI , MIPI , etc.
・ Peripherals:RJ45 connector , 3.5mm Headphone jack , Electret microphone , Second TF
card slot , Camera connector , 4 buttons , MIPI connector and RGB LED

シングルコアのARM CPUでRAM64MBなので無印のラズパイゼロよりもだいぶ見劣りする。ただし有線LANがついているのとSDカードスロットが2つついているのはラズパイにはない特徴か。LCDポートというのもあるにはあるが、対応するLCDが少なくとも秋月では売っていない。
RGB LEDがついているようだが、電源パイロットLEDはないようだ。物理ボタンも付いている。

さて、OSは
https://dl.sipeed.com/LICHEE/Nano
ここからダウンロードというように記載されているのだが、じつはこれが間違っていて、これはLichee Nanoという別シリーズ用にリンクされている。

https://dl.sipeed.com/shareURL/LICHEE/Zero
Lichee Zeroはこっちのはず。いずれにしてもこのアドレスからは速度が遅くてとてもダウンロードに時間がかかるため、
https://mega.nz/folder/A8g1Hb4J#WcuoqvbpasKlVB8-YEpWPA
こっちからダウンロードするほうが現実的であった。
LICHEE>Zero>images>dd_imgに進んでイメージファイルをダウンロードする。

minX_dd.tar.bz2 -->min system include Xorg
mindb_dd.tar.gz -->min Debian��include gcc,python, etc.
brpy_dd.tar.bz2 -->buildroot image��include python etc.(no gcc)
brmin_dd.tar.bz2 -->min buildroot
minmin_dd.tar.bz2 -->min Debian (almost nothing but apt)
lichee_zero_test_Debian_LXDE.tar.bz2 -->Debian with LXDE

Debianがインストールできるようになっているらしい。
GUIを使う予定はないのでmindb_dd.tar.gzでいいような気もするが、とりあえず lichee_zero_test_Debian_LXDE.tar.bz2をインストールしてみることにする。

Macでダウンロードしたあとbz2を解凍しておく。
SDカードにはddコマンドで書き込めばいいのだけど、MacならbalenaEtcherで書き込み可能である。

さて、書き込みが終わったSDカードをボードのLichee Zero相当の部分についているスロットに装着してUSBを繋いでみるもやはり何も変化は現れない。どうやらUSBは標準ではUSBホストとなっていて、IPアドレスが割り当てられていないためUSB経由のsshは通らないということらしい。シリアルポートからシリアル接続でひとまずアクセスするほか最初は方法がないわけだ。LCDパネルでもあればどうにかなるんだろうけど。(ちなみにもう一つのスロットに装着して起動することは試してみたができないっぽい。)

なので、とにかくシリアルポートに接続するためにヘッダピンをはんだ付けする他ない。
ここで気をつけたいのだが、ピンヘッダを裏側に付ける必要がある。ラズパイのように上向きにピンを立てると、使い勝手はいいのだが、2nd SDスロットが死んでしまう。GPIOの一部のピンを省略すれば大丈夫かもしれないが。裏にピンを立てるとブレッドボードに刺して使えて便利そうだが、実際はボードの幅が広すぎてブレッドボードに刺すと、他のものが全く刺せないので実は意味がない。
というわけでユニバーサル基板をインターフェースボードとして追加することにした。
ひとまず必要なシリアルポートと5V、Gndをピンヘッダで使いやすいように引き出しておく。いつも使っているESP32ボードに合わせた配置順にしておく。シリアルポートはU0T, Rの2本をTxd,Rxdピンとして使う。
なお、例のスペックシートに記載されているピンレイアウトの図は間違っていて、RとTが逆になっている。ボード上のシルク印刷が正しい。

1�X���� 1  0 
switch to partitions #0, OK
mmc0 is current device
Scanning mmc 0:1...
Found U-Boot script /boot.scr
reading /boot.scr
287 bytes read in 14 ms (19.5 KiB/s)
## Executing script at 41900000
reading zImage
3987024 bytes read in 655 ms (5.8 MiB/s)
reading sun8i-v3s-licheepi-zero.dtb
8585 bytes read in 26 ms (322.3 KiB/s)
## Flattened Device Tree blob at 41800000
   Booting using the fdt blob at 0x41800000
   Loading Device Tree to 42dfa000, end 42dff188 ... OK

Starting kernel ...

起動メッセージがこのように流れ、

Debian GNU/Linux 8 LicheePi ttyS0

LicheePi login: root

Password:
Linux LicheePi 4.10.2-licheepi-zero+ #15 SMP Sat Apr 15 21:38:23 CST 2017 armv7l

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@LicheePi:

とこのようにちゃんとDebianが起動する。
パスワードはtoortoorらしい。

root@LicheePi:~# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: usb0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
    link/ether 8e:ee:8f:5e:b0:66 brd ff:ff:ff:ff:ff:ff

このままではネットワークからsshログインはできないので、設定をしていく必要がある。

EPS代替フォーマット

最近のmacOSでは Preview.app が EPS(Encapsulated PostScript)をネイティブ表示できなくなったため、研究資料や図を作る際に困ることがある。
いろいろなウェブツールが標準でEPS形式の図を出力、ダウンロードするようになっているためだ。
Macで広く使えて、かつ長期的に安心できる 「EPSの代替となる汎用ベクターフォーマット」 を、実用性と互換性の観点から考えた。

結論としてはPDF一択と思われる。

  • macOS / Preview.app が 完全ネイティブ対応
  • Illustrator / Inkscape / Affinity Designer / Keynote など主要アプリで編集・表示が可能
  • LaTeX(特に pdfLaTeX)とも相性が良い
  • 研究分野でも「EPS → PDF」への移行が進んでいる
  • ベクター情報をそのまま保持できる(PDF 1.4+)

すでにEPSで保存蓄積されたデータファイルをPDFに変換するには?
EPSファイルをドラッグ&ドロップでPDFに変換して、Preview.appで開くところまで自動化するスクリプトPythonAutomatorで作成する。

方針
EPS ファイルをテキストとして読み込む
%%BoundingBox: x1 y1 x2 y2 の行を探す(「図の上が切れる」問題をほぼ確実に解消)
上側(y2)に少しマージンを足す(ついでに四辺に少し足してもOK)
一時ファイルとして書き出す
その一時 EPS を Ghostscript で PDF 化
最後に Preview で開く

事前準備(Ghostscriptのインストール: Homebrewが必要)

brew install ghostscript

スクリプト本体
eps2pdf_preview.py という名前で保存:

#!/usr/bin/env python3
import sys
import subprocess
from pathlib import Path
import re
import tempfile
import shutil

# Ghostscript のフルパス(環境に合わせて変更)
GHOSTSCRIPT = "/opt/homebrew/bin/gs"  # 例: Apple Silicon + Homebrew
# GHOSTSCRIPT = "/usr/local/bin/gs"   # Intel Mac の場合はこちらかも

# BoundingBox を何ポイント広げるか(必要に応じて調整)
MARGIN = 20


def expand_bounding_box(eps_path: Path) -> Path:
    """
    EPS の BoundingBox を四辺に MARGIN 分広げた一時ファイルを作る。
    元ファイルは変更しない。
    """
    bb_pattern = re.compile(r"^(%%BoundingBox:\s*)(-?\d+)\s+(-?\d+)\s+(-?\d+)\s+(-?\d+)(\s*)$")

    with eps_path.open("r", encoding="latin-1") as f:
        lines = f.readlines()

    modified = False
    new_lines = []

    for line in lines:
        m = bb_pattern.match(line)
        if m and not modified:
            prefix, x1, y1, x2, y2, suffix = m.groups()
            x1 = int(x1) - MARGIN
            y1 = int(y1) - MARGIN
            x2 = int(x2) + MARGIN
            y2 = int(y2) + MARGIN
            new_line = f"{prefix}{x1} {y1} {x2} {y2}{suffix}\n"
            new_lines.append(new_line)
            modified = True
        else:
            new_lines.append(line)

    # BoundingBox が見つからない場合はそのまま使う
    if not modified:
        return eps_path

    # 一時ファイルに書き出す
    tmp_dir = Path(tempfile.mkdtemp(prefix="eps2pdf_"))
    tmp_eps = tmp_dir / eps_path.name
    with tmp_eps.open("w", encoding="latin-1") as f:
        f.writelines(new_lines)

    return tmp_eps


def eps_to_pdf(eps_path: Path) -> Path:
    if not eps_path.exists():
        raise FileNotFoundError(f"File not found: {eps_path}")

    pdf_path = eps_path.with_suffix(".pdf")

    # BoundingBox を広げた一時 EPS を作成
    fixed_eps = expand_bounding_box(eps_path)

    cmd = [
        GHOSTSCRIPT,
        "-dBATCH",
        "-dNOPAUSE",
        "-dSAFER",
        "-sDEVICE=pdfwrite",
        "-dCompatibilityLevel=1.4",
        "-dEPSCrop",  # 修正済み BoundingBox でトリミング
        f"-sOutputFile={pdf_path}",
        str(fixed_eps),
    ]

    result = subprocess.run(cmd, capture_output=True, text=True)
    if result.returncode != 0:
        raise RuntimeError(
            f"Ghostscript failed for {eps_path}:\n"
            f"STDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}"
        )

    # 一時ディレクトリを掃除
    if fixed_eps != eps_path:
        try:
            shutil.rmtree(fixed_eps.parent)
        except Exception:
            pass

    return pdf_path


def main():
    if len(sys.argv) < 2:
        print("EPS ファイルをこのアプリにドラッグ&ドロップしてください。")
        return

    pdf_files = []

    for arg in sys.argv[1:]:
        eps_path = Path(arg)
        try:
            pdf_path = eps_to_pdf(eps_path)
            print(f"Converted: {eps_path} -> {pdf_path}")
            pdf_files.append(pdf_path)
        except Exception as e:
            print(f"[ERROR] {eps_path}: {e}", file=sys.stderr)

    if pdf_files:
        subprocess.run(["open", "-a", "Preview"] + [str(p) for p in pdf_files])


if __name__ == "__main__":
    main()
  • Automator で アプリケーション を作る
  1. Automator を起動
  2. 「新規書類」を選ぶ
  3. ここで 必ず「アプリケーション」 を選ぶ クイックアクション」や「ワークフロー」を選ぶと、ドラッグ&ドロップアプリにならない
  • Run Shell Script の設定

左上の検索欄に シェル と入れて
シェルスクリプトを実行」 をダブルクリックしてワークフローに追加。
その上で、上部の設定を次のようにする:
シェル:/bin/zsh(または /bin/bash でも可)
入力の引き渡し方法:「引数として」
スクリプト欄には例えばこのように書く(パスは環境に合わせて):

/usr/bin/env python3 "/Users/k-kuro/scripts/eps2pdf_preview.py" "$@"
  • アプリとして保存

メニューの「ファイル」→「保存…」

完全版:AlmaLinux 8 に ownCloud を構築し Mac から安全にファイル共有する(Nginx + WebDAV 対応)

OS:AlmaLinux 8
Web サーバ:Nginx
ownCloud を /owncloud/ に配置
Mac(Finder / Cyberduck / 公式クライアント)から使えるようにする

パッケージ導入

sudo dnf install -y epel-release
sudo dnf install -y \
  nginx mariadb-server \
  php php-fpm php-mysqlnd php-gd php-mbstring php-xml php-json \
  php-intl php-zip php-pecl-redis php-opcache php-process

サービス起動:

sudo systemctl enable --now nginx
sudo systemctl enable --now mariadb
sudo systemctl enable --now php-fpm

DB を作成

mysql -u root -p

MySQL プロンプトで:

CREATE DATABASE owncloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'ownclouduser'@'localhost' IDENTIFIED BY '強いパスワード';
GRANT ALL PRIVILEGES ON owncloud.* TO 'ownclouduser'@'localhost';
FLUSH PRIVILEGES;
EXIT;

ownCloud を配置

cd /var/www
sudo curl -L -o owncloud-latest.tar.bz2 https://download.owncloud.com/server/stable/owncloud-latest.tar.bz2
sudo tar -xjf owncloud-latest.tar.bz2
sudo chown -R nginx:nginx /var/www/owncloud

PHP-FPM を nginx ユーザーで動かす

sudo sed -i 's/^user = .*/user = nginx/; s/^group = .*/group = nginx/' /etc/php-fpm.d/www.conf
sudo sed -i 's|^listen = .*|listen = /run/php-fpm/www.sock|' /etc/php-fpm.d/www.conf
sudo systemctl restart php-fpm

Nginx 設定(Flask 共存 & WebDAV 完全対応)

sudo nano /etc/nginx/conf.d/yoursite.conf

全文貼り替え

server {
    listen 443 ssl http2;
    server_name rnaseq.nbenthamiana.jp;

    root   /var/www;
    index  index.php index.html;

    ssl_certificate     /etc/ssl/certs/selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/selfsigned.key;

    add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;

    client_max_body_size 0;
    fastcgi_read_timeout 300s;
    fastcgi_buffers 64 4k;

    # --- ownCloud -------------------------------------------------

    location = /owncloud { return 301 /owncloud/; }

    location /owncloud/ {
        try_files $uri $uri/ /owncloud/index.php$is_args$args;
    }

    # WebDAV / Finder / Cyberduck を確実に動かす
    location ~ ^/owncloud/remote\.php {
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;

        # ★ remote.php を固定する(これが安定化の鍵)
        fastcgi_param SCRIPT_FILENAME /var/www/owncloud/remote.php;
        fastcgi_param PATH_INFO       $fastcgi_path_info;
        fastcgi_param HTTPS           on;

        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_request_buffering off;
        client_max_body_size 0;
    }

    location ^~ /.well-known/carddav { return 301 /owncloud/remote.php/dav; }
    location ^~ /.well-known/caldav { return 301 /owncloud/remote.php/dav; }

    location ~ ^/owncloud/(?:\.htaccess|data|config|db_structure\.xml|README) {
        deny all;
    }

    # --- PHP 共通 -------------------------------------------------
    location ~ \.php(?:$|/) {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        include fastcgi_params;

        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO       $fastcgi_path_info;
        fastcgi_param HTTPS           on;

        fastcgi_pass unix:/run/php-fpm/www.sock;
        fastcgi_index index.php;
        fastcgi_intercept_errors on;
        fastcgi_request_buffering off;
    }

    # --- Flask ----------------------------------------------------
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;
    server_name yourserver.domain.jp;
    return 301 https://$host$request_uri;
}

反映:

sudo nginx -t
sudo systemctl reload nginx


Web から初期セットアップ
ブラウザで:

https://yoursite.domain.jp/owncloud

管理ユーザー作成
DB 情報:

DB: owncloud
ユーザー: ownclouduser
ホスト: localhost

(任意)データ格納先を安全な場所へ(例として/home/owncloud/dataにおくことにする)

sudo mkdir -p /home/owncloud/data
sudo chown -R nginx:nginx /home/owncloud
sudo -u nginx php /var/www/owncloud/occ config:system:set datadirectory --value "/home/owncloud/data"
sudo rsync -Aavx /var/www/owncloud/data/ /home/owncloud/data/
sudo -u nginx php /var/www/owncloud/occ files:scan --all

推奨設定(本番向け)
Redis ロック有効化

sudo dnf install -y redis
sudo systemctl enable --now redis

/var/www/owncloud/config/config.php へ追記:

'filelocking.enabled' => true,
'memcache.locking' => '\OC\Memcache\Redis',
'redis' => [
  'host' => '127.0.0.1',
  'port' => 6379,
  'timeout' => 0.0,
],

cron(推奨)

echo "*/5 * * * * nginx php /var/www/owncloud/occ system:cron" | sudo tee /etc/cron.d/owncloud

なお上記例ではFlaskによるホームページサービスに組み込んでいる。

VNCがまた使えなくなった

RHEL/AlmaLinux 系での最新版 TigerVNC に準拠した vncsession-start ベースの構成では
vncserver :3
のような起動の仕方ではなく、/etc/tigervnc/vncserver.users でユーザーと表示番号を紐付けて管理する構成になっているとのこと。

sudo nano /etc/tigervnc/vncserver.users

:3=kuro

のように:1=ユーザー名 の形式で1行1ユーザー記述する。

あとは

sudo systemctl daemon-reload
sudo systemctl enable vncserver@:3.service
sudo systemctl start vncserver@:3.service

のように起動させればサーバー立ち上げればVNCも起動するようになる。