M5StickCPlusをスマホから遠隔操作したい話（Webサーバ）

# 目的
　趣味でラジコンを制作しておりますが、擬似エアーサスペンション機能を実装したく、スマホからマイコンの遠隔操作を行いたい。

# 環境
- MacStudio
- M5StickCplus
- VSCode
- (PlatformIO)

# 参考サイト
　以下のサイトが大きく役に立ちました。
デバイスは異なりますが、Arduino関連のデバイスならば扱えるかと思います。

https://logikara.blog/prog_arduino_wifi_ap/

# コピペからの改造
　上記サイトに記載されていたプログラムはとてもシンプルかつ、改造を少し行うだけでラジコンには使用できそうでした。

いざ開始。


以下はコピペしたままのプログラムになります。
~~~c++
#include <M5Atom.h>
#include <WiFi.h>       //WiFi接続用
#include <WebServer.h>  //サーバー設定用

// アクセスポイント設定
const char ssid_AP[] = "logikara";  //SSID
const char pass_AP[] = "atomlite";  //パスワード(8文字以上)

// サーバー設定ポート80で接続
WebServer server(80);
// htmlメイン画面データ（「"」は「\"」に置き換え、htmlの改行は「\n」、コードの改行は「\」）
char html[] = "\
<!DOCTYPE html><html lang=\"jp\"><head>\n\
<meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\
<title>REMOTE-SWITCH</title>\n\
</head><body>\n\
  <h1>REMOTE-SWITCH</h1>\n\
  <div>\n\
    <button style=\"height:50px\" onclick=\"location.href='/get/btn_on'\">　ON　</button>\n\
    <button style=\"height:50px\" onclick=\"location.href='/get/btn_off'\">　OFF　</button>\n\
  </div>\n\
</body></html>\n";

// FastLED（CRGB構造体）設定
CRGB dispColor(uint8_t r, uint8_t g, uint8_t b) {
  return (CRGB)((r << 16) | (g << 8) | b);
}
//-------------------------------------------------
// サーバーリクエスト時処理関数
//-------------------------------------------------
// ルートアクセス時の応答関数
void handleRoot() {
  server.send(200, "text/html", html);  //レスポンス200を返しhtml送信
}
// エラー（Webページが見つからない）時の応答関数
void handleNotFound() {
  server.send(404, "text/plain", "404 Not Found!");  //text送信
}
// ブラウザONボタン処理
void btnOn() {
  digitalWrite(21, HIGH);                   //出力0 ON（青色LED）
  M5.dis.drawpix(0, dispColor(0, 0, 200));  //本体LED（青）
  server.send(200, "text/html", html);  //レスポンス200を返しhtml送信
}
// ブラウザOFFボタン処理
void btnOff() {
  digitalWrite(21, LOW);                    //出力0 OFF（青色LED）
  M5.dis.drawpix(0, dispColor(20, 20, 20)); //本体LED（白）
  server.send(200, "text/html", html);  //レスポンス200を返しhtml送信
}
//-------------------------------------------------
// 初期設定
//-------------------------------------------------
void setup() {
  M5.begin(true, false, true);    //Serial,POWER,LED
  Serial.begin(9600);             //標準のシリアル通信設定

  // アクセスポイント設定
  WiFi.softAP(ssid_AP, pass_AP);  //ソフトAP設定

  // サーバー設定
  server.on("/", handleRoot);         //ルートアクセス時の応答関数を設定
  server.onNotFound(handleNotFound);  //Webページが見つからない時の応答関数を設定
  server.on("/get/btn_on", btnOn);    //ボタンオン受信処理
  server.on("/get/btn_off", btnOff);  //ボタンオフ受信処理
  server.begin();                     //Webサーバー開始

  // シリアル出力
  Serial.printf("\nSSID:%s\n", ssid_AP);//SSID
  Serial.printf("PASS:%s\n", pass_AP);  //PASSWORD
  Serial.print("IP address: ");
  Serial.println(WiFi.softAPIP());      //IPアドレス（配列）

  // 本体LED設定
  M5.dis.drawpix(0, dispColor(20, 20, 20)); //LED点灯（白）
  // 入出力端子設定
  pinMode(21, OUTPUT);  //出力端子
}
//-------------------------------------------------
// メイン
//-------------------------------------------------
void loop() {
  server.handleClient();  //クライアントからのアクセス確認
  delay(200);             //遅延時間（ms）
}
~~~
こちらですと一行目からM5Atom専用のプログラムになっていますので分割してM5StickCplus用のものに改造していこうかと思います。(同じメーカーなのでほぼ変わりません)


~~~c++
#include <M5StickCPlus.h>
#include <WiFi.h>       //WiFi接続用
#include <WebServer.h>  //サーバー設定用
#include <ESP32Servo.h> //servo

Servo servo1;
int pmin = 500;
int pmax = 2400;
~~~
こちらは宣言部分ですが、一番上のM5AtomのヘッダファイルをM5StickCplusのものに置き換え、サーボモーターを扱うライブラリ、変数を宣言しただけになります。


~~~c++
// アクセスポイント設定
const char ssid_AP[] = "M5SCP_RCCar";  //SSID
const char pass_AP[] = "Mazda_rccar";  //パスワード(8文字以上)
~~~
こちらもアクセスポイントの設定になりますのでお好きなものを設定しましょう。
（ラジコンボディがMazda3ですので…）


~~~c++
// サーバー設定ポート80で接続
WebServer server(80);
// htmlメイン画面データ（「"」は「\"」に置き換え、htmlの改行は「\n」、コードの改行は「\」）
char html[] = "\
<!DOCTYPE html><html lang=\"jp\"><head>\n\
<meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n\
<title>REMOTE-SWITCH</title>\n\
</head><body>\n\
  <h1>REMOTE-SWITCH</h1>\n\
  <div>\n\
    <button style=\"height:50px\" onclick=\"location.href='/get/btn_on'\">　ON　</button>\n\
    <button style=\"height:50px\" onclick=\"location.href='/get/btn_off'\">　OFF　</button>\n\
  </div>\n\
</body></html>\n";
~~~
この箇所は特に変更しません。ボタンを増やしたりシークバーなどを加えれば自在に車高を調節できるラジコンが誕生しそうですが、遠目で操作している際に細かな調整は無理そうなので今回は車高アップをON、車高ダウンをOFFとしてそのまま使用します。

コードもHTMLですし、凝った操作盤を制作することも容易なようです。

~~~c++
// // FastLED（CRGB構造体）設定
// CRGB dispColor(uint8_t r, uint8_t g, uint8_t b) {
//   return (CRGB)((r << 16) | (g << 8) | b);
// }
~~~
こちらは元のプログラムにおいてM5AtomのLEDユニットを操作するためのものなので今回は使用しません。（コメントアウト）

~~~c++
//-------------------------------------------------
// サーバーリクエスト時処理関数
//-------------------------------------------------
// ルートアクセス時の応答関数
void handleRoot() {
  server.send(200, "text/html", html);  //レスポンス200を返しhtml送信
}
// エラー（Webページが見つからない）時の応答関数
void handleNotFound() {
  server.send(404, "text/plain", "404 Not Found!");  //text送信
}
// ブラウザONボタン処理
void btnOn() {
  servo1.write(500);//車高高い
  server.send(200, "text/html", html);  //レスポンス200を返しhtml送信
}
// ブラウザOFFボタン処理
void btnOff() {
  servo1.write(1200);//車高低い
  server.send(200, "text/html", html);  //レスポンス200を返しhtml送信
}
~~~

この箇所では元々ブラウザ内でON、OFF各ボタンが押下された際にはLEDを操作するコードが記載されておりましたが、今回はサーボを操作するライブラリを用いておりますので、サーボモーターを動かします。

~~~c++
//-------------------------------------------------
// 初期設定
//-------------------------------------------------
void setup() {
  M5.begin();    
  M5.Axp.EnableCoulombcounter();
  Serial.begin(9600);             //標準のシリアル通信設定

  // アクセスポイント設定
  WiFi.softAP(ssid_AP, pass_AP);  //ソフトAP設定

  // サーバー設定
  server.on("/", handleRoot);         //ルートアクセス時の応答関数を設定
  server.onNotFound(handleNotFound);  //Webページが見つからない時の応答関数を設定
  server.on("/get/btn_on", btnOn);    //ボタンオン受信処理
  server.on("/get/btn_off", btnOff);  //ボタンオフ受信処理
  server.begin();                     //Webサーバー開始

  // シリアル出力
  Serial.printf("\nSSID:%s\n", ssid_AP);//SSID
  Serial.printf("PASS:%s\n", pass_AP);  //PASSWORD
  Serial.print("IP address: ");
  Serial.println(WiFi.softAPIP());      //IPアドレス（配列）

  // 本体LED設定
  // M5.dis.drawpix(0, dispColor(20, 20, 20)); //LED点灯（白）
  // 入出力端子設定
  // pinMode(21, OUTPUT);  //出力端子

  servo1.setPeriodHertz(50);  // 50Hz
  servo1.attach(26, pmin, pmax);
}
~~~
　こちらは少し注意です。
　まず上部に記載されている`M5begin()`ですが、Atom仕様の場合には引数にTrueやFalseなどによるLED、Power、Serial等の設定が存在します。
　M5StickCPlusの場合にも設定自他は存在しますが引数を渡す必要なく初期化されるためここでは引数なしです。
　一つ下の`M5.Axp.EnableCoulombcounter();`は５v出力を開始するためのメソッドなのでサーボ等を使用する際は入れてください。
　下部LED設定に関しては今回は使用しませんのでコメントアウト。
　以降はサーボの初期設定になります。

~~~c++
//-------------------------------------------------
// メイン
//-------------------------------------------------
void loop() {
  server.handleClient();  //クライアントからのアクセス確認
  delay(200);             //遅延時間（ms）
}
~~~

メインは変更なしです。

# 以上
　結果的に制御はうまくできました。
しかし、M5StickCPlusから謎のノイズが発生してしまっています。
機構の方ではなかなかうまく動作しているようですので引き続き改造を続けます。