Panda Noir

JavaScript の限界を究めるブログでした。最近はいろんな分野を幅広めに書いてます。

電子ペーパーで卓上カレンダーをつくる (googleカレンダー連携つき)

完成品

こんな感じのものができる。

Googleカレンダーから予定を取得して、8時間おきに画面を更新する。裏にraspberry piがついていて、これで画面表示を制御している。

用意するもの

あわせると15000円程度で揃えられる (ACアダプターやケーブルは家に余ってると思うのでノーカン)。

電子ペーパーディスプレイの選び方

電子ペーパーのみ選ぶとき注意が必要なので解説する。

今回購入したのはコレ↓。よく分からなければとりあえずコレでよい。

7.5inch E-Paper E-Ink Display HAT (B) For Raspberry Pi, 800×480, Red / Black / White, SPI

選ぶときの主な観点は2つ。

  • 画面サイズと解像度 (インチ数が同じでも解像度が異なるパターンがある)
  • いくつ色がつかえるか
    • 赤や黄色など、他の色も使えるか?
    • 白と黒の2色のみか? 灰色も使えるか?

サイズと色を決めたら、ドキュメント もチェックしておく。 ドキュメントにpythonのデモが入っているものを選ぶとよい。 C言語しかデモがないと大変だと思うので。

カレンダーの作成手順

全体の手順はこちら。各手順の詳細は後半に記載。

  1. raspberry piのセットアップをする
  2. 電子ペーパーとraspberry piを接続する
  3. デモプログラムを実行して動作確認をする
  4. カレンダープログラムを書く

1. raspberry piのセットアップをする

ここでは簡単な流れだけ書く。もっと詳しい記事やドキュメントが大量にあるので、わからなければ他の記事をあたってほしい。

  1. Raspberry Pi Imager をダウンロードしてインストールする
  2. Imagerでwifiやsshの設定をする
  3. Imagerを使ってmicroSDカードにOSを書き込む (色々あるがLite版でよい)
  4. raspberry piにmicroSDカードを入れて電源を接続する
  5. raspberry piが自動的に立ち上がる

起動が完了すると ssh [設定したユーザー名]@raspberrypi.local で接続ができるはず。できない場合はwifiの設定などが間違えている可能性がある。

2. 電子ペーパーとraspberry piを接続する

まず、ドライバーHAT(小さい青いパーツ)の黒い部分を立てる。これがロックになっている。 ロックを外したらディスプレイのオレンジの端子を挿入する。 ロックしたままだとかなり力を入れないと入らない。

小さい青いパーツと電子ペーパーを接続した様子

ドライバーHAT(青い部品)とraspberry piの接続は簡単だ。GPIOピンを差し込めば良い。 結構深く刺さって固定されたら完了。

GPIOピンで接続されている様子

3. デモプログラムを実行して動作確認をする

必要な各種ライブラリをインストールし、デモプログラムを動かしてみる。うまくいくとこのような画面になる。

デモプログラムが動くとこのようになる

注意として、インチ数、解像度、色が合致しているデモプログラムを選ばないと動かない。 デモプログラムのファイル名にはインチ数しか書いてないので、ドキュメントに記載されているデモファイル名をチェックしよう。

4. カレンダープログラムを書く

あとはカレンダープログラムを書けば完成だ。 カレンダープログラムはデモプログラムをもとに改造すれば自ずと書けるはず。 なので解説は省く。各々でデザインしよう。

作ったものはこんな感じ

  • googleカレンダーから自分のカレンダーのicsファイルをダウンロード
  • googleカレンダーから祝日のicsファイルをダウンロード
  • カレンダー画像を生成

そんなに難しいことはしてない。pillowというpythonライブラリで画像を作って、作った画像をディスプレイに送って表示している。

まとめ

今回 トータルで15,930円かかった (家にあったものを除く)。しかし、毎年ロールカレンダーを買っていた手間が無くなると考えれば 十二分すぎるほど安上がりだ。 個人的には大満足である。

javaだとかgradleだとか

最近チームを異動して、仕事内容がサーバーサイドとなってjavaとかkotlinを書いてるので、その備忘録的なものを書いておこうかなと。マジで右も左もわからん…

(※現段階の理解を雑に書きなぐってるので、多分間違えてるところがままある)

java/kotlin

  • kotlinはJava仮想マシンで動作するプログラミング言語で、コンパイルするとJavaのバイトコードに変換される
  • javaは null安全じゃない。これがとてもつらい。
  • kotlinはnull安全。とても快適。
  • kotlinもjavaも型システムがnominal
    • typescriptはstructuralでそっちに慣れてしまっているので、辛く感じることがある
  • kotlinは構文がとてもエレガント
    • ifが文ではなく式だし、when式もあるし、fun foo() = result みたいに書けるし、本当に快適
  • javaはコンパイルが(基本的に)必要な言語
    • .javaファイルをコンパイルするとjavaバイトコード(.classファイル)になる
    • シングルファイルであればコンパイルせずに実行できる(java11以降)

kotlinとjavaの関係はtypescriptとjavascriptの関係に似てる。kotlinはjavaにコンパイルできるし、javaより型が便利だし。

gradle

(maven は知らないので書けない)

  • gradle はビルドツール
  • gradle にプラグインを入れてjavaやkotlinをコンパイルする
    • javaプラグインを入れると src/main/java 以下においてあるjavaファイルたちがコンパイルされる (多分)
    • kotlinプラグインを入れると src/main/kotlin 以下においてあるkotlinファイルたちがコンパイルされる (多分)
    • kotlinからjavaを読み込んだりもできる
  • ./gradlew build でビルドしたり、 ./gradlew bootRun で実行したりできる
  • gradleの設定ファイルはgroovyかkotlin DSLで記述する
    • groovyはJavaプラットフォーム上で動作する動的プログラミング言語らしい (by wiki)
    • build.gradleはgroovy、build.gradle.ktsはkotlin DSL
  • build-logicというものを使うとビルドロジックを共通化できる
    • これがあんまわかってないんだよな…

gradle は結構フレームワークに近い動きをする。たとえば src/main/java に配置されたファイルをコンパイルする動作のあたりとか、特にフレームワークっぽいよね。

まとめ

こうして書き出してみるとそこまで複雑じゃないな。でもいざ書いてみると全然書けないんだよな……

onUnmountedはonMountedのなかで呼ぶとスッキリしやすい

vueのcomposition APIはどこで呼んでも問題ありません。たとえばonMountedのなかでonUnmountedを呼んでも良いです。

onMounted(() => {
  const handler = () => {};
  window.addEventHandler('event', handler);
  onUnmounted(() => {
    window.addEventHandler('event', handler);
  });
});

onMountedのときに使ったもの(handlerとか)をそのまま流用してonUnmountedを設定できるので、このパターンはかなり有用です。ぜひ使ってみてください。

spread演算子を使うときに {} と {foo:undefined} は挙動が異なる

const obj1 = {
  foo: 42,
  ...({ foo: undefined }),
}; // {foo: undefined}


const obj2 = {
  foo: 42,
  ...({}),
}; // {foo: 42}

↑こんな感じで、undefinedが指定されているときとプロパティ自体がないときでは動作が異なります。

(実は上のコードはそのままだとfooが二重で定義されているとTSに怒られるので、実際に問題になるケースは少ないです)

問題になるケース

const props: {style: CSSProperties} = {
  style: {
    background: cond ? 'blue' : undefined,
  },
};
const style = {
  background: 'red',
  ...props.style,
};

このとき、condがfalseだとbackgroundは'red'ではなくundefinedになってしまいます。

これを解消するために、condがfalseのときにbackgroundを追加しないようにします。

const props = {
  style: {
    ...(cond && {background: 'blue'}),
  },
};
const style = {
  background: 'red',
  ...props.style,
};

これであれば意図した通りに動きます。