17
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SORACOMAdvent Calendar 2021

Day 6

Elixir meets SORACOM (2021/12/06)

Last updated at Posted at 2021-12-05

2021/12/06(月)の回です。

前日は、@takiponeさんによる「SORACOM IoT SIM planX1がHuawei MS2372h-607でも使えるようになっていた」でした。
planX1 x MS2372h-607を試してみた様子をレポートされています。

はじめに

Elixirを楽しんでいますか :bangbang::bangbang::bangbang:

な記事をお届けします:gift::gift::gift:

2021/12/14(火) 19:30〜21:00開催の「SORACOM UG x NervesJP #1 ~Hello, world!~」にて、NervesJP枠で少ししゃべらせてもらう時間を私はいただいております。
この記事自体がそのイベントでご説明する資料です。

サマリ

We are the Alchemists, my friends!

Nervesはナウでヤングなcoolなすごいやつです🚀

What's Elixir?

Elixirは関数型言語に分類されるプログラミング言語です。
Erlang VM上でイゴいています1
Rubyになんとなく文法が似ています。2

Elixirは、日本語に訳すと不老不死の霊薬です。
そのElixirと名付けられたプログラミング言語の使い手は、**Alchemist(錬金術師)**と尊称されます。

What's Nerves?

NervesElixirのIoTで
$\huge{ナウでヤングなcoolなすごいヤツです🚀}$

Elixir meets SORACOM

AK-020で接続します。
私は、Raspberry Pi 4を使いました。

IMG_20211204_151525.jpg

Raspberry Pi OS

  • まずは基本に則って、Raspberry Pi OS上で、Elixirを動かします。
  • SORACOMさんのSIMを使って、インターネットの世界へ飛び出してみますの例です:rocket::rocket::rocket:

リンク先に従って、Raspberry Pi OSを焼きましょう。
以下、Raspberry Pi上での操作です。

pi@raspberrypi:~ $ uname -a
Linux raspberrypi 5.10.17-v7l+ #1403 SMP Mon Feb 22 11:33:35 GMT 2021 armv7l GNU/Linux

Elixirをインストールする

まずは普通(?)のネットワーク4でRaspberry Piに、Elixirのインストールを進めましょう。

$ sudo apt-get update
($ sudo apt-get update --allow-releaseinfo-change)
$ sudo apt install build-essential automake autoconf git squashfs-tools ssh-askpass pkg-config curl
$ sudo apt install libssl-dev libncurses5-dev default-jdk unixodbc-dev fop xsltproc libxml2-utils
$ sudo apt install libwxgtk-webview3.0-gtk3-dev
$ sudo apt install libwxgtk3.0-gtk3-dev

$ git clone https://github.com/asdf-vm/asdf.git ~/.asdf --branch v0.8.0
$ echo -e '\n. $HOME/.asdf/asdf.sh' >> ~/.bashrc
$ echo -e '\n. $HOME/.asdf/completions/asdf.bash' >> ~/.bashrc
$ source ~/.bashrc
$ asdf plugin-add erlang
$ asdf plugin-add elixir
$ asdf install erlang 24.1.4

:coffee::coffee::coffee::coffee::coffee:
I have five cups of coffee.

Building Erlang/OTP 24.1.4 (asdf_24.1.4), please wait...
APPLICATIONS INFORMATION (See: /home/pi/.asdf/plugins/erlang/kerl-home/builds/asdf_24.1.4/otp_build_24.1.4.log)
 * wx             : wxWidgets was not compiled with --enable-webview or wxWebView developer package is not installed, wxWebView will NOT be available

:point_up::point_up_tone1::point_up_tone2::point_up_tone3::point_up_tone4::point_up_tone5:こんな警告がでていました。
よくはないけどとりあえず放っておきます。

Elixirのインストールはけっこうあっけなくすぐおわります。

$ asdf install elixir 1.12.3-otp-24
$ asdf global erlang 24.1.4
$ asdf global elixir 1.12.3-otp-24

Qiita API

普通(?)のネットワーク1でイゴかしておきます2
このプログラムは、Elixir界でHexと呼ばれるライブラリを初回実行時にダウンロードしています。
その意味(通信費の節約、ダウンロード時間の短縮)でも普通(?)のネットワーク1でイゴかしておくことをオススメします。
Qiitaの場ですので、通信はQiita APIを使ってみることにします。

qiita_api.exs
Mix.install([{:jason, "~> 1.2"}, {:httpoison, "~> 1.8"}])

"https://qiita.com/api/v2/items?query=elixir"
|> HTTPoison.get!([], [timeout: 50_000, recv_timeout: 50_000])
|> Map.get(:body)
|> Jason.decode!()
|> Enum.map(& Map.take(&1, ["title", "url"]))
|> IO.inspect()
  • QiitaのAPIエンドポイントがありまして
    • |> GETするでしょ、いつやるの? 今でしょ!
    • |> bodyを取り出します、いつやるの? 今でしょ!
    • |> JSONデーコードするでしょ、いつやるの? 今でしょ!
    • |> そうすると各要素がマップのリストが得られるので、情報量が多いからとりあえずタイトルとURLだけにするでしょ、いつやるの? 今でしょ!
    • |> 取得したものは表示するでしょ、いつやるの? 今でしょ!

ってな感じの$\huge{Awesome}$なプログラムを書くことができます。
|>は、パイプ演算子と言いまして、前の計算結果を次の関数の第一引数にいれて計算をしてくれます。
パイプ演算子は、Elixirのプログラムでよく使います。

$ elixir qiita_api.exs

実行結果例は、後述します。

AK-020をセットアップする

IMG_20211204_151525.jpg

MS2372h-607 をセットアップするを参考にするとよいです。

$ curl -O https://soracom-files.s3.amazonaws.com/setup_air.sh
$ sudo bash setup_air.sh

setup_air.shを読むと、AK-020のことも書いてあります。
だからいいんです:thumbsup::thumbsup::thumbsup::thumbsup::thumbsup:

Run:rocket::rocket::rocket:

$ ping pong.soracom.io
PING pong.soracom.io (100.127.100.127) 56(84) bytes of data.
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=14 ttl=64 time=94.1 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=15 ttl=64 time=98.8 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=16 ttl=64 time=101 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=17 ttl=64 time=481 ms
64 bytes from 100.127.100.127 (100.127.100.127): icmp_seq=18 ttl=64 time=111 ms

:ping_pong: のやりとりができています。

$ elixir qiita_qpi.exs 
[
  %{
    "title" => "Elixir + SendGrid でメール送信してみる",
    "url" => "https://qiita.com/koyo-miyamura/items/34e369200fe1aeafb0af"
  },
  %{
    "title" => "[LiveView] phx_gen_authで認証したcurrent_userをLiveViewで取得する方法",
    "url" => "https://qiita.com/omini/items/0fd2ab3be79ecb42dccd"
  },
  %{
    "title" => "UnityEngine.Localizationをスクリプトから呼ぶ",
    "url" => "https://qiita.com/ELIXIR/items/91bd827cfc312e38b1b8"
  },
  %{
    "title" => "[liveview] ** (UndefinedFunctionError) function Phoenix.LiveView.Helpers.__component__/3 is undefined or privateに対する対応",
    "url" => "https://qiita.com/omini/items/946841ece651cb671e5f"
  },
  %{
    "title" => "MacBookでElixirの2ノード間通信を試してみる",
    "url" => "https://qiita.com/electronics_diy721/items/0817a449344201f03cc7"
  },
  %{
    "title" => "Elixirのobserver.start( )で、マシンの論理コア数を確認する",
    "url" => "https://qiita.com/electronics_diy721/items/9b815286f43d9f486a52"
  },
  %{
    "title" => "Erlang:プロセス間の並行プラミング入門1",
    "url" => "https://qiita.com/electronics_diy721/items/91f8075bf4f5da05854f"
  },
  %{
    "title" => "ElixirをMacbookに入れて、IExを動かした",
    "url" => "https://qiita.com/electronics_diy721/items/6ff0c60d32ab0a9cbc5c"
  },
  %{
    "title" => "MAKER PI RP2040をPlatformIOからArduinoとして使う",
    "url" => "https://qiita.com/ELIXIR/items/a00a8e0d9c9b9fcf1942"
  },
  %{
    "title" => "ElixirのIExをElixirカラーにする",
    "url" => "https://qiita.com/mnishiguchi/items/a339850b0fadeecc6632"
  },
  %{
    "title" => "Phx.gen.liveで作成したスキーマにJSON APIを作成する方法",
    "url" => "https://qiita.com/omini/items/a157960ebe24cf890091"
  },
  %{
    "title" => "elixirでtupleとlistの要素の取り出し",
    "url" => "https://qiita.com/omini/items/3cc16f94e70e60b91e62"
  },
  %{
    "title" => "Liveviewで作るスロットゲーム",
    "url" => "https://qiita.com/omini/items/9ae9063c8fe51a5f3ff2"
  },
  %{
    "title" => "【毎日自動更新】Azure Kubernetes Serviceに関する記事を投稿しよう!(2021/10/10–2021/11/10) LGTMランキング!",
    "url" => "https://qiita.com/torifukukaiou/items/2db585bf7dbe39ed6f5d"
  },
  %{
    "title" => "Phoenix 1.6、devでSQLite3、prodではPostgres",
    "url" => "https://qiita.com/mnishiguchi/items/6b29314edcb4157f5e18"
  },
  %{
    "title" => "おまけ: LiveViewでエラー通知編【Sentryを使ったElixir/Phoenixアプリのエラー監視】",
    "url" => "https://qiita.com/koyo-miyamura/items/24757fbae5dfc5cde602"
  },
  %{
    "title" => "特定ユーザーのQiita記事一覧を得る(5)",
    "url" => "https://qiita.com/ELIXIR/items/9a582ae2eb75909acdab"
  },
  %{
    "title" => "[Liveview] LiveviewとLiveComponent間の状態管理",
    "url" => "https://qiita.com/omini/items/ab70e2a300df78e36c41"
  },
  %{
    "title" => "⑤Elixirユーザ認証ライブラリ「phx_gen_auth」の本番向け改造ポイント:コントローラ内/テンプレート内のメッセージのカスタマイズ",
    "url" => "https://qiita.com/piacerex/items/6d6963e18463eb920971"
  },
  %{
    "title" => "【初心者QiitaAPI練習】Qiita内の麻雀関連記事リンク集を作ってみた。",
    "url" => "https://qiita.com/yurisg/items/f1c5d4da7cf423894d05"
  }
]

:tada::tada::tada:

SORACOMさんのSIMを挿したAK-020を通じて、ElixirのプログラムがQiita APIをたたくことができました :rocket::rocket::rocket:

参考(受信の参考)


Nerves meets SORACOM

復習です。
NervesElixirのIoTで
$\huge{ナウでヤングなcoolなすごいヤツです🚀}$

Raspberry Pi OSNervesの違いです。

Raspberry Pi OS Nerves
ベースのOS Linux 最小規模のLinuxカーネル(※)
パッケージの追加 apt install等でいつでも ビルド時(必要なものはBuildrootで予め追加)
デスクトップ(GUI) アリ ナシ(sshしたら、IExというElixirの世界)

(※)どのくらい最小規模なのかというと、デフォルトではSORACOMさんのSIM入りドングルを挿しても何も通信ができません。
wvdial有りません。

  • pppdありません
  • usb_modeswitchありません

ではどうするかというと、必要なものはBuildrootであらかじめ自分でインストールしましょうという寸法です。

L-02C

IMG_20211204_151423.jpg

SORACOM Air for セルラーのSIMをセットしたL-02CNervesがイゴいているRaspberry Pi 4に挿してセルラー通信を楽しんだことがあります。

上の記事は、多少強引なところはあります。
たぶんいまでもイゴくとはおもいますが、最新のNervesと組み合わせて動作するかどうかはわかりません。
じゃあ、確かめろよ! という話ですが、年の瀬でいろいろ詰まっておりまして手を出せていません(言い訳)。
だってめちゃくちゃ時間かかるのですもの……
ご興味のある方は上記記事をご参考にトライしていただいて、忌憚のないご批正を賜り得ば大幸の至りでございます。

記事を書いたのが、2021/06です。
いまが2021/12なので、それから半年というのは、Nervesの世界ではもう随分昔の話なのです。
だって、なんてったって、$\huge{ヤング!}$ですから進化のスピードが速いのです:rocket::rocket::rocket:

に書いてありました。
IoTつながりということで、

Don’t sleep through life!
ぼーっと生きてんじゃねえよ!

まさにコレです。

ですから、Nervesのことは公式ドキュメントを必ずご確認ください。
朗報です。
もしも詰まったことがあったら、ぜひぜしぜひぜひぜし、NervesJPのSlackワークスペースにご参加ください。
IoTやNervesのことが好きな愉快なfolksたちがあなたの訪れを熱烈歓迎します。

$\huge{れっつじょいなす}$

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

SORACOM Arc x Nerves

これからやってみます。
たぶんできそうな気がしています。
イベント当日までには更新します。
記事はわけるとおもいます。

追記

書きました。


まずは基本通り:interrobang:、Raspberry Pi OSにWireGuardをインストールして、イゴく1ことを確認できました。(ElixirのプログラムがQiita APIのコールに成功:rocket::rocket::rocket:)
応用編としてNerves上でやってみます。
ビルドをしかけていますが、なかなか終わりません :sob:
ということで後日、更新します。


Nervesはじめてみる

Nervesに興味を持っていただけましたでしょうか。
一点難点とでも申しますか、ちょっと準備(開発環境の構築)がたいへんなところがあります。
俺コマンドバリバリっす、みたいな人には全然たいしたことはないとおもいます。
ですが、GUIでポチポチやることが主みたいな人には最初はつらいかもしれません。

楽しむためには最初に越えなければならないハードルというものは何にでもあるものです。
それを越えた先に楽しみは必ず待っています!!!

だって私は、
$\huge{日本マイクロソフト④}$受賞しまして、日本マイクロソフトのYouTubeチャンネルに出させてもらったことがあります。

Nervesをはじめて、楽しんで、Qiitaに記事投稿を続けたから受賞できたのだとおもっています。
そうして私は欲張りですから、こんなところで満足はしていません。
もっともっと楽しいことを引き寄せようとおもっています。
これは私の身に起きた楽しみです。

話をNervesに戻します。
開発環境の構築が、人によってはたいへんなことがあるのですが、$\huge{朗報}$です。
3つお示しします。

①高瀬先生の記事を参考にしながら公式を読み込む

高瀬先生の記事の通りにやればできるはずです!

NervesJPのSlackワークスペースに参加する

さきほどもご紹介した通り、愉快なfolksたちがあなたの訪れを熱烈歓迎します:tada::tada::tada:

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

Nerves Livebook

これがいま一番オススメです。
2021/12/04現在、一番簡単にNervesを体験できます。

準備するものは以下の通りです。

  • パソコン(Windows or macOS)
  • Raspberry Pi Imager
  • ヤル気
  • Raspberry Pi本体
  • microSDカード
  • USBカードリーダ
  • LANケーブル
  • 電源ケーブル

ヤル気から下のものは皆様お持ちでしょうし、パソコンもお持ちでしょうから、実質必要なものはRaspberry Pi Imagerのインストールだけです。
インストーラをダウンロードしてきてポチポチやってくだされ。

Nerves Livebook

説明が面倒になってきた(ごめん:pray::pray_tone1::pray_tone2::pray_tone3::pray_tone4::pray_tone5:)ので、動画を撮っておきました。
こちらを御覧ください。
動画の中で使っているソースコードはここに掲載しておきます。
細かいことはアレコレいいません。

$\huge{感じてください!}$

君は小宇宙(コスモ)を感じたことがあるか:interrobang:

基礎知識

動画をみてもらう前に少し基礎知識をご伝達いたします。
Nerves Livebookを説明する前に話はWebアプリケーション開発に飛びます。

Phoenix

  • 日本のかたは、聖闘士星矢の一輝にならって「フェニックス」と発音しています
  • 海外のかたの言い方は「フィーニックス」みたいに言っているように私には聞こえます
  • Phoenixは、ElixirでWebアプリケーション開発を楽しむフレームワークです。
  • Rubyで言うところのRuby on Rails、PHPで言うところのLaravel、Pythonで言うところのDjangoに相当します

Phoenix LiveView

Phoenix LiveView enables rich, real-time user experiences with server-rendered HTML.

Phoenix Livebook

Livebook is a web application for writing interactive and collaborative code notebooks for Elixir, built with Phoenix LiveView.

PythonのJupyter Notebookをイメージしていただけるとよいです。

Nerves Livebook

なんと、Phoenix LivebookNervesの上でイゴきます1
ざっくり別の言い方をするとRaspberry Piの上でイゴかすことができます。

動画

前置きが長くなりました。
動画です。

手順

ファームウェアを焼く :fire::fire::fire:

  1. Raspberry Pi Imagerを開発マシンにインストールしておきます
  2. https://github.com/livebook-dev/nerves_livebook/releases からお使いのターゲット(ハードウェア)に合致するファームウェアの.zipをダウンロードします
  3. microSDカードを開発マシンに接続し、Raspberry Pi Imagerを起動します
  4. Raspberry Pi Imagerを起動し、CHOOSE OS > Use customと進み、ダウンロードした.zipファイルを指定します
  5. CHOOSE STORAGEにて、これからファームウェアの焼き込みをするmicroSDカードを選択
  6. WRITE:fire::fire::fire:

Livebook

  1. こんがり焼き上がったmicroSDカードをターゲット(ハードウェア)に差し込んで、LANケーブルでネットワークに接続して、ターゲット(ハードウェア)の電源をON
  2. 10秒〜30秒程度待って、ping nerves.localが通ることを確認する
  3. ブラウザでhttp://nerves.localへアクセス
  4. パスワードはnerves
  5. New notebookからElixirのプログラミングを楽しむ

ソースコード

動画で使ったソースコードを掲載しておきます。

メモリ使用量のグラフを描く

alias VegaLite, as: Vl

memory = [
  total: :red,
  processes: :yellow,
  atom: :green,
  binary: :pink,
  code: :orange,
  ets: :blue
]

layers = 
  for {layer, color} <- memory do
    Vl.new()
    |> Vl.mark(:line)
    |> Vl.encode_field(:x, "iteration", type: :quantitative)
    |> Vl.encode_field(:y, Atom.to_string(layer), type: :quantitative, title: "Memory usage (MB)")
    |> Vl.encode(:color, value: color, datum: Atom.to_string(layer))
  end

widget = Vl.new(width: 500, height: 200)
  |> Vl.layers(layers)
  |> Kino.VegaLite.new()
Kino.VegaLite.periodically(widget, 200, 0, fn i ->
  point =
    :erlang.memory()
    |> Enum.map(fn {type, bytes} -> {type, bytes / 1_000_000} end)
    |> Map.new()
    |> Map.put(:iteration, i)

  Kino.VegaLite.push(widget, point, window: 1000)
  {:cont, i + 1}
end)
for i <- 1..1_000_000 do
  :"atom#{i}"
end

Lチカ

defmodule Example do
  use GenServer

  @pin_number 18

  def init(state) do
    {:ok, gpio} = Circuits.GPIO.open(@pin_number, :output)
    set_interval()
    {:ok, {gpio, state}}
  end

  defp set_interval() do
    Process.send_after(self(), :tick, 1000)
  end

  def handle_info(:tick, {gpio, state}) do
    Circuits.GPIO.write(gpio, state)
    set_interval()
    {:noreply, {gpio, flip(state)}}
  end

  defp flip(1), do: 0
  defp flip(0), do: 1
end

GenServer.start_link(Example, 0)

Wrapping up :lgtm::lgtm::lgtm::lgtm::lgtm:

まとめます。

2021/12/14(火) 19:30〜21:00開催の「SORACOM UG x NervesJP #1 ~Hello, world!~」でご説明します。

サマリ

We are the Alchemists, my friends!

Nervesはナウでヤングなcoolなすごいやつです🚀

Elixirは、日本語に訳すと不老不死の霊薬です。
そのElixirと名付けられたプログラミング言語の使い手は、**Alchemist(錬金術師)**と尊称されます。

説明
Elixir Fun Elixir is a dynamic, functional language for building scalable and maintainable applications.
Nerves IoT Nerves is the open-source platform and infrastructure you need to build, deploy, and securely manage your fleet of IoT devices at speed and scale.
Phoenix Web Build rich, interactive web applications quickly, with less code and fewer moving parts. Join our growing community of developers using Phoenix to craft APIs, HTML5 apps and more, for fun or at scale.
LiveView Web Phoenix LiveView enables rich, real-time user experiences with server-rendered HTML.
Livebook Web Livebook is a web application for writing interactive and collaborative code notebooks for Elixir, built with Phoenix LiveView.
Nerves Livebook IoT The Nerves Livebook firmware lets you try out the Nerves projects on real hardware without needing to build anything. Within minutes, you'll have a Raspberry Pi or Beaglebone running Nerves. You'll be able to run code in Livebook and work through Nerves tutorials from the comfort of your browser.
Nx AI Multi-dimensional arrays (tensors) and numerical definitions for Elixir

NervesJP

NervesJPのSlackワークスペースにご参加ください。
IoTやNervesのことが好きな愉快なfolksたちがあなたの訪れを熱烈歓迎します!!!
$\huge{れっつじょいなす}$

朗報です。

#NervesJPアドベントカレンダーにはまだ若干の空きがございます。

ぶっちゃけNerves関係ないけど,こんなんやってみたよ! (ElixirかIoTくらいは絡んでいればおけ

ですので、みなさま奮ってご参加ください。
ネタがかぶったとか全然おけ:ok:です。
2番煎じ、3番煎じ、N番煎じ万歳です。
そんな細かいことは気にしなくていいです。
その積み重ねは発展です。進展です。:rocket::rocket::rocket:
そうやって人類は進化を続けているのです。

IoTを楽しむなら

Nerves

Webアプリケーションを楽しむなら

Phoenix

AIを楽しむなら

Nx + Livebook

そうです。
Elixirでぜーんぶできちゃいます。


追記

一体、ぜんたいどういうことか、私にはさっぱりわかりませんが、Raspberry PiなどのデバイスがなくてもNervesをはじめられるとうのは、

スクリーンショット 2021-12-10 8.15.09.png

$\huge{すぎょい!!}$

です。
イベント当日、@ma2shita さんからお話を聞けることを楽しみにしています!


明日は、@ciniml さんによる「WireGuard for ESP32あたり?」です。
WireGuardの話とのことで、興味津々です:bangbang::bangbang::bangbang:
楽しみにしています〜:thumbsup::thumbsup::thumbsup::thumbsup::thumbsup::thumbsup::thumbsup_tone1::thumbsup_tone2::thumbsup_tone3::thumbsup_tone4::thumbsup_tone5:

  1. 「動いています」の意。おそらく西日本の方言、たぶん。NervesJPではおなじみ。少し古いオートレースの映像ですが、実況アナウンサーが「針3イゴきます」とはっきり言っています。https://autorace.jp/netstadium/SearchMovie/Movie/iizuka?date=2017-01-04&p=5&race_number=11&pg= 2 3 4 5

  2. ElixirがRubyに似ているというのは私だけの感じ方ではなく、作者であるJosé Valimさん自身が"The main, the top three influences are Erlang, Ruby, and Clojure."とおっしゃっており、Elixirに影響を与えた言語としてRubyが挙げられています。またRuby on Railsを使われたことがある方ならご存知だとおもいます、あのdevise最初のコミットJosé Valimさんによるものです。 2

  3. 大時計の針のこと。針がイゴいてある地点まで到達すると選手はスタートを切って良い発走の合図。針がイゴきはじめると(おそらく)選手は緊張するし、スタートはその後のレース展開に大きく影響するので、車券を握りしめている観客たちがもっとも緊張する瞬間であるため、先の尖った鋭いものを連想させる針は緊張の暗喩としても言い得て妙。

  4. ご家庭のルータにLANケーブルやWi-Fiで接続している使い方を指しています。

17
2
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
17
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?