LoginSignup
12
6

More than 1 year has passed since last update.

【続】SORACOM x Nerves できたとはいえるとぼくおもいます

Last updated at Posted at 2021-06-13

はじめに

  • Elixir 楽しんでいますか:bangbang::bangbang::bangbang:
  • 先日、「SORACOM x Nerves できたとはいえるとぼくおもいます」を書きました
  • こちらは、本当にやりたいことはできませんでした〜 という内容でした
  • その後、進展がありましたので改めて書いておきます
    • 本当にやりたかったことにだいぶ近づきました
  • この記事は、2021/06/12(土) 00:00 〜 2021/06/14(月) 23:59開催のautoracex #32というElixirのもくもく会での成果です

この記事で書いていること

E0Y2GqDVgAInS0U.jpeg

SORACOM Beamとは

  • https://soracom.jp/services/beam/ の説明、図を拝借させていただきました
  • SORACOM Beam(以下、Beam)は、IoT デバイスにかかる暗号化等の高負荷処理や接続先の設定を、クラウドにオフロードできるサービスです。Beam を利用することによって、クラウドを介していつでも、どこからでも、簡単に IoT デバイスを管理することができます。大量のデバイスを直接設定する必要はありません。

fig_beam_01.png

スクリーンショット 2021-06-13 18.16.56.png

スクリーンショット 2021-05-08 17.35.08.png

① 準備

  • この記事では割愛します :pray::pray_tone1::pray_tone2::pray_tone3::pray_tone4::pray_tone5:
  • いくつかリンクを示しておきます

公式

環境構築

Nervesアプリの開発の流れがよくわかる記事

WiFi設定

② SIMの購入・データ通信端末の購入

L-02Cがデフォルトでモデムモードになっているようにする

  • 素のRaspberry Pi OSでは自動的にモデムモードとして認識されますが、Nervesの場合はCDROMとして認識されてしまいます
    • 以前は素のRaspberry Pi OSでもデフォルトはCDROMとして認識していたようです
    • もしRaspberry Pi OSのアップデート等でこのへんの動きが変わってしまった場合は、ejectusb_modeswitchlsusbあたりでググると答えにたどりつけるとおもいます
  • そこであらかじめ、素のRaspberry Pi OSでモデムモードとして認識されるように設定しておきました
$ cu -l /dev/ttyUSB2

ATZ

AT%USBMODEM=0

~.
  • (たしかこんな感じだったはずです)

Customizing Your Own Nerves System

  • :point_up::point_up_tone1::point_up_tone2::point_up_tone3::point_up_tone4::point_up_tone5: を参考にカスタマイズしていきます

環境

  • 私が使用した環境です
  • macOS Catalina 10.15.7
  • Elixir: 1.11.4-otp-23
  • Erlang: 23.0.1
  • Docker version 20.10.6, build 370c289
    • Dockerをイゴかしておく必要があります
    • のちほど行うmix firmwareのときにNerves Systemのビルドで使われます
    • イゴかしておけば勝手に使われる感じです

フォルダ構成

soracom
├── custom_rpi4
└── cellular_sample

custom_rpi4

$ git clone https://github.com/nerves-project/nerves_system_rpi4.git custom_rpi4 -b v1.15.1

カスタマイズ

mix.exs
-defmodule NervesSystemRpi4.MixProject do
+defmodule CustomRpi4.MixProject do
   use Mix.Project

-  @github_organization "nerves-project"
-  @app :nerves_system_rpi4
+  @github_organization "TORIFUKUKaiou"
+  @app :custom_rpi4
   @source_url "https://github.com/#{@github_organization}/#{@app}"
   @version Path.join(__DIR__, "VERSION")
            |> File.read!()
linux-5.4.defconfig
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_USB_NET_CDC_NCM=m
+CONFIG_USB_NET_HUAWEI_CDC_NCM=m
+CONFIG_USB_NET_QMI_WWAN=m
+CONFIG_USB_SERIAL_OPTION=m
nerves_defconfig
+BR2_PACKAGE_USB_MODESWITCH=y
+BR2_PACKAGE_PPPD=y
+BR2_PACKAGE_PPPD_FILTER=y
+BR2_PACKAGE_BUSYBOX_CONFIG_FRAGMENT_FILES="${NERVES_DEFCONFIG_DIR}/busybox.fragment"
busybox.fragment
CONFIG_MKNOD=y
CONFIG_WC=y

cellular_sample

$ mix nerves.new cellular_sample --target rpi4

ファイルの変更

mix.exs
defmodule CellularSample.MixProject do

   @app :cellular_sample
   @version "0.1.0"
-  @all_targets [:rpi4]
+  @all_targets [:rpi4, :custom_rpi4]

   def project do
     [
@@ -41,7 +41,13 @@ defmodule CellularSample.MixProject do
       {:nerves_pack, "~> 0.4.0", targets: @all_targets},

       # Dependencies for specific targets
-      {:nerves_system_rpi4, "~> 1.13", runtime: false, targets: :rpi4}
+      {:nerves_system_rpi4, "~> 1.13", runtime: false, targets: :rpi4},
+      {:custom_rpi4, path: "../custom_rpi4", runtime: false, targets: :custom_rpi4, nerves: [compile: true]},
+      {:elixircom, "~> 0.2.0"},
+      {:circuits_i2c, "~> 0.3.8"},
+      {:httpoison, "~> 1.8"},
+      {:jason, "~> 1.2"},
+      {:timex, "~> 3.7"}
     ]
   end

セルラー通信のための設定ファイル

  • 素のRaspberry Pi OSで動作するファイルをこさえるとよいです
  • ググったり、pppconfigでひな形つくって、ググって変更したり地道にがんばる必要があります
    • 各行の意味はそれほど理解しているわけではありません :sweat_smile:
  • L-02Cに関しては多くの先人の方々の記録があり、たいへん参考にさせてもらいました
  • ありがとうございます!
  • データ通信端末ごとに多少異なる部分があるとおもいます
rootfs_overlay/etc/chatscripts/soracom
# This chatfile was generated by pppconfig 2.3.18.
# Please do not delete any of the comments.  Pppconfig needs them.
# 
# ispauth CHAP
# abortstring
ABORT BUSY ABORT 'NO CARRIER' ABORT VOICE ABORT 'NO DIALTONE' ABORT 'NO DIAL TONE' ABORT 'NO ANSWER' ABORT DELAYED
# modeminit
'' ATZ
'' ATH
# ispnumber
OK-AT-OK "ATDT*99***1#"
# ispconnect
CONNECT \d\c
# prelogin

# ispname
# isppassword
# postlogin

# end of pppconfig stuff
rootfs_overlay/etc/ppp/chap-secrets
# Secrets for authentication using CHAP
# client    server  secret          IP addresses


sora    *   sora
"sora@soracom.io" * "sora"
"sora@soracom.io" * "sora"
rootfs_overlay/etc/ppp/peers/soracom
# This optionfile was generated by pppconfig 2.3.18. 
# 
#
hide-password 
noauth
connect "/usr/sbin/chat -v -f /etc/chatscripts/soracom"
debug
/dev/ttyUSB2
115200
defaultroute
noipdefault 
user "sora@soracom.io"

ipparam soracom

セルラー通信

lib/cellular_sample.ex
defmodule CellularSample do
  use Toolshed

  def connect do
    cmd("mknod /dev/ppp c 108 0")
    cmd("pon soracom")
  end

  def update_route do
    cmd("ip rou delete default")
    cmd("ip rou add default via #{ip_address()} dev ppp0")
  end

  # ifconfigしてIP Addressを取得している感じです
  defp ip_address do
    {:ok, list} = :inet.getifaddrs()

    Enum.filter(list, fn {type, _} -> type == 'ppp0' end)
    |> Enum.at(0)
    |> elem(1)
    |> Enum.at(1)
    |> elem(1)
    |> Tuple.to_list()
    |> Enum.join(".")
  end
end

Grove AHT20 I2C温度および湿度センサー 工業用グレードで取得した値を、SORACOM Beamを経由して自分のサーバーに送り込む

lib/cellular_sample/aht20.ex
defmodule CellularSample.Aht20 do
  alias Circuits.I2C

  @i2c_bus "i2c-1"
  @i2c_addr 0x38
  @initialization_command <<0xBE, 0x08, 0x00>>
  @trigger_measurement_command <<0xAC, 0x33, 0x00>>
  @two_pow_20 :math.pow(2, 20)

  def read do
    {:ok, ref} = I2C.open(@i2c_bus)

    I2C.write(ref, @i2c_addr, @initialization_command)
    Process.sleep(10)

    I2C.write(ref, @i2c_addr, @trigger_measurement_command)
    Process.sleep(80)

    ret =
      case I2C.read(ref, @i2c_addr, 7) do
        {:ok, val} -> {:ok, val |> convert()}
        {:error, :i2c_nak} -> {:error, "Sensor is not connected"}
        _ -> {:error, "An error occurred"}
      end

    I2C.close(ref)

    ret
  end

  defp convert(<<_, raw_humi::20, raw_temp::20, _>>) do
    humi = Float.round(raw_humi / @two_pow_20 * 100.0, 1)
    temp = Float.round(raw_temp / @two_pow_20 * 200.0 - 50.0, 1)

    {temp, humi}
  end
end
lib/cellular_sample/worker.ex
defmodule CellularSample.Worker do
  require Logger

  @url "http://beam.soracom.io:8888/"
  @headers [{"Content-Type", "application/json"}]

  def run do
    {:ok, {temperature, humidity}} = CellularSample.Aht20.read()

    inspect({temperature, humidity}) |> Logger.debug()

    post(temperature, humidity)
  end

  defp post(temperature, humidity) do
    time = Timex.now() |> Timex.to_unix()
    json = Jason.encode!(%{value: %{temperature: temperature, humidity: humidity, time: time}})
    HTTPoison.post(@url, json, @headers)
  end
end

WiFi(オプション)

ビルド

$ cd soracom/cellular_sample
$ export MIX_TARGET=custom_rpi4
$ mix deps.get
$ mix firmware
  • mix firmwareはNerves Systemのビルドも行われるのでかなり時間がかかります
  • :coffee: でも飲んで気長に待ちましょう
    • マシンスペックやネットワーク環境によるとおもいますが、私は4時間くらいかかりました
      • 時間がかかっていたのはダウンロードのところだとおもいます
      • 夜は家のインターネットがおそいのです……
  • ビルドが終わったらmicroSDカードをPCに差し込んでmix burnです

④ Run

  • ssh nerves.local等でNervesにログインします
iex> lsusb
Bus 001 Device 003: ID 1004:618f NTT DOCOMO, INC. docomo L02C
...
  • 618fがモデムモードです
  • 618fになっていない場合には、ココを参照してください
iex> Elixircom.run("/dev/ttyUSB2")
AT+COPS=1,2,"44010"
  • このATコマンドはおまじないです
    • 44010はドコモを表しています
    • 私の家の周辺ではこれを行っておくことでIPアドレスが割り当てられることが多くなったようにおもいます
  • Ctl + Bで抜けます
iex> CellularSample.connect
# cmd("mknod /dev/ppp c 108 0")
# cmd("pon soracom")
# の実行と同じです

iex> CellularSample.update_route
# ifconfigでppp0のIPアドレスを確認して
# cmd("ip rou delete default")
# cmd("ip rou add default via #{ip_address} dev ppp0")
# の実行と同じです

iex> CellularSample.Worker.run
# AHT20から温度・湿度を取得して、SORACOM Beamを経由して自分自身のサーバーにデータが送られます

スクリーンショット 2021-05-08 17.35.08.png

  • こんな感じのセルラー通信が実現しました!
    • ただし、L-02Cには無通信監視タイマーというものが満了すると通信状態が途切れてしまいます(青表示から白表示)
    • 正確な時間はわかっていないのですが60秒くらいでしょうか
    • 団長! いつでもどこでも温度・湿度が測れるのであります! (Elixir, SORACOM Air for セルラー)でやったときは、たしか10秒に1回くらいデータを送っていたのでこういうものがあることに気づかなかったのだとおもいます
    • また24時間以上経つと不通になってしまうことがあるそうです(そんな記事を読みました)
  • というような制限みたいなものはありますが、そのへんはさまざまなデータ端末で今後試してみたいとおもいます

進展したきっかけ

  • 行き詰まりを感じて、その思いを吐露(SORACOM x Nerves できたとはいえるとぼくおもいます)したあと寝ました
  • 翌朝ダメもとで、RingLogger.attachでログ出してみました
  • そうすると、:os.cmd('pon soracom')実行時に設定ファイルがみつからないというエラーログがでていまして、配置場所を間違えていたことにようやく気づきました

Wrapping Up :lgtm::lgtm::lgtm::lgtm::lgtm:

ソースコード

NervesJP

https___qiita-user-contents.imgix.net_https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F240349%2F5ef22bb9-f357-778c-1bff-b018cce54948.png_ixlib=rb-1.2.png


  1. SIMの場合は小は大を兼ねます。ただしSIMアダプターでサイズをあわせるのはススメられてはいません。https://soracom.zendesk.com/hc/ja/articles/223454148-SIM-%E3%81%AE%E3%82%B5%E3%82%A4%E3%82%BA%E5%A4%89%E6%9B%B4-%E5%86%8D%E7%99%BA%E8%A1%8C%E3%81%AF%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%81%8B- 

12
6
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
12
6