Newer
Older
m5stickcplus / week1.rst
@Motoki Motoki 22 days ago 36 KB Plus2
.. .. |br| raw:: html
..    <br />


1週目
========================

.. note:: 以下の項目について、**全員がすべてを試す必要はありません** (時間が足りなくなる可能性が高いです)。役割分担をして、手分けして動作の確認と、同じ班のメンバーへの動作説明・共有をして、すすめてください。必要に応じて、検証する機能を絞り込んでください。**なお、一部のサンプルプログラムは編集しないと意図通りに動作しない場合があります。**

電源の切り方・入れ方
----------------------------------------

- 電源を入れるには、電源ボタンを、**2秒間** 長押しします。
- 電源を **完全に** 切るには、**USBを外したうえで** 、電源ボタン(M5と書かれたAボタンの左側面)を、**5秒間** 長押しします。緑色のLEDが点灯したら、ボタンを離します。
- 動画をみる→ https://youtu.be/Lo1jZbAeT8Y
- `M5StickCガイド <https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/M5StickC_Guide_Japanese.pdf>`_ の、4ページ目、ハードウェアの概要も参考になります。 

プログラムの書き込み方
--------------------------------------

`講義システム <https://lattr.istlab.info/ino>`_ のサンプルソースコードについて、ビルド済みバイナリをダウンロードして書き込むには、Git Bashやターミナルから、以下のように入力してください。 :: 

	$ cd 
	$ cd m5scp2_exp    (実験フォルダに移動)
	$ ./upload_by_hash.sh (ハッシュ値)
	例→ ./upload_by_hash.sh 2f4b280a7701
	$ ./upload_by_hash.sh のように、ハッシュ値を省略すると、Enter the hash: と訊かれます。

ハッシュ値は、講義システムのHash 列をクリックすると、クリップボードにコピーされます。

自分が編集したソースコードについては、ハッシュ値を毎回指定するよりも、`こちらの(方法2) <https://scrapbox.io/iot-programming/Build_on_Server>`_ を使うのが便利です。(ハッシュ値はソースコードを修正するたびに変わります。)


以下のサンプルを参考に、コードを書くときのヒントと注意
----------------------------------------------------------------------------------------------------------

- **以下のサンプルソースコードのキャプション「リスト x  src/XXXX.ino」のXXXX部分をみて、講義システムの該当するサンプルを参照してください。** たとえば、リスト 1 src/serial01.ino だったら、Name: serial01 になります。
  - 2025年では、M5Unified.h を使用するため、``#include <M5StickCPlus.h>`` とかかれているプログラムはコンパイルできません。 参考:`2024以前のサイト <https://cit.istlab.info/m5stickcplus/week1.html>`_
  - M5Unified.h を用いるときの ``M5.begin()`` の書き方は以前と異なります。講義システムで、ソースコードの新規作成時に生成されるコードを参考にしてください。
  - 一部のサンプルについては、需要がすくないこともあり、M5Unified.h 版のサンプルはまだ準備していません。
- 画面(LCD)をつかうプログラムを書き込んだあと、画面を使わないプログラムを書き込むと、前のプログラムの画面が残ることがあります。また、画面を使わないプログラムは実行していることが分かりにくいので、注意してください。LCDが付いているとすこしだけ明るくなります。
- Cプログラムの命令(関数等)について、大文字と小文字は厳密に区別されます。
- その他、よくある質問は、:ref:`FAQ` に随時追記していきます。

Serial通信
---------------------------------------

:numref:`serial01` (``serial01``) は、シリアルモニタおよび画面へ文字列を出力する例です。プログラムでは、まずグローバル変数 num を 0 に設定したうえで、``setup()`` 関数が1回実行されます。その後、``loop()`` 関数が繰り返し実行されます。このあたりは Processing言語のsetup, draw と似ています。( `ChatGPTによると、Arduino側が参考にしたらしい <https://chatgpt.com/share/67ecdfe2-4000-8010-8137-f271fe08b460>`_ )

プログラムの出力をみるには、「シリアルモニタ」をひらいてください。**(ただし、シリアルモニタをひらきっぱなしにすると、プログラムの書き込みができません。ターミナル内であればCTRL+Cで終了してください。)**
Serialクラスには、print()関数、println()関数、 printf()関数があります。Serial通信を用いると、デバイス側の変数の値や状況を、PC側で確認するプログラムを書くことができます。
なお、文字化けしたり、???と表示されるときは、シリアルモニタの通信速度(ボーレート)を ``115200 bps`` に変更し、プログラムで設定した速度と一致させてください。 
`→動画 serial01 <https://drive.google.com/file/d/1ADluPDkIUoT1FblQ8O2055mJjnE01Zj9/view?usp=sharing>`_

.. literalinclude:: src/serial01.ino
  :caption: 
  :name: serial01
  :language: arduino
  :emphasize-lines: 5,14,16
  :linenos:

シリアルモニタは、デバイスからの出力を確認するだけではなく、デバイスに文字を送信することもできます。:numref:`serial02` (``serial02``)は、シリアルモニタを介して、PCからデバイスに文字を送信する例です。シリアルモニタ上部のテキストエリアから 0 を送信すると、カウンタ num をリセットします。19〜23行目で、PCから送信された文字を、1文字ずつ ``Serial.read()`` で読み取って、配列 ``buf`` に格納しています。PCから複数の文字を1回で送信すると、 ``Serial.available()`` は連続して 1 (=true) を返しますので、1回に送信された文字をすべて ``buf`` に格納することができます。ちなみに、シリアルモニタで送信された文字列の最後は、改行コード(10)が付与されます。
`→動画 serial02 <https://drive.google.com/file/d/12Cna7jnipuzeDQcHSM4xofy33iExYpJK/view?usp=sharing>`_

.. literalinclude:: src/serial02.ino
  :caption: 
  :name: serial02
  :language: arduino
  :emphasize-lines: 19-23
  :linenos:

.. note:: 25行目の処理を忘れると、どうなるでしょうか? また、buf が溢れると、なにが起きるでしょうか?プログラムを書くときは、常に例外的な事象がおきる可能性を考えておきましょう。ただし、全ての例外的な事象に対処するのは困難な場合が多いです。


本体の液晶ディスプレイ(LCD)
-------------------------------------------

- M5StickC Plus2動作確認 その1 画面周り <https://lang-ship.com/blog/work/m5stack-m5stickc-plus2-1/> が詳しいです。
- :numref:`lcd01` のサンプル(``lcd01``)では、外側から内側に向かって、10ピクセルずつ余白を残しながら、色を変えて塗りつぶしています。その後、8,16,26ピクセルフォント(それぞれ1,2,4番)をsetCursor()関数で指定して、print()関数で文字列を描画しています。
- **M5Unified.h では、M5.Lcd → M5.Display になりました。**

.. literalinclude:: src/lcd01.ino
  :caption: 
  :name: lcd01
  :language: arduino
  :linenos:
..  :emphasize-lines: 10-14

.. figure:: images/lcd_sample.png
   :scale: 100%

画面の向きは、setRotation() 関数で、設定します。0〜3までの整数で、指定します。

.. figure:: images/setRotation.gif
   :scale: 100%

.. note:: フォントの種類を設定するときは、setFont() をつかいます。setTextSize() は、指定したフォントを「縦横何倍に引き伸ばして表示するか?」を設定します。ここの「倍率」を2以上に増やすと、若干ギザギザが目立つようになります。 `M5Unified.h をつかうと、日本語も表示できるようになりました。 <https://lang-ship.com/blog/work/m5stack-m5stickc-plus2-1/#toc8>`_  


本体のボタン
------------------------------------------

本体には、3つのボタンがあります。
(参考:`M5StickCガイド <https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/M5StickC_Guide_Japanese.pdf>`_ の、4ページ目、ハードウェアの概要)

:numref:`button01` に、ボタンをおしたらシリアルモニタに表示する例を示します。ボタンも、M5StickCPlus の機能なので、 ``#include <M5StickCPlus.h>`` を指定し、``M5.begin()`` を呼び出しておく必要があります。 **また、ボタンの状態を読み出して、ボタンオブジェクト(BtnA,BtnB)に設定するために、** ``M5.update()``  **を、ループのなかに入れておく必要があります** ( `update()の定義 <https://github.com/m5stack/M5StickC-Plus/blob/master/src/M5StickCPlus.cpp>`_ )。長押しと普通押しを区別したい場合は、ifで確認する順番(長押しを先に判定する)が重要です。

.. literalinclude:: src/button01.ino
  :caption: 
  :name: button01
  :language: arduino
  :linenos:
..  :emphasize-lines: 10-14

.. note:: `非公式リファレンスの「ボタン管理(Button)」 <https://lang-ship.com/reference/unofficial/M5StickC/Class/Button>`_ も参考になります。


文字列の扱い、16進数や10進数の変換
-------------------------------------------------------------

:numref:`str01` に、文字列から整数に変換したり、16進数表現の文字列を10進数に変換する例を示します。
Stringクラスを用いると、char配列 / byte配列 / 小文字変換や、空白改行の削除(ただし文字列の前後のみ)、部分文字列の取得などができます。
元のデータが保持されるメソッドと、破壊されるメソッドがあることに注意しましょう。
M5系デバイスのLCDでは、色を16ビット値(赤5bit/緑6bit/青5bit)で表現しています。 ``M5.Lcd.color565()`` という関数もありますが、ここではビット演算の理解を深めるため、関数を定義しています。
C言語のポインタや、シフト演算などを理解していると、このような処理を円滑に記述できるようになります。
`→動画 str01 <https://drive.google.com/file/d/15gusIhhWg8lxBa8hQmrM5L9h1ppAb4Tl/view?usp=sharing>`_


.. literalinclude:: src/str01.ino
  :caption: 
  :name: str01
  :language: arduino
  :linenos:
..  :emphasize-lines: 10-14

.. note::  `変数の型一覧 <https://nobita-rx7.hatenablog.com/entry/28248811>`_ uint16_t は unsigned int 16bit type の略です。参考:`M5StackのLCDディスプレイの色をRGBで指定する。 <https://qiita.com/nnn112358/items/ea6b5e81623ba690343c>`_  


ブザー(Beep)
-------------------------------------

:numref:`sound01` は、内蔵Beepを鳴らすサンプルです。音の周波数は、最初の1オクターブ (f[0]〜f[6]) のみを配列宣言時に指定し、残りの3オクターブはsetup()内で、倍数を計算して配列に設定しています。

Arduino標準tone関数 をつかう方法と、M5.Speaker.tone() をつかう方法があります。`Lang-ship <https://lang-ship.com/blog/work/m5stack-m5stickc-plus2-2/#toc8>`_ に詳しいです。Arduino標準tone関数をつかうと、画面が暗くなってしまうので、暫定的に M5.Display.setBrightness(255); をいれています。

.. literalinclude:: src/sound01.ino
  :caption: 
  :name: sound01
  :language: arduino
  :linenos:
  :emphasize-lines: 18-22,29-30


内蔵LED(赤・赤外) 
----------------------------------------------------------

M5StickCPlus2には、2つのLEDランプが内蔵されています。GPIO(General Purpose Input/Output)の19番 には、赤色LED および 赤外LEDが接続されています。
場所は、USBケーブルを挿す面の反対側の面にある、小さな穴2つの内側です。

:numref:`led01` は、これらのLEDを点滅させるシンプルなプログラムです。

.. literalinclude:: src/led01.ino
  :caption: 
  :name: led01
  :language: arduino
  :linenos:
..  :emphasize-lines: 10-14


外部のLED等を接続(Plus2未確認)
--------------------------------------------------------------

外部のLED等は、 G0, G25, G26 に接続します。(GROVE端子のG32, G33も利用できます。)
サンプルプログラムは、:numref:`led01` と同様です。PIN 番号を、0 / 25 / 26 に変更してください。

ブレッドボードに、抵抗とLEDを直列に接続します。
LEDの長い足のほうに、G0 (or G25 or G26) をジャンパワイヤで接続し、反対側(短い足)のほうを、GND (グランド)に接続します。
ポートを「HIGH」にすると、3.3Vの電圧がかかります。

.. figure:: images/SimpleLED_m5s.png
   :scale: 60%

.. warning:: LEDのみで回路を構成しないよう注意!一般に、LEDは抵抗をつないで使用しないと、故障したり、最悪の場合破裂したりして危険です。ただし、抵抗入りLEDであれば問題ありません。

PWM (Pulse Width Modulation)
-------------------------------------------------------------------

パルス幅変調とは、信号の周期Tに対する、パルスの幅を変化させる方法です。
直流モータの速度制御や、サーボモータの制御、LEDの明度調整などに使われます。

.. figure:: images/pwm.jpg
   :scale: 50%

`Copyright (C) 2021 by Junichi Akita, CC BY-SA <https://www.dropbox.com/s/p6und70og1rzgrn/PWM.svg?dl=0>`_


:numref:`pwm01` は、赤色LEDを点滅させるシンプルなプログラムです。 ``ledcSetup(チャンネル, 周波数, 分解能)`` で、PWMチャンネル(0〜3)に対する周波数と分解能設定をし、 ``lecAttachPin(ピン番号, チャンネル)`` で、PWMチャンネルを適用する出力ピンを選択します。そのあと、 ``ledcWrite(チャンネル, 値)`` で、分解能で設定したビットで表現できる最大値+1以下の値を指定することで、デューティー比を設定します。:numref:`pwm01` の場合、分解能で8bitを指定したので、0〜256 の値が、デューティー比 0〜1 のパルス生成に対応します。

.. literalinclude:: src/pwm01.ino
  :caption: 
  :name: pwm01
  :language: arduino
  :linenos:
  :emphasize-lines: 6-8, 16,28,33

.. note:: ソースコードのコメントにも書いていますが、周波数を小さくする(20〜30程度)と、どうなるでしょうか?ぜひやってみてください。

サーボモータ(サーボハット利用)(Plus2未確認)
-----------------------------------------------------------------------------

:numref:`servohat01` は、`サーボハット <https://www.switch-science.com/catalog/6076>`_ のサーボモータを動かすサンプルです。サーボモータの制御はPWMですので、基本的に、上のPWMとやっていることは同じです。おまけとして、LEDも点灯させました。``map関数`` は、Arduinoで使える関数で、範囲に対応する値を変換するときに使います。ここでは、サーボ制御で用いる5〜33の値を、LED制御の値0〜256に変換しています。

.. figure:: images/servohat.png
   :scale: 70%


.. literalinclude:: src/servohat01.ino
  :caption: 
  :name: servohat01
  :language: arduino
  :linenos:
..  :emphasize-lines: 6-7, 15,19



アナログセンサを接続 (ADC)(Plus2未確認)
-----------------------------------------------------------------------------

ADCは、Analog to Digital Converter の意味です。
``analogRead(PIN)`` は、PIN番ピンの電圧(0~3.3V)を、0〜4095 の値で返します。一般に、抵抗値が変化するタイプのセンサは、この方法をつかって、読み取ることができます。

.. warning:: `ADC <https://lang-ship.com/reference/unofficial/M5StickC/Peripherals/ADC>`_ にかいてあるように、G26, G32, G33, G36のみ使えます。G26は、無線利用時には使えません。

:numref:`cds01` はCdSセル(照度センサ) の値を読み取るサンプルです。

.. literalinclude:: src/cds01.ino
  :caption: 
  :name: cds01
  :language: arduino
  :linenos:
..  :emphasize-lines: 6-7, 15,19


圧力センサや、曲げセンサも、同様の方法で利用することができます。


加速度センサ
-----------------------------------------

:numref:`imu01` は、内蔵されている加速度センサ(MPU6886) の値を表示するサンプルです。ちなみに、IMUとは、Inertial Measurement Unit: 慣性計測装置 の略です。
「ツール」→「シリアルプロッタ」をひらくと、値をグラフでみることができます。(通信速度設定を、115200bps に設定してください)
ジャイロセンサの値も取得可能です。


.. literalinclude:: src/imu01.ino
  :caption: 
  :name: imu01
  :language: arduino
  :linenos:
  :emphasize-lines: 14,16


赤外(InfraRed)リモコン(Plus2未確認)
------------------------------------------------------------------------------

.. note:: 実験時間中に、送信のみを手軽に試したい場合は、付録の :numref:`irsend02` を書き込んで、教員を呼んでください。

信号の読み取り
~~~~~~~~~~~~~~~~~~~~~

赤外線リモコン受信モジュールが必要です。ここでは、`GP1UXC41QS <https://akizukidenshi.com/catalog/g/gI-06487>`_ を前提に、話をすすめます。

また、準備として、ライブラリマネージャにて、IRremoteESP8266 をインストールします。ちなみに、テストしたバージョンは2.7.15でした。 ``2.7.15`` **より新しいバージョンだと、失敗する場合があります。もし最新版を入れてうまくいかない場合はダウングレードしてください。**

.. note:: ライブラリマネージャは、「スケッチ」→「ライブラリをインクルード」→「ライブラリを管理…」で、ひらきます。

ファイルメニュー → スケッチ例 → IRremoteESP8266 → IRrecvDumpV2 を選択します。

ブレッドボードに、以下の図のように配線します。Pとかいてある面が、受光器が出っ張っている面だとおもってください。47Ωの抵抗を、5Vとの間に入れます。
**赤外線リモコン受信モジュールに接続したピンを** ``const uint16_t kRecvPin = 36;`` **として設定します。**
受光器にリモコンを向けて、ボタンを押すと、シリアルモニタに情報が表示されます。ここでは、Protocol : NEC , Code 0x2FD48B7 (32 Bits) と表示されたとします。この数値(uint32_t)を覚えておきます。

.. figure:: images/ir_sensor_47owm.png
   :scale: 60%


信号の送信
~~~~~~~~~~~~~~~~~~~~~~~~

内蔵の赤外LEDを用いて、信号を送信する例を :numref:`irsend01` に示します。
内蔵の赤外LED光はあまり強くないため、50cm程度まで近づかないと反応しない場合があります。また、太陽光の影響を強く受けます。
単体の赤外LEDを接続して用いると、距離を伸ばすことができます。

なお、NECフォーマットではない赤外線リモコンの通信フォーマットについては、 `赤外線リモコンの通信フォーマット <http://elm-chan.org/docs/ir_format.html>`_ や、スケッチ例を参考にしてください。

.. literalinclude:: src/irsend01.ino
  :caption: 
  :name: irsend01
  :language: arduino
  :linenos:
..  :emphasize-lines: 6-7, 15,19




スプライト表示(TFT_eSprite クラス)
-------------------------------------------------------------------------------------------------

M5.Display ではじまる画面描画命令は、表示されている画面に対して、直接描画するため、画面のちらつきが生じることがあります。
LGFX_Sprite クラスを用いると、画面に表示されていない仮想画面(オフスクリーン)を作成して、そこに対して描画を行っておき、さいごにまとめて仮想画面の内容をメインの画面(Lcd)に描画することができます。
これにより、画面のちらつきが抑制できます(ダブルバッファリング)。
ちらつき防止のほかに、なめらかに画面スクロールする目的で、実画面よりも大きい仮想画面を作成しておき、その一部の領域のみを実画面に描画するといった用途にも使われます。

LGFX_Sprite クラスは、M5.Displayクラスとほぼ同様の描画関数を備えています。:numref:`sprite01` は、スプライトの効果あり/なしをAボタンで切り替えて確認できるサンプルプログラムです。
`→動画 sprite01 <https://drive.google.com/file/d/1NIMYSQ2n01aGo71O-tkVfiB088s9DN5f/view?usp=sharing>`_

.. literalinclude:: src/sprite01.ino
  :caption: 
  :name: sprite01
  :language: arduino
  :linenos:
..  :emphasize-lines: 6-7, 15,19


Wifi 接続
--------------------------------------

:numref:`wifi01` は、Wifi接続のサンプルです。``ssid`` と ``password`` には、環境にあわせたものを入力してください。接続すると、m5デバイスのIPアドレスを画面に表示します。本当にWifi接続できたかどうかを、PCのターミナルからpingを打つなどして、確認してみましょう。(Windowsの場合、コマンドプロンプトをひらき、ping のあとに、半角スペースと、確認したいIPアドレスを入れます)

.. literalinclude:: src/wifi01.ino
  :caption: 
  :name: wifi01
  :language: arduino
  :linenos:
  :emphasize-lines: 4-5,17-21

注:以前掲載していた、`Telnetサーバを起動する例 <https://cit.istlab.info/m5stickcplus/week1.html#wifi>`_ については省略しました。



NTPサーバ (Network Time Protocol) と時刻の取得
-----------------------------------------------------------------------------------------------------------------------------------

M5StickCPlusには、システム時間(localTime)と、RTC(リアルタイムクロック:時計の機能を備えたICのこと)の2種類の時計があります。システム時間は、システムリセット(再起動)のたびに、時刻もリセットされますが、後者のRTCはリセットされません。

:numref:`ntp01` に、NTPサーバを使ってシステム時間の修正をしたのち、システム時間を1秒ごとに取得して、シリアルモニタに表示する例を示します。configTime()でNTPサーバを設定しておくと、1時間に1回、NTPサーバに接続して、時刻修正します。
`→動画 ntp01 <https://drive.google.com/file/d/1vUr6b5I_NZcJS3cvXXhbySj8AiUyU3TO/view?usp=sharing>`_


.. literalinclude:: src/ntp01.ino
  :caption: 
  :name: ntp01
  :language: arduino
  :linenos:
  :emphasize-lines: 24-25

.. warning:: configTime() を行わない状況で、システム時間(localTime)を取得しようとすると、取得に数秒ほど時間がかかります。

:numref:`rtc01` は、RTCに時刻を設定し、表示するサンプルです。ここではWifi&NTPで時刻同期してから、RTCに設定していますが、Wifiネットワークに接続できない場合は、RTCを利用することが望ましいです。

.. literalinclude:: src/rtc01.ino
  :caption: 
  :name: rtc01
  :language: arduino
  :linenos:
..  :emphasize-lines: 5

.. note:: RTCに一旦時刻を設定しておくと、初期状態で書き込まれているプログラムFactoryTest の「BMP8563 RTC Time」でも、その時刻が表示されるようになります。

WebClient
---------------------------------------

:numref:`webclient01` は、HTTP通信で天気予報Web APIに接続するサンプルです。HTTPClientクラスを用いると、ブラウザでURLを指定してWebページを開くように、WebサーバにGETメソッドやPOSTメソッドでリクエストを送信して、ステータスコードやレスポンスを取得することができます。このサンプルでは、シリアルコンソールに、天気予報をJSON形式で表示します。JSON(ジェイソン)とは、Javascriptのオブジェクトの形式でデータを表現する記法です。


.. literalinclude:: src/webclient01.ino
  :caption: 
  :name: webclient01
  :language: arduino
  :linenos:
..  :emphasize-lines: 5

.. role:: strike


.. warning:: https (SSL) 通信でPOSTメソッドで送信する場合は、付録→LINE Notify (:numref:`line01`)を参考にしてください。(注:Plus2未確認)


WebServer
----------------------------------------

:numref:`webserver01` は、80番ポートでHTTPでの通信を待ち受け(listenし)て、クライアントからの接続情報(ヘッダ情報)を返すWebサーバの例です。クライアント(ブラウザ)からのリクエスト行のうち、 ``GET`` または ``POST`` ではじまる行があれば、変数 ``meth`` に格納します。つまり、変数 ``meth`` には、ブラウザで発行したリクエストのURLが含まれることになります。(methはmethodの略。)POSTメソッドで送信されていれば、リクエストボディに記述されたデータを変数 ``post`` に格納します。 クライアント(ブラウザ)に返す「レスポンス」は、 ``client.println()`` で送信します。最後に、``client.stop()`` で、サーバ側から接続を切断します。
`→動画 httpserver01 <https://drive.google.com/file/d/1eyvI234icBSim9vFfhKeEu93iiE8LWpT/view?usp=sharing>`_



.. literalinclude:: src/webserver01.ino
  :caption: 
  :name: webserver01
  :language: arduino
  :linenos:
  :emphasize-lines: 33,38,42,59,96

センサデータを返すだけなら問題ありませんが、クライアントからのデータを GET / POST で受信して処理する場合は、 ``key1=val1&key2=val2`` のような文字列を要素に分解する必要がでてきます。あまり深入りしませんが、正規表現で文字列を照合・抽出する ``Regexp`` や、抽出した結果をハッシュ/辞書として保存する ``Dictionary`` ライブラリを導入すると、複雑なリクエストやデータを扱いやすくなるでしょう。
:numref:`regexp01` に、RGBの色指定文字列などのリクエスト文字列をパースして辞書に追加するプログラムの断片を示します。**注意:このプログラム(** :numref:`regexp01` **)は、単体では動作しません。また、Plus2未確認です。**

.. literalinclude:: src/regexp01.ino
  :caption: 
  :name: regexp01
  :language: arduino
  :linenos:


MQTT 
-------------------------------------------

MQTT(Message Queue Telemetry Transport) は、センサデータをデバイス間で共有・流通させるときなどに使われる、軽量のメッセージ送受信プロトコルです。
ここで、「軽量」とは、HTTPに比べて、ヘッダ部分のデータが少ない、という意味です。
MQTTでは、サーバのことを「ブローカ」と呼びます。ここでは、データを送信するPublisherと、データを受信するSubscriberの2つのデバイスと、ブローカの3つの構成要素で説明します。

Publisherは、ブローカに接続しデータを送信します。このとき、 **「トピック」** と呼ばれる、データの登録先を文字列で指定します。また、retainデータとして送信するかどうかも指定します。retainとは「保持・維持」の意味で、最後に送ったデータをブローカに残しておきたい場合、rateinデータとします。
Subscriberは、ブローカに接続し、トピックをサブスクライブ(購読)します。このとき、トピックに書き込まれているretainデータがあれば、最初にそのデータを受信します。retainデータがなければ、Publisherが新しいデータをトピックに送信したタイミングで、データを受信します。
このような通信手段(プロトコル)を、パブリッシュ/サブスクライブモデル と呼びます。`参考サイト:
IoT初心者向け!「MQTT」について簡単にまとめてみる <https://tech-blog.rakus.co.jp/entry/20180912/mqtt/iot/beginner>`_


MQTT Publish
~~~~~~~~~~~~~~~~~~~~~~~~

:numref:`mqtt01pub` は、MQTT Publisherのサンプルです。実験用ブローカ(mqtt.istlab.info)に接続して、 ``jikken`` というトピックにデータ(シリアルコンソールで送信した文字列)を書き込みます。 


.. literalinclude:: src/mqtt01pub.ino
  :caption: 
  :name: mqtt01pub
  :language: arduino
  :linenos:


MQTT Subscribe
~~~~~~~~~~~~~~~~~~~~~~~~

:numref:`mqtt01sub` は、MQTT Subscriberのサンプルです。実験用ブローカ(mqtt.istlab.info)に接続して、 ワイルドカード文字 ``+`` (プラス) は、任意の単一トピック階層にマッチするすべてのトピックを購読します。ワイルドカード文字 ``#`` は / で区切られた下位トピック階層も含めてマッチします。   JSON形式のデータを処理するときは、ArduinoJsonをつかってパージング(deserialize) すると便利です(90行目以降にサンプルがあります)。

.. literalinclude:: src/mqtt01sub.ino
  :caption: 
  :name: mqtt01sub
  :language: arduino
  :linenos:
  :emphasize-lines: 36-37

トピック名とワイルドカード
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

上述のとおり、サブスクライブするときのトピック名には、ワイルドカード文字が指定できます。例えば、 ``office/+`` 
と指定すると、 ``office/temp`` にも ``office/humid`` にもマッチします。`参考:MQTT トピック <https://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/topics.html>`_


mosquitto コマンド例
~~~~~~~~~~~~~~~~~~~~~~~~~~

mosquitto は、オープンソースのMQTT Broker/Client 実装の1つです。参考までに、mosquitto クライアントを使用するコマンド例を示します。:: 

   #(パブリッシュ。retainなし: -m "メッセージ" ) 
      mosquitto_pub -h mqtt.istlab.info -u ex1 -P PASS -t office/temp -m "data or message"  
   #(パブリッシュ。retainあり: -r )
      mosquitto_pub -h mqtt.istlab.info -u ex1 -P PASS -t office/temp -m "data or message" -r 
   #(retainデータを削除。-n : send a null (zero length) message.)
      mosquitto_pub -h mqtt.istlab.info -u ex1 -P PASS -t office/temp -n -r 
   #(サブスクライブ)
      mosquitto_sub -h mqtt.istlab.info -u ex1 -P PASS -t office/temp  
  
3つ目の例に示すように、長さ0のretainデータを送信すると、retainデータを削除できます。

.. note:: サンプルでは文字列(ASCII)データを送受信していますが、値をbyte配列・バイナリで送受信することもできます。


Bluetooth Serial Protocol Profile (SPP) (Plus2未確認)
---------------------------------------------------------------------------------------------------

「Serial通信」では、開発用PCのシリアルコンソールをつかって、USB接続を介して、文字列を送受信しました。
「Wifi接続」のTelnetサーバでは、TCP/IP通信上でのTelnet接続を介して、文字列を送受信しました。

ここでは、Bluetoothを用いて、上記と同様、2台のデバイス間で文字列を送受信する例を示します。Bluetoth では、機器同士は同一の「プロファイル」という通信方式に対応している必要があります。Serial Protocol Profile は、Bluetooth無線通信で仮想シリアル接続を可能にするプロファイルです。

.. note:: 基本的に `ESP32同士をBluetoothシリアルでつないでみる <http://robooptions.blog.fc2.com/blog-entry-9.html>`_ と同じです。その他、一般的なBluetoothとBLEについての解説は、`ESP32による近距離無線通信の実験② BLE通信 <http://marchan.e5.valueserver.jp/cabin/comp/jbox/arc212/index212.html>`_ が参考になります。

(1) スレーブ用デバイスのMACアドレスを調べる
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:numref:`bts01_slave` を、1台目のM5StickCPlusで実行し、BluetoothのMACアドレスを調べます。

.. literalinclude:: src/bts01_slave.ino
  :caption: 
  :name: bts01_slave
  :language: arduino
  :linenos:

(2) マスタ用デバイスにサンプルを書き込む
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

:numref:`bts01_master` を、2台目のM5StickCPlusで実行します。このとき、ハイライトした行のところに、(1)で調べたMACアドレスを指定します。少し時間がかかりますが、デバイス名で接続先を指定することもできます。起動するときは、スレーブ側を先に起動しておき、あとでマスタ側を起動してください。

.. literalinclude:: src/bts01_master.ino
  :caption: 
  :name: bts01_master
  :language: arduino
  :linenos:
  :emphasize-lines: 30



その他のBluetooth利用例
--------------------------------------

BLEHIDDeviceを用いると、Human Interface Device(HID) Profileを導入して、マウスやキーボードの代用品が作成できます。(詳細は省略します。)


ESP-NOW による通信
-------------------------------------------

Wifi (802.11) を使いますが、基地局に接続せず、MACアドレス宛に直接通信する方式です。詳細は
https://lang-ship.com/blog/work/m5stickc-esp-now-1/ を参照してください。

:numref:`espnow02` に、MACアドレスにFF:FF:FF:FF:FF:FF を指定し、ブロードキャストする例を示します。
ブロードキャストの場合、送信先のMACアドレスを指定しなくてよいので楽ですが、他の班のデバイスからも受信できます。``espnow02`` は、数値データのみを送受信する例です。

.. literalinclude:: src/espnow02.ino
  :caption: 
  :name: espnow02
  :language: arduino
  :linenos:

なお、``espnow03`` は、整数・小数・文字列をまとめてbyte (uint8_t) 配列に埋め込んで送信し、あとで取り出して利用する例です。(講義システムで確認してください。)


Preference・電源制御
-------------------------------------------

再起動をすると、プログラムがリセットされて、通常の変数や配列データは消えてしまいますが、Preferenceを用いると、不揮発性のフラッシュ領域を使ってデータを保存・復元することができます。
すこし下にある ``pref01`` (:numref:`pref01`)を参照してください。ボタンを押した回数が、本体に記録され、再起動しても継続されます。

以下に、電源に関連する命令を示します。

- ``ESP.restart()`` で、再起動します。
- ``M5.Power.powerOff()`` で、電源OFFします。(ただし、USB給電中だとフリーズします)
- ``esp_sleep_enable_timer_wakeup(60*60*24 * 1000000ULL); // 1日後に復帰`` のようにしたうえで、 ``esp_deep_sleep_start()`` で、タイマー設定Deep Sleepします。こちらはUSB給電中でも大丈夫です。
- ``setCpuFrequencyMhz(240)`` で、CPUのクロック数を変更できます。設定できる値は、240、160、80、40、20、10ですが、無線通信するなら80以上を設定する必要があります。
- ``M5.Display.setBrightness(255)`` で、画面の明るさを調整できます。

:numref:`pref01` に、Preferenceと電源制御のサンプルを示します。Aボタンでcountを増やし、Aボタン長押しでcountをリセットします。Bボタンで再起動、電源ボタンで電源OFFします。 **ただし、35行目のM5.Power.isCharging() はPlus2の場合、充電中かどうかを正しく検出できませんので注意してください。** (一応、Plus用に残しておきます。)

.. literalinclude:: src/pref01.ino
  :caption: 
  :name: pref01
  :language: arduino
  :linenos:
  :emphasize-lines: 35


.. その他
.. - CO2 (CCS811)
.. - RCS-620S (Felica)
.. - JJYClock
.. - RTC(BM8563)
.. - OTA