Newer
Older
m5stickcplus / week1.rst
@Motoki Motoki on 25 Feb 41 KB 2024
.. .. |br| raw:: html
..    <br />


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

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

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

- 電源を入れるには、電源ボタンを、**2秒間** 長押しします。
- 電源を切るには、電源ボタン(M5と書かれたAボタンの左側面)を、**6秒間** 長押しします。その際、プログラムによっては再スタートしたように見える場合がありますが、気にせずに長押し継続してください。
- 動画をみる→ https://youtu.be/Lo1jZbAeT8Y
- `M5StickCガイド <https://m5stack.oss-cn-shenzhen.aliyuncs.com/resource/docs/M5StickC_Guide_Japanese.pdf>`_ の、4ページ目、ハードウェアの概要も参考になります。 

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

IoTP プログラムで、ソースコード(.ino)を開いて、**Uploadボタン** を押してください。

または、git bash で SampleSrc フォルダに cd で移動したあとで、``./Upload.sh`` を実行してください。なお、引数にファイルを指定する(例えば ``./Upload.sh lcd01.ino``)と、直接コンパイル&書き込みを行います。

.. note:: IoTP プログラムのUploadボタンは、Upload.bat (または Upload.sh) を実行しているだけです。このバッチファイル(またはスクリプト)は、指定したソースコードを TestBuild/TestBuild.ino にコピーしたのち、フォルダを指定してコンパイルします。実行ファイルはbuildフォルダの中に生成されます。arduino-cli では、実行ファイルを直接指定して書き込む方法もあります。(参考:``_Upload_FactoryTest.bat``)


実機での動作検証を時間短縮・効率化したいとき
-----------------------------------------------------------------------------------------

基本的にはSampleSrc 内のソースコード(.ino) を開き、コンパイル&書き込みを行って確認していくことになりますが、コンパイルに時間がかかる場合は、以下の方法で事前コンパイル済みの実行イメージファイル(.bin.img)を取得し、直接書き込むこともできます。

https://git.istlab.info/miura250/TestBuild においてある実行イメージファイルと、シェルスクリプトを用いる方法です。

1.  ``cd`` で、ホームディレクトリに移動する。
#.  ``git clone https://git.istlab.info/git/miura250/TestBuild.git`` で、ホームディレクトリに ``TestBuild`` フォルダを作成しダウンロードする。
#. ``cd TestBuild/build`` 
#. ``./DirectBinUpdate.sh sprite01.bin.img`` のように、シェルスクリプトを用いて、bin.img ファイルを指定して書き込み(アップロード)をする。

.. note:: なお、bin.img 実行イメージファイルは、前述の ``Upload.sh`` シェルスクリプトで自動的に ``M5StickCPlus_FactoryTest2022/SampleSrc/TestBuild/build/`` 以下に生成されます。または、時間がかかりますが、``M5StickCPlus_FactoryTest2022/SampleSrc/_AllBuildTest.sh`` にあるシェルスクリプトですべてのサンプルプログラムをビルドし、実行イメージファイルを準備することもできます。

コードを書くときのヒントと注意
-------------------------------------------------------

- 以下のソースコード例は、リストの右上の□ボタンを押すと、クリップボードに簡単にコピーできます。右上のボタンが表示されていないときは、右にスクロールしてください。**(ただし、一部のWeb掲載プログラムは古く動かない場合があります。リスト上部のファイル名を確認して、IoTPプログラムが一覧をだしてくれる SampleSrcフォルダ 内のファイルをつかってください。)**
- ソースコードのインデントは、CTRL+T (Macの場合はCommand+T)キーで、自動整形できます。
- ``#include <M5StickC.h>``  は、M5StickC用のヘッダファイル指定です。 **M5StickCPlus** では、画面解像度が異なるので、 ``#include <M5StickCPlus.h>`` に変更しないと、表示がおかしくなることがあります。
- 大文字と小文字は厳密に区別されます。
- 画面(LCD)をつかうプログラムを書き込んだあと、画面を使わないプログラムを書き込むと、前のプログラムの画面が残ることがあります。
- スケッチブック(プログラムを構成する、複数のソースコードを含むファイル、他のIDEではプロジェクトと呼ぶ場合もある)をコピーしたいときは、内包するフォルダごとコピーしてください。その際、フォルダ名と、メインのソースコードファイル名(拡張子以外の部分)は、一致している必要があります。
- その他、よくある質問は、:ref:`FAQ` に随時追記していきます。

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

:numref:`serial01` は、シリアルモニタへ文字列を出力するシンプルな例です。グローバル変数 num を 0 に設定したうえで、setup()関数が1回実行されます。その後、loop()関数が繰り返し実行されます。
プログラムの出力をみるには、サンプルフォルダの ./monitor.sh またはVSCodeで 「シリアルモニタ」をひらいてください。
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: 2
  :linenos:

シリアルモニタは、デバイスからの出力を確認するだけではなく、デバイスに文字を送信することもできます。:numref:`serial02` は、シリアルモニタを介して、PCからデバイスに文字を送信する例です。シリアルモニタ上部のテキストエリアから 0 を送信すると、カウンタ num をリセットします。10〜14行目で、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: 10-14
  :linenos:

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


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

- M5StickCのDisplay周り解析 <https://lang-ship.com/blog/work/m5stickc-display> が詳しいです。
- :numref:`lcd01` のサンプルでは、外側から内側に向かって、10ピクセルずつ余白を残しながら、色を変えて塗りつぶしています。その後、8,16,26ピクセルフォント(それぞれ1,2,4番)をsetCursor()関数で指定して、print()関数で文字列を描画しています。
- M5StickCPlusの機能である液晶ディスプレイ(LCD)を使用するため、 ``#include <M5StickCPlus.h>`` を指定し、``M5.begin()`` を呼び出しています。

.. 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:: フォントの種類を設定するときは、setTextFont() や、setCursor()の第3引数をつかいます。setTextSize() は、指定したフォントを「縦横何倍に引き伸ばして表示するか?」を設定します。ここの「倍率」を2以上に増やすと、若干ギザギザが目立つようになります。


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

本体には、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()内で、倍数を計算して配列に設定しています。

``setBeep()`` と ``beep()`` を使うと、同じビープ音を使い回すときは記述が短くて便利ですが、通常は ``tone()`` のほうが簡潔です。
どちらの記法でも、命令実行のあと、音の停止を待たずに、すぐに次の処理に移行します(ノンブロッキング)。
継続ミリ秒数(ms)は、 ``M5.Beep.update()`` が周期的に呼び出せるときに指定し、それ以外は省略するとよいでしょう。(結局、 ``delay()`` と ``mute()`` が必要になるため) 

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


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

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

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

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


外部のLED等を接続
-----------------------------------------

外部の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-7, 15,19

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

サーボモータ(サーボハット利用)
-------------------------------------------------------------

: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)
-----------------------------------------

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


.. literalinclude:: src/accgyro.ino
  :caption: 
  :name: accgyro01
  :language: arduino
  :linenos:
  :emphasize-lines: 11,16-17


赤外(InfraRed)リモコン
------------------------------------------

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

TFT_eSprite クラスは、Lcdクラスとほぼ同様の描画関数を備えています。: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,13-18

Wifi接続するだけでは、あまり意味がないので、Telnetサーバを起動する例を :numref:`telnet01` に示します。
シリアルモニタを開いて、IPアドレス(10.85.xxx.xxx)を確認したら、ターミナル(コマンドプロンプト)で、``telnet 10.85.xxx.xxx`` と入力して、接続します。(Windowsでは https://www.imamura.biz/blog/27493 を参照し、telnetを有効にしてください。)
telnet から文字を入力すると、シリアルモニタに表示されます。
逆に、シリアルモニタから文字を入力すると、Telnet接続しているターミナルに、文字が表示されます。
``WiFiServer server(23)`` で、23番ポートで待ち受けるサーバを、作成しています。
ちなみに、``WiFiMulti`` は、複数のアクセスポイントに対して、Wifi接続を試みることができる機能(クラス)です。ただし、最終的に繋がるのは1つのアクセスポイントになります。

.. note:: Telnet接続を切断するときは、まずControlキーをおしながら ``]`` をおしてください。プロンプトに ``telnet>`` と表示されますので、``quit`` と打ち込むと終了します。 ``Escape character is '^]'.`` の ``^`` は、Controlキーのことです。

.. literalinclude:: src/telnet01.ino
  :caption: 
  :name: telnet01
  :language: arduino
  :linenos:
  :emphasize-lines: 29-30, 32


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

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

:numref:`ntp01` に、NTPサーバを使ってシステム時間の修正をしたのち、システム時間を1秒ごとに取得して、シリアルモニタに表示する例を示します。こちらは、M5のライブラリは不要です。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: 13

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

:numref:`rtc01` は、RTCの時刻を表示するサンプルです。なお、5行目で ``USE_NTP`` に 1 が設定してあれば、RTCに時刻を設定します。常時Wifiネットワークに接続できない場合は、RTCを利用することが望ましいです。

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

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

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

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


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

.. role:: strike


.. warning:: https (SSL) 通信でPOSTメソッドで送信する場合は、付録→LINE Notify (:numref:`line01`)を参考にしてください。以前は「スケッチ例→HTTPClient→BasicHttpsClient を参照して、WiFiClientSecure クラスを使用してください。」と書いていましたが、WifiClientSecureは必要ありません。


Google Spreadsheet にデータを送信する
--------------------------------------------------------

HTTP通信で、サーバにデータを送信して格納したいとおもっても、適当なサーバを準備するのは手間がかかることがあります。

Google Apps Scriptを用いると、HTTP通信で Google Spreadsheet にデータを書き込んだり、読み取ったりするWebサービスを作成し、WebAPIとして公開することができます。

(Google Spreadsheetのメニューで) 拡張機能→Apps Script で、:numref:`doget` を「コード.gs」に書き込みます。14行目で「シート1」の一番下の行に、配列array の要素を追加します。18行目のgetRange(1,3).getValue()は、スプレッドシートのC1(3列1行)の値を取得しています。ここに「=average(C2:C200)」のようにしておくと、データの平均値を取得することもできます。

作成した「コード.gs」について、「デプロイ」→「新しいデプロイ」で、アクセスできるユーザを「全員」にし、「デプロイ」を押します。すると、ウェブアプリのURLが表示されますのでコピーします。**このあと、Googleアカウント権限について聞かれる場合があります。**

作成できたかどうかをパソコンから確認するには、コマンドラインから、以下のように入力します(ただし、curlが必要です。val1=10 と val2=20 のあいだは&記号ですので、シェルでバックグラウンド処理されないように、URLをシングルクォートで囲っています。-L (--location)は、リダイレクト先にリクエストを発行するcurlのオプションです。) exec のあとに ? をいれて、名前1=値1&名前2=値2 のようにしてデータを渡します。 ::

   curl -L  'https://script.google.com/macros/s/XxXxXxXxXx/exec?val1=10&val2=20' 


.. literalinclude:: src/doget.js
  :caption: 
  :name: doget
  :language: javascript
  :linenos:
  :emphasize-lines: 14,18

.. warning:: doGet関数の返却値は、本来はHTTPのレスポンスとして受け取れるのですが、現状の ``HTTPClient`` ではリダイレクトをうまく処理できない?ため、Google App Scriptの出力を受け取れない可能性があります。

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

すこし長いですが、:numref:`httpserver01` は、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/httpserver01.ino
  :caption: 
  :name: httpserver01
  :language: arduino
  :linenos:
  :emphasize-lines: 18,22,26,43,80

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

.. 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)に接続して、 ``office/temp`` というトピックにデータ(シリアルコンソールで送信した文字列)を書き込みます。 


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


.. warning:: 学内ネットワークからは、ポート制限のため、実験用ブローカ(mqtt.istlab.info) に接続できません。実験室に仮設するWifi基地局を利用してください。

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

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

.. 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)
--------------------------------------------

「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.ino は、数値データのみを送受信する例です。espnow03.ino は、整数・小数・文字列をまとめてbyte (uint8_t) 配列に埋め込んで送受信する例です。

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



Preference
-------------------------------------------

再起動をすると、プログラムがリセットされて、通常の変数や配列データは消えてしまいます。Preferenceを用いると、不揮発性のフラッシュ領域を使ってデータを保存・復元することができます。

https://github.com/espressif/arduino-esp32/blob/master/libraries/Preferences/examples/StartCounter/StartCounter.ino


電源制御・電力制御
-----------------------------------

- ``ESP.restart()`` で、再起動します。
- ``M5.Axp.Write1Byte(0x32, M5.Axp.Read8bit(0x32) | 0x80);`` で、電源OFFします。
- ``setCpuFrequencyMhz(240)`` で、CPUのクロック数を変更できます。設定できる値は、240、160、80、40、20、10ですが、無線通信するなら80以上を設定する必要があります。
- ``M5.Axp.ScreenBreath(8)`` で、画面の明るさを調整できます。8〜15が標準的ですが、7でも微かに読めます。

:numref:`pref01` に、Preferenceと電源制御のサンプルを示します。Aボタンでcountを増やし、Aボタン長押しでcountをリセットします。Bボタンで再起動、電源ボタンで電源OFFします。 

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


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