kuroの覚え書き

96の個人的覚え書き

Raspberry Pi zero 2Wでシリアル通信

Raspberry Pi zero 2Wでピンヘッダからマイコン等とシリアル通信する。

まずシリアルコンソールを無効化する。
デフォルトでは/dev/serial0はLinuxコンソールに割り当てられているのでraspi-configで無効化する。
GUIからだとraspi-config>インターフェイスに入り
シリアルポート: 有効
シリアルコンソール: 無効
を選択し
すると再起動するかと聞かれるので再起動する。

次にBluetoothモジュールの接続を切り替える。
デフォルトでは
/dev/serial0 -> /dev/ttyS0
/dev/serial1 -> /dev/ttyAMA0
シンボリックリンクが張られている。このままではBluetoothモジュールがUART0(/dev/ttyAMA0)を使ってしまっているので切り替えが必要。
一つの方法はdisable-btでBluetoothを無効化する方法。ただしこれだと多分Bluetoothが全く使えなくなる。
もう一つの方法はminiuart-btという方法で、こちらはBluetoothをmini UART(/dev/ttyS0)に割り当てる。結果的にBluetoothの速度は低下するが使えなくなることはない。
今回はこちらを採用する。

$ sudo nano /boot/config.txt

# Use miniUART to connect to Bluetooth module
dtoverlay=miniuart-bt
core_freq=250

というふうに追記し、もう一度再起動する。

これによって
/dev/serial0 -> /dev/ttyAMA0
/dev/serial1 -> /dev/ttyS0
と入れ替わり、Primary UART(/dev/serial0)がUART0(/dev/ttyAMA0)のシンボリックリンクとなる。

この状態で

$ screen /dev/ttyAMA0 115200 8N1

のようにしてやればシリアル通信ができるようになっている。

なお使用するピンは8番ピンがTxDで、10番ピンがRxD、6番ピンのGNDと並びで3つのピンを3.3vマイコンのUART IOに直結で通信が可能である。
まちがって5Vの機器やRS232Cレベルと直結するとあっけなく壊れるらしいので要注意。

Jetson Nanoにminiforge

Ubuntu20.04にアップグレードできたことだし、Linuxマシンとして色々使うならやはりanaconda入れておきたいよね、と思ったんだがどうやらこれもまたJetsonNanoでは一筋縄では行かないらしい。

aarch64用のminiconda-likeな環境であるminiforge(https://github.com/conda-forge/miniforge)
をインストールするといいらしい。
方法はGitHubのページの通り。
確かにこれでcondaも使えるようになった。
メイン言語がpythonだからこれはありがたい。pythonのバージョンも3.10.14が入ったし。

特に2Gの方はこれでエッジAIとかなかなか厳しいから簡易なpython開発環境くらいがちょうどいいかな。ubuntuサーバのローカルテストベッドくらいで使うのがちょうどいいと思う。

Jetson Nano 2GB Ubuntuのアップデート

長いことほったらかしにしていたJetson Nano 2GBの電源を久々に入れてみたら、Ubuntu18.04だった。
B01のほうすらほとんど使い道がなくて放置しているけど、あれは20.04にはしてあったはず。

というわけでいちおうシステムをアップデートしておこう。
多分トラブル頻発してすんなり行かないから記録しつつやってみよう。

まずは
sudo apt update && sudo apt -y upgrade

あ、なんだ、すんなりいけた。
sudo apt -y dist-upgrade && sudo apt -y autoremove

なんかchromiumをアンインストールしておかないとダメらしい。
sudo apt-get remove --purge chromium-browser chromium-browser-l10n

そして
sudo nano /etc/update-manager/release-upgrades

で、一番下の行の
prompt=never

prompt=normal
に変える。

一旦再起動して、
sudo do-release-upgrade


あれ?やっぱりここでだめだ。インデックスファイルのダウンロードが失敗して

システムを元に戻しています

中断します

とか言われてプロンプトに戻ってしまうよ。

んー。素直にJetPackをバージョンの上がったものにして再インストールかな。
と思ったらJetson NanoはB01も2GBもubuntu18.04ベースのJetPack4.6.4以上は対応してないのね。
ちょっと製品寿命短すぎない?

とりあえずJetPackをNanoの最終バージョンである4.6.4にしてクリーンインストールし、設定を一通りやった上で、上記のウップグレードをもう一度試してみる。

その結果無事に20.04にアップデートできた。

~$ screenfetch
                          ./+o+-       kkuro@kkuro-JN-2G
                  yyyyy- -yyyyyy+      OS: Ubuntu 20.04 focal
               ://+//////-yyyyyyo      Kernel: aarch64 Linux 4.9.253-tegra
           .++ .:/++++++/-.+sss/`      Uptime: 12m
         .:++o:  /++++++++/:--:/-      Packages: 2465
        o:+o+:++.`..```.-/oo+++++/     Shell: bash 5.0.17
       .:+o:+o/.          `+sssoo+/    Disk: 19G / 30G (68%)
  .++/+:+oo+o:`             /sssooo.   CPU: ARMv8 rev 1 (v8l) @ 4x 1.479GHz
 /+++//+:`oo+o               /::--:.   RAM: 730MiB / 1971MiB
 \+/+o+++`o++o               ++////.  
  .++.o+++oo+:`             /dddhhh.  
       .+.o+oo:.          `oddhhhh+   
        \+.++o+o``-````.:ohdhhhhh+    
         `:o+++ `ohhhhhhhhyo++os:     
           .o:`.syhhhhhhh/.oo++o`     
               /osyyyyyyo++ooo+++/    
                   ````` +oo+++o\:    
                          `oo++.


ちなみになんで今更Jetson Nanoなんて持ち出したかというと学会のポスターとしてディスプレイを貼り付けてやって、chromeのキオスクモードを使ってインタラクティブにいじれるとおもしろうそうかな、と思って、PCを持っていくのもアレなのでJetsonでいいんじゃないか?と思ったのでした。

scRNA-seqやってみる(その3)

時系列や各種処理を与えたサンプル間で比較するなら、それらをマージして同じクラスターで重ね合わせて考えたい。
まずはumapチャートを出力するまで。4つの実験区の比較を行う例をやってみたい。Seuratのハンズオンでは2区の重ね合わせなんだけど、実際に10xの出力として保存されたデータからではなく、ライブラリからサンプルデータを読み出して処理しており、実際的ではない。なのでCellrangerの出力するfiltered_feature_bc_matrixフォルダに3つのファイル(barcode.tsv.gz, features.tsv.gz, matrix.mtx.gz)が保存されているところをスタートとした解析を試してみる。

if(!require(Seurat)){install.packages("Seurat")}
if(!require(patchwork)){install.packages("patchwork")}
if(!require(dplyr)){install.packages("dplyr")}

library(Seurat)
library(patchwork)

install.packages('BiocManager')
# Bioconductorのバージョンを設定
BiocManager::install(version = "3.19")
BiocManager::install('glmGamPoi')

#####Remove all objects before running#####
rm(list = ls())

input.dir <- "/path/to/sample/"

# サンプル1のデータセットを読み込む
sample1 <- Read10X(paste(input.dir, "sample1/filtered_feature_bc_matrix", sep=""))

# サンプル2のデータセットを読み込む
sample2 <- Read10X(paste(input.dir, "sample2/filtered_feature_bc_matrix", sep=""))

# サンプル3のデータセットを読み込む
sample3 <- Read10X(paste(input.dir, "sample3/filtered_feature_bc_matrix", sep=""))

# サンプル4のデータセットを読み込む
sample4 <- Read10X(paste(input.dir, "sample4/filtered_feature_bc_matrix", sep=""))

sample1 <- CreateSeuratObject(counts = sample1, project = "sample1")
sample2 <- CreateSeuratObject(counts = sample2, project = "sample2")
sample3 <- CreateSeuratObject(counts = sample3, project = "sample3")
sample4 <- CreateSeuratObject(counts = sample4, project = "sample4")

# 各データセットにユニークなセル識別子を追加
sample1 <- RenameCells(sample1, new.names = paste("sample1", Cells(sample1), sep = "_"))
sample2 <- RenameCells(sample2, new.names = paste("sample2", Cells(sample2), sep = "_"))
sample3 <- RenameCells(sample3, new.names = paste("sample3", Cells(sample3), sep = "_"))
sample4 <- RenameCells(sample4, new.names = paste("sample4", Cells(sample4), sep = "_"))

# サンプルデータセットのリストを作成
datasets <- list(sample1, sample2, sample3, sample4)

# データセットの前処理
datasets <- lapply(datasets, function(x) {
  x <- SCTransform(x, verbose = FALSE)
  x <- FindVariableFeatures(x, selection.method = "vst", nfeatures = 2000)
  x
})

# 統合のためのアンカーフィーチャーの選択
features <- SelectIntegrationFeatures(object.list = datasets, nfeatures = 2000)

# SCT正規化を使用した統合アンカーの発見
datasets <- PrepSCTIntegration(object.list = datasets, anchor.features = features)
anchors <- FindIntegrationAnchors(object.list = datasets, normalization.method = "SCT", anchor.features = features)

# データセットの統合
combined <- IntegrateData(anchorset = anchors, normalization.method = "SCT")

# 統合データの次の解析ステップ
combined <- ScaleData(combined, verbose = FALSE)
combined <- RunPCA(combined, npcs = 30, verbose = FALSE)
combined <- RunUMAP(combined, reduction = "pca", dims = 1:30)
combined <- FindNeighbors(combined, reduction = "pca", dims = 1:30)
combined <- FindClusters(combined, resolution = 0.5)

# ここでデータを一旦保存しておく
# 後で読み込むときはcombined <- readRDS(file =paste(input.dir, "combined.rds", sep=""))
saveRDS(combined, file = paste(input.dir, "combined.rds", sep=""))

# 結果の可視化
p1 <- DimPlot(combined, reduction = "umap", group.by = "orig.ident")
p2 <- DimPlot(combined, reduction = "umap", label = TRUE, repel = TRUE)
p1 + p2

DimPlot(combined, reduction = "umap", split.by = "orig.ident")

# 出来上がったクラスターに名前をつけることができる
combined <- RenameIdents(combined, `0` = "Cluster 0", `1` = "Cluster 1", `2` = "Cluster 2", `3` = "Cluster 3")
DimPlot(combined, label = TRUE)

# マーカー遺伝子の可視化
markers.to.plot <- c("GeneA", "GeneB", "GeneC")
DotPlot(combined, features = markers.to.plot, cols = c("blue", "red"), dot.scale = 8, split.by = "orig.ident") + RotatedAxis()

# Find Differentially Expressed Genes:
fold_change_threshold <- 0.25
pct_threshold <- 0.25
p_value_threshold <- 0.01

# クラスタごとのDEG
all_markers <- FindAllMarkers(
  object = combined,
  only.pos = FALSE,
  min.pct = pct_threshold,
  logfc.threshold = fold_change_threshold,
  test.use = "wilcox"
)

# p値でフィルタリング
significant_markers <- all_markers[all_markers$p_val_adj < p_value_threshold, ]

# 結果をCSVファイルとして保存
write.csv(significant_markers, file = "Differentially_Expressed_Genes.csv", row.names = FALSE)

# 上位10個のDEGを可視化
top10 <- significant_markers %>% group_by(cluster) %>% top_n(n = 10, wt = avg_log2FC)

pdf(file = "Top10_Differentially_Expressed_Genes.pdf", width = 8.27, height = 11.69)
DoHeatmap(combined, features = top10$gene) + NoLegend()
dev.off()

Rの基本的なこと

本当に何もわからないRのお作法で学んだことをメモしとこう。

文字列をつなげる
> paste ("a", "b")
a b
間にスペースが入る。スペースを入れたくなかったら
> paste ("a", "b", sep = "")
ab

ベクトル(=pythonのリスト)の合成にも使える。

随時書き足していく

scRNA-seqやってみる(その2)

ちょっとごちゃごちゃしてきたので一旦整理すると

#Seuartパッケージのインストール
install.packages('Seurat')
library(Seurat)

#テストファイルの準備とdirectoryの移動
setwd("~/scrnaseqtest/immune_alignment_matrix/")

#data読み込み
ctrl.data <- read.table("immune_control_expression_matrix.txt", sep = '\t', row.names = 1)
stim.data <- read.table("immune_stimulated_expression_matrix.txt",  sep = "\t", row.names = 1)

#Seuratオブジェクトの作成
ctrl <- CreateSeuratObject(counts = ctrl.data, project = "IMMUNE_CTRL", min.cells = 5)
stim <- CreateSeuratObject(counts = stim.data, project = "IMMUNE_STIM", min.cells = 5)

#metadataの付与
ctrl@meta.data$ctrl <- "CTRL"
stim@meta.data$stim <- "STIM"

#フィルタリング
ctrl <- subset(ctrl, subset = nFeature_RNA > 500)
stim <- subset(stim, subset = nFeature_RNA > 500)

#正規化
ctrl <- NormalizeData(ctrl, scale.factor = 10000)
stim <- NormalizeData(stim, scale.factor = 10000)

#Z-Scoreの算出
ctrl <- ScaleData(ctrl)
stim <- ScaleData(stim)

#使用する遺伝子の選定
ctrl <- FindVariableFeatures(ctrl)
stim <- FindVariableFeatures(stim)

# 上位1000の変動遺伝子を取得
g.1 <- head(VariableFeatures(ctrl), 1000)
g.2 <- head(VariableFeatures(stim), 1000)

#g1、g2で共通して発現変動の大きい遺伝子をunique関数で抽出
genes.use <- unique(c(g.1, g.2))
scaled_genes <- rownames(GetAssayData(ctrl, slot = "scale.data"))
genes.use <- intersect(genes.use, scaled_genes)
scaled_genes <- rownames(GetAssayData(stim, slot = "scale.data"))
genes.use <- intersect(genes.use, scaled_genes)

とりあえずここまでは良さげ。

データを可視化していく。

CCA(Canonical Correlation Analysis)によるオブジェクトのマージ

# データセットをリストにまとめる
immune.list <- list(ctrl = ctrl, stim = stim)

# アンカーを見つける
anchors <- FindIntegrationAnchors(object.list = immune.list, anchor.features = genes.use)

# データセットの統合
immune.combined <- IntegrateData(anchorset = anchors, dims = 1:30)

# 統合データのスケーリングと主成分分析
immune.combined <- ScaleData(immune.combined)
immune.combined <- RunPCA(immune.combined)

PCAのプロットを出してみる

# 次元削減結果のプロット
p1 <- DimPlot(object = immune.combined, reduction = "pca", group.by = "stim", pt.size = 0.5)
p1

PC_1をy軸に取ったViolin plot

p2 <- VlnPlot(object = immune.combined, features = "PC_1", group.by = "stim")
p2

各次元の重要な遺伝子のヒートマップを表示

DimHeatmap(object = immune.combined, dims = 1:2, cells = 500, balanced = TRUE)

とりあえずできた気になっているが、自分のデータで意味のある解析ができるかというとかなり怪しい。

scRNA-seqやってみる(その1)

とりあえずSeuratというRのパッケージで解析すると良いらしい。
ちなみに10xのCell Rangerでマッピングまではやっている前提で。


こちらの情報を参考にやってみることにする。ちょっと古いけど大丈夫かな?
Seuratを駆使する会 ① - ばいばいバイオ


Rはほんとうに敬遠していて何もわからないのだけど。
まずはパッケージをインストールから
わからないなりに環境だけいっちょ前にLinux上に構築してRStudioサーバを建ててあり、ネット越しにブラウザでアクセスして解析ができるようにしていたりする。なにもわからないのに。
Rのバージョンは4.4.0である。

> install.packages('Seuart')

とするといつ終わるかもわからないくらいずらずらとメッセージが流れる。
幸い、致命的エラーは出ずにインストールできたらしい。

> library(Seuart)

ライブラリインポートも問題なくできたっぽい。
さて、チュートリアルを一通りやっておこう。
ConsoleタブからTerminalタブに切り替えて、

$ mkdir scrnaseqtest
(ngs) kuro@guilty6-x2110:~$ cd scrnaseqtest/
(ngs) kuro@guilty6-x2110:~/scrnaseqtest$ mkdir immune_alignment_matrix
(ngs) kuro@guilty6-x2110:~/scrnaseqtest$ cd immune_alignment_matrix/
(ngs) kuro@guilty6-x2110:~/scrnaseqtest/immune_alignment_matrix$ wget https://www.dropbox.com/s/79q6dttg8yl20zg/immune_alignment_expression_matrices.zip

`immune_alignment_expression_matrices.zip' へ保存完了 [21329741/21329741]

(ngs) kuro@guilty6-x2110:~/scrnaseqtest/immune_alignment_matrix$ unzip immune_alignment_expression_matrices.zip
Archive:  immune_alignment_expression_matrices.zip
  inflating: immune_control_expression_matrix.txt.gz  
   creating: __MACOSX/
  inflating: __MACOSX/._immune_control_expression_matrix.txt.gz  
  inflating: immune_stimulated_expression_matrix.txt.gz  
  inflating: __MACOSX/._immune_stimulated_expression_matrix.txt.gz  
(ngs) kuro@guilty6-x2110:~/scrnaseqtest/immune_alignment_matrix$ rm immune_alignment_expression_matrices.zip 
(ngs) kuro@guilty6-x2110:~/scrnaseqtest/immune_alignment_matrix$ gunzip immune_control_expression_matrix.txt.gz 
(ngs) kuro@guilty6-x2110:~/scrnaseqtest/immune_alignment_matrix$ gunzip immune_stimulated_expression_matrix.txt.gz 

と、これで下準備OK

さてConsoleに戻って

> ctrl.data <- read.table("immune_control_expression_matrix.txt", sep = '\t', row.names = 1)
file(file, "rt") でエラー: コネクションを開くことができません
追加情報: 警告メッセージ:
file(file, "rt") で:
  ファイル 'immune_control_expression_matrix.txt' を開くことができません: そのようなファイルやディレクトリはありません

ああ、そうかディレクトリを移動しないといけないのか。どうやって?(このレベルです)

> setwd("~/scrnaseqtest/immune_alignment_matrix/")
> ctrl.data <- read.table("immune_control_expression_matrix.txt", sep = '\t', row.names = 1)
> stim.data <- read.table("immune_stimulated_expression_matrix.txt",  sep = "\t", row.names = 1)
> ctrl <- CreateSeuratObject(raw.data = ctrl.data, project = "IMMUNE_CTRL", min.cells = 5)
CreateSeuratObject(raw.data = ctrl.data, project = "IMMUNE_CTRL",  でエラー: 
  引数 "counts" がありませんし、省略時既定値もありません

またひっかかる。どうもバージョン3のSeuratでなにか変わっているらしい。なのでチャットGPTの出番。

エラーの原因は、CreateSeuratObject 関数の引数の名前が変更されたためです。Seuratのバージョン3以降では、raw.data 引数が counts 引数に変更されました。

> ctrl <- CreateSeuratObject(counts = ctrl.data, project = "IMMUNE_CTRL", min.cells = 5)
警告: Feature names cannot have underscores ('_'), replacing with dashes ('-')
警告: Data is of class data.frame. Coercing to dgCMatrix.

なんか警告は出たけどいいらしい。
続いてmetadataの付与

> ctrl@meta.data$stim <- "CTRL"
> head(ctrl@meta.data$stim)
[1] "CTRL" "CTRL" "CTRL" "CTRL" "CTRL" "CTRL"

フィルタリング

> ctrl <- FilterCells(ctrl, subset.names = "nGene", low.thresholds = 500, high.thresholds = Inf)
FilterCells(ctrl, subset.names = "nGene", low.thresholds = 500,  でエラー: 
  関数 "FilterCells" を見つけることができませんでした

chatGPT

FilterCells 関数は、Seuratのバージョン3以降では廃止されました。代わりに、subset 関数を使用します。

らしい。

> ctrl <- subset(ctrl, subset = nFeature_RNA > 500)

正規化

> ctrl <- NormalizeData(ctrl, scale.factor = 10000)
Normalizing layer: counts
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|

z-scoreの算出

> ctrl <- ScaleData(ctrl)
Centering and scaling data matrix
  |=================================================================================================================| 100%

同様にstimulatedのほうも

> stim <- CreateSeuratObject(counts = stim.data, project = "IMMUNE_STIM", min.cells = 5)
警告: Feature names cannot have underscores ('_'), replacing with dashes ('-')
警告: Data is of class data.frame. Coercing to dgCMatrix.
> stim@meta.data$stim <- "STIM"
> stim <- subset(stim, subset = nFeature_RNA > 500)
> stim <- NormalizeData(stim)
Normalizing layer: counts
Performing log-normalization
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
> stim <- ScaleData(stim)
Centering and scaling data matrix
  |=================================================================================================================| 100%

分散の大きい(発現変動の大きい)遺伝子を抽出

> ctrl <- FindVariableGenes(ctrl)
FindVariableGenes(ctrl) でエラー: 
  関数 "FindVariableGenes" を見つけることができませんでした
> ctrl <- FindVariableFeatures(ctrl)
Finding variable features for layer counts
Calculating gene variances
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Calculating feature variances of standardized and clipped values
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|

FindVariableGenes 関数も、Seuratのバージョン3以降では廃止されました。代わりに FindVariableFeatures 関数を使用します。

> g.1 <- head(rownames(ctrl@hvg.info), 1000)
dimnames(x)[[1L]] %||% if (do.NULL) NULL else { でエラー: 
  名前 "hvg.info" というスロットが、クラス "Seurat" のこのオブジェクトには存在しません
> # 上位1000の変動遺伝子を取得
> g.1 <- head(VariableFeatures(ctrl), 1000)
> g.2 <- head(VariableFeatures(stim), 1000)

hvg.info スロットも Seurat のバージョン3以降では変更されています。変動遺伝子の情報は VariableFeatures スロットに格納されています。

g1、g2で共通して発現変動の大きい遺伝子をunique関数で抽出

> genes.use <- unique(c(g.1, g.2))
> genes.use <- intersect(genes.use, rownames(ctrl@scale.data))
dimnames(x)[[1L]] %||% if (do.NULL) NULL else { でエラー: 
  名前 "scale.data" というスロットが、クラス "Seurat" のこのオブジェクトには存在しません

scale.data スロットはSeuratのバージョン3以降では存在しません。代わりに ScaleData 関数を使用してデータをスケーリングし、ctrl"RNA"@scale.data からスケールされたデータにアクセスできます。
だそうで

> genes.use <- intersect(genes.use, rownames(ctrl[["RNA"]]@scale.data))
dimnames(x)[[1L]] %||% if (do.NULL) NULL else { でエラー: 
  名前 "scale.data" というスロットが、クラス "Assay5" のこのオブジェクトには存在しません

まだだめ。
scale.data スロットが存在しないというエラーは、Seuratのバージョンによる変更の結果です。現在のSeuratバージョンでは、スケールされたデータは scale.data スロットではなく、ScaleData 関数が返すオブジェクト内に格納されます。

> scaled_genes <- rownames(ctrl[["RNA"]]@data)
dimnames(x)[[1L]] %||% if (do.NULL) NULL else { でエラー: 
  名前 "data" というスロットが、クラス "Assay5" のこのオブジェクトには存在しません

Seuratの最新バージョンでは、スケールされたデータは ScaleData 関数の出力として直接取得できないため、正しいスロットにアクセスする必要があります。スケーリングされたデータは通常、ctrl"RNA"@scale.data ではなく、ctrl"RNA"@scale.data に格納されます。

しかし、最新のバージョンではスケールデータが標準的なアクセス方法ではなく、GetAssayData 関数を使う必要があります。

> scaled_genes <- rownames(GetAssayData(ctrl, slot = "scale.data"))
警告メッセージ:
The `slot` argument of `GetAssayData()` is deprecated as of SeuratObject 5.0.0.
ℹ Please use the `layer` argument instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated. 
> genes.use <- intersect(genes.use, scaled_genes)


なんかあやしくなってきたぞ?
一旦停止して整理する。

MAiX II DOCKをいじってみる

どうもこれに入っているLinuxはTina-LinuxといってOpenWRTに由来する組み込みLinuxらしいとの情報を得た。
Allwinner Tina Linux · GitHub
https://d1.docs.aw-ol.com/en/study/study_1tina/

OpenWRT懐かしい。2008年頃、FONという無線LANをみんなでシェアしようと言う趣旨のプロジェクトがあって、そこで安価で配布された無線LANルータのFoneraという装置があった。このFoneraの基板には意味ありげなピンヘッダが立っていて、それがシリアルコンソールであることに気がついた人たちのあいだで、そのコンソールからファームウェアを流し込んでもとの無線LANルータ機能を上書きしてしまうハックが流行った。そこで使われていたのがOpenWRTであった。基本無線LANルータとしてのファームウェアなんだけど、実態としては組み込みLinuxだということで、無線LANルータとしてではなく、安価なLinuxboxとして使ってしまおうというプロジェクトも派生していった。基板上のパターンを解析してSDカードを乗せるSPI接続を実現したり、シリアル通信でArduinoと接続してIOを制御したり、まさに現在、ラズベリーパイがやっているようなことを先駆けてやっていたのよね。当然GUIが動くほどの能力がルータのSOCにあるはずもなく、すべてCUI。多くのコマンドが入っているBusyBoxを駆使しまくってた。WebサーバとかSDカードを使ったファイル共有サーバとか温湿度計を繋いでエアコンリモート制御とか。

閑話休題

で、MAiX ll DOCKですよ。

BusyBox v1.27.2 () built-in shell (ash)

------run profile file-----
 _   .-')      ('-.            ) (`-.
( '.( OO )_   ( OO ).-.         ( OO ).
 ,--.   ,--.) / . --. /  ,-.-')(_/.  \_)-.
 |   `.'   |  | \-.  \   |  |OO)\  `.'  /
 |         |.-'-'  |  |  |  |  \ \     /\
 |  |'.'|  | \| |_.'  |  |  |(_/  \   \ |
 |  |   |  |  |  .-.  | ,|  |_.' .'    \_)
 |  |   |  |  |  | |  |(_|  |   /  .'.  \
 `--'   `--'  `--' `--'  `--'  '--'   '--'
   __   _
  / /  (_)__  __ ____ __ ------------------------
 / /__/ / _ \/ // /\ \ /  sipeed.com (Neptune)
/____/_/_//_/\_,_//_\_\  ------------------------

ほらBusyBoxじゃん。
ということでかつてのお約束のようにシリアルコンソール接続でログインを試みる。
その前にネットワークにつなぐ設定をしておく。SDカードをカードリーダーで覗くとトップディレクトリにWIFIの設定ファイル wpa_supplicant.conf があるので、SSIDとパスワードを入力しておく。
(追記)技適取れてないので無線接続はしてはいけません。日本語の情報が極端に少ないのはこのせいか。

USBでMacとつなぐと/dev/cu.usbserial-14330が現れるので

cu -l /dev/cu.usbserial-14330 -s 115200

としてやってMAiXをリセットすると

Last login: Fri May 17 13:58:12 on ttys002

The default interactive shell is now zsh.
To update your account to use zsh, please run `chsh -s /bin/zsh`.
For more details, please visit https://support.apple.com/kb/HT208050.
------run rc.modules file-----
------run rc.final file-----
Load mpp modules
insmod: can't insert '/lib/modules/4.9.118/videobuf2-core.ko': No such file or directory
insmod: can't insert '/lib/modules/4.9.118/videobuf2-memops.ko': No such file or directory
insmod: can't insert '/lib/modules/4.9.118/videobuf2-v4l2.ko': No such file or directory
load /etc/asound.conf ...
alsactl: set_control:1461: Cannot write control '2:0:0:codec trigger playback time value:0' : Operation not permitted
alsactl: set_control:1461: Cannot write control '2:0:0:codec trigger capture time value:0' : Operation not permitted
exist /root/wpa_supplicant.conf
Starting app...
enable android usb
Initializing random number generator... done.
Starting network...
Start dropbear: OK
Starting ntpd: done


BusyBox v1.27.2 () built-in shell (ash)

------run profile file-----
 _   .-')      ('-.            ) (`-.
( '.( OO )_   ( OO ).-.         ( OO ).
 ,--.   ,--.) / . --. /  ,-.-')(_/.  \_)-.
 |   `.'   |  | \-.  \   |  |OO)\  `.'  /
 |         |.-'-'  |  |  |  |  \ \     /\
 |  |'.'|  | \| |_.'  |  |  |(_/  \   \ |
 |  |   |  |  |  .-.  | ,|  |_.' .'    \_)
 |  |   |  |  |  | |  |(_|  |   /  .'.  \
 `--'   `--'  `--' `--'  `--'  '--'   '--'
   __   _
  / /  (_)__  __ ____ __ ------------------------
 / /__/ / _ \/ // /\ \ /  sipeed.com (Neptune)
/____/_/_//_/\_,_//_\_\  ------------------------

root@sipeed:/# WARNING: Logging before InitGoogleLogging() is written to STDERR
I0522 03:02:09.853097   795 dup2SeldomUsedFd.c:20]          <dup2SeldomUsedFdInit> gFdLock init
Successfully initialized wpa_supplicant
udhcpc: started, v1.27.2
udhcpc: sending discover
I0522 03:02:10.799209   795 mpi_sys.c:766]                  <AW_MPI_SYS_SetConf> kfctmpdir is [/tmp]
I0522 03:02:10.800760   795 mpi_sys.c:1195]                 <AW_MPI_SYS_Init> ISP init
I0522 03:02:10.800925   795 mpi_sys.c:1197]                 <AW_MPI_SYS_Init> ISP init done
I0522 03:02:10.807874   795 hwdisplay.c:83]                 <hw_display_init> <(hwd_init 989)d_init:989> 95 hwdisplay.c:989] 
I0522 03:02:10.808255   795 hwdisplay.c:1044]               <hwd_init> <hwd_iniret[0][2,0]ch[2]lyl[0] init: enable[1], screenwin[0,0, 240x240], zorder[16], alpha[mode:0, value:255]
I0522 03:02:10.808458   795 alsa_interface.c:659]           <alsaOpenMixer> open mixer:hw:0
I0522 03:02:10.848054   795 alsa_interface.c:721]           <alsaOpenMixer> set player pa switch level 0
I0522 03:02:10.848312   795 alsa_interface.c:709]           <alsaOpenMixer> set playback vol_val to value: 27
E0522 03:02:10.969903   795 video_buffer_manager.c:211]     <VideoBufMgrCreate> Alloc 20 input frame buffers in list manager.
E0522 03:02:10.970302   795 VideoVirVi_Component.c:481]     <VideoViSetViDevAttr> fps 20 nbufs 3
E0522 03:02:11.001482   795 video_buffer_manager.c:211]     <VideoBufMgrCreate> Alloc 20 input frame buffers in list manager.
E0522 03:02:11.001873   795 VideoVirVi_Component.c:481]     <VideoViSetViDevAttr> fps 20 nbufs 3
I0522 03:02:11.002887   795 hwdisplay.c:1244]               <hwd_get_disp_type>Current the  disp_type:0x1  tv_mode:0x0
I0522 03:02:11.003194   795 hwdisplay.c:1117]               <hwd_layer_request_hlay:0, zorder=0, cnt:2t_hlay:1117> 
I0522 03:02:11.003396   795 mpi_vo.c:1030]                  <AW_MPI_VO_SetVideoLayerAttr> ch[0]lyl[0]:dispRect changed, [0, 0, 320x240]->[0, 0, 240x240]
I0522 03:02:11.003574   795 hwdisplay.c:408]                <hwd_layer_set_rectch[0]lyl[0]: screen_win[0,0, 240x240]
E0522 03:02:11.004062   795 vo.c:683]                       <vo_init> debuf create vo channel[0] success!
I0522 03:02:11.004436   896 Clock_Component.c:1109]         <Clock_ComponentThread> ClockComp state[0x1]->Idle!
E0522 03:02:11.004617   795 vo.c:718]                       <vo_init> debuf create clock channel[0] success!
I0522 03:02:11.004777   896 cedarx_avs_counter.c:148]       <avscounter_start> (f:avscounter_start, l:148) Avscounter status [pause]->[run], pauseDuration[0][0]ms
I0522 03:02:11.004956   795 hwdisplay.c:1117]               <hwd_layer_request_hlay:9, zorder=9, cnt:3t_hlay:1117> 
I0522 03:02:11.005098   795 mpi_vo.c:1030]                  <AW_MPI_VO_SetVideoLayerAttr> ch[2]lyl[1]:dispRect changed, [0, 0, 320x240]->[0, 0, 240x240]
I0522 03:02:11.005194   795 hwdisplay.c:408]                <hwd_layer_set_rectch[2]lyl[1]: screen_win[0,0, 240x240]
I0522 03:02:11.005291   795 mpi_vo.c:1130]                  <AW_MPI_VO_SetVideoLayerAlpha> video layer alpha changed, [0, 128]->[0, 25]
E0522 03:02:11.006791   795 vo.c:454]                       <CreateVoUiLayer> create vo channel[0] success!
udhcpc: sending discover
before paHostApiInitializers[0].
ALSA version (build): 1.1.4.1
ALSA version (runtime): 1.1.4.1
BuildDeviceList: Ignoring ALSA plugin device [cards] of type [unknown]
BuildDeviceList: Found plugin [default] of type [unknown]
BuildDeviceList: Found plugin [sysdefault] of type [unknown]
BuildDeviceList: Found plugin [front] of type [unknown]
BuildDeviceList: Found plugin [rear] of type [unknown]
BuildDeviceList: Found plugin [center_lfe] of type [unknown]
BuildDeviceList: Found plugin [side] of type [unknown]
BuildDeviceList: Found plugin [surround21] of type [unknown]
BuildDeviceList: Found plugin [surround40] of type [unknown]
BuildDeviceList: Found plugin [surround41] of type [unknown]
BuildDeviceList: Found plugin [surround50] of type [unknown]
BuildDeviceList: Found plugin [surround51] of type [unknown]
BuildDeviceList: Found plugin [surround71] of type [unknown]
BuildDeviceList: Found plugin [iec958] of type [unknown]
BuildDeviceList: Found plugin [spdif] of type [unknown]
BuildDeviceList: Found plugin [hdmi] of type [unknown]
BuildDeviceList: Found plugin [dmix] of type [unknown]
BuildDeviceList: Ignoring ALSA plugin device [dsnoop] of type [unknown]
BuildDeviceList: Found plugin [modem] of type [unknown]
BuildDeviceList: Found plugin [phoneline] of type [unknown]
BuildDeviceList: Ignoring ALSA plugin device [hw] of type [hw]
BuildDeviceList: Ignoring ALSA plugin device [plughw] of type [plug]
BuildDeviceList: Ignoring ALSA plugin device [plug] of type [plug]
BuildDeviceList: Ignoring ALSA plugin device [shm] of type [shm]
BuildDeviceList: Ignoring ALSA plugin device [tee] of type [file]
BuildDeviceList: Ignoring ALSA plugin device [file] of type [file]
BuildDeviceList: Ignoring ALSA plugin device [null] of type [null]
BuildDeviceList: Filling device info for 19 devices
FillInDevInfo: Filling device info for: sun8iw19-codec: - (hw:0,0)
GropeDevice: collecting info ..
GropeDevice: collecting info ..
Default input device: sun8iw19-codec: - (hw:0,0)
Default output device: sun8iw19-codec: - (hw:0,0)
FillInDevInfo: Adding device sun8iw19-codec: - (hw:0,0): 0
FillInDevInfo: Filling device info for: sysdefault
GropeDevice: collecting info ..
GropeDevice: Limiting number of plugin channels to 128
GropeDevice: collecting info ..
GropeDevice: Limiting number of plugin channels to 128
FillInDevInfo: Adding device sysdefault: 1
FillInDevInfo: Filling device info for: front
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.front
OpenPcm: Opened device 'front' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: front, all channels == 0
FillInDevInfo: Filling device info for: rear
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
OpenPcm: Opened device 'rear' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: rear, all channels == 0
FillInDevInfo: Filling device info for: center_lfe
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
OpenPcm: Opened device 'center_lfe' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: center_lfe, all channels == 0
FillInDevInfo: Filling device info for: side
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
OpenPcm: Opened device 'side' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: side, all channels == 0
FillInDevInfo: Filling device info for: surround21
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround21
OpenPcm: Opened device 'surround21' ptr[0] - result: [-2:No such file or directory]
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround21
OpenPcm: Opened device 'surround21' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: surround21, all channels == 0
FillInDevInfo: Filling device info for: surround40
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround40
OpenPcm: Opened device 'surround40' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: surround40, all channels == 0
FillInDevInfo: Filling device info for: surround41
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround41
OpenPcm: Opened device 'surround41' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: surround41, all channels == 0
FillInDevInfo: Filling device info for: surround50
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround50
OpenPcm: Opened device 'surround50' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: surround50, all channels == 0
FillInDevInfo: Filling device info for: surround51
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround51
OpenPcm: Opened device 'surround51' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: surround51, all channels == 0
FillInDevInfo: Filling device info for: surround71
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.surround71
OpenPcm: Opened device 'surround71' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: surround71, all channels == 0
FillInDevInfo: Filling device info for: iec958
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
OpenPcm: Opened device 'iec958' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: iec958, all channels == 0
FillInDevInfo: Filling device info for: spdif
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
OpenPcm: Opened device 'spdif' ptr[0] - result: [-2:No such file or directory]
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.iec958
OpenPcm: Opened device 'spdif' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: spdif, all channels == 0
FillInDevInfo: Filling device info for: hdmi
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
OpenPcm: Opened device 'hdmi' ptr[0] - result: [-2:No such file or directory]
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.hdmi
OpenPcm: Opened device 'hdmi' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: hdmi, all channels == 0
FillInDevInfo: Filling device info for: modem
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
OpenPcm: Opened device 'modem' ptr[0] - result: [-2:No such file or directory]
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.modem
OpenPcm: Opened device 'modem' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: modem, all channels == 0
FillInDevInfo: Filling device info for: phoneline
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
OpenPcm: Opened device 'phoneline' ptr[0] - result: [-2:No such file or directory]
ALSA lib pcm.c:2501:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.phoneline
OpenPcm: Opened device 'phoneline' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: phoneline, all channels == 0
FillInDevInfo: Filling device info for: default
GropeDevice: collecting info ..
GropeDevice: Limiting number of plugin channels to 128
GropeDevice: collecting info ..
GropeDevice: Limiting number of plugin channels to 128
Default input device: default
Default output device: default
FillInDevInfo: Adding device default: 2
FillInDevInfo: Filling device info for: dmix
ALSA lib pcm_direct.c:1421:(snd1_pcm_direct_initialize_poll_fd) unable to open timer 'hw:CLASS=3,SCLASS=0,CARD=0,DEV=0,SUBDEV=0'
ALSA lib pcm_dmix.c:1183:(snd_pcm_dmix_open) unable to initialize poll_fd
OpenPcm: Opened device 'dmix' ptr[0] - result: [-2:No such file or directory]
FillInDevInfo: Skipped device: dmix, all channels == 0
BuildDeviceList: Building device list took 0.692696 seconds
after paHostApiInitializers[0].
before paHostApiInitializers[1].
PaOSS BuildDeviceList: Total number of devices found: 0
after paHostApiInitializers[1].
PaAlsaStreamComponent_Initialize: Host Chans P 2
AlsaOpen: Opening device default
PaAlsaStreamComponent_InitialConfigure: device MMAP SND_PCM_ACCESS_MMAP_INTERLEAVED: YES
PaAlsaStreamComponent_InitialConfigure: device MMAP SND_PCM_ACCESS_MMAP_NONINTERLEAVED: YES
PaAlsaStreamComponent_InitialConfigure: device can MMAP: YES
PaAlsaStreamComponent_DetermineFramesPerBuffer: user-buffer (frames)           = 1024
PaAlsaStreamComponent_DetermineFramesPerBuffer: user-buffer (sec)              = 0.046440
PaAlsaStreamComponent_DetermineFramesPerBuffer: suggested latency (sec)        = 0.008707
PaAlsaStreamComponent_DetermineFramesPerBuffer: suggested host buffer (frames) = 2048
PaAlsaStreamComponent_DetermineFramesPerBuffer: suggested host buffer (sec)    = 0.092880
PaAlsaStreamComponent_DetermineFramesPerBuffer: periods min = 2, max = 512, req = 4 
PaAlsaStreamComponent_DetermineFramesPerBuffer: suggested host buffer period   = 1024 
PaAlsaStreamComponent_DetermineFramesPerBuffer: device period minimum          = 64
PaAlsaStreamComponent_DetermineFramesPerBuffer: device period maximum          = 16384
PaAlsaStreamComponent_DetermineFramesPerBuffer: host buffer period             = 1024
PaAlsaStreamComponent_DetermineFramesPerBuffer: host buffer period latency     = 0.046440
PaAlsaStream_Configure: Playback period size: 1024, latency: 0.046440
OpenStream: Stream: framesPerBuffer = 1024, maxFramesPerHostBuffer = 1024, latency i=0.000000, o=0.046440
I0522 03:02:15.532507   891 VideoVirVi_Component.c:360]     <DoVideoViReturnAllValidFrames> release [1]validFrames
I0522 03:02:15.532899   891 VideoVirVi_Component.c:1077]    <Vi_ComponentThread> wait using frame return done
W0522 03:02:15.543506   890 videoInputHw.c:4128]            <VideoInputHw_CapThread> VIPP[0], No Virvi Component, drop this one yuv data.
E0522 03:02:15.548595   795 video_buffer_manager.c:211]     <VideoBufMgrCreate> Alloc 20 input frame buffers in list manager.
E0522 03:02:15.548939   795 VideoVirVi_Component.c:481]     <VideoViSetViDevAttr> fps 20 nbufs 3
I0522 03:02:16.274793   897 video_render_linux.cpp:224]     <vr4l_init> mDisplayFormat[0x200], new CedarXNativeRenderer
I0522 03:02:16.275141   897 CedarXNativeRenderer.cpp:174]   <CedarXNativeRenderer> hwc disp fmt[0x80], color space:260
I0522 03:02:16.275300   897 hwdisplay.c:239]                <hwd_layer_set_src>x: 0, y: 0, width: 0xf0, height: 0xf0
I0522 03:02:16.275398   897 hwdisplay.c:246]                <hwd_layer_set_src>width: 0xf000000000, height: 0xf000000000
I0522 03:02:16.275480   897 hwdisplay.c:366]                <hwd_layer_set_src>set fb.format 128 0, color_space 260 end, size0[240x240], size1[0x0]
E0522 03:02:16.275611   897 vo.c:281]                       <VoUiCallbackWrapper> debuf vo report video display size[240x240]
I0522 03:02:16.275724   897 VideoRender_Component.c:2316]   <VideoRender_ComponentThread> init video_render, param: displayRect[0,0][240x240], bufSize[240x240], vdecColorFormat[0xb]
I0522 03:02:16.275821   897 mpi_vo.c:508]                   <VideoRenderEventHandler> KeyFrameDecoded, pts[0]us
E0522 03:02:16.276000   897 vo.c:286]                       <VoUiCallbackWrapper> debuf vo report rendering start
udhcpc: sending discover
udhcpc: sending select for 10.0.1.201
udhcpc: sending select for 10.0.1.201
udhcpc: lease of 10.0.1.201 obtained, lease time 172800
udhcpc: ifconfig wlan0 10.0.1.201 netmask 255.255.255.0 broadcast +
udhcpc: setting default routers: 10.0.1.1

root@sipeed:/#  ls
bin          home         overlay      rom          squashfs     usr
data         lib          proc         root         swapfile     var
dev          lost+found   pseudo_init  run          sys
etc          mnt          rdinit       sbin         tmp

これは普通にLinux boxとして使えそうだ。エッジAIとして使うかどうかはさておき。

追記
とりあえずできること
viでテキスト編集

できないこと
すでに入っている以外のアプリケーションのopkgでのインストール

とりあえずファイルの転送手段としてネットワーク越しは一旦おいておくことにしてまずUSBでの転送を考える。
このデバイスにはUSB-Cが2つついていて、1つを電源&シリアルコンソールとして使っている。もう1つのUSBはOTGということだが、そのままではPCからUSBデバイスとして認識されるモードとなっているため、これをUSBホストに変えてやる。

echo "usb_host" > /sys/devices/platform/soc/usbc0/otg_role

これでOK。
ひとまずSDカードをリーダに入れて接続してみる。
exFATはだめっぽい
FAT32なら

mount -t vfat /dev/sda1 ./media

であらかじめ作っておいたマウントポイント./mediaにマウントすることができた。
これで、とりあえずMacpythonコードを書いてMAiX IIに移して実行。ということは可能になった。アプリのインストールもできるかな。

しかし本格的に活用するなら、やはり無線LANに繋がないとどうにもならないだろうな。
一応無線LANチップ自体は技適が取れているようなので、
総務省 電波利用ホームページ|その他|技適未取得機器を用いた実験等の特例制度
こちらで申請すれば短期間なら試験できるようだけど、180日経ったら廃止申請を出すとか面倒すぎるので、やっぱり使い物にはならない。せめてUSB無線子機を繋いで使えるようにできればいいのだが、それもなかなかハードルが高い。

追記
色々と忘れてしまっていたのだが、OpenWRTではSquashFSを使っているため、読み込み専用でファイルへの書き込みができない。なので、設定を書き換えようが、なにかインストールしようが、電源を切るともとに戻ってしまう。overlayとかchrootを使うんだったかなあ。忘却の彼方。