「Linuxのこと」カテゴリーアーカイブ

Codexをぶん回してLinux用のカメラドライバを作った話

ども。こんばんは。

超久々の投稿がこんな長々としたAIに書かせた内容であれですが、、生きてます。 長いのでAIにでも要約させてお読みください。 人生で一回は書いてみたいと思っていたデバイスドライバ、もうAI使えばすぐかけちゃう時代なんですねぇ… まぁとりあえず動くだけってレベルですが… ではここからはCodexがまとめた内容です。

最近バイブコーディングが楽しすぎて寝不足気味です。
あと、一応多少実践的なモデル選択についても触れています。


買ったまま放置気味だったIdeaPad Duet Chromebook 10.1型(ZA6F0038JP)、なんかもっさりしてるし面白いことできないかなーと思ってたらpostmarketOSなるものを発見。
ウキウキで入れたもののカメラが動きませんでした。ほかの部分は思った以上に使えます。

最近仕事でもAIAIしてるので、いっそカメラのドライバをAIで作れないかな、ってことでCodexと一緒に2026年9月23日開発が始まりました。

翌朝には最初の画像が出ました。ここだけを切り取れば、難しいドライバを一晩で作れた話になります。

しかし、私が欲しかったのは一枚の実験画像ではなく、普通のアプリで前後のカメラを使える状態です。連続撮影、終了後の再利用、スリープ復帰、画質、導入と復元まで進むと、話はずっと長くなりました。

実装の大半はCodexに任せました。それでも、私の仕事がなくなったわけではありません。完成条件を伝え直し、遠回りしている設計を見直し、実機で見える結果を確かめる必要がありました。

今回はCodexにセッションの記録と使用量を集計してもらい、何ができたのかだけでなく、どこで苦労したのかも日ごとに振り返ります。Day1は9月23日、時刻は日本時間です。

作ったのは、センサーからアプリまでの接続

対象は初代Duetの前面OV02A10、背面OV8856、そしてMediaTek MT8183です。

元のChromeOSでは動いていたカメラでも、別のLinux環境にOSを入れ替えれば、そのまま使えるとは限りません。センサーのドライバがあるだけでは足りず、基板ごとの接続情報、画像入力回路、補助プロセッサーとの通信、アプリへ画像を渡す仕組みまで揃える必要があります。

最終的な構成は、センサーからSENINFとP1を通して画像を受け取り、V4L2とMedia Controllerで公開し、libcameraの画像処理と自動調整を使い、PipeWireとportalを経由して通常のGNOME Snapshotから利用する形になりました。

専門用語が並びますが、ハードウェア側で受け取ったRAW画像を、Linuxの標準的なカメラ経路へ乗せる仕事です。

Codexは既存のLinuxセンサードライバ、ChromeOS側の実装、SCP firmware、libcameraなどを調べて移植・修正・接続しました。すべてを白紙から発明したわけではありません。

むしろ後半になるほど、既存の標準部品をどこまで使うかが、完成までの距離を大きく変えました。

開発環境はMacを中心に、ARM64 LinuxのビルドにはRaspberry Pi、動作確認にはDuet実機を使いました。途中から接続経路も簡素化しています。

画像の見え方は数値だけでは決まらないため、Codexによるフレーム数や設定値の測定と、私が実際に画面を見た評価を組み合わせました。

どのくらいCodexを使ったのか

今回の使用量は、CodexのセッションJSONLに残る記録から、主担当だけでなく、実装や独立レビューを分担したサブエージェント、自動承認レビューも含めて集計しています。OS導入からカメラ開発、公開後の修正までが対象です。

現存する198セッションのJSONLから、延べ2,541,490,422トークン(約25.41億)を確認できました。

人間と対話する親が15、実装や独立レビューを行う子が70、自動承認レビューが113です。この記事を作るための調査は含めていません。

区分 トークン数
入力合計 2,529,665,600
うちキャッシュ入力 2,454,364,416
キャッシュ以外の入力 75,301,184
出力合計 11,824,822
うち推論出力 3,334,618
入力+出力 2,541,490,422

入力の約97.02%はキャッシュ入力でした。長い文脈を繰り返し処理した延べ量を含む数字であり、この量の文章を新しく書いたわけではありません。

キャッシュ入力は入力、推論出力は出力の内数なので、二重には足していません。料金や実際の請求額への換算でもありません。

使用量は呼び出しIDで重複を除き、19,696件の記録と、圧縮で失われた冒頭部分の累計21,449,924トークンを集計しました。別に各セッションの最終累計からも計算し、一致を確かめています。

ChatGPT側の相談、クラウド側の補助処理、一部の自動承認レビューには使用量を取得できない部分があり、確認可能な範囲の合計です。

会話の中で選んだつもりの設定と、実際に使われた設定も分けました。以下のモデルと推論量は、可能な限り記録上の値に基づいています。

特に親の設定を変えたあとも、既存の子には以前の設定が残っていた点は、振り返って初めて全体像が見えた部分です。

Day 日付 モデルと推論設定 トークン数
1 9/23 Astra Medium・Ultra 183,479,126
2 9/24 Astra Ultra 553,198,613
3 9/25 Astra Ultra 174,611,128
4 9/26 対象記録なし —
5 9/27 Astra High・Ultra 265,908,314
6 9/28 対象記録なし —
7 9/29 対象記録なし —
8 9/30 Astra High 56,445,817
9 10/1 Astra High 6.1 Sol High 198,165,267
10 10/2 Astra High 6.1 Sol High 120,076,187
11 10/3 Astra High・Ultra 6.1 Sol High 233,307,986
12 10/4 Astra High 6.1 Sol High 207,915,907
13 10/5 Astra High・Ultra 6.1 Sol High 157,107,248
14 10/6 Astra Ultra 6.1 Sol High 159,164,859
15 10/7 Astra Ultra 6.1 Sol High 57,746,816
16 10/8 Astra Ultra 6.1 Sol High 151,542,612
別枠 日付不明 自動承認レビュー3件 22,820,542
合計 2,541,490,422

時刻は日本時間です。10月8日は22:52までを集計しています。

日別の合計は作業用の子エージェントと日付の分かる自動承認レビューも含みます。表のモデルと推論設定は親・作業子の設定で、親だけの設定ではありません。

元の日付が失われた自動承認レビュー3件は別枠にしています。

開発前:9月3日にAstraが登場

GPT-6 Astraの登場は、この開発を始める約3週間前でした。OpenAIの公式API更新履歴では、リリース日は2026年9月3日とされています。

これは公式の日付表記で、私のアカウントで使えるようになった時刻を特定したものではありません。

今回の記録は、すでにAstraを使っていた9月23日から始まります。旧モデルから初めて移った瞬間の比較はできませんが、この開発中のMedium、Ultra、Highの使い分けは追えます。

最初は難しいドライバなら強い推論を使いたいと考えていました。やがて、その選び方だけでは解決しない問題にぶつかります。

Day1:9月23日、OSを入れたらカメラが動かなかった

17時台、Astra Mediumを使うCodexにpostmarketOSのイメージ作成を任せました。最初から順調ではありません。

手元のRaspberry Piは32bit OSで、Codexは当初そのまま進められると判断しましたが、QEMUの制約が問題になりました。私は64bitのビルド機へ切り替え、前の環境に残した作業用ファイルも片付けてもらって再出発しました。

USBからOSを起動し、Phosh、日本語、画面の向きを調整しました。画面回転にも何度か手戻りがありました。

Codexが設定を保存できたことを、再起動後にも正しく表示できることと取り違えていたためです。直ったはずの画面が、再起動するとまた逆さまになります。

私が見た結果を伝え、Codexがログイン後まで含めて確認し直して、ようやく落ち着きました。後から振り返ると、今回繰り返し出会う落とし穴の最初でした。

18時48分、私はカメラが使えないことに気づきました。Codexが調べると、当時の環境では未対応でした。

ほかの部分は使えそうだったのでOSの導入は進めましたが、カメラまで諦める必要があるのかが気になりました。20時53分頃から、カメラ対応を独立したプロジェクトとして調べ始め、ここで主担当のモデルをAstra Ultraへ切り替えました。

調査ですぐに分かったのは、単独のドライバを追加して終わる規模ではないということでした。ChromeOS側の古いカーネルで動いていた実装を、新しいカーネルへ合わせなければなりません。

センサー、Device Tree、SENINF、P1、SCPを順番につなぐ必要があります。Linuxに映像デバイスが見えていても、実体は動画codecなどの別機能で、カメラが使える証拠にはなりませんでした。

何度も失敗するでしょうから、私は実機試験を簡単にしたいと考えました。Codexが示した案から、動作中のLinuxからRAM上の実験用OSへ移るkexecを選びました。毎回USBを作り直す手間を減らし、通常のOSへ戻れる実験環境を先に用意する方針です。

ところが、まずそのkexecが動きません。SCPの停止につまずき、さらにカーネルの制御フローを検査するKCFIの問題で黒画面になりました。

Codexがクラッシュ後のログを追うと、新しいOSが起動に失敗しているのではなく、移行直前の古いカーネル自身が落ちていました。対象を限定したRAM上の回避処理を用意し、23時51分、通常OSへのkexecに初めて成功しました。

それでもRAM上の実験OSはまだネットワークへ戻りません。カメラを作り始めたはずが、初日はカメラを繰り返し試せる実験環境の準備で終わりました。画像は一枚も出ていませんが、ここを越えないと実験が回せないので、まずは土台からです。

Day2:9月24日、夜を越えて最初の一枚

日付をまたいで、CodexはRAM環境のネットワークを修正しました。実機のコマンドが対応していないオプションを使っていた問題もあり、想定ではなく実バイナリの挙動を確かめ直しました。

00時22分に接続が戻り、00時30分にはRAMからRAMへの再移行も確認できました。ようやく、ドライバを繰り返し試す準備が整いました。

主担当も並行作業を分担する子もAstra Ultraでした。00時49分には前面センサーのIDを読めました。

しかし、認識できたから撮影できるわけではありません。Codexはセンサーから入力回路へ、入力回路からメモリへと経路を組み立て、02時台に接続を登録しました。通信、画像転送、メモリ管理、電源管理を一つずつ確かめていきました。

特に難しかったのが、撮影を安全に止めることでした。firmwareから終了応答が返っても、DMAによるメモリへの書き込みが止まった保証にはなりません。まだ書き込み中の領域を解放すれば、別の用途に使い始めたメモリを壊す可能性があります。

一方で、Codexが書いた安全確認のコードにも誤りがあり、停止後に消える値が残り続ける前提で検査して、正常な状態を失敗と判定していました。

05時59分、初めてフレーム完了が届きました。そこで終わればよかったのですが、後処理の検査が実機に存在しない項目を読んで止まりました。

Codexはその一枚を保持したまま検査を直し、06時03分に1600×1200、約2.4MBのRAW取得を確定しました。非ゼロデータで、行ごとにも変化があり、転送前後の一致と撮影後の停止・復帰も確認できました。

CodexにJPEGへ現像してもらい、私が見てみました。06時18分、補正は必要そうですが、確かに写っていました。

数値が正しそうだという段階から、ようやくカメラの画像になった瞬間でした。前夜の開始から考えると、最初の一枚まではかなり速いです。

ただ、ここで私とCodexの完成イメージを合わせ直しました。一枚を取り出せたことは大きな成果ですが、私が欲しいのは普通のアプリを開けば映るカメラです。

夜にはCodexが連続取得を始め、有限3フレーム、6フレームへ伸ばしました。センサー制御、画像処理、連続撮影を分担して進めましたが、アプリから見えるカメラはまだありませんでした。

Day3:9月25日、部品は増えるがアプリには届かない

CodexはAstra Ultraによる並行作業を続け、6個の画像バッファを使った9フレームの取得まで進めました。一部のバッファも再利用できました。

しかし、コピーと比較にかかる時間が撮影の制約に収まりません。CPUの使い方を調整してコピーが速くなっても、比較の処理時間は残ります。連続撮影は、一枚の成功を単純に繰り返せばよいわけではありませんでした。

PipeWireへ合成映像を渡す試験でも、Codexは終了処理、映像形式の解釈、受信側が遅い場合のフレーム番号の飛びを修正しました。センサーに書いた設定を実際に読み返す仕組みも加えました。

後の露光問題を見つけるためには役立ちましたが、合成映像が流れたことは、実機カメラがアプリで動いたことと同じではありません。

必要な部品と試験は増えています。Codexから届く進捗も間違いではありません。

それでも、私が開きたいカメラアプリにはまだ届きません。部品表は立派になっていくのに、完成品が手元に来ない感覚でした。個々の成功を聞くだけでは、全体の残りが見えにくくなっていました。

Day4:9月26日

おやすみ

Day5:9月27日、Ultraを回し続けて終わるのか

再開時、私が気になっていたのは実装の細部より、完成と消費でした。Ultraで進めながら利用枠のリセットをすでに3回経験し、このまま続けて本当に終わるのかが見えなくなっていました。

難しい作業には強い推論を使えばよい、という最初の考え方を見直す必要がありました。

Codexと全体を整理すると、実機で確認できたのは有限9フレームと限定的なバッファ再利用まででした。通常アプリへ渡す部分は未完成です。

Codexも、細かな実験、補助機能、重複した検証を増やす一方で、完成に直結する統合を後ろへ回しすぎたと認めました。安全な停止は必要ですが、確認項目が増えることと、使える状態へ近づくことは同じではありません。

13時52分、私は主担当をAstra Highへ切り替えました。必要な場面ではUltraを使うとしても、日常的な実装まで常に最大の推論量で回すのはやめることにしました。

ところが、あとで記録を見ると、すでに動いていた子エージェントにはUltraが残っていました。親だけ節約モードにして、別室ではUltraがそのまま働いていた格好です。設定を変えたつもりと、全体の実設定は別でした。

実機ではCodexが約15fpsにして時間の余裕を作り、有限30フレームまで進めました。

メモリを直接比較する方法が実機ではかえって遅く、いったんコピーしてから比較した方が速いという結果も出ました。理屈の上で簡単に見える最適化でも、対象のCPUとメモリで測らなければ分かりません。

新たに問題になったのは露光値です。430を指定しているのに、実際に読み返すと390になっています。再起動後に成功しても、繰り返すと再発します。

画像転送やSCPを使わないセンサー単体の診断でも再現したため、Codexはセンサーへの書き込みと待ち時間へ調査を絞りました。診断版を準備したところで、この日の作業は利用上限による中断となりました。

私はモデル選びと消費についても別に相談し始めました。難しい場面だけ推論量を上げればよいとしても、その難しさを事前に判断するのが難しいのです。

感覚だけで選ぶより、実際にどのモデルが動き、どこへトークンを使ったかをログで確認する必要があると感じました。

Day6:9月28日

おやすみ

Day7:9月29日、開発の途中で6.1 Solが登場

この日の対象開発記録はありませんが、モデルの選択肢が増えました。GPT-6.1 Solの公式リリース日は2026年9月29日です。Astraに近い性能をより低いコストで目指すモデルとして登場しました。

日付は公式API更新履歴、位置付けは公式モデル説明に基づきます。

登場日にすぐ切り替えたわけではありません。翌日はまだAstra Highで再開し、実際に主実装を移したのは10月1日の夜でした。

Day8:9月30日、担当と実験の範囲を決め直す

再開前に、Codexに途中の実装と実機の状態を見直してもらいました。過去の引き継ぎに書いてあることと、現物やログで確認できることが一致しない部分もありました。長い開発では、引き継ぎ文書そのものも検証が必要になります。

21時45分、新しいAstra Highのセッションで再開しました。今度はCodexの分担を小さくし、主担当が実装と実機試験を行い、独立レビューは一つに絞りました。

実験も、書き込みの待ち時間0µsと250µsを一組比較するところまでに区切りました。節約のために安全確認を省くのではなく、重複調査や広すぎる並行作業、成果の見えない反復を減らすことを意識しました。

結果は、0µsでは読み返し390、250µsでは430でした。

違いは出ましたが、各一回の比較で、実行順、CPUの状態、温度なども完全には分離できていません。待ち時間が原因を解決したと結論するには早い段階です。画像を撮っていないセンサー単体の試験なので、カメラ全体が安定したことにもなりません。

Codexは次に、最初だけ待つのか、書き込み間で待つのか、その両方なのかを四条件に分ける準備へ進みました。作業範囲を狭めたことで、少なくとも何を確かめ、どこで判断するのかは以前より明確になりました。

この頃、私は進行管理にもAIを使えないか考えていました。Dotsを含め、Codexが止まった理由と証拠を見て、次の作業へ進める役割を分ける発想です。

実装を任せるだけでなく、実装を進める相手も欲しくなっていました。ただ、完成条件が曖昧なままでは、管理役を増やしても解決しない問題が残ります。

Day9:10月1日、6.1 Sol Highへ切り替える

午前はCodexが四条件のセンサー診断を行い、露光の読み返しは430、390、430、430となりました。失敗を含めて結果を残し、通常OSへの復帰も確認しました。

午後は非同期処理、メモリの所有、保存、回収、停止時間の上限などを管理する仕組みが増えました。個別の試験は通っても、普通のアプリで撮れる状態はまだ遠いままでした。

夜、私はモデルの選び方を再び見直しました。実設定の切替は21時09分、Astra Highから6.1 Sol Highへです。

以後、Codexの主実装にはSolを使い、必要な節目でAstraによる独立レビューを組み合わせる形にしました。

私の体感では、切替後は消費をそれほど気にせず、Codexに実機試験を進めてもらいやすくなりました。以前より積極的に試して終わらせたいと思えたのは確かです。

ただし、同じ課題を同じ条件で二つのモデルに解かせた比較ではありません。何割安くなった、何倍速くなったという測定結果にはできません。

しかも、モデルを替えた直後から、すべてが順調になったわけでもありません。以前から積み重なった細かな停止条件と不足部品が残り、Codexの作業はまた小さな単位に分かれました。

エンジンを替えても、目的地までの道順はそのままだったようなものです。モデルの性能と、プロジェクトの進め方は別の問題でした。

Day10:10月2日、全体の完成に近づいているのか

Codexの主実装は6.1 Sol High、独立レビューにはAstra Highを使いました。保存、転送、起動確認、回収の仕組みが整っていきました。

一方で、レビューからも、本来の要求を超えた防御まで必須条件になっているという指摘が出ました。

統合した環境を実機へ入れると、また露光430に対して390が返り、撮影前に止まりました。今度は失敗後のRAMと資源を完全に保全する条件が、復旧の妨げになりました。

Codexは将来の失敗へ備える仕組みをさらに作ろうとします。カメラを作っていたはずが、実験を終える仕組みを作る話になっていました。

私はCodexに、個々の進捗ではなく、全体として完成へ近づいているのかを確認しました。部分的な前進はあっても、ここ数回は完成にあまり近づけていない、という整理になりました。

安全な停止や証拠の保存は必要です。ただ、その場の実験を終えて次を試す道まで塞いでしまうと、何を守るための仕組みなのかを見直さなければなりません。

この試行は打ち切り、再起動して通常OSへ戻りました。これは撮影処理が正常に資源を回収できた成功例ではありません。失敗した実験から復帰した、という結果を残しました。

Day11:10月3日、普通のアプリへ届き、設計も変わった

朝から別の問題が続きました。Codexが用意した保存処理は、72MBのRAWを64MBの領域へ保存しようとしていました。

さらに、小さな読み出しごとに大きなhash検査を繰り返していました。データは救出できましたが、保存する仕組み自体の設計に問題がありました。

続いて、正常な実機の状態を復旧チェックが拒否する条件の逆転が見つかりました。Codexのテストは、実機では起こらない状態を用意して通っていました。

テストが成功したことは、テストの前提が正しいことまで保証してくれません。AIがコードも試験も作る開発では、この区別を特に意識する必要があると感じました。

昼に新しいセッションへ移り、同じ6.1 Sol Highで方針を組み直しました。私は、小さな完了を積み上げるより、実機での反復と通常アプリへの接続を優先したいと伝えました。

Codexは復旧条件を実際の失敗記録から直し、接続もMacから実機へ直接行える形に簡素化しました。必要な保護を残しながら、実験そのものを回せる構成へ変えました。

13時台にV4L2/VB2の連続取得、正常停止、再openが通り、CodexがPipeWire、portal、GStreamerとの接続を修正しました。

そして14時39分、通常のGNOME Snapshotでライブ表示、JPEG撮影、終了と再撮影まで到達しました。新セッションの開始から約2時間半でした。

もちろん、2時間半ですべてを作ったわけではありません。それ以前に積み上げたドライバ、実験環境、失敗記録を使った結果です。

それでも、同じモデルのまま完成条件と進め方を変えたあとに、欲しかった画面まで届いたことは印象に残りました。

夕方には、もう一つ大きな設計変更をしました。Codexは自動露光とホワイトバランスを独自に実装する方向へ進みかけていました。

しかし私は、完成したらコミュニティに提供したいと考えていました。ここで専用の画像処理まで増築するより、既存の標準的な経路へ載せたいところです。

Codexと方針を見直し、標準RAWとMedia Controllerから、libcameraの既存Software ISP、自動露光、ホワイトバランスへ接続する構成へ移りました。21時22分には、その経路でもSnapshotが動きました。

うれしい一方、私は最初からこちらでよかったのでは、と聞かずにはいられませんでした。Codexも、方針選択が遠回りだった可能性を認めました。

ただし、前半の知見があったから接続できた部分もあるので、かかった時間を丸ごと無駄だったとは言えません。

最後にCodexが上下の向き、遅延、描画速度を直し、夜には私の目で見てもかなりよくなりました。モデルの変更だけでなく、標準部品の使い方を見直したことが、ここで大きく効きました。

Day12:10月4日、後ろのカメラと長時間運転

Codexは6.1 Sol Highで背面OV8856へ進みました。既存のセンサードライバを利用しながら、clock、映像形式、タイミングを調整しました。

04時23分、背面も通常Snapshotで約15fpsのライブ、撮影、再利用まで到達しました。前面だけの実験から、前後のカメラを使える段階へ進みました。

午後は前後切替、オートフォーカス、画質、高解像度を確認しました。Codexは既存のAF処理を使い、背面の高解像度RAWとqcamでの3200×2400撮影も確かめました。

私は見え方を確認しましたが、暗さや色には部屋の照明などの撮影条件も影響します。実装だけを変数として考えると、誤った方向へ調整しかねません。

周辺作業でも失敗がありました。CodexがRAM上へarchiveを展開した際、rootディレクトリの所有権まで変えてしまい、接続が壊れました。

再起動で戻し、Codexが展開と転送の方法を修正しました。カメラのコード以外にも、実験環境を扱う手順が不具合を作ります。

短い試験では動いても、30分連続や繰り返しでは空のバッファや再起動失敗が出ました。深夜には、映像バッファを毎回コピーする設定が、宣言上は有効でも実際のオブジェクトでは初期化されていないと分かりました。

設定ファイルに正しい値があることと、実行中にその値が効いていることは別でした。

Day13:10月5日、30fpsと配布の準備

Codexが初期化を修正すると、30分連続運転とその後の再利用が通りました。私は30fpsも試したいと考え、Codexに調整を進めてもらいました。前面・背面とも約30fpsを5分ずつ確認できました。

背面の高解像度側は約10fpsのままで、すべての解像度を30fpsにしたわけではありません。

Codexは署名したAPKを作り、版の入れ替えとrollbackも試しました。このあたりで私は、ここまでの作り込みが本当にすべて必要だったのかを振り返りました。

画像の受信、メモリの所有、安全な停止、標準APIへの接続は必要でした。一方で、独自ISP、細分化された復旧基盤、大量の候補と文書、OSイメージまで広げた作業には、先に減らせた部分がありました。

この話をすると、Codexもすべてが必要だったわけではないと認めました。

頼れる実装担当なのですが、必要そうな仕事を見つけるのも、とても得意です。私がカメラの完成を待っている横で、周辺設備が充実していきます。何を作れるかに加えて、今回どこまで作るべきかを私が判断する必要がありました。

公開用には実験機能を整理し、ソース、ビルド方法、配布物、日英の利用者文書を揃える方向へ進みました。CodexではSol Highを実装に、Astra Ultraを厳しいレビューに使う場面も増えました。

最適化実行で消えるassertを安全チェックに使っていた問題、低fps時のtimeout、manifestのパス検査など、試験を通した後にも不具合が見つかりました。

それでも、私が初めて読む人のつもりで文書を見ると、未確定の説明が残り、導入方法も複雑でした。Codexと私の間では通じる説明でも、利用者には通じません。

動くものを作る仕事と、他の人が迷わず使えるものにする仕事は別でした。公開文書も完成条件に含めないと、実装だけが先に完成したことになってしまいます。

Day14:10月6日、簡単に入れて戻せる形へ

Codexが外部USBで新規導入を検証すると、firmware、package、音声、起動準備の調整が増えていきました。

私はここでも、配布の仕方を簡素化したいと伝えました。カメラを使ってもらいたいのに、導入の前にOS構築の実習が始まりそうな手順になっていたためです。

目標を、対応環境へ必要なものを導入し、問題があれば復元できる更新一式へ絞りました。新しいOS一式を配るところまで広げる必要はありません。

Codexと何度も確認したのは、開発者が再現できることと、利用者が簡単に試せることを混ぜない、という点でした。

独立した環境でのレビューでは、電源管理、署名、mount失敗、検証コマンドが失敗しても先へ進めてしまう手順などが見つかりました。

インストーラーは正常に動く経路だけを用意しても足りません。途中で止まったとき、何を書き換え、何をまだ書き換えていないかを正しく扱う必要があります。

さらに、開発時の試験手順がそのまま利用者向けの条件になっていたため、Codexが利用者向けのinstallとrestore、開発者向けのビルドと試験資料を分けました。

夜には導入と切戻しを実機で確かめ、私は前後の撮影に大きな異常がないことを目視しました。Codexは元のパッケージ、設定、起動領域へ戻るところまで照合しました。カメラが映ることに加えて、試す前の環境へ戻せることも確認できました。

Day15:10月7日、公開後にも不具合は見つかる

Codexが電源管理のdeadlockを修正し、撮影、スリープ復帰、復元を確認しました。00時40分、当時のリポジトリでv1.0.0を正式公開しました。

公開先から認証なしで取得し、配布物も検証しました。長かったですが、ようやく他の人へ渡せる形になりました。

ただし、到達点には限界があります。公開時のPipeWire経由の実測平均は約24fpsで、常時30fpsを保証するものではありません。

音声はデバイスの列挙と再生コマンドの成功までで、この時点では聴取確認が終わっていませんでした。完全な画質やAFの校正、日単位の耐久まで確認したわけでもありません。

朝には、私はそれまでの開発の流れから離れた別のCodexセッションでレビューを依頼しました。既存のホスト試験は通りましたが、追加の再現確認で二つの不具合が見つかりました。

外部ユーザーから自然に届いた報告ではなく、自分で依頼した別視点のレビューから見つかった問題です。

    • 導入後に利用者が変更した設定を、復元処理が検出せず上書き・削除してしまいます。
    • 起動準備で書き込み前に失敗すると、中間状態から再試行も復元もできなくなります。

CodexはSol Highで修正し、回帰試験を追加しました。ここでも、私が配布物の更新まで確認すると、ソース修正だけでは利用者の手元が直らない点が課題になりました。

ダウンロードするものまで揃って、初めて不具合修正版です。最後の一歩のはずなのに、その一歩が何度も出てきます。

09時19分にv1.0.1を公開しました。installerの修正だったため、11個のAPKは検証済みのものとのbyte一致を根拠に再利用し、ソースとbundleを更新しました。これは当時の公開履歴で、後の公開構成の整理とは分けています。

この時点で、v1.0.1を新たに導入した実機受入がすべて終わっていたわけではありません。夜から改めて導入し、Codexの測定と私の確認を組み合わせて、設定保護、音声、前後各15分の撮影、スリープ、復元を確かめていきました。

Day16:10月8日、明るさの調整と公開文書の見直し

日付をまたぎ、前面15分と背面15分の撮影、静止画、アプリの再起動、スリープ復帰後の撮影を終えました。Codexが確認した受信完了カウンタでは約30fpsでした。

ただし、これは画面表示のfpsや、前日のPipeWire経由の平均fpsとは測る場所が異なります。同じ30fpsという表記でも、どこの値かを揃えないと比較になりません。

復元の保護も実機で確認しました。設定へ意図的な変更を加えると、復元処理は書き込み前に止まり、その変更だけを戻せば通常復元できました。起動準備の中間状態からの安全な取消も試しました。

最後に元OSへ戻し、Codexが1,104パッケージ、188の設定対象、32MiBの起動領域を照合しました。ディスク全体がbyte単位で同一だという意味ではなく、復元対象を決めて一致を確認した結果です。

残ったのは背面映像の暗さでした。15fpsに落とせば露光時間を増やせますが、私は30fpsを維持したいと考えました。

そこでCodexと、露光制御とフレームレートを保ち、gammaを2.2から2.4へ変える案を比較しました。入ってくる光を増やすのではなく、中間調を明るく見せる調整です。

Codexが7条件、2,880requestの数値を確認し、私が元の設定と変更後の見え方を比べました。

変更後の方がほどよく明るく、見やすく感じました。元の設定へ戻して比較しても、その印象は変わりませんでした。前面に大きな異常がないことも確認し、この調整を採用しました。

ここまで大量のコードと試験を使ってきましたが、最後の見やすさは数値だけでは決まりませんでした。Codexが測定値を確かめ、私が実際の見え方を判断します。画質の評価では、どちらか片方だけでは足りないと感じました。

公開したはずなのに 文書の読者がまだ私でした

明るさの調整はPRにまとめ、署名したAPKと配布候補も作りました。設定以外の実行ファイルが変わっていないことまで確認し、朝にはPRも取り込まれました。

ただし、この時点では新しいAPK一式を使った実機への導入・復元をまだ試しておらず、v1.0.2は下書きの配布候補でした。PRのマージと正式Releaseの公開は別です。

これで一区切りと思いましたが、GitHubのmainにある文書を読み直すと、またCodexを呼び止めることになりました。READMEや技術文書、PR本文のあちこちに、私とCodexの作業のための文章が入り込んでいたのです。

世界へ公開したはずなのに、文章の読者はまだ私ひとりでした。

古い実験版を現在の候補として説明する箇所もありました。初めて読む人には、何が使えて、何が未確認なのかが伝わりにくい状態です。コードを人に渡す段階になっても、私たちの会話がそのまま説明書になってしまう問題が残っていました。

ここからの整理には6.1 Sol Highを使いました。公開文書53件とJSON45件を見直し、導入方法、検証した版と結果、既知の制限を中心に整えました。

これは監査した数で、98件すべてを書き換えたという意味ではありません。実行コードや元のライセンスは変えず、失敗や未確認事項も残しています。

文書の修正だけでは、開発中のコメントが残った公開履歴まで整理できません。

旧Git、PRやIssueの本文、正式版と下書き版の全12添付物を手元に保全したうえで、GitHubを同じURLに作り直しました。公開履歴は、整理したソースの初回コミット一件から出直しました。

10月8日の朝には、明るさ調整を含むソースを公開し直せました。この時点では配布Releaseがなく、以前のv1.0.0、v1.0.1やIssue、PRは旧リポジトリの履歴になりました。

そして夜、まだ一つ足りないものに気づきます。

最後に、配布物がありませんでした

これで公開作業も一区切りと思ったのですが、夜になって肝心の配布物がないことに気づきました。Codexは最初、下書きReleaseに置いてあるため見つけにくいのだと説明しました。

しかし確認し直すと、朝にリポジトリを作り直したあと、Releaseそのものをまだ置き直していませんでした。ソースと文書は整ったのに、ダウンロードするものがありません。

最後まで、ソースの公開と配布物の公開は別々に確認する必要がありました。

ここからは6.1 Sol Highで仕上げました。Codexが文書整理後のソースから配布アーカイブを作り直し、署名済みAPKは検証済みのものと同じデータを再利用しました。

古いアーカイブをそのまま戻したわけではなく、公開中の初回コミットに対応する文書とソースを収録しています。この作業でドライバを追加修正したり、新しくビルドし直したりはしていません。

全364項目の対応を確かめ、展開後のホスト試験を通常実行と最適化実行で計72回通し、公開先から認証なしで取得して内容を照合しました。

21時55分、v1.0.2をプレリリースとして公開しました。この時点では、新しいAPK一式の実機への導入と復元が未確認だったためです。

自分で導入して、正式リリースへ

その後は私が、公開したINSTALL.ja.mdに沿って手動でインストールしました。22時08分には導入できたことを確認し、正式リリースへ進めることにしました。

開発環境で動いていたものを、公開手順から自分で入れられたことで、配布までの流れがようやくつながりました。

22時12分、Codexが同じv1.0.2を正式版・Latestへ変更しました。タグや添付ファイルの中身は変えず、導入結果と残る制限をRelease本文へ追記しています。

新しいAPK一式の導入はできましたが、この版での復元はまだ確認していません。以前の版で行った復元試験とは分けて記録しています。

今度のIssue #1は、動画録画のカクつき

導入後にSnapshotで動画を録画すると、かなりカクつきました。録画中のプレビューだけでなく、保存した動画を再生しても動きがカクつきます。

静止画やライブ表示が使えることと、滑らかな動画を保存できることも、別の到達点でした。

Codexには、原因候補と調べるべき項目を整理してIssueへ登録してもらいました。

Software ISPによるCPU上の画像処理に録画の負荷が加わること、SCPとの競合を避ける構成がハードウェア動画処理の利用を制限していること、フレームを受け渡す間隔の乱れなどが候補です。

ただし、実際のエンコーダーやCPU負荷、動画のフレーム間隔はまだ測っておらず、原因は確定していません。

この問題は再作成後のリポジトリのIssue #1になりました。前に登場した設定復元のIssue #1は旧リポジトリのもので、今回は番号が同じだけの別の問題です。

公開後の管理へ

最後に、GitHubのAdvanced Securityについても相談しました。Codexが現在の設定を調べ、すでに有効なSecret scanningとPush protectionに加え、CodeQLによる解析や、脆弱性を非公開で受け付ける仕組みを使う案を整理しました。

この時点では設定の確認と相談までで、新たな解析を実行したわけではありません。こうした機能だけで、ドライバや配布物全体の安全性を証明できるわけでもありません。

10月8日夜の時点では、v1.0.2を正式版として取得できます。手動導入はでき、録画のカクつきは未解決のIssueとして残りました。

ここからは、新しい機能を一気に増やすより、使って見つかった不具合と、まだ確認できていない範囲へ対応していきます。

6.1 Solへ替えて、どうだったか

私の体感として、Codexの主実装を6.1 Sol Highへ移せたことは大きかったです。消費への心理的な負担が軽くなり、実機試験を任せやすくなりました。

通常Snapshotへの接続、背面対応、前後切替、30fps化、配布物の整備、公開後の不具合修正まで、CodexはSolを使って主担当の仕事を進めました。安い補助役にしか使えない、という印象ではありませんでした。

一方で、切替直後の10月2日までは、復旧機構の作り込みと細かな停止が続いていました。保存容量の見落とし、復旧条件の不整合、映像バッファの初期化など、Solを使っていてもCodexは誤りました。

10月3日に大きく進んだときは、モデルだけでなく、実験の回し方、完成条件、接続経路、標準libcameraの利用も変わっていました。

Astraも最後まで使いました。独立レビュー、公開前の点検、実機受入や明るさ調整などではUltraの記録があります。

全部をSolに置き換えた経緯でも、Ultraが不要だと分かった実験でもありません。Codexに任せる役割と場面に合わせて、両方を使っていました。

モデル 推論設定 トークン数
圧縮前の設定不明 不明 21,449,924
自動承認レビュー Low 123,800,111
Astra High 413,316,508
Astra Medium 38,596,745
Astra Ultra 1,250,547,800
6.1 Sol High 693,779,334

この開発だけから、UltraよりHighが優秀、Solなら同じ仕事が何倍速いとは一般化できません。切替時点で蓄積したソースと知見が違いますし、仕事の区切り方も変わっています。

私にとって確かだったのは、新モデルの登場で主実装を任せる選択肢が増えたことと、モデルを選ぶだけでは進め方の問題は解決しなかったことです。

私のまとめ:想像していなかったことができました

途中ではクレジットが何度も切れ、そのたびに作業が止まりました。再開してもすぐに完成へ近づくとは限らず、Codexと進め方を何度も話し直しています。

画面に進捗がたくさん並んでいるのに、肝心のカメラが普通に使えない時期は、なかなかもどかしかったです。

それでも、自分がデバイスドライバの開発に取り組み、実機でカメラを動かして公開することになるとは、以前は想像もしていませんでした。

既存のドライバやライブラリを調べ、それらをつなぐコードを書き、実機の反応を見ながら直していく。この作業をCodexと進められたことには、改めてAIを活用する凄みを感じます。

一方で、動いたからといって、中身をすべて理解できたわけではありません。ハードウェア制御やカーネルに近い処理、メモリの扱い、停止と復旧の条件、そして脆弱性がないかという点まで、自分で十分に見抜ける自信はまだありません。

Codexが書いたコードをCodexがレビューし、試験が通ったとしても、それだけで安全だと言い切ることはできないと思っています。

できることが大きく増えた実感と、自分が理解し切れていないものを公開する難しさ。その両方を感じた開発でした。

まずは公開までたどり着けたことを一区切りにして、録画のカクつきなどのIssueやレビューに向き合い、確認できる範囲を少しずつ広げていきたいです。

Codexからの振り返り

Codex側から振り返ると、今回の反省は、作業量を増やすことと完成へ近づくことを何度も取り違えた点です。

画像を受け取り、安全に止め、標準のアプリへ渡すための実装は必要でした。しかし、その周りの診断、保存、復旧、独自の画像処理まで広げた結果、利用者が欲しかった画面に届くのを遅らせました。

普通のアプリで使いたい、標準のlibcameraを使いたい、簡単に導入して戻せる形にしたい。そうした指摘のたびに、Codexは作業の優先順位を組み直しています。

本来は、これらを最初から完成条件に据え、そこへつながる最小の経路を先に確かめるべきでした。後半に進みやすくなったのは、新しいモデルだけでなく、開発の順番を修正できたからでもあります。

試験の作り方にも反省が残りました。実機で起こらない状態を用意してテストを通し、正常な実機の状態を拒否するチェックを書いた場面があります。

Codexが実装と試験の両方を担当できることは便利ですが、同じ思い込みが両方へ入ることもあります。試験の数だけでなく、実機の事実や独立したレビューと食い違っていないかを確認する必要がありました。

公開文書と配布物も、機能の外側にある付け足しではありませんでした。開発中の作業メモを残したまま公開し、リポジトリを整え直した後には配布物を置き忘れました。

最後に使う人が、説明を読んで入手し、導入し、問題があれば報告できるところまでを一つの仕事として扱うべきでした。

Codexが提供できた価値は、ひとりでは着手しにくい調査や実装を、実機で試せる具体的な形へ進めたことだと考えます。

その価値を、正しさや安全性の保証と混同してはいけません。今後は、新しいコードを増やす速さよりも、未確認事項を明確にし、再現条件を揃え、指摘を確実に修正することが大切です。

ソース、導入手順、配布物はlenovo-duet-linux-cameraのGitHubリポジトリにあります。今回公開した版はv1.0.2です。

 

 


  ではでは。またの機会に。  

SSHでno matching host key type found. Their offer: ssh-rsa」と言われたら

ども。こんばんは。

macOS 13 Venturaにアップデートしてから、一部のサーバやネットワーク機器へのSSH接続で以下のようなエラーが出るようになりました。

Unable to negotiate with [IPアドレス] port 22: no matching host key type found. Their offer: ssh-rsa,ssh-dss

どうやらssh-rsaとかもうやめようぜ!っていう流れらしいです。が、そうも行かないので、以下参考記事と同じ要領で、~/.ssh/configに追記します。
※公開鍵認証を使ってない場合は、PubkeyAcceptedAlgorithmsは不要。

host IPアドレスorホスト名
HostKeyAlgorithms=+ssh-rsa
PubkeyAcceptedAlgorithms=+ssh-rsa

参考:SSHで「no matching key exchange method found. Their offer: diffie-hellman-group1-sha1」と言われたら

ではでは。またの機会に。

新しいラズパイを買った(Raspberry Pi 4 Model B 8GBモデル)

ども。こんばんは。

先日からRaspberry Pi 3 Model BのSDが不調に陥ってまして、また壊れました。

で、たまたまAmazonでタイムセールをやっていたので買ってしまいました。

TRASKIT Raspberry Pi 4 Model B /ラズベリーパイ4B(8GB RAM)技適マーク付/MicroSDHCカード128GB NOOBSプリインストール/簡単に取り付けるケース/5.1V/3A Type-C スイッチ付電源/2つのMicroHDMI-to-HDMIケーブルライン/3つヒートシンクと2つの透明静音冷却

¥3,000引きで¥16,800なり。

ファン付きのケースやSDカード、USB Type-Cケーブルなど諸々付属です。自分でこの辺選ぶのも楽しみではありますが、面倒くさいので今回はキットを購入しました。

今回購入したのはメモリ8GBモデルです。

付属品はこんな感じ。

  • 技適マーク付きのラズベリーパイ4B(8GB RAM)
  • 128GB(Class10)のmicroSDカード ※Sandiskだった
  • カードリーダー
  • 5.1V3.0AのUSB Type-Cホワイト電源アダプター
  • スイッチ付きの電源ケーブル
  • MicroHDMI-to-HDMIケーブル 2本
  • 透明ケース
  • ヒートシンク 3つ
  • 冷却ファン(LED*1、LEDなし*1)
  • GPIOリファレンスカード、日本語の取扱説明書など

microSDカードはSanDiskのものでした。

マニュアルは日本語です。

本体お目見え。

まずはヒートシンクを取り付けます。

ケースはこんな感じ。結構いい感じじゃないかな。

ケースにファンを付けます。片方はLED付きだということに後で気づきました。
あと、商品紹介画像とは多分つける向きが逆ですがこっちが正しい・・はず。

ケースに収めていきます。

冷却ファンは、GPIOのに接続します。接続するピン(3.3V or 5V)によって回転数が変わります。

ACアダプタはこんな感じ。PSEマークもありますね。

動作確認中。

このディスプレイはこれ。

俺はVGAを少し甘く見ていたのかもしれない ― 小型モバイルディスプレイ購入

USBケーブルにはスイッチがついてます。これは便利。

あー、光ってるわー。

初期ではNOOBSが入っています。

とりあえず今回は64bit版を入れたかったので、公式サイトからダウンロードします。
※Raspberry Pi Imagerでは64bit版Raspberry pi osは選択できません。

ダウンロードしたやつをRaspberry Pi Imagerでやきます。

ここからはまぁ適当に。

ちなみに、やっぱりRaspberry Pi 3 Model BもSDが壊れただけでなんかもったいないなーと思ったので結局SDカードを買い直しました。

サンディスク microSD 128GB UHS-I Class10 Nintendo Switch メーカー動作確認済 SanDisk Ultra SDSQUA4-128G-EPK エコパッケージ

¥2,040なり。

あれ、これ今回かったキットと同じじゃね?

同じようにRaspberry Pi 3 Model Bにも64bit版のRaspberry Pi OSを入れました。無事起動。

ではでは。またの機会に。

久々にRaspberry Piをアップデートしたらぶっ壊れたので作り直し(2021年Raspberry Pi インストール)

ども。こんばんは。

我が家のRaspberry Pi 3 Model B Rev 1.2ですが、たまにはアップデートしようとおもってapt updateやらapt full-upgrade的なことをやってましたら、途中でエラーになりまして。
※最近はapt-getじゃなくてaptが推奨らしいですね。

まぁとりあえず再起動でもするかーとやってみたところ・・・

SQUASHFS error:Unable to read page…といったエラーが大量にorz

んー、SDカード壊れたのか、単に本当にファイルシステムが壊れただけなのかはいまいちわからないものの、まぁしばらく使ってなかったし、1から入れ直してみることにしました。

SDカードの故障ならインストール中にエラーになるでしょ、っていう感じです。

ということで久々にRaspberry PIネタで、初期設定周りの話です。

さて、最近はRaspbianじゃなくて正式にはRaspberry Pi OSというそうですが、随分入れやすくなりましたね。

以前はNOOBSをインストールしてそこからでしたが、最近は、Raspberry Pi Imagerという公式ツールでかんたんにイメージが作れます。

https://www.raspberrypi.org/software/

早速macOS版をインストールしてみました。

シンプルなUIで、OSと書き込み先のSDカードを選ぶだけでイメージが作れます

OSはおすすめのRaspberry Pi OS (32-bit) with desktop and recommended softwareを選びました。

SDカードはUSBのカードリーダーに挿したものです。
※一応先にアンマウントはしています。

あとは書き込みを押すだけ。かんたんです。

書き込みが終わったら早速Raspberry Piに差し込んで起動します。

おー。ちゃんと立ち上がった。SDカードは無事だったってことかな・・・。

このままウィザードで以下を設定します。(画像は省略)

  • 国・言語・タイムゾーン
  • piユーザのパスワード
  • スクリーンの調整
  • WiFiの設定

なお、これらは最低でもモニタとキーボードが必要です。(マウスはなくてもTABキーなどを駆使すればなんとかなります。)
初っ端からWiFiとSSHを有効にしてネットワーク経由で設定する場合は以下のサイトのやり方が参考になります。

Raspberry Pi 初期設定 – Qiita

基本的な設定が終わったので、そのままアップデートします。
そこそこ時間がかかります。25分くらいかかりました。

最新になったのでrestartします。

再起動後、ノリでrpi-updateをしてしまったのですが、やらないほうがいいそうです。

理由は以下のサイトに詳しく載っていますが、要するに「まってりゃaptで安定版が提供されるから、人柱になりたくないならやらないでね」って感じです。

おいらはてっきりチップセット?とかのファームウェアの更新だと思いこんでやってましたが、普通にパッケージの更新だったんですね。。。

まぁやってしまったのでしょうがないですが次回以降気をつけよう。

やらないで!! rpi-update | ゆっくり遅報

さて、あとはモニタとキーボードでやることはraspi-configでのSSH有効化です。Interfaceのところにあります。

これ以降はSSHで行います。

割と好みによるところも多いですが、おいらは以下の感じで設定しています。

【raspi-config】

・GUIの起動と自動ログインを無効化。

・ホスト名

・I2Cの有効化(LCDモジュールを使うため)

・パーティションの拡張

一旦再起動。

無事パーティションも拡張されました。

最後に、OS起動中はLEDにピカピカしてほしいので、/boot/config.txtの末尾に以下の設定を追加しています。

dtparam=pwr_led_trigger=heartbeat

そんなところですかね。

過去の自分の記事を見ていると、新しいユーザを作ったり、piユーザからsudoを剥奪したりと色々やってますが、今回は面倒臭いのでやらないです・・・。

参考:いまさらはじめるRaspberry Pi(2017) – 2 –

とりあえず今の所動いているのでいいかな。

あー、色々消えたスクリプトとか復活させるかぁ。。

ではでは。またの機会に。

 

 

いつものノリでkernelをアップデートしたらエライ目にあった話

ども。こんばんは。

いつものノリでAWS EC2上のCentOS 7.9.2009のカーネルをyumで「kernel-3.10.0-1160.11.1.el7.x86_64」にアップデートしたら、Kernel Panicになりました。。

EC2のトラブルシュートからシステムログを見るとこんな感じ。

Kernel panic – not syncing: VFS: Unable to mount root fs on unknown-block(0,0)

お、おう。

クラウドでカーネルパニックになるって地味に大変だぞ。。。

ということで、結論は、

インスタンス停止
ボリュームをデタッチ
新しいCentOS 7のインスタンスを作成
ボリュームをアタッチ
マウントしてなおす

という感じです。

以下の手順に従っても直りませんでした。

カーネルをアップグレードした後、または EC2 Linux インスタンスを再起動しようとすると、「カーネルパニック」エラーが表示されます。どうすれば解決できますか?

で、結局古いカーネルで上がるようにしました。

更新後、Amazon EC2 インスタンスの再起動に失敗します。既知の安定したカーネルに戻すにはどうすればよいですか。

/etc/grub.confの中身はいまこんな感じ。

  1 default=1
  2 timeout=0
  3 
  4 
  5 title CentOS Linux (3.10.0-1160.11.1.el7.x86_64) 7 (Core)
  6     root (hd0)
  7     kernel /boot/vmlinuz-3.10.0-1160.11.1.el7.x86_64 ro root=UUID=29342a0b-e    20f-4676-9ecf-dfdf02ef6683 console=hvc0 LANG=ja_JP.UTF-8
  8 title CentOS Linux (3.10.0-1160.6.1.el7.x86_64) 7 (Core)
  9     root (hd0)
 10     kernel /boot/vmlinuz-3.10.0-1160.6.1.el7.x86_64 ro root=UUID=29342a0b-e2    0f-4676-9ecf-dfdf02ef6683 console=hvc0 LANG=ja_JP.UTF-8
 11     initrd /boot/initramfs-3.10.0-1160.6.1.el7.x86_64.img
 12 title CentOS Linux (3.10.0-1127.19.1.el7.x86_64) 7 (Core)
(略)

いやー、びっくりしたわ。。

ではでは。またの機会に。

codey rocky(ESP32)でMicroPythonを動かす(2) – MQTTでメッセージの送受信

ども。こんばんは。

codey rockyではメッセージキューWとしてMQTTWが実装されています。

せっかくなのでちょっと試してみます。

MQTTについては少し古いですがこの辺がわかりやすいです。

MQTTで始めるIoTデバイスの作り方 第1回:「MQTT」を知り「Mosquitto」を導入する (1/3)

まずはMQTTのブローカーとしてmosquittoをRaspberry Pi上にインストールします。
※そのうちRabbit MQも試したいなと思いますがまずは手軽そうなmosquittoで試します。

ブローカーの動作環境はRaspbian GNU/Linux 9.11 (stretch)で、2020/02/16時点最新のパッケージになっています。

・mosquittoのインストール

sudo apt install mosquitto

うちの環境ではlibev4とlibwebsockets8も合わせてインストールされました。

・続いてクライアントもインストールします。

sudo apt install mosquitto-clients

libmosquitto1が合わせてインストールされました。

・ブローカーの起動

sudo systemctl start mosquitto

sudo systemctl status mosquittoで確認して特に問題なく起動していました。
1883ポートでLISTENしているみたいですね。

・テスト

以下のコマンドでtestというトピックを購読(サブスクライブ)します。

mosquitto_sub -d -t test

実行結果はこんな感じ。
※lexはうちのラズパイのホスト名です。

Client mosqsub/6162-lex sending CONNECT
Client mosqsub/6162-lex received CONNACK
Client mosqsub/6162-lex sending SUBSCRIBE (Mid: 1, Topic: test, QoS: 0)
Client mosqsub/6162-lex received SUBACK
Subscribed (mid: 1): 0

・テストメッセージ送信

別のターミナルでSSHに接続してtestトピックに投稿(publish)します。

mosquitto_pub -d -t test -m “test 1”

すると、サブスクライバー側で以下のように表示されます。

Client mosqsub/6162-lex received PUBLISH (d0, q0, r0, m0, ‘test’, … (6 bytes))

test 1

これで最低限の動作はokですね。

続いてcodey側にこんなコードをアップしてみました。
以下のリファレンスからサンプルコードを流用していますが、リファレンス内のサンプルコードの「from mqtt import MQTTClient」は誤りで、正しくは「from cloud_message.mqtt import MQTTClient 」です。

https://makeblock-micropython-api.readthedocs.io/en/latest/public_library/Third-party-libraries/mqtt.html

■サンプルコード

#import mqtt package
#リファレンスは"from mqtt import MQTTClient"となっているが実際はcloud.message.mqttが正しい
#https://forum.makeblock.com/t/importerror-no-module-named-mqtt/15029
from cloud_message.mqtt import MQTTClient

import codey, time, event
import utime

MQTTHOST = "mosquittoのサーバのIPアドレス"
MQTTPORT = 1883

#なんでもいいらしいが重複はだめっぽい。
#https://qiita.com/egnr-in-6matroom/items/3aef4bd45857e75bb1d3
client_id = "101"

# Example Path
#ここの指定方法がまだわからない・・・。全部取得してみる。
Topic = "#"

# ID / PWはオプション。今回はmosquitto側では指定していないので省略した。
#mqttClient = MQTTClient(client_id, MQTTHOST, port=MQTTPORT, user='test', password='test', keepalive=0, ssl=False)
mqttClient = MQTTClient(client_id, MQTTHOST, port=MQTTPORT, keepalive=0, ssl=False)

# Connect to the MQTT server
def on_mqtt_connect():
    mqttClient.connect()

# publish a message
def on_publish(topic, payload, retain=False, qos = 0):
    mqttClient.publish(topic, payload, retain, qos)

# message processing function
def on_message_come(topic, msg):
    #print(topic + " " + ":" + str(msg))
    #b'test'みたいな表示になるので、文字列化して分割。なおメッセージ自体に'(シングルクォーテーション)がある場合は""で囲まれるが未対応
    temp_msg = str(msg).split("'")[1]
    codey.display.show(temp_msg,wait=True)

# subscribe message
def on_subscribe():
    mqttClient.set_callback(on_message_come)
    mqttClient.subscribe(Topic, qos = 1)

@event.button_a_pressed
def on_button_a_pressed():
    codey.display.show("Start",wait=True)
    codey.wifi.start('SSID', 'パスワード', codey.wifi.STA)
    time.sleep(1)
    if codey.wifi.is_connected():
        codey.emotion.smile()
        on_mqtt_connect()
        on_subscribe()
        codey.display.show("Ready!",wait=True)
        while True:
            #RTCがついてないので起動からの時間となる。
            #nowtime = utime.time()
            #codey.display.show(nowtime,wait=True)
            #codey.display.show(codey.battery.get_percentage(),wait=True)
            # Blocking wait for message
            mqttClient.wait_msg()
            #ブロッキングしない方
            #mqttClient.check_msg()
            time.sleep(1)

    else:
        codey.emotion.shiver()


@event.button_b_pressed
def on_button_b_pressed():
    codey.display.show("Stopped",wait=True)
    codey.stop_all_scripts()

これをcodeyにアップロードすると、パブリッシュされたメッセージを表示するようになります。

だいぶ前に作ったbrouteから消費電力を取得するスクリプトを少し改変して、取得した結果を単純にmosquitto_pub -d -t test -m 値で投稿(publish)するようにしました。
#python上からosコマンドを実行しています。こういう移植性のないことしちゃだめですね。ちゃんとmqttをpythonから操作するように変更しないと・・・

動作の様子はこんな感じです。
エアコンを入れると消費電力がちゃんと上がってますね。

 

本当はメッセージが無い間は、時計にでもしようと思ったのですが、odey rockyにはRTCが搭載されていないようで、utime.time()で取得した値は、電源投入後からの起動時間(秒)となります。

mqttClient.wait_msg()はブロックされますが、mqttClient.check_msg() はブロックされないので、まぁバッテリーの残量を出すとかそういう感じのことはできそうですね。

あと、MQTTのトピックの指定の仕方がよくわかりませんでしたので”#”としています。一応トピックは「test」で来てるみたいですが、「/test/」とかにしてもうまく拾えないんですよね。
これはもうちょっと調べないと。

次は赤外線周りをなんかやってみようかな。

【バックナンバー】

ではでは。またの機会に。

久々にRaspberry PIのパッケージを更新したらwpa_supplicantがどっかいった話

ども。こんばんは。

久々にうちのラズパイ(Raspberry Pi 3 Model B)のパッケージを更新しました。
※raspbianのバージョンは9.1 Stretchです。ディストリビューションはそのままでパッケージの更新だけした感じです。

普通にapt-get updateしてapt-get upgradeしただけなのになぜか無線がつながらない・・・。

結構悩みました。wpa_supplicantがうまく動いてない様子。

# systemctl status wpa_supplicant.service
● wpa_supplicant.service – WPA supplicant
Loaded: loaded (/lib/systemd/system/wpa_supplicant.service; enabled; vendor p
Active: failed (Result: exit-code) since Fri 2016-11-04 02:16:45 JST; 38s ago
Process: 376 ExecStart=/sbin/wpa_supplicant -u -s -O /run/wpa_supplicant (code
Main PID: 376 (code=exited, status=203/EXEC)

11月 04 02:16:45 lex systemd[1]: Starting WPA supplicant…
11月 04 02:16:45 lex systemd[1]: wpa_supplicant.service: Main process exited, cod
11月 04 02:16:45 lex systemd[1]: Failed to start WPA supplicant.
11月 04 02:16:45 lex systemd[1]: wpa_supplicant.service: Unit entered failed stat
11月 04 02:16:45 lex systemd[1]: wpa_supplicant.service: Failed with result ‘exit

これ、結論から言うと、wpa_supplicantの実行ファイルが何故か消えてました。

# ls /sbin/wpa_supplicant
ls: ‘/sbin/wpa_supplicant’ にアクセスできません: そのようなファイルやディレクトリはありません

ただ、wpasupplicantパッケージ自体はapt上に存在している模様でしたので、apt-get remove wpasupplicantしてinstallしたら復活しました。

一体何だったんだろう・・・。

特に設定とかは変えてませんが、直ったあとに、wpa_passphraseコマンドでPSKを暗号化しておきました。

ではでは。またの機会に。

 

自分の尻拭いプロジェクト – 2 – Zabbix 4.4をCentOS 8(8.0.1905 (Core))にインストールする

ども。こんばんは。

気づけば年末年始の休暇もあと1日となりました。

この年末年始の最後に、やかして吹き飛ばした旧Zabbixの代わりに、新しいZabbixを構築します。

せっかくなのでCentOS 8にインストールします。

以下、とりあえずzabbixがうごいたやったーくらいの手順です。
SELinuxやfirewalldもノリで無効化しています。

apacheの設定周りとかもデフォルトのままです。

■CentOS 8.0.1905のインストール

VMWare ESXi 6.7.0U3上に仮想マシンを作成してDVD ISOファイルをもとにインストールします。
インストーラーは7とあんまり変わらないです。

とりあえず必要なものは後で入れるので、サーバにしました。
※あとでランレベルは3にします。

とりあえず入りました。

あれ、ifconfigとかnetstatとかそのまま使えちゃうのね。ipとかssとかせっかく覚えかけてたのに。

■CentOS 8 初期設定(1) ランベルを3にしたい

昔の人なので、inittabを探し回ってましたが、今はないようですので、以下のコマンドで変更します。
※CentOS 7系からこれのようですね。おじさんなので、inittabとかinit 3とかrunlevelな人ですorz

# systemctl set-default multi-user.target

以下確認コマンド

# systemctl get-default

■CentOS 8 初期設定(2) SELinuxを無効にする

無効にします。が、やらかしてカーネルパニックになります。
※今でもカーネルパニックっていうのかな。

Failed to load SELinux policy, freezing.

昔ながらのやらかして、/etc/sysconfig/selinuxの書き間違いです。

なので、シングルユーザで入って直します・・・が、シングルユーザモードの入り方がわからない。カーネルパラメータに–singleとか追加して起動すればいいんでしょ?と思ってたらこれも違っていた。。。

4行目のroを消してrw init=/sysroot/bin/shに書き換えてctrl+xで起動します。

はいれました。chroot /sysrootしてもいいですし、今回は/etc/selinuxを直すだけなので直接編集しました。

おバカですねぇ。disabledはそっちじゃない。(このミスはCentOS 5の頃からやらかしている気がしますね)

こんな感じに直してrebootして無事起動しました。

■CentOS 8 初期設定(3) その他初期設定

firewalldの無効化とSSHのrootログインの無効化をしました。
このあたりは特に特殊なことはなかったので割愛。

■(未解決)SSH経由での操作のレスポンスが悪い

ぱっと解決できなかったので放置していますが、ログイン後の操作のレスポンスがとても悪いです。エンター押すだけでもちょっと待たされます。VMWareのコンソール上だとそんなに遅く感じないのでSSH関連かtty関連のなにかかなー。ちょっとイライラします。

これはおいおい調査します。

■CentOS 8 初期設定(4) アップデート

CentOS 8からはyumが廃止されdnfになりました。
※pythonを3系に完全移行するためにdnfに変えたらしいです。

なおyumコマンドはdnfのラッパーとして残っていますのでyum updateでもdnf updateと同義になります。

とりあえずOSはこんな感じでしょうか。

■ZABBIX 4.4のインストール

以下のURLに従います。

https://www.zabbix.com/jp/download

うちは今回、4.4 / CentOS 8 / MySQL / Apache の構成としました。

なお、コマンドどおりに実行してもうまく行かない点がありましたのでその点は補足します。

まず先にmysql-serverとmysqlのインストールが必要です。
※サーバとクライアント。普通にdnf install mysql mysql-serverとかでいいです。自動起動とか忘れずに。

上記サイトに従ってコマンドを実行してきます。

・「a. Install Zabbix repository」はそのまま。

# rpm -Uvh https://repo.zabbix.com/zabbix/4.4/rhel/8/x86_64/zabbix-release-4.4-1.el8.noarch.rpm
# dnf clean all

・「b. Install Zabbix server, frontend, agent」もそのまま。

# dnf -y install zabbix-server-mysql zabbix-web-mysql zabbix-apache-conf zabbix-agent

・「c. Create initial database」は案内されたgrant文がmysql 8系では通りません。

# mysql -uroot -p
password
mysql> create database zabbix character set utf8 collate utf8_bin;
mysql> grant all privileges on zabbix.* to zabbix@localhost identified by ‘password’;
mysql> quit;

MySQL 8系ではGRANTと同時にユーザを作れなくなったようで、以下のようなエラーになります。

mysql> grant all privileges on zabbix.* to zabbix@localhost identified by ‘password’;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ‘identified by ‘password” at line 1

なので、ユーザ作成とGRANTをバラバラに実行します。
※passwordのところにはパスワードを設定します。
(“mysql> create database zabbix character set utf8 collate utf8_bin;”は実行できるはず。)

mysql> create user zabbix@localhost identified by ‘password’;
mysql> grant all privileges on zabbix.* to zabbix@localhost;

以下のコマンドも普通に流せました。

# zcat /usr/share/doc/zabbix-server-mysql*/create.sql.gz | mysql -uzabbix -p zabbix

あとは、サイトの記載どおりです。

とりあえずコアスイッチだけぽいっと追加しましたがうまく動いてそうです。

【バックナンバー】

ではでは。またの機会に。

自分の尻拭いプロジェクト – 1 –

ども。こんばんは。

先日のオペミスで仮想マシンを全台虚空に返したわけですが、昨日(2019/12/30)午後〜夜まで時間をかけてなんとか復旧しました。

結局ほぼ元通りの構成で面白みはないですが、バージョンがだいぶ新しくなりました。

  • AD(Windows Server 2008 R2)
    • Windows Server 2012 R2をインストールして再構築。
      ※手持ちのライセンスが2012 R2しかないので・・・。
    • NPS(RADIUS)の構築までサクッとできた。
      • ちなみにAD DSはインストールしていたけど、証明機関のをインストールしておらずPEAPが使えなくてハマった。
        証明機関さえインストールすれば解決できる。
        (旧ADはIISの証明書とかも発行してたので証明機関入ってたみたい。)
    • MacBook Airは一旦Active Directoryのバインド解除して再度バインドした。無事AD上のコンピュータに表示されたし新しい証明書もインストールされているので多分大丈夫だと思う。。。プロファイルも消えなかった。
    • 過去もハマったけど、評価版にそのままライセンス入れようとして、「このエディションのWindowsのライセンス認証にそのキーは使えません。別のキーを試してください」とか言われたので以下のコマンドでキーを登録。

      DISM /online /Set-Edition:ServerDatacenter /ProductKey:xxxxx-xxxxx-xxxxx-xxxxx-xxxxx /AcceptEula

  • 大昔から動いているLinux(CentOS 5)
    • 正直作り直しは困難と判断・・・。DHCPの設定ファイルも抜き出したかったし。思い出はなかなか消せなかった。
    • あと、基本DNSがこいつで、ADもDNSはこいつを参照/更新している関係で意外と大事な役割になっている。
      (今回もADにはDNSサーバをインストールせずに作ってしまった・・・。)
    • Acronisで取得したバックアップ(tibファイル)から復元 
      • vSphere Converter Standaloneでは6.0までしかサードパーティのイメージ変換機能が実装されておらず、手元の5.0でtibをvmdkに変換・・・するも失敗。
      • しょうがないのでAcronis TrueImageのブータブルCDで頑張って復元。
      • vSphere 6.7だと、マウスが動かない。
        TABキーとかの操作だとどうもうまく行かず、マウス機能(F10とか押すとテンキーでカーソルが動かせる)で頑張った。(わざわざMacBook Airにキーボード取り付けて・・・)
      • 一部どうしても見えないボタンがあったのでTABキーとか駆使してなんとか復元。結局これが一番うまくいくなー。(数年前にも同じことした気がする。)
  • PBX
    • FreePBX STABLE SNG7-PBX-64bit-1910(FreePBX 15/Linux 7.6/Asterisk 16)で再構築。
    • アナウンス音声の日本語版が付属していて嬉しい。
    • ひかり電話とのトランク、Cisco IP Phone 7961のレジスト、内線、外線発信/着信までまさか数時間で終わるとは思わなかった。
    • 特にIP PhoneのConfigが旧FreePBXサーバのtftpbootフォルダにしかおいてないと思い込んでいたのでもう一度Config作成は絶望かと半分あきらめていましたが、過去の自分がうまく動いたときの一連のパケットキャプチャを保存してくれていたので、パケットキャプチャの中身からtftpでやりとしていたファイルを抽出して復元できました。まぁファイルとしておいておいてくれれば言うことないのですが、自分の所業なので諦めます。(どうせパケットおいておけば最悪どうにかなるだろうとか当時考えたんだろうな。)
    • FreePBXのバージョンもだいぶ変わっていたけどCisco側は設定修正なしでいけた。日本語化まで数時間でできた。
  • 監視サーバ
    • まだ未構築・・・。年明けかな。

以下、電話関連(FreePBX)のメモ。

環境、バージョンは以下の通り。

ハイパバイザー:VMware vSphere 6.7.0U3
イメージ:FreePBX STABLE SNG7-PBX-64bit-1910(CentOS 7.6ベース)
FreePBX :15
Asterisk:16

今回は全部WebUIだけで完結しましたが、以下ハマったポイントを書いておきます。

なおSIPドライバーはPJSIPです。

  • FreePBXのIPアドレス変更
    • /etc/sysconfig/network-scripts/ifcfg-eth0を編集。
      ※CentOS 7系だけどNetworkManagerは使ってないみたい。
  • tftpサーバの起動
    • /etc/xinetd.d/tftpを編集
    • disableをnoにしてservice xinetd restartする。
  • Fail2Banの解除
    • なんかGUIからできなくなった?ぽいのでコマンドで
      service fail2ban stop
      /var/log/fail2ban.logをけす
      service fail2ban start
  •  FreePBXのモジュール更新
    • 量が多すぎて一度にアップグレードができなかった。少しずつ更新する必要あり。
  • TCPのリッスン(Cisco IP Phoneのため)
    • Asterisk SIP設定で、chan_pjsipの設定を変えばいいが、Apply Configだけでは反映されず、osごと再起動が必要だった(Asteriskの再起動でも良かったかも)。
  • Outboundルール(外線発信)
    • 以前は「0X」だけでマッチした気がしますが、今回はそれだとルールにマッチせず11桁と10桁それぞれ登録しました。
  • ひかり電話にRegistできているのに、外線発信、着信両方FreePBXまでこない。
    • 正直ここが一番ハマりましたが、Asterisk SIP設定のGeneral SiP SettingsのNAT設定の外部アドレスを自分自身のIPアドレスにして、再起動したらうまくいきました。
      ※我が家はFreePBXからひかり電話HGWまでの間はNATはしていません。
      デフォルトでは、へんてこなIPアドレス(104.145.12.102)が1設定されており、実際のSIPパケット見ているとViaとかContactとかにそのIPがセットされていました。

以下、最低限のひかり電話発着信のためにいじったところ
(WebUIは日本語にしていたので項目が日本語のところがあります。)
※DID/CID制御とかはしていません。やりたい方はVoIP-Info.jp Wikiが大変参考になります。

  • ひかり電話HGW側
    • 内線設定
    • ダイジェスト認証なし
    • MACアドレスはうちの場合だとL3スイッチになる
      ※最初SVIのMACアドレスにしてたけど、よく考えたらNAT関わるわけじゃないから普通に出ていくところのインターフェースのMACアドレスを登録。
  • 接続
    •  トランク
      • General
        • トランク名:適当
        • アウトバウンドCID:自分の外線番号
        • 最大チャネル数:1(うちは1本の契約)
      • ダイヤル番号の操作ルール
        • 未設定
      • PJSIP設定
        • General
          • ユーザ名:0003(HGWの内線番号の頭に0を3つ)
          • Language Code:日本語
          • SIP Server:ひかり電話HGWIPアドレス
        • 高度な設定
          • DTMF Mode:バンド内
          • From Domain:ひかり電話HGWIPアドレス
          • From User:3(HGWの内線番号。0なし)
          • クライアントURI:sip:3@ひかり電話HGWIPアドレス
          • なんとなくパケットみてこんなリクエストになってれば行けるはず。

            REGISTER sip:ひかり電話HGWIPアドレス SIP/2.0
            From: <sip:3@ひかり電話HGWIPアドレス>;(略)
            To: <sip:3@ひかり電話HGWIPアドレス>

    • インバウンドルート
      • 単純に全部着信回すなら気にしなくていい。
      • 宛先をセット:自分で作った着信グループ
    • アウトバンドルート
      • ルートCID:意味ないらしいけど一応外線番号
      • 内線を上書き:はい
      • 一致したルートのトランクシーケンス:ひかり電話とのトランク
    • Dial Patterns
      • こんなかんじで。(これだと110とか119とか104とかは発信できませんのでご注意。うちは発信するつもりはないのであえて携帯と固定電話だけトランクに流すようにしています。)

あら、書いてみると意外と設定すくないですね。

あ、書いてないですが内線は普通に設定しています。
といっても番号とSecretくらしか設定してないですが・・・。

あと、我が家専用ですが、Cisco側がTCP/5061でSIPをしゃべるのでPJSIPがTCPで5061を待ち受けるようにしています。
※過去のFreePBXではchan_ipでしかTCPが設定できずいろいろ悩んでポート変えていたみたいです。

今日は家の掃除もしたし、なんとか新年が迎えられます。

皆様良いお年を。

ではでは。またの機会に。

 

我ながら自分のバカさ加減に腹が立つ・・・。

ども。こんばんは。

派手にオペミスしました。

というか、してたことに数時間後に気づきました。

移行したばかりのVMのデータストア吹き飛ばしました\(^o^)/

そして、旧NASからも削除済みで、旧NAS上はすでにThikで新しいLUNを作ってしましましたので、もう復元できない\(^o^)/

10数年分のログと思い出が(´;ω;`)

まぁいい。これが仕事じゃなくてよかったと思うことにしよう。
そし旧環境から気持ちを一新するいい機会を得たと、新しいことにチャレンジする機会を得たということにしよう(´;ω;`)

結局、何をやらかしたか。

  1. 旧NAS上にバックアップ用にiSCSIのLUNを作る(←問題なし)
  2. 新NAS上で旧NASをiSCSIでマウントする(←IPアドレスを間違ってしまい自分を自分でをマウントしていた)
    タイムマシンがあったら過去に戻ってぶん殴りたい。
  3. iSCSIのLUNをEXT4でフォーマット(←大惨事発生、気づかずorz)
  4. 新NASでLUNのエクスポートジョブ開始(←更に問題をややこしくした。自らとどめを・・・)
  5. なんか仮想マシンの調子が悪いぞ?(←気づき)
  6. VMからデータストアがみえないなー(←軽い気持ち)
  7. LUNエクスポートジョブキャンセル(←これはまあありかな)
  8. データストア消失に気づく(←まだ希望があった)
  9. パーティションの作り直し方とか調べて色々する(←無意味)
  10. 作業中のスクリーンショットとかを見直す(←あっ)

\(^o^)/

まぁいつかはやめようと思ってたわけだし、OSも全体的にサポート切れだし、メールはオンプレExchangeからO365へ、DNSはCloudflareへ、ブログもAWSにそれぞれ移行は終わってるので、実害は少ないかも。

直前まで使っていた仮想マシンと消失により想定される影響はこんなところかな。

  • AD(Windows Server 2008 R2)
    • Exchangeはもう役目を終えている(無駄にo365とのハイブリッドのままだけど・・・)
    • ADのSyncしなくなったからo365側もちょっと設定見直そう。
    • NPS(RADIUSサーバ)が死んだので一部機器の無線LANが死ぬ
      →もう.1Xもやめようかと思うけど、FreeRADIUSで再構築かな・・・。やっぱAD作り直すか・・・。(手持ちが2012しかないけど。。。)
    • MacBook AIRのアカウントに影響でるかも?
      →これ一番まずいかも・・・。今一番怖い。
  • 大昔から動いているLinux(CentOS 5)
    • 最悪Acronisのバックアップから戻せる(今朝フルバックアップが取れてる)けど、どうするかな・・・。
    • LAN内のDNSのマスターだった(セカンダリは物理サーバなのでセーフ)
    • そういえばADもこのDNSサーバを更新しにいく方式だっけか。。
    • DHCPサーバがいなくなった
      • 一部MACアドレス固定で払い出している機器がある。。。
        地道にFortiGateに移行するか。。。
    • SYSLOGを受けていた
    • SNMP-TRAPをメールに変換して飛ばしてた。
    • LAN内の機器のSMTP中継サーバだった。
    • AWSのサーバのSSHキーをここにしかおいてない気がしたけど一応バックアップとってた
  • PBX
    • 電話死亡
    • クソ苦労して作ったIP PhoneのConfigがとんだと思う。。
    • DHCPサーバでtftpサーバのIPとか渡してたっけか・・・。
  • 監視サーバ
    • ふるーいzabbix。2.xとかそんくらいかも
    • トラフィックとか見るのに便利だったなぁ

実質4台が常時稼働だったけど、それぞれ思い入れというか、もう存在が日常なサーバたちだったので、実のところショック大。。。

まぁ年末年始を利用して色々作り直そう。

来年は良いことあるといいな。

ではでは。またの機会に。