の後編です。
mRubyで時計のプログラムを書けるようにする
といっても,もくもくとAPIを実装するだけです。
用意したAPIはこれだけあります。
こういうプログラムをHTTPで書き込んで時計で実行できるようになりました。
i = 0 # ラインアート lines = [] aline = [20, 10, 3, 8, 2, 3, 2, -1] # 次の時間の線を生成する def succ_line l x0, y0, x1, y1, dx0, dy0, dx1, dy1 = l[0], l[1], l[2], l[3], l[4], l[5], l[6], l[7] x0 += dx0; y0 += dy0; x1 += dx1; y1 += dy1 (dx0 *= -1; x0 = 0) if x0 < 0 (dy0 *= -1; y0 = 0) if y0 < 0 (dx1 *= -1; x1 = 0) if x1 < 0 (dy1 *= -1; y1 = 0) if y1 < 0 (dx0 *= -1; x0 = 31) if x0 > 31 (dy0 *= -1; y0 = 31) if y0 > 31 (dx1 *= -1; x1 = 31) if x1 > 31 (dy1 *= -1; y1 = 31) if y1 > 31 [x0, y0, x1, y1, dx0, dy0, dx1, dy1] end 8.times {|t| lines << aline aline = succ_line(aline) } time_format = "%H:%M" message = "Welcome to KMC! Please Enjoy Yourself!" # Loopの開始 Task::loop do # 描画削除 Led::clear 0 # 色指定 R, G, B (0 ~ 7) Led::color 0, 1, 0 lines.each {|l| # 線描画 x1, y1, x2, y2 # 座標は(0 ~ 31, 0 ~ 31) Led::line l[0], l[1], l[2], l[3] } lines.shift() lines << succ_line(lines[-1]) # フォント指定(番号) Led::font 2 Led::color 7, 3, 3 # 時刻更新 Time::update # 文字出力 x, y, 文字列 # Time::str(strftime文字列) Led::text 1, 12, Time::str(time_format) Led::font 0 Led::color 5, 0, 5 # 流れる表示 時間, 遅さ, y, 文字列 Led::show i.div(2), 23, message Led::font 5 # cmdで入力された文字列 w = Task::cmd() Led::color 0, 5, 5 Led::show i.div(2), 1, w # [注意] Led::flushを実行しないと画面が更新されません。 Led::flush i += 1 end
これは,[前編]の動画のプログラムです。 ラインアートを背景で動かしています。
mRuby拡張により配列や文字列を使えるので,普通のrubyプログラムのように書くことができます。
APIの実装
例えば Led::line
の場合,
Cで実装されている部分はこのように,ブレゼンハムのアルゴリズムを持ってきます。
void b_line(int x0, int y0, int x1, int y1, int r, int g, int b) { mrb_int dx = abs(x1-x0), dy = abs(y1-y0), sx = (x0 < x1) ? 1 : -1, sy = (y0 < y1) ? 1 : -1, err = dx-dy; while (true){ b_set(x0, y0, r, g, b); if (x0 == x1 && y0 == y1) break; mrb_int e2 = 2 * err; if (e2 > -dy) { err = err - dy; x0 = x0 + sx; } if (e2 < dx) { err = err + dx; y0 = y0 + sy; } } }
mRubyの言葉に持ち上げます。
static mrb_value ledline(mrb_state* mrb, mrb_value self) { mrb_int x0, y0, x1, y1; mrb_get_args(mrb, "iiii", &x0, &y0, &x1, &y1); b_line(x0, y0, x1, y1, p_r, p_g, p_b); return self; }
そして Led::line
で呼べるように,環境に登録します
struct RClass *Led = mrb_define_module(mrb, "Led"); mrb_define_class_method(mrb, Led, "line", ledline, MRB_ARGS_REQ(4));
簡単ですね。
辛い点
- ESP32のメモリが足りないので,重いプログラムが動かない
- slackから対局できるオセロ時計が作られたが,メモリ不足で最後まで対局できなかった。
- 日本語のエンコードにISO-2022-JPを選んでしまった。
- 最初
Led::flush
がLed::flash
になっていた - なぜ整数の割り算
3/2
が小数1.5
に評価されてしまうんだろう…。ここでRubyとの非互換性ができてしまった歴史的経緯が知りたい。
部員が作ってくれたプログラムたち
KMC部室にある時計の様子です。 pic.twitter.com/OVTERHy4Lg
— 月鈴那知 (@ten986) 2018年7月11日
この他にも様々プログラムを書いてもらえました。
現在のデフォルトの時計は,室内気温や天気の表示を行うものになっています。
室内気温の情報は昔に作ったこのシステムの情報を使っています。
感想
- ESP32を扱うのはサンプルコードが豊富なので割と簡単。
- はんだ付けとか実装のほうが難しい。
- 実際に動くものができるのは楽しいので良いですね。
- Exploitがめっちゃ簡単にできそうなコードをCだと書いてしまうので,安全なIoT開発手法はもっとほしい…
- RustでESP32開発とかできたらいいんですが,まだLLVM backendがないんですよね。ほしい。