前回組み立てた、 SG90 サーボを使った ESP32-Cam パンチルト 雲台に命を吹き込むべく、Espressif社謹製サンプル スケッチ を元に様々な改良を施したRevisited版スケッチを改変して、WebUIから パンチルト 操作できるようにします。
ESP32-Cam Revisitedスケッチをベースに
今回の改造のベースにするのは、ESP32-Camの謹製サンプルスケッチを元に様々な改良を施したRevisited版です(実際の導入記事はこちら)。
このスケッチへ以降の手順で、パンチルト制御のサーボ機能を実装します。
esp32-cam-webserver.ino の改造
先ず、メインスケッチ冒頭でサーボライブラリと、ついでにESP32-Camの起動時に頻発するBrownout現象対策に必要なファイルをインクルードします。
1 2 3 |
#include "soc/soc.h" // disable brownout problems #include "soc/rtc_cntl_reg.h" // disable brownout problems #include <ESP32Servo.h> |
Arduino IDEのライブラリマネージャでESP32Servoをインストールしておきます。
次に関数定義より上方で、様々な初期設定をしているところに、サーボ関連の初期設定を挿入します。
1 2 3 4 5 6 7 8 |
// Pan-Tilt Servo #define SERVO_1 14 #define SERVO_2 15 Servo servo1; Servo servo2; int servo1Pos = 90; int servo2Pos = 90; |
その下方にある setup() 関数の中に、Brownout現象回避のおまじないと、サーボオブジェクトの設定と初期ポジショニング命令を挿入します。
1 2 3 4 5 6 7 8 9 10 |
void setup() { WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector // Pan-Tilt Servo servo1.setPeriodHertz(50); servo2.setPeriodHertz(50); servo1.attach(SERVO_1, 1000, 2000); servo2.attach(SERVO_2, 1000, 2000); servo1.write(servo1Pos); delay(100); servo2.write(servo2Pos); |
ここで、パンとチルト各サーボを servo.write() で動かす際には、間に適当な遅延 delay() を挟まないと、瞬間的な電圧降下でデバイスが再起動してしまうので注意。
app_httpd.cpp の改造
以上でサーボ制御するオブジェクトは実装できました。続いて app_http.cppを開き、上・下・左・右・中のパンチルトアクションを定義します。
まず、冒頭で同様にサーボライブラリをインクルード。
1 |
#include <ESP32Servo.h> |
次に、メインスケッチから変数や定数の引き継ぎを宣言しているところへ、以下のサーボ関連の変数を加えます。
1 2 3 4 5 6 7 8 |
// Pan-Tilt Servo extern int SERVO_1; extern int SERVO_2; extern Servo servo1; extern Servo servo2; extern int servo1Pos; extern int servo2Pos; #define SERVO_STEP 5 |
その下方に列挙されている様々なハンドラから、 cmd_handler と言うハンドラを見つけます。ここにはカメラ設定をWebUIで変えた際の振る舞いが記述されているので、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
static esp_err_t cmd_handler(httpd_req_t *req){ . . . sensor_t * s = esp_camera_sensor_get(); int res = 0; if(!strcmp(variable, "framesize")) { if(s->pixformat == PIXFORMAT_JPEG) res = s->set_framesize(s, (framesize_t)val); } else if(!strcmp(variable, "quality")) res = s->set_quality(s, val); else if(!strcmp(variable, "xclk")) { xclk = val; res = s->set_xclk(s, LEDC_TIMER_0, val); } else if(!strcmp(variable, "contrast")) res = s->set_contrast(s, val); . . . } else if(!strcmp(variable, "reboot")) { . . } <--[ここに挿入!!] else { res = -1; } . . . |
if 文の最後に、次の5つのサーボアクションの定義を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
} // Pan-Tilt Servo else if(!strcmp(variable, "servo")) { switch (val) { case 1: //SERVO-DOWN if(servo1Pos <= 170) { servo1Pos += SERVO_STEP; servo1.write(servo1Pos); } Serial.print("Servo Down: "); Serial.println(servo1Pos); break; case 2: //SERVO-UP if(servo1Pos >= 10) { servo1Pos -= SERVO_STEP; servo1.write(servo1Pos); } Serial.print("Servo Up: "); Serial.println(servo1Pos); break; case 3: //SERVO-LEFT if(servo2Pos <= 170) { servo2Pos += SERVO_STEP; servo2.write(servo2Pos); } Serial.print("Servo Left: "); Serial.println(servo2Pos); break; case 4: //SERVO-RIGHT if(servo2Pos >= 10) { servo2Pos -= SERVO_STEP; servo2.write(servo2Pos); } Serial.print("Servo Right: "); Serial.println(servo2Pos); break; case 5: //SERVO-CENTERING if(servo1Pos != 90) { servo1Pos = 90; servo1.write(servo1Pos); } if(servo2Pos != 90) { servo2Pos = 90; delay(100); servo2.write(servo2Pos); } Serial.print("Servo Centerized: "); Serial.print(servo1Pos); Serial.print("-"); Serial.println(servo2Pos); break; default: Serial.println("Servo No Action"); break; } } else { res = -1; } |
これにより、WebUIこそまだ実装していませんが、ブラウザなどから以下の要領で直接URLをリクエストすることで、パンチルト操作ができるようになりました。
1 2 3 4 5 |
http://IP_ADDRESS/control?var=servo&val=1 //DOWN http://IP_ADDRESS/control?var=servo&val=2 //UP http://IP_ADDRESS/control?var=servo&val=3 //LEFT http://IP_ADDRESS/control?var=servo&val=4 //RIGHT http://IP_ADDRESS/control?var=servo&val=5 //CENTER |
index_ov2640.h UIの改造
最後にWebUIから上述のサーボアクションを呼び出せるようにしましょう。
UIはカメラモジュールがOV2640の場合は、 index_ov2640.h に記述されています。ファイル中ほどに Frame Duration Limit の設定と Preferences の間をみつけ、
1 2 3 4 5 6 7 8 9 |
<div class="input-group" id="min_frame_time-group" title="Minimum frame time
Higher settings reduce the frame rate
Use this for a smoother stream and to reduce load on the WiFi and browser"> <label for="min_frame_time">Frame Duration Limit</label> . . </div> <--[ココに追加!!] <div class="input-group" id="preferences-group"> . . </div> |
ここにサーボアクション用のボタンを挿入します。
1 2 3 4 5 6 7 8 |
<div class="input-group" id="servo-group"> <label for="servos" style="line-height: 2em;">Servo</label> <button id="servo3" value="3" title="Pan Left"><</button> <button id="servo1" value="1" title="Tilt Down">D</button> <button id="servo5" value="5" title="Centerize Position">|</button> <button id="servo2" value="2" title="Tilt Up">U</button> <button id="servo4" value="4" title="Pan Right">></button> </div> |
その下方の <script> タグの中では、 <button> タグエレメントを定数に格納して onClick アクションを付与、それによって呼び出される関数でクエリ文字列を非同期発信する仕組みがJavsScriptで記述されています。
既存のコードを参考にしながら、次の3箇所にスクリプトを追加します。
まずは <script> 始まってすぐの定数定義部分で、5つのサーボボタンを定数に格納。
1 2 3 4 5 6 |
// Pan-Tilt Servo const servo1Button = document.getElementById('servo1') const servo2Button = document.getElementById('servo2') const servo3Button = document.getElementById('servo3') const servo4Button = document.getElementById('servo4') const servo5Button = document.getElementById('servo5') |
const 文の列記の下方にある updateConfig 関数の下へ、次のサーボアクション用関数を追加。
1 2 3 4 5 6 7 8 9 |
// Pan-Tilt Servo function updateServo (el) { value = el.value const query = `${baseHost}/control?var=servo&val=${value}` fetch(query) .then(response => { console.log(`request to ${query} finished, status: ${response.status}`) }) } |
ファイルの終わりにはUIの様々な部品への onClick が定義されているので、 swapButton.onclick の下辺りに、サーボボタンの onclick を追加します。
1 2 3 4 5 6 |
// Pan-Tilt Servo servo1Button.onclick = (e) => {updateServo(e.target);} servo2Button.onclick = (e) => {updateServo(e.target);} servo3Button.onclick = (e) => {updateServo(e.target);} servo4Button.onclick = (e) => {updateServo(e.target);} servo5Button.onclick = (e) => {updateServo(e.target);} |
スケッチの改変は以上です。コンパイルしてESP32-Camへ書き込めば、WebUIからパンチルト制御できるようになります。
※なお、今回はオリジナルのコーディングに沿って追加しましたが、スケッチサイズを気にするなら、 <button> タグ内に直接 onClick イベントを貼って関数を呼び出す方がシンプルになるでしょう。
1 |
<button id="servo4" value="4" title="Pan Right" onClick="updateServo(this);">></button> |
パンチルト基台の製作
仕上げに前回ブレッドボードで組んだ配線とキャパシタをユニバーサル基板に移植し、適当な鉛の重りと共に樹脂ケースを収めました。
重りを入れて自倒するようなことはなくなりましたが、サーボの動きが速すぎて揺れてしまうので、サーボをゆっくり動かすライブラリを探してみようと思います。