LoginSignup
11
3

More than 1 year has passed since last update.

Elixir × Nerves で MH-Z19 を動かす

Last updated at Posted at 2021-05-15

はじめに

Elixir で CO2センサー MHZ19 の読み取り方を書いたものが見つからなかったため,この記事を書くことにした.

使用するもの

前提

  • Nervesが入っている
  • mh-z19 が配線されている

本編

ライブラリのインストール

  • mh-z19 は UART通信を用いるので,Circuits.UART(ドキュメント) を使う.

まずは mix.exs に追加したあと,mix deps.get する.

defp deps do
    [
      # ....
      {:circuits_uart, "~> 1.3"}
    ]
  end

コード

mh-z19 データシート に従って,後々使いやすいように(?) GenServerの形で実装する.

init

  • UART の GenServer (このGenServerとは別) を起動して,pid を状態としてを持っておく.
  • UART GenServer の "ttyAMA0" インタフェースを開く.このとき,データシートに従って baud_rate 等のパラメタを設定しておく.

handle_call

GenServer へ :read の呼び出しが来たときに,センサーの読み取り値を返す.

  • センサーの値を読み取るコマンド @sensor_read_commandwrite してあげる.
  • read_uart を呼び出して,帰ってきた値を読む.
    • read_uart では,結果を含む返信を読むまでループして,結果を返すか,結果が得られなければ {:error, reason} を返す.(reasonCircuits.UART のエラーを含む)

measure

値を読むためのメソッド.使うときはこれを呼べば良い.

defmodule WarblerUi.Sensors.Mhz19 do
  use GenServer, restart: :permanent

  alias Circuits.UART

  @baud_rate 9600
  @sensor_read_command <<0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79>>

  def init(_opts) do
    {:ok, uart_pid} = UART.start_link()

    :ok =
      UART.open(uart_pid, "ttyAMA0",
        speed: @baud_rate,
        data_bits: 8,
        stop_bits: 1,
        parity: :none,
        active: false
      )

    {:ok, uart_pid}
  end

  def handle_call(:read, _from, uart_pid) do
    :ok = UART.write(uart_pid, @sensor_read_command)
    read_uart(uart_pid)
  end

  defp read_uart(uart_pid) do
    case UART.read(uart_pid) do
      {:ok, <<255, 134, higher_bits, lower_bits, _, _, _, _>>} ->
        decoded_data = higher_bits * 256 + lower_bits
        {:reply, {:ok, decoded_data}, uart_pid}

      {:error, reason} ->
        {:reply, {:error, reason}, uart_pid}

      _ ->
        read_uart(uart_pid)
    end
  end

  def measure(pid) do
    GenServer.call(pid, :read)
  end
end

使い方

Nerves のコンソールを ssh nerves.local して開き,こんな感じで実行すれば,CO2 濃度がわかる.

alias WarblerUi.Sensors.Mhz19
{:ok, mhz19_pid} = GenServer.start_link(Mhz19, {})
{:ok, co2} = Mhz19.measure(mhz19_pid)

あとがき

現在は Phoenix フレームワークを使って,センサーの値を liveview で表示したりしようとしています.
気が向いたらそれについても書くかもしれません.

また,Elixir, Nerves を始めて間もなく,初めての記事なので,間違い等あるかもしれないです.
見つけた場合は指摘していただければ幸いです.

11
3
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
11
3