ESP32でwebserverを立ち上げてRGBLEDをスマホから調光する
初めまして。wak-techと申します。初投稿です。
普段はwak-tech.comに電子工作系の記事を投稿しています。
今回はタイトルの通りスマホからRGBLEDの値を変えて調光していきます。
動いている様子
ESP32開発ボードでRGBLEDをスマホから操作できました。完全フルカラー対応です!!🏮📗📘 pic.twitter.com/wyzMKezmDV
— 𝘄𝗮𝗸-𝘁𝗲𝗰𝗵 (@wak_tech) 2018年12月16日
参考にしたサイト
この記事の内容は殆ど「ESP32 Servo Motor Web Server with Arduino IDE」と同じです。参考記事においてはサーボモーターをスマホから操作していましたが、その部分を本記事ではRGBLEDへと変更しています。
それに伴ってスライドバーも3つ付けました。
回路
回路は以下の通りで、ESP32でRGBLEDを使うと全く同じです。RGBLEDの使い方・購入がまだな方はこちらの記事を先にご覧ください。
ソースコード(ほぼコピペで動きます)
#include <WiFi.h>
// 使用するWi-Fiとそのパスワードに書き換えてください
const char* ssid = "ssid";
const char* password = "password";
// ポート80番を使用
WiFiServer server(80);
// HTTPリクエストを格納する変数
String header;
// 値の設定に使用する変数
String valueString = String(5);
int pos1 = 0;
int pos2 = 0;
//ledpin
const int ledR = A18;
const int ledG = A4;
const int ledB = A5;
void setup() {
//ledc setting
//R
ledcSetup(0, 12800, 8);
// ledRをチャネル0へ接続
ledcAttachPin(ledR, 0);
//G
ledcSetup(1, 12800, 8);
// ledGをチャネル1へ接続
ledcAttachPin(ledG, 1);
//B
ledcSetup(2, 12800, 8);
// ledBをチャネル2へ接続
ledcAttachPin(ledB, 2);
Serial.begin(115200);
// Wi-Fiに接続
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// ローカルIPを表示(このIPにスマホなどからアクセスします)
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
server.begin();
}
void loop(){
WiFiClient client = server.available(); // Listen for incoming clients
if (client) { // If a new client connects,
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()) { // loop while the client's connected
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();
// Display the HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>ESP32 RGB LED controller</title>");
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>body { text-align: center; font-family: \"Trebuchet MS\", Arial; margin-left:auto; margin-right:auto;}");
client.println("#servoPosR{color: red;}");
client.println("#servoPosG{color: green;}");
client.println("#servoPosB{color: blue;}");
client.println(".slider { width: 300px; }</style>");
client.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>");
// Web Page
client.println("</head><body><h1>ESP32 with RGB LED</h1>");
//R slide bar
client.println("<p>Brightness of R: <span id=\"servoPosR\"></span></p>");
client.println("<input type=\"range\" min=\"0\" max=\"255\" class=\"slider\" id=\"servoSliderR\" onchange=\"servo(this.value,'R')\" value=\""+valueString+"\"/>");
//G slide bar
client.println("<p>Brightness of G: <span id=\"servoPosG\"></span></p>");
client.println("<input type=\"range\" min=\"0\" max=\"255\" class=\"slider\" id=\"servoSliderG\" onchange=\"servo(this.value,'G')\" value=\""+valueString+"\"/>");
//B slide bar
client.println("<p>Brightness of B: <span id=\"servoPosB\"></span></p>");
client.println("<input type=\"range\" min=\"0\" max=\"255\" class=\"slider\" id=\"servoSliderB\" onchange=\"servo(this.value,'B')\" value=\""+valueString+"\"/>");
client.println("<script>");
//send R value
client.println("var sliderR = document.getElementById(\"servoSliderR\");");
client.println("var servoPR = document.getElementById(\"servoPosR\"); servoPR.innerHTML = sliderR.value;");
client.println("sliderR.oninput = function() { sliderR.value = this.value; servoPR.innerHTML = this.value; }");
//send G value
client.println("var sliderG = document.getElementById(\"servoSliderG\");");
client.println("var servoPG = document.getElementById(\"servoPosG\"); servoPG.innerHTML = sliderG.value;");
client.println("sliderG.oninput = function() { sliderG.value = this.value; servoPG.innerHTML = this.value; }");
//send B value
client.println("var sliderB = document.getElementById(\"servoSliderB\");");
client.println("var servoPB = document.getElementById(\"servoPosB\"); servoPB.innerHTML = sliderB.value;");
client.println("sliderB.oninput = function() { sliderB.value = this.value; servoPB.innerHTML = this.value; }");
//HTTP getのための関数
client.println("$.ajaxSetup({timeout:1000}); function servo(pos,color) { ");
client.println("$.get(\"/?value\" + color + \"=\" + pos + \"&\"); {Connection: close};}</script>");
client.println("</body></html>");
//HTTPリクエストの処理部分
//GET /?value=180& HTTP/1.1
//Rの値をledの出力に変換
if(header.indexOf("GET /?valueR=")>=0) {
pos1 = header.indexOf('=');
pos2 = header.indexOf('&');
valueString = header.substring(pos1+1, pos2);
//LEDをvalueStringの値で点灯
ledcWrite(0, valueString.toInt());
Serial.println(valueString);
}
//Gの値をledの出力に変換
if(header.indexOf("GET /?valueG=")>=0) {
pos1 = header.indexOf('=');
pos2 = header.indexOf('&');
valueString = header.substring(pos1+1, pos2);
//LEDをvalueStringの値で点灯
ledcWrite(1, valueString.toInt());
Serial.println(valueString);
}
//Bの値をledの出力に変換
if(header.indexOf("GET /?valueB=")>=0) {
pos1 = header.indexOf('=');
pos2 = header.indexOf('&');
valueString = header.substring(pos1+1, pos2);
//LEDをvalueStringの値で点灯
ledcWrite(2, valueString.toInt());
Serial.println(valueString);
}
// HTTPレスポンスの終了
client.println();
// Break out of the while loop
break;
} else {
currentLine = "";
}
} else if (c != '\r') {
currentLine += c;
}
}
}
// Clear the header variable
header = "";
// 接続を切断
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
}
ソースコードの解説
まずwebserver部分ですが、これはESP32を使ってスマホからLチカ(LED点滅)する【webserver】と全く同様です。こちらの記事をご覧になり、HTTPリクエスト関係の理解を深めましょう。
今回肝となるのが、スライドバーの値の送信です。そこを詳しく解説します。
RGBLEDの接続
ESP32でRGBLEDを使うで使用したledcWrite系の関数を使います。
//ledpin
const int ledR = A18;
const int ledG = A4;
const int ledB = A5;
でそれぞれのLEDがつながっているAポートのピンを登録しています。
どのピンがどのポートなのか等はhttps://ht-deko.com/arduino/esp-wroom-32.htmlのピンアウトの項目に詳しく記載されています。
これにsetup()内で
//R
ledcSetup(0, 12800, 8);
// ledRをチャネル0へ接続
ledcAttachPin(ledR, 0);
とすることで、赤色LEDをチャンネル0に登録しました。よって赤色LEDを点灯させたい場合は
ledcWrite(0,value);
とすればvalueの分だけ明るく光ります。valueの最大値は8bitを指定したので255です。
スライドバーを表示する
コード上では一行ずつ表示されているので非常に見にくいですね。html形式だけで取り出してみると、表示されるページの部分は以下のようになります。
<!DOCTYPE html><html>
<head><meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="data:,">
<style>body { text-align: center; font-family: "Trebuchet MS", Arial; margin-left:auto; margin-right:auto;}
.slider { width: 300px; }
#servoPosR{color: red;}
#servoPosG{color: green;}
#servoPosB{color: blue;}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head><body><h1>ESP32 with RGB LED</h1>
<p>Brightness of R: <span id="servoPosR"></span></p>
<input type="range" min="0" max="255" class="slider" id="servoSliderR" onchange="servo(this.value,'R')" value="5"/>
<p>Brightness of G: <span id="servoPosG"></span></p>
<input type="range" min="0" max="255" class="slider" id="servoSliderG" onchange="servo(this.value,'G')" value="5"/>
<p>Brightness of B: <span id="servoPosB"></span></p>
<input type="range" min="0" max="255" class="slider" id="servoSliderB" onchange="servo(this.value,'B')" value="5"/>
<script>
var sliderR = document.getElementById("servoSliderR");
var servoPR = document.getElementById("servoPosR"); servoPR.innerHTML = sliderR.value;
sliderR.oninput = function() { sliderR.value = this.value; servoPR.innerHTML = this.value; }
var sliderG = document.getElementById("servoSliderG");
var servoPG = document.getElementById("servoPosG"); servoPG.innerHTML = sliderG.value;
sliderG.oninput = function() { sliderG.value = this.value; servoPG.innerHTML = this.value; }
var sliderB = document.getElementById("servoSliderB");
var servoPB = document.getElementById("servoPosB"); servoPB.innerHTML = sliderB.value;
sliderB.oninput = function() { sliderB.value = this.value; servoPB.innerHTML = this.value; }
$.ajaxSetup({timeout:1000}); function servo(pos,color) {
$.get("/?value" + color + "=" + pos + "&"); {Connection: close};}</script>
</body></html>
まず
<p>Brightness of R: <span id="servoPosR"></span></p>
<input type="range" min="0" max="255" class="slider" id="servoSliderR" onchange="servo(this.value,'R')" value="5"/>
とすることで以下のようになります。
後でこのバーの値を取得するため、idを付けておきます。スライドバーのRなのでservoSliderRとしました。
また、スライドバーの値が確定した時にonchange属性にある関数が呼び出されます。onchangeの属性に値をESP32へ送信するservo() 関数を指定しておきます。名前は参考にした記事の名残なので、適当に変えて構いません。
スライダーの値を更新する
スライダーの値を更新しないと、動かしても元に戻ってしまいます。これはjavascriptで解決します。
<script>
var sliderR = document.getElementById("servoSliderR");
var servoPR = document.getElementById("servoPosR"); servoPR.innerHTML = sliderR.value;
sliderR.oninput = function() { sliderR.value = this.value; servoPR.innerHTML = this.value; }
</script>
最初にスライダーの要素をsliderRに、スライダーの上にある値を表示する要素をservoPRに入れます。
次にservoPRの値をsliderRの値に変更します。これで初期設定ができました。5という初期設定値が指定されるはずです。
また、スライダーが変更された時に起こるsliderR.oninputを設定します。これは見ての通りですが、sliderRの値をinputの値(this.value)
に、そしてその値をservoPRの値にも入れ込み表示します。
スライダーの値をHTTP GETで送る
$.ajaxSetup({timeout:1000}); function servo(pos,color) {
$.get("/?value" + color + "=" + pos + "&"); {Connection: close};}</script>
</body></html>
ここでやっとajaxを使うときが来ました。ajaxの使い方はここでの開設の範囲を超えるので、各自お調べくださいm(__)m
私はここで色の情報を入れ込むために、参考ページでのservo関数の引数にcolorを追加しました。これを指定してHTTP GETの中身を
GET /?valueR=200& HTTP/1.1
のように送信することにしました。
HTTPリクエストを処理する
//Rの値をledの出力に変換
if(header.indexOf("GET /?valueR=")>=0) {
pos1 = header.indexOf('=');
pos2 = header.indexOf('&');
valueString = header.substring(pos1+1, pos2);
//LEDをvalueStringの値で点灯
ledcWrite(0, valueString.toInt());
Serial.println(valueString);
}
2行目でそのGETの中身の判定をしています。これで、RGBの出力を分けて認識できるようになりました。
次の行は=と&の間にある値を抜き出すものです。これでスライドバーの値が200の時はvalueStringに200が入ります。
この値は文字列として入るので、実際の値にするにはtoInt()メソッドを使います。
ledcWrite(0, valueString.toInt());
これで無事赤色LEDがスライドバーの値で点灯するようになりました。
他の色
他の色に関しては、ソースを見てもらえば分かりますが全てRの場合をコピペしただけです。頑張ればもっと綺麗なソースコードになる。絶対。ごめんなさい。
応用
今回使った技術でいろいろなことができます。
まずinnerHTML辺りの物を使えばセンサーの値を表示できますし、当然扇風機の風量コントロールなんかもできますね。
今回のLEDの部分を様々なモーターやサーボに換えれば簡単にラジコンもできるでしょう。応用は無限大です。何か作ったら是非twitter等でお声掛け下さい!
twitterで新着記事が自動通知されるようになっているのでフォローしていただけると喜びます。
ご覧いただきありがとうございました!