【ESP32】非同期Webサーバー上でNeoPixelの彩度・明度・色相を操作する

こんにちは!今回はSPIFFS領域内のHTMLファイルでLチカWeb Serverを立てると、マイコン内蔵の便利なRGBLED「NEOPIXEL」を使ったWS2812の使い方の2記事の技術を使って、非同期webサーバー上でNeoPixelの彩度・明度・色相を制御していきます。
各技術の詳細は上記の2つの記事を参考にしてください。詳細が分からない場合はコメントもお待ちしています。

動いている様子

準備・環境

  • Windows10
  • Arduino 1.8.10
  • ボード: esp32 1.0.4

インストールが必要なライブラリ

  • ESPAsyncWebServer
    このプロジェクトではSPIFFS領域のHTMLを送信するためにESPAsyncWebServerというライブラリを使用します。このライブラリの詳細は記事の後半で解説します。

    では、インストールしていきましょう!

    1. 公式Githubからzipファイルをダウンロードする
    2. zipを解凍する
    3. Arduinoのライブラリがある場所へ移動し、名前を「ESPAsyncWebServer」にする

    これで基本のインストールは完了ですが、依存している「AsyncTCP」というライブラリも必要です。上記と同様に、以下の手順でインストールします。

    1. AsyncTCPのGithubページからzipファイルをダウンロードする
    2. zipを解凍する
    3. Arduinoのライブラリがある場所へ移動し、名前を「AsyncTCP」にする

    これでサーバーに必要なライブラリは揃いました!

  • Adafruit_NeoPixel

    ライブラリマネージャーからNeoPixelのライブラリを探し、インストールします。

インストールが必要なソフト

今回はSPIFFS領域と呼ばれるファイル保存領域を使って、htmlファイルなどを直接ESP32に格納します。
SPIFFSにアップロードするには、「ESP32 Sketch Data Upload」が必要です。これはmgo-techさんの解説ページを参考にインストールしてください。

回路


27番ピンにWS2812(NeoPixel)のDINを接続します。
またWiFi起動確認用のLEDも動作させたい場合は21番ピンに適当なLEDをセットしましょう。

データ構成

ESP_AP_NEOPIXEL.ino
├── data
│   ├── index.html
│   └── style.css
└── ESP_AP_NEOPIXEL.ino

プログラムを書き込む前に、SPIFFSアップローダを使用してdataフォルダの中身を送信しておきます。

完成プログラム

  • ESP_AP_NEOPIXEL.ino

    #include "WiFi.h"
    #include "ESPAsyncWebServer.h"
    #include <AsyncTCP.h>
    #include "SPIFFS.h"
    #include <Adafruit_NeoPixel.h>
    #define PIN 27 //INが接続されているピンを指定
    #define NUMPIXELS 7 //LEDの数を指定
    
    //NeoPixel設定
    Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); //800kHzでNeoPixelを駆動
    int saturation = 100;
    int value = 0;
    int brightness = 100;
    int HUE = 32768;
    
    //APモードの場合
    const char* ssid = "ESP32_RGB";
    const char* pass = "esp32";
    const IPAddress ip(192,168,0,1);
    const IPAddress subnet(255,255,255,0);
    // GPIOの21番ピンをledPinとして設定
    const int ledPin = 21;
    // LEDの状態を保持する変数
    String ledState;
    // ポート80にサーバーを設定
    AsyncWebServer server(80);
    
    // 実際のピン出力によってhtmlファイル内のSTATEの文字を変える
    String processor(const String& var){
      Serial.println(var);
      if(var == "STATE"){
        if(digitalRead(ledPin)){
          ledState = "ON";
        }
        else{
          ledState = "OFF";
        }
        Serial.print(ledState);
        return ledState;
      }
      return String();
    }
    
    void setup(){
      Serial.begin(115200);
      pinMode(ledPin, OUTPUT); //GPIO02をアウトプットに
      pixels.begin(); //NeoPixelを開始
      // SPIFFSのセットアップ
      if(!SPIFFS.begin(true)){
        Serial.println("An Error has occurred while mounting SPIFFS");
        return;
      }
      //AP(親機)モードの場合
      digitalWrite(ledPin,HIGH);
      WiFi.softAP("esp32_rgb","password");
      delay(100);
      digitalWrite(ledPin,LOW);
      WiFi.softAPConfig(ip,ip,subnet);
      delay(1000);
      // サーバーのルートディレクトリにアクセスされた時のレスポンス
      server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
        int paramsNr = request->params();
        digitalWrite(ledPin,HIGH);
        Serial.println(paramsNr);
        for(int i=0;i<paramsNr;i++){
            AsyncWebParameter* p = request->getParam(i);
            if(p->name() =="HUE"){
              HUE = p->value().toInt();
            }else if(p->name() == "Brightness"){
              brightness = p->value().toInt();
            }else if(p->name() == "Saturation"){
              saturation = p->value().toInt();
            }
            OnPixels(HUE,saturation,brightness);
        }
        request->send(SPIFFS, "/index.html", String(), false, processor);
        digitalWrite(ledPin,LOW);
      });
    
      // style.cssにアクセスされた時のレスポンス
      server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
        request->send(SPIFFS, "/style.css", "text/css");
      });
      // サーバースタート
      server.begin();
    }
    
    void loop(){
    }
    
    void OnPixels(int hue,int saturation, int brightness){
      for(int i=0; i<NUMPIXELS; i++) { //LEDを1つずつ制御  
          pixels.setPixelColor(i, pixels.ColorHSV(hue, saturation, brightness)); //LEDの色を設定
          pixels.show();   //LEDに色を反映
          delay(40);
      }
    }
    void FirePixels(int hue,int saturation){
      delay(5000);
      for(int value=0;value<255;value++){
        for(int i=0; i<NUMPIXELS; i++) { //LEDを1つずつ制御  
          pixels.setPixelColor(i, pixels.ColorHSV(hue + i*200, saturation, value)); //LEDの色を設定
          pixels.show();   //LEDに色を反映
          delay(80);
        }
        delay(80);
      }
      delay(10000);
      for(value=255;value>0;value--){
        for(int i=0; i<NUMPIXELS; i++) { //LEDを1つずつ制御  
          pixels.setPixelColor(i, pixels.ColorHSV(hue, saturation, value)); //LEDの色を設定
          pixels.show();   //LEDに色を反映
        }
        delay(20);
      }
      pixels.clear(); //NeoPixelの出力をリセット
      pixels.show();
    }
    
  • index.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; }
    #HUE{color: red;}
    </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>HUE: <span id="HUE"></span></p>
    <input type="range" min="0" max="65536" class="slider" id="SliderHUE" onchange="send_value(this.value,'HUE')" value="0"/>
    <p>Brightness: <span id="Brightness"></span></p>
    <input type="range" min="0" max="255" class="slider" id="SliderB" onchange="send_value(this.value,'Brightness')" value="0"/>
    <p>Saturation: <span id="Saturation"></span></p>
    <input type="range" min="0" max="255" class="slider" id="SliderS" onchange="send_value(this.value,'Saturation')" value="0"/>
    <script>
    
    var sliderHUE = document.getElementById("SliderHUE");
    var sliderB = document.getElementById("SliderB");
    var sliderS = document.getElementById("SliderS");
    
    var HUE = document.getElementById("HUE"); 
    var Brightness = document.getElementById("Brightness");
    var Saturation = document.getElementById("Saturation");
    HUE.innerHTML = sliderHUE.value;
    Brightness.innerHTML = sliderB.value;
    Saturation.innerHTML = sliderS.value;
    sliderHUE.oninput = function() { sliderHUE.value = this.value; HUE.innerHTML = this.value; }
    sliderB.oninput = function() { sliderB.value = this.value; Brightness.innerHTML = this.value; }
    sliderS.oninput = function() { sliderS.value = this.value; Saturation.innerHTML = this.value; }
    $.ajaxSetup({timeout:1000}); function send_value(pos,name) { 
    $.get("/?" + name + "=" + pos + "&"); {Connection: close};}</script>
    </body></html>
    
    
  • style.css

    html {
        font-family: Helvetica;
        display: inline-block;
        margin: 0px auto;
        text-align: center;
      }
      h1{
        color: #0F3376;
        padding: 2vh;
      }
      p{
        font-size: 1.5rem;
      }
      .button {
        display: inline-block;
        background-color: #008CBA;
        border: none;
        border-radius: 4px;
        color: white;
        padding: 16px 40px;
        text-decoration: none;
        font-size: 30px;
        margin: 2px;
        cursor: pointer;
      }
      .button2 {
        background-color: #f44336;
      }
    

書き込んであげるとESP32_RGBというWi-Fiが飛びます。パスワードにesp32を入れ、ブラウザから192.168.0.1にアクセスすると、以下のようなコントロール画面が出てきます。

同様の方法で、RGBLED以外の電子部品等も操作可能です。

コメントする

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です