0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

ちょっと前の記事 はじめてね AtomVM(5) GenServer が AtomVM でもいごいた でアルファ版の AtomVM で GenServer がいごくことを確認しました。当然次は Supervisor です。

GenServer を用いたLチカ

はじめてね AtomVM(5) GenServer が AtomVM でもいごいた を元にちょっと複雑なLチカを作成しました。

  • ただの単純な点滅だと他のプログラムが動作した場合と区別がつきにくいので複雑な点滅を可能にした
  • start/0 関数は削除した
    • ブートに起動するのは後で述べる Supervisor である
  • Supervisor による自動再起動を確認するのに、起動後一定時間経つとクラッシュするようにした
    • GenServerがクラッシュする場合(クラッシュと言っても stop 関数)
    • システム全体のクラッシュする場合
blinky.ex
defmodule Blinky do
  @compile {:no_warn_undefined, [GPIO, :atomvm]}
  # for GPIO of ESP32
  @gpio_pin 2
  @steps 8

  use GenServer

  def start_link(_arg \\ []) do
    IO.puts("LED Blink starts")
    GenServer.start_link(__MODULE__, {@gpio_pin, 0}, name: __MODULE__)
  end

  def init({pin, n}) do
    GPIO.set_pin_mode(pin, :output)
    GPIO.digital_write(pin, :low)
    set_next_step(1500)
    set_crash(10_000)
    IO.puts("GenServer init finished")
    {:ok, {pin, n}}
  end

  def set_next_step(ms), do: Process.send_after(self(), :step, ms)
  def set_crash(ms), do: Process.send_after(self(), :crash, ms)

  def handle_info(:crash, state) do
    IO.puts("crash now")
    GenServer.stop(self(), :crash)
    {:noreply, state}
  end

  def handle_info(:step, {pin, n}) do
    case n do
      0 -> {:noreply, {pin, {:high, 50}, n}, {:continue, :toggle_common}}
      1 -> {:noreply, {pin, {:low, 50}, n}, {:continue, :toggle_common}}
      2 -> {:noreply, {pin, {:high, 50}, n}, {:continue, :toggle_common}}
      3 -> {:noreply, {pin, {:low, 350}, n}, {:continue, :toggle_common}}
      4 -> {:noreply, {pin, {:high, 100}, n}, {:continue, :toggle_common}}
      5 -> {:noreply, {pin, {:low, 100}, n}, {:continue, :toggle_common}}
      6 -> {:noreply, {pin, {:high, 100}, n}, {:continue, :toggle_common}}
      7 -> {:noreply, {pin, {:low, 700}, n}, {:continue, :toggle_common}}
      _ -> {:noreply, {pin, {:low, 1000}, 0}, {:continue, :toggle_common}}
    end
  end

  def handle_continue(:toggle_common, {pin, {level, interval}, n}) do
    GPIO.digital_write(pin, level)
    IO.puts("step #{n}, led level: #{level}, interval: #{interval} ms")
    set_next_step(interval)
    {:noreply, {pin, rem(n + 1, @steps)}}
  end
end

これ、クラッシュさせないで動作させるとLEDが「パパ、パッパッ……」というパターンを繰り返します。

Supervisor から呼ぶようにする

上の Blinky モジュールの GenServer を起動・再起動する Supervisor を作ります。AtomVMの起動でこちらを呼ぶように start/0 関数として定義します。

atomvmsmblinky.ex
defmodule AtomvmSVblinky do
  def start() do
    children = [Blinky]

    {:ok, _pid} =
      Supervisor.start_link(
        children,
        strategy: :one_for_one,
        name: :blinky
      )

    Process.sleep(:infinity)
  end
end

実行

クラッシュの種類を2種類用意したので、それぞれで実行してみます。

GenServer.stop によるクラッシュ

先に紳士的なクラッシュの方を試します。blinky.ex で意図的にクラッシュさせる部分は以下のようにしてあります。

blinky.ex
    GenServer.stop(self(), :crash)
#    raise "crash"

依存関係解決して、コンパイルして、パックして、実機にロードします。

% mix deps.get
% mix compile
% mix atomvm.packbeam # Supervisor は無いかもよとか warning が出ます
% mix atomvm.esp32.flash --port /dev/tty.usbserial-xxx
# /dev/tty.usbserial-xxx のデバイスは各自の母艦ごとに異なります。

これでちょっと複雑なLチカが始まります。そして10秒ごとにパターンの最初からやりなおします。これは10秒ごとにGenServerがクラッシュして、それを検出したSupervisorがGenServerを立上げ直すのためにこのような動作になります。Supervisorはちゃんと機能しているようです。

これを確認するのに screen コマンドでログを見てみます。以下、色々と出てくるので動作が始まるまでの行は適当に端折ってます。

% screen /dev/tty.usbserial-xxx 115200

    ###########################################################

       ###    ########  #######  ##     ## ##     ## ##     ## 
      ## ##      ##    ##     ## ###   ### ##     ## ###   ### 
     ##   ##     ##    ##     ## #### #### ##     ## #### #### 
    ##     ##    ##    ##     ## ## ### ## ##     ## ## ### ## 
    #########    ##    ##     ## ##     ##  ##   ##  ##     ## 
    ##     ##    ##    ##     ## ##     ##   ## ##   ##     ## 
    ##     ##    ##     #######  ##     ##    ###    ##     ## 

    ###########################################################

I (781) AtomVM: Starting AtomVM revision 0.7.0-alpha.1+git.078004b
I (791) sys: Loaded BEAM partition boot.avm at address 0x1d0000 (size=524288 bytes)
I (821) network_driver: Initialized network interface
I (821) network_driver: Created default event loop
I (841) AtomVM: Found startup beam esp32init.beam
I (851) AtomVM: Starting esp32init.beam...
---
AtomVM init.
I (931) sys: Loaded BEAM partition main.avm at address 0x250000 (size=1048576 bytes)
Starting application...
LED Blink starts
GenServer init finished
step 0, led level: high, interval: 50 ms
step 1, led level: low, interval: 50 ms
step 2, led level: high, interval: 50 ms
step 3, led level: low, interval: 700 ms
step 4, led level: high, interval: 100 ms
step 5, led level: low, interval: 100 ms
step 6, led level: high, interval: 100 ms
step 7, led level: low, interval: 1700 ms
step 0, led level: high, interval: 50 ms
step 1, led level: low, interval: 50 ms
step 2, led level: high, interval: 50 ms
step 3, led level: low, interval: 700 ms
step 4, led level: high, interval: 100 ms
step 5, led level: low, interval: 100 ms
step 6, led level: high, interval: 100 ms
step 7, led level: low, interval: 1700 ms

とLEDを制御している様子が見えてます。そして10秒したらこうなります。ちょっと長いですが丸ごと掲載します。

step 7, led level: low, interval: 1700 ms
step 0, led level: high, interval: 50 ms
crash now
CRASH 
======
pid: <0.610.0>

Stacktrace:
[{Elixir.GenServer,stop,3,[{file,"/__w/AtomVM/AtomVM/libs/exavmlib/lib/GenServer.ex"},{line,1104}]},{Elixir.Blinky,handle_info,2,[]},{gen_server,handle_msg,4,[{file,"/__w/AtomVM/AtomVM/libs/estdlib/src/gen_server.erl"},{line,561}]},{proc_lib,init_p,5,[{file,"/__w/AtomVM/AtomVM/libs/estdlib/src/proc_lib.erl"},{line,272}]},{proc_lib,init_p,5,[{file,"/__w/AtomVM/AtomVM/libs/estdlib/src/proc_lib.erl"},{line,272}]}]

cp: #CP<module: 5, label: 93, offset: 0>

x[0]: exit
x[1]: {calling_self,{Elixir.GenServer,stop,[<0.610.0>,crash,infinity]}}
x[2]: {0,0,0,0,[{Elixir.GenServer,stop,3,[{file,"/__w/AtomVM/AtomVM/libs/exavmlib/lib/GenServer.ex"},{line,1104}]},{Elixir.Blinky,handle_info,2,[]},{gen_server,handle_msg,4,[{file,"/__w/AtomVM/AtomVM/libs/estdlib/src/gen_server.erl"},{line,561}]},{proc_lib,init_p,5,[{file,"/__w/AtomVM/AtomVM/libs/estdlib/src/proc_lib.erl"},{line,272}]},{proc_lib,init_p,5,[{file,"/__w/AtomVM/AtomVM/libs/estdlib/src/proc_lib.erl"},{line,272}]}],exit}

Stack 
-----



Mailbox
-------
step


Monitors
--------
link to <0.4.0>


**End Of Crash Report**
LED Blink starts
GenServer init finished
step 0, led level: high, interval: 50 ms
step 1, led level: low, interval: 50 ms
step 2, led level: high, interval: 50 ms
step 3, led level: low, interval: 700 ms

このように GenServer が再起動します。これは Supervisor が GenServer を監視してて、GenServerが停止したことを受けて再立ち上げをしたものです。

raise/1 によるクラッシュ

先程の GenServer のプログラムでクラッシュさせるところはこうなってました。

blinkky.ex
    GenServer.stop(self(), :crash)
#    raise "crash"

これをraise/1 に変更します。

blinkky.ex
#    GenServer.stop(self(), :crash)
    raise "crash"

こうするとカーネルを強制的にクラッシュさせるようになります。この場合の結果はこのようになりました。ちょっと面白いことになってます。

step 7, led level: low, interval: 1700 ms
crash now
CRASH 
======
pid: <0.5.0>

Stacktrace:
[{erlang,error,3,[]},{gen_server,handle_msg,4,[{file,"/__w/AtomVM/AtomVM/libs/estdlib/src/gen_server.erl"},{line,561}]},{proc_lib,init_p,5,[{file,"/__w/AtomVM/AtomVM/libs/estdlib/src/proc_lib.erl"},{line,272}]},{proc_lib,init_p,5,[{file,"/__w/AtomVM/AtomVM/libs/estdlib/src/proc_lib.erl"},{line,272}]}]

cp: #CP<module: 5, label: 93, offset: 0>

x[0]: exit
x[1]: {#{__exc D/IRAM
I (709) heap_init: At 4009A860 len 000057A0 (21 KiB): IRAM
I (715) spi_flash: detected chip: generic
I (718) spi_flash: flash io: dio
W (721) i2c: This driver is an old driver, please migrate your application code to adapt `driver/i2c_master.h`
I (731) main_task: Started on CPU0
I (741) main_task: Calling app_main()

    ###########################################################

       ###    ########  #######  ##     ## ##     ## ##     ## 
      ## ##      ##    ##     ## ###   ### ##     ## ###   ### 
     ##   ##     ##    ##     ## #### #### ##     ## #### #### 
    ##     ##    ##    ##     ## ## ### ## ##     ## ## ### ## 
    #########    ##    ##     ## ##     ##  ##   ##  ##     ## 
    ##     ##    ##    ##     ## ##     ##   ## ##   ##     ## 
    ##     ##    ##     #######  ##     ##    ###    ##     ## 

    ###########################################################

I (781) AtomVM: Starting AtomVM revision 0.7.0-alpha.1+git.078004b
I (791) sys: Loaded BEAM partition boot.avm at address 0x1d0000 (size=5step 5, led level: low, interval: 100 ms
step 6, led level: high, interval: 100 ms
step 7, led level: low, interval: 1700 ms
step 0, led level: high, interval: 50 ms
step 1, led level: low, interval: 50 ms
step 2, led level: high, interval: 50 ms
step 3, led level: low, interval: 700 ms
step 4, led level: high, interval: 100 ms
step 5, led level: low, interval: 100 ms
step 6, led level: high, interval: 100 ms
step 7, led level: low, interval: 1700 ms

こちら AtomVM ごと再起動してるのにも関わらず、いきなり step 5 からはじまってます。これはどうしたことでしょう。もし、AtomVM ごとESP32が再起動したのなら start/0 関数が呼ばれて Supervisor が立ち上がって、それから GenServer が立ち上がるから 0 からスタートしそうなものです。こちらは時間のあるときにもうちょっと深堀りしてみたいと思います。

まとめ

AtomVM v0.7.0-a.1 で GenServer だけでなく Supervisor が使えるのを確認しました。

リリースノートのv0.7.0-alpha.1

  • Added code:get_object_code/1

なんて書いてあるのを見つけましたので、次回はモジュールのBEAM形式のバイナリを取り出して、それを再ロードして実行とかしてみたいと思います。ただ、そのバイナリを単体の ESP32 で code:load_binary/3 しても同じモジュールが同じAtomVMにロードされるだけです。何が起こったのかをどうやって確認すればよいのでしょうか… などという不安もあるのですが、それはまた今度。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?