ESP32で奏でる「ぼっちクリスマス」に響くシングルなベル🎄


本記事は<a href="https://qiita.com/advent-calendar/2020/arduino">Arduino Advent Calendar 2020</a>の12月24日分です。

はじめまして！　記事をご覧くださりありがとうございます。
半分NEETのようなフリーランスエンジニアのたつのぶと申します。
今回は、**ESP32で奏でる「ぼっちクリスマス」に響くシングルなベル🎄**と題して
MQTT(IoT向け軽量メッセージプロトコル)などを使って、ESP32に接続された圧電ブザーを外部ネットワークからボタン一つで鳴らしてみた記事です。ぜひ最後まで読んでくださると幸いです！！
#できたもの
⇒チープな圧電ブザーを使ったので、音が悪いかも...？？？
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">MQTT（IoT向け軽量メッセージプロトコル）と圧電ブザー制御によるジングルベルの演奏。 <a href="https://t.co/vYZtoSYlrK">pic.twitter.com/vYZtoSYlrK</a></p>&mdash; 🐑たつのぶ（24）@AI/IoT/Web/App/心理学 (@ryuryudoragon_) <a href="https://twitter.com/ryuryudoragon_/status/1337964176900149249?ref_src=twsrc%5Etfw">December 13, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
#準備するもの
- レンタルサーバー
- PC： WindowsPC
- ESP32-DevKitC
- 圧電ブザー
- ブレッドボード

#システム構成図

![mqtts.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/580405/a28c99a2-49f0-6a01-d9b2-f1025dcefb9a.png)

#MQTTについて
MQTTは「Message Queue Telemetry Transport」の略でIoT向けの非同期軽量メッセージプロトコルのことを指します！！

MQTTは3つの要素で構成されており、具体的には下記通りです。
①**Publisher**（メッセージを送信するプログラム）
②**Broker**（Publisherからメッセージを受け取り、Subscriberに送信する仲介役となるサーバーなど）
③**Subscriber**（メッセージを受信するプログラム）
![Untitled Diagram (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/580405/0ed5ceb9-08ad-b605-f3c7-c6c12e1dd566.png)
※メッセージの送り先を指定し、メッセージを指定して受け取ることができる機能のことを**topic(トピック)**といいます。
※**QoS(Quality of Service)** とは、データを送信する確実性のことです。
下記のように3つのレベルがあり、サーバの性能に依存します。
 ★レベル0:最大1回届く
 ★レベル1:1回以上届く
 ★レベル2:正確に1回届く
#ブザーイベントsubscribeプログラム(ESP32側)
```C++:buzzermqtt.ino
#include <WiFi.h>
#include "PubSubClient.h"

const char* ssid = "ssid";
const char* password =  "pass";
const char* mqttServer = "xxxx.cloudmqtt.com";
const int mqttPort = 12345;
const char* mqttUser = "mqttuser";
const char* mqttPassword = "mqttpass";
 
WiFiClient espClient;
PubSubClient client(espClient);
const int pin = 27;
const int ch = 1;
//-----------音階定義--------------
//---------------------------------
#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978
//---------------------------------
//音階の指定→　melody[]   
int melody[] = {
  NOTE_G4, NOTE_G4 ,NOTE_E5, NOTE_D5, NOTE_C5, NOTE_G4,
  NOTE_G4, NOTE_G4, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_A4,
  NOTE_A4, NOTE_A4, NOTE_F5, NOTE_E5, NOTE_D5, NOTE_A4,
  NOTE_G5, NOTE_G5, NOTE_F5, NOTE_D5, NOTE_E5, NOTE_C5,
  NOTE_G4, NOTE_G4, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_G4,
  NOTE_G4, NOTE_G4, NOTE_E5, NOTE_D5, NOTE_C5, NOTE_A4,
  NOTE_A4, NOTE_F5, NOTE_E5, NOTE_D5, NOTE_G5, NOTE_G5, NOTE_G5, NOTE_G5,
  NOTE_A5, NOTE_G5, NOTE_F5, NOTE_D5, NOTE_C5,
  NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5,
  NOTE_E5, NOTE_G5, NOTE_C5, NOTE_D5, NOTE_E5,
  NOTE_F5, NOTE_F5, NOTE_F5, NOTE_F5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5,
  NOTE_E5, NOTE_D5, NOTE_D5, NOTE_C5, NOTE_D5, NOTE_G5,
  NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5,
  NOTE_E5, NOTE_G5, NOTE_C5, NOTE_D5, NOTE_E5,         
  NOTE_F5, NOTE_F5, NOTE_F5, NOTE_F5, NOTE_E5, NOTE_E5, NOTE_E5, NOTE_E5,
  NOTE_G5, NOTE_G5, NOTE_F5, NOTE_D5, NOTE_C5}; 
//音の長さの指定→　duration[] 
  int duration[] = {
  250,250,250,250,250,500,  
  250,250,250,250,250,500,  
  250,250,250,250,250,500,   
  250,250,250,250,250,1500, 
  250,250,250,250,250,500,  
  250,250,250,250,250,500,  
  250,250,250,250,250,250,250,250,
  250,250,250,250,500,            
  250,250,500,250,250,500,        
  250,250,250,250,500,            
  250,250,250,250,250,250,250,250,
  250,250,250,250,500,500,        
  250,250,500,250,250,500,        
  250,250,250,250,500,            
  250,250,250,250,250,250,250,250,
  250,250,250,250,1000};  
void musicFunc(int ch,int meloFreq,int meloTime){
  ledcWriteTone(ch,meloFreq); //ch freq
  delay(meloTime);
} 
void callback(char* topic, byte* payload, unsigned int length) {
 
  Serial.print("Message arrived in topic: ");
  Serial.println(topic);
  for(int i=0;i<sizeof  melody / sizeof( melody[0]);i++){
        musicFunc(ch, melody[i], duration[i]+30);
  }
  ledcSetup(ch,12000,8); //ch basefreq dutybit
  Serial.print("Message:");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }
 
  Serial.println();
  Serial.println("-----------------------");
 
}
 
void setup() {
  Serial.begin(115200);
  ledcSetup(ch,12000,8); //ch basefreq dutybit
  ledcAttachPin(pin,ch); //pin ch
 
  WiFi.begin(ssid, password);
   Serial.begin(115200);
  ledcSetup(ch,12000,8); //ch basefreq dutybit
  ledcAttachPin(pin,ch); //pin ch
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected to the WiFi network");
 
  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
 
  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");
 
    if (client.connect("ESP32Client", mqttUser, mqttPassword )) {
 
      Serial.println("connected");  
 
    } else {
 
      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);
 
    }
  }
 
  client.subscribe("esp/buzzer");
 
}
 
void loop() {
  client.loop();
}
```
# ブザーイベントpublishスクリプト(サーバ側)
```php:buzzerpub.php
<?php
  require("./phpMQTT.php");
  $username = "mqttuser";
  $password = "mqttpass";
  $server = "xxxxx.cloudmqtt.com";     // change if necessary
  $port = 12345;                     // change if necessary
  $client_id ='ClientID'.rand().'phpMQTT-publisher'; // make sure this is unique for connecting to sever - you could use uniqid()
  $mqtt = new Bluerhinos\phpMQTT($server, $port, $client_id);
  if ($mqtt->connect(true, NULL, $username, $password)) {
        $mqtt->publish('esp/buzzer', 'Hello World! at ' . date('r'), 0, false);
        $mqtt->close();
   } else {
        echo "Time out!\n";
   }
?>
```
#ブザーを鳴らすボタンのあるフロントページ(サーバ側)
```html:Christmas.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>クリスマス</title>
</head>
<body>
<p>サンタの画像をクリックするとジングルベルが流れるよ！</p>
<button type="submit" onclick="btnClick();"><img src="images/cms.png" width="150" height="150" alt="送信" /></button>
<script>
function btnClick() {
let request = new XMLHttpRequest();
request.onreadystatechange = function () {
    if (this.readyState === 4) {
        if (this.status === 200) {
            document.body.className = 'ok';
            console.log(this.responseText);
        } else if (this.response == null && this.status === 0) {
            document.body.className = 'error offline';
            console.log("The computer appears to be offline.");
        } else {
            document.body.className = 'error';
        }
    }
};
request.open("GET", "https://hogehoge.jp/buzzermqtt.php", true);
request.send(null);
 alert("メリークリスマス♪♪");
}

</script>
</body>
</html>
```
#最後に
いかがだったでしょうか？

**MQTTをまだ知らない組込み開発者の方へ**
MQTTの仕組みとMQTTを使った圧電ブザー制御について、ざっくり理解できたのではないでしょうか？
これを応用して、圧電ブザーを赤外線LEDに変え、遠隔でリモコン操作をしてもいいですし、
アイデア次第でIoT開発の幅をぜひ広げてみてください。面白いですよ！

**全国のおひとり様の方々へ**
ESP32で奏でる「ぼっちクリスマス」に響くシングルなベル🎄ということで、
ジングルベルが鳴り響くことで、ひとりクリスマスの楽しむ幅が広がったのではないでしょうか？（白目）

ということで、今年もそれぞれの楽しいクリスマス🎅🎄をお過ごしください！！

それでは✋