【ESP32】【Wi-Fi駆動】サーボモーターと音楽を流す機械を作りたかったんです

# ないなら作ればいい（作れてない）
私の部屋の室内灯をON/OFFにするスイッチは部屋の奥にあって、机に座って作業中に電気をつけるためには席を立たないといけない。
机からスイッチまで、距離にして3歩くらい。近い。  
でも電子工作に集中している時にはこのたった3歩が、渋谷駅の東急東横線と京王井の頭線乗り換えくらいの絶妙に面倒臭い遠さを感じるし、手を止めていざスイッチをつけるともう集中力が切れてしまい、部屋や家の外に出て散歩して空を眺めてたらやる気なくなってるのがオチである。  
とはいえ、この手間を惜しんで暗い中作業したり、寝ようと思った時に電灯を消すため遠くまで歩くのはよろしくない。さてどうしたものか…  
**じゃあ自分で作っちゃえ！**
電子工作は買うほどではないけど、あればいいなあ…という超ニッチなものを自分好みに作れる。そう、それは課題解決である。  
サーボモーターを少し傾けると電気のスイッチをON/OFFに出来るし。  
でもそれだと前回の記事よりダウングレードした感じがしちゃうしなあ…  
**そうだ、最近ブザーを鳴らすものを作ったし、遊び心を加えて音楽とサーボモーターを一緒に制御してみるぞ！**
# 結果
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">失敗例⚠️音量注意！<br>Wi-Fiで音楽を流すのは早すぎたようです。<a href="https://twitter.com/hashtag/ESP32?src=hash&amp;ref_src=twsrc%5Etfw">#ESP32</a> <a href="https://twitter.com/hashtag/%E9%9B%BB%E5%AD%90%E5%B7%A5%E4%BD%9C?src=hash&amp;ref_src=twsrc%5Etfw">#電子工作</a> <a href="https://twitter.com/hashtag/PassiveBuzzer?src=hash&amp;ref_src=twsrc%5Etfw">#PassiveBuzzer</a> <a href="https://t.co/cLkbqAN2de">pic.twitter.com/cLkbqAN2de</a></p>&mdash; ikedat_mtbukoh3 (@ikedat_mtbukoh3) <a href="https://twitter.com/ikedat_mtbukoh3/status/1688549963268239360?ref_src=twsrc%5Etfw">August 7, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

失敗。挫折。（）  
なぜか挙動がバグると言うか、ブザーの制御ができないんですよね。  
Wi-Fiを使ってパッシブブザーを制御することは出来ないのかな…  
LEDを光らせることはできた分、なおさら悔しい。  
# ちょっと面白いことを目標に作る
前回の記事のとおり、多分サーボモーターだけだったら作成できる。  
そもそもブザーだって、東海道新幹線の『Ambitious Japan!』の車内メロディが終わったから作っただけであって、普通はいらないはずである。  
~~大体お前1年に1回名古屋からのぞみに乗って品川で降りるくらいしかしてないくせに、何ツウぶってるんだ~~
しかし、ちょっとした面白さがあることで、人生は格段に楽しくなるし、ものづくりをする方も使う方も面白くなると思っている。  
ものづくりとは、相手のことを思いやり、楽しませたいという心意気が顕著に出てくるのではないだろうか。たとえ個人開発だとしても、こういう遊び心は大事だと私は考えている。  
でもこんなことを書いたところで、ネットワークとつなげると挙動がバグるのは直せないしなあ…    
家のWi-Fiが弱いのもあるかもしれないけど、もう少し勉強してみます。  
続編で完成してたら良いな！  
**みなさんも良きESP32ライフを！**  
# おまけ　　
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">⚠️音量注意！<br>ちなみにこれをWi-Fiで制御したかった。<br>東海道新幹線らしさを目指したけど、そもそも何か違う感じがします笑<a href="https://twitter.com/hashtag/ESP32?src=hash&amp;ref_src=twsrc%5Etfw">#ESP32</a> <a href="https://twitter.com/hashtag/%E9%9B%BB%E5%AD%90%E5%B7%A5%E4%BD%9C?src=hash&amp;ref_src=twsrc%5Etfw">#電子工作</a> <a href="https://twitter.com/hashtag/PassiveBuzzer?src=hash&amp;ref_src=twsrc%5Etfw">#PassiveBuzzer</a> <a href="https://twitter.com/hashtag/AmbitiousJapan?src=hash&amp;ref_src=twsrc%5Etfw">#AmbitiousJapan</a> <a href="https://t.co/iUQe7Ixoot">https://t.co/iUQe7Ixoot</a> <a href="https://t.co/ieqYEjNQgq">pic.twitter.com/ieqYEjNQgq</a></p>&mdash; ikedat_mtbukoh3 (@ikedat_mtbukoh3) <a href="https://twitter.com/ikedat_mtbukoh3/status/1689262766182563840?ref_src=twsrc%5Etfw">August 9, 2023</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

こんな感じで音楽を流したかったという成果物。  
そもそもこの時点で挙動が少し怪しいのは草。もっと精進します。  


# ソースコード
``` C#
#include <WiFi.h>
#include <Servo.h>

const char *ssid = "XXXXXXXXXX"; //SSID
const char *password = "YYYYYYYYYY"; //password

// Set web server port number to 80
WiFiServer server(80);

// Variable to store the HTTP request
String header;

// Auxiliar variables to store the current output state
String servoMotorOutput18State = "off";

// Assign output variables to GPIO pins
const int servoMotorOutput18 = 18;

Servo myservo; //Servoオブジェクトを作成

// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0; 
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;

// 圧電ブザーの出力ピン
#define BUZZER_PIN 13
// 音を鳴らす時間
#define BEAT_MIN 300
#define BEAT 400
#define BEAT_2 700
#define BEAT_3 1200
// 音階名と周波数の対応
#define C4 261.6
#define Csharp4 277.18
#define D4 293.665
#define Dsharp4 311.127
#define E4 329.63
#define F4 349.228
#define Fsharp4 369.994
#define G4 391.995
#define Gsharp4 415.305
#define A4 440
#define Asharp4 466.164
#define B4 493.883
#define C5 523.251

boolean buzzerFlag = true;
boolean servoMotorFlag = true;

void playTurnOnMusic(){
    ledcWriteTone(1,A4); // 1.5
    delay(BEAT_3);
    ledcWriteTone(1, 0); // 消音
    ledcWriteTone(1,C5);
    delay(BEAT);
    ledcWriteTone(1,C5);
    delay(BEAT);
    ledcWriteTone(1,Asharp4);
    delay(BEAT);
    ledcWriteTone(1,A4);
    delay(BEAT);
    ledcWriteTone(1,A4);
    delay(BEAT);
    ledcWriteTone(1,Asharp4); // 1
    delay(BEAT_2);
    ledcWriteTone(1,A4); // 1
    delay(BEAT_2);
    ledcWriteTone(1,E4); // 1
    delay(BEAT_2);
    ledcWriteTone(1,G4);
    delay(BEAT);
    ledcWriteTone(1,F4); // 2.5
    delay(BEAT_3);
    ledcWriteTone(1,F4); 
    delay(BEAT);
    ledcWriteTone(1,F4);
    delay(BEAT);
    ledcWriteTone(1,F4);
    delay(BEAT);
    ledcWriteTone(1,G4);
    delay(BEAT);    
    ledcWriteTone(1,A4);
    delay(BEAT);
    ledcWriteTone(1,Asharp4);
    delay(BEAT_2);
    ledcWriteTone(1,A4);
    delay(BEAT_2);
    ledcWriteTone(1,C5);
    delay(BEAT);
    ledcWriteTone(1,A4);
    delay(BEAT);
    ledcWriteTone(1,G4);
    delay(BEAT);
    ledcWriteTone(1,A4);
    delay(BEAT);
    ledcWriteTone(1,F4); // 2
    delay(BEAT_3);
    
    ledcWriteTone(1, 0); // 消音
    buzzerFlag = false;
}

void playTurnOffMusic(){
    ledcWriteTone(1,Fsharp4); // 1
    delay(BEAT_2);
    ledcWriteTone(1,Fsharp4); // 1
    delay(BEAT_2);
    ledcWriteTone(1,Gsharp4); // 1
    delay(BEAT_2);
    ledcWriteTone(1,F4);
    delay(BEAT);
    ledcWriteTone(1,Fsharp4);
    delay(BEAT_3);    
    ledcWriteTone(1, 0);    // 消音
}

void setup() {

  Serial.begin(115200);

  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
  
  // サーボモーターの設定
  myservo.attach(servoMotorOutput18);
  // 圧電ブザーの設定
  ledcSetup(1,12000, 8);
  ledcAttachPin(BUZZER_PIN,1);
}

void loop(){    
  WiFiClient client = server.available();   // Listen for incoming clients

  if(client){                               // If a new client connects,
    currentTime = millis();
    previousTime = currentTime;
    Serial.println("New Client.");          // print a message out in the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    while (client.connected() && currentTime - previousTime <= timeoutTime) {  // loop while the client's connected
      currentTime = millis();
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        header += c;
        if (c == '\n') {                    // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();
        
            
            // turns the GPIOs on and off
            if (header.indexOf("GET /18/on") >= 0) {
              Serial.println("GPIO 18 on");
              servoMotorOutput18State = "on";
              Serial.println("onが押されました");
              
              playTurnOnMusic();          
              
              //myservo.write(90); 
              //servoMotorFlag = false;
                    
              
            } else if (header.indexOf("GET /18/off") >= 0) {
              Serial.println("GPIO 18 off");
              servoMotorOutput18State = "off";
              Serial.println("offが押されました");
              // サーボモーターを0に戻す
              //myservo.write(0); 
              
              playTurnOffMusic();
              
            } else {
              ;
            }
            
            // Display the HTML web page
            client.println("<!DOCTYPE html><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons 
            // Feel free to change the background-color and font-size attributes to fit your preferences
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
            client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
            client.println(".button2 {background-color: #555555;}</style></head>");
            
            // Web Page Heading
            client.println("<body><h1>ESP32 Servo Motor LED</h1>");
            
            // Display current state, and ON/OFF buttons for GPIO 18  
            client.println("<p>GPIO 18 - State " + servoMotorOutput18State + "</p>");
            // If the servoMotorOutput18State is off, it displays the ON button       
            if (servoMotorOutput18State=="off") {
              client.println("<p><a href=\"/18/on\"><button class=\"button\">ON</button></a></p>");
            } else {
              client.println("<p><a href=\"/18/off\"><button class=\"button button2\">OFF</button></a></p>");
            } 
                       
            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else { // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}
```


# 参考リンク
- 『ESP32でのサーボモーターの使い方』（角度をPWM信号化するライブラリを使用）

https://wak-tech.com/archives/1534
- 『【超簡単】 ESP32 を使って携帯でLEDを点灯する(Wifi使用）』

https://enjoy-life-fullest.com/2022/02/05/%E3%80%90%E8%B6%85%E7%B0%A1%E5%8D%98%E3%80%91-esp32-%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E6%90%BA%E5%B8%AF%E3%81%A7led%E3%82%92%E7%82%B9%E7%81%AF%E3%81%99%E3%82%8Bwifi%E4%BD%BF%E7%94%A8%EF%BC%89/
- Lesson 07-01 ドレミファソラシドを繰り返す

https://omoroya.com/