Arduino UNO ウェブ対応サーボ制御 – PHPoC WiFiシールド使用
部品と材料
| Arduino UNO | ×1 |
| PHPoC WiFi Shield for Arduino | ×1 |
| Seeed サーボモーター | ×1 |
| ジャンパーワイヤー | ×1 |
このプロジェクトについて
はじめに
初心者の方は、Arduino - サーボモーターとArduino - WiFiについて事前に読むことをお勧めします。
Arduinoをインターネットに接続するには、Ethernetシールド、WiFiシールド、PHPoCシールド、PHPoC WiFiシールドなどのインターネットシールドが必要です。
PHPoCシールドの優れた点は、EthernetやWiFiシールドのようにインターネット接続を提供するだけでなく、ウェブサーバー機能を内蔵しており、ウェブブラウザからArduinoを制御・監視できることです。またWebSocketにも対応しているため、ページリロードなしでリアルタイム制御・監視が可能です。
PHPoCシールドにはいくつかの組み込みウェブアプリケーションがあります。詳細はこちらを参照してください。
このプロジェクトでは、Web Remote Control/SlideウェブアプリケーションのUI(ユーザーインターフェース)を変更し、ウェブブラウザから受信した角度に応じてサーボモーターを回転させるArduinoコードを書きました。
配線
- PHPoCシールドをArduinoにスタック
- サーボモーターのGND、VCC、信号ピンをそれぞれArduinoのGND、5V、ピン9に接続
データフロー
ウェブブラウザ → PHPoCシールド → Arduino
ウェブアプリがタッチ/マウスイベントに基づいて角度を計算し、WebSocket経由でPHPoCシールドに送信。PHPoCシールドがデータを受信するとArduinoに渡し、Arduinoはサーボモーターを指定角度に回転させます。
やるべきこと
- PHPoCシールドのWiFi情報設定(SSIDとパスワード)
- 新しいUIをPHPoCシールドにアップロード
- Arduinoコードを書く
PHPoCシールドのWiFi情報設定
手順はこちら: https://www.phpoc.com/support/manual/p4s-347_user_manual/contents.php?id=network_first_setup
PHPoCシールドへの新UIアップロード
- PHPoCソースコード
remote_rotate.phpをダウンロード(コードセクション参照) - PHPoCデバッガーを使ってPHPoCシールドにアップロード(手順はこちら)
Arduinoコードの作成
- Arduino IDEでPHPoC用ライブラリをインストール(手順参照)し、Arduino IDEを再起動
- Arduino IDEで
ファイル → スケッチ例 → Phpoc → WebRemoteSlideとファイル → スケッチ例 → Servo → sweepを開く - この2つのサンプルを組み合わせて、ウェブページ経由でサーボモーターを制御するコードを作成(コードセクションのソースコード参照)
テスト
- Arduino IDEのシリアルボタンをクリックしてIPアドレスを確認
- ウェブブラウザで
https://IPアドレスを置換/remote_rotate.phpを開く - Connectボタンをクリックしてテスト
初心者向け最高のArduinoスターターキット
Arduinoキットをお探しの方は「初心者向け最高のArduinoキット」を参照してください。
関数リファレンス
- Arduino - Servo Library
- Servo.attach()
- Servo.write()
- Servo.writeMicroseconds()
- Servo.read()
- Servo.attached()
- Servo.detach()
- Serial.begin()
- Serial.println()
- delay()
- for loop
- while loop
- if else
- loop()
- setup()
- String.toInt()
コード
remote_rotate.php PHP
<!DOCTYPE html> <html> <head> <title>PHPoC Shield - Web Remote Control for Arduino</title> <meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=0.7"> <style> body { text-align: center; font-size: 15pt; } h1 { font-weight: bold; font-size: 25pt; } h2 { font-weight: bold; font-size: 15pt; } button { font-weight: bold; font-size: 15pt; } </style> <script> var canvas_width = 400, canvas_height = 400; var plate_radius = 160; var click_state = 0; var last_plate_angle = 0 var plate_angle = 0, last_angle = 0; var ws; function init() { var canvas = document.getElementById("remote"); canvas.addEventListener("touchstart", mouse_down); canvas.addEventListener("touchend", mouse_up); canvas.addEventListener("touchmove", mouse_move); canvas.addEventListener("mousedown", mouse_down); canvas.addEventListener("mouseup", mouse_up); canvas.addEventListener("mousemove", mouse_move); var ctx = canvas.getContext("2d"); ctx.translate(canvas_width / 2, canvas_height / 2); rotate_plate(0); } function connect_onclick() { if(ws == null) { var ws_host_addr = "<?echo _SERVER("HTTP_HOST")?>"; var debug = document.getElementById("debug"); if((navigator.platform.indexOf("Win") != -1) && (ws_host_addr.charAt(0) == "[")) { ws_host_addr = ws_host_addr.replace(/[\[\]]/g, ''); ws_host_addr = ws_host_addr.replace(/:/g, "-"); ws_host_addr += ".ipv6-literal.net"; } ws = new WebSocket("ws://" + ws_host_addr + "/remote_slide", "text.phpoc"); document.getElementById("ws_state").innerHTML = "CONNECTING"; ws.onopen = ws_onopen; ws.onclose = ws_onclose; ws.onmessage = ws_onmessage; } else ws.close(); } function ws_onopen() { document.getElementById("ws_state").innerHTML = "<font color='blue'>CONNECTED</font>"; document.getElementById("bt_connect").innerHTML = "Disconnect"; } function ws_onclose() { document.getElementById("ws_state").innerHTML = "<font color='gray'>CLOSED</font>"; document.getElementById("bt_connect").innerHTML = "Connect"; ws.onopen = null; ws.onclose = null; ws.onmessage = null; ws = null; } function ws_onmessage(e_msg) { e_msg = e_msg || window.event; alert("msg : " + e_msg.data); } function rotate_plate(angle) { var canvas = document.getElementById("remote"); var ctx = canvas.getContext("2d"); ctx.clearRect(-canvas_width / 2, -canvas_height / 2, canvas_width, canvas_height); ctx.rotate(angle / 180 * Math.PI); ctx.strokeStyle="red"; ctx.fillStyle="red"; ctx.beginPath(); ctx.arc(0, 0, plate_radius, Math.PI, Math.PI * 1.5); ctx.lineTo(0, 0); ctx.lineTo(-plate_radius, 0); ctx.fill(); ctx.stroke(); ctx.strokeStyle="blue"; ctx.fillStyle="blue"; ctx.beginPath(); ctx.arc(0, 0, plate_radius, 0, Math.PI / 2); ctx.lineTo(0, 0); ctx.lineTo(plate_radius, 0); ctx.fill(); ctx.stroke(); ctx.strokeStyle="black"; ctx.arc(0, 0, plate_radius, 0, Math.PI * 2); ctx.stroke(); ctx.rotate(-angle / 180 * Math.PI); } function mouse_down() { if(ws == null) return; var dist_min, dist_max, dist; event.preventDefault(); if(event.offsetX) { x = event.offsetX - canvas_width / 2; y = canvas_height / 2 - event.offsetY; dist_min = 40; dist_max = plate_radius; } else if(event.layerX) { x = event.layerX - canvas_width / 2; y = canvas_height / 2 - event.layerY; dist_min = 40; dist_max = canvas_width / 2; } else { x = (Math.round(event.touches[0].pageX - event.touches[0].target.offsetLeft)) - canvas_width / 2; y = canvas_height / 2 - (Math.round(event.touches[0].pageY - event.touches[0].target.offsetTop)); dist_min = 40; dist_max = canvas_width / 2; } var dist = Math.round(Math.sqrt(x * x + y * y)); if((dist > dist_min) && (dist < dist_max)) { click_state = 1; last_angle = Math.round(Math.atan2(y, x) / Math.PI * 180); } } function mouse_up() { if(ws == null) return; event.preventDefault(); click_state = 0; } function mouse_move() { if(ws == null) return; var dist_min, dist_max, angle, dist; event.preventDefault(); if(!click_state) return; if(event.offsetX) { x = event.offsetX - canvas_width / 2; y = canvas_height / 2 - event.offsetY; dist_min = 40; dist_max = plate_radius; } else if(event.layerX) { x = event.layerX - canvas_width / 2; y = canvas_height / 2 - event.layerY; dist_min = 40; dist_max = canvas_width / 2; } else { x = (Math.round(event.touches[0].pageX - event.touches[0].target.offsetLeft)) - canvas_width / 2; y = canvas_height / 2 - (Math.round(event.touches[0].pageY - event.touches[0].target.offsetTop)); dist_min = 40; dist_max = canvas_width / 2; } angle = Math.round(Math.atan2(y, x) / Math.PI * 180); dist = Math.round(Math.sqrt(x * x + y * y)); if((dist < dist_min) || (dist > dist_max)) { click_state = 0; return; } if((Math.abs(angle) > 90) && (angle * last_angle < 0)) { if(last_angle > 0) last_angle = -180; else last_angle = 180; } plate_angle += (angle - last_angle); last_angle = angle; if(plate_angle > 180) plate_angle = 180; if(plate_angle < 0) plate_angle = 0; rotate_plate(-plate_angle); if(plate_angle != last_plate_angle) { if(ws && ws.readyState) { ws.send("C" + plate_angle.toString() + "\r\n"); last_plate_angle = plate_angle; debug = document.getElementById("debug"); debug.innerHTML = plate_angle; } } } window.onload = init; </script> </head> <body> <p> <h1>Web Remote Control / Rotate</h1> </p> <canvas id="remote" width="400" height="400"></canvas> <h2> <p> WebSocket : <span id="ws_state">null</span><br> Angle : <span id="debug">0</span> </p> <button id="bt_connect" type="button" onclick="connect_onclick();">Connect</button> </h2> </body> </html>
Arduinoコード Arduino
/* arduino web server - remote control (slide switch) */
#include "SPI.h"
#include "Phpoc.h"
#include <Servo.h>
PhpocServer server(80);
Servo myservo;
char slideName;
int slideValue;
void setup() {
Serial.begin(9600);
while(!Serial) ;
Phpoc.begin(PF_LOG_SPI | PF_LOG_NET);
server.beginWebSocket("remote_slide");
Serial.print("WebSocket server address : ");
Serial.println(Phpoc.localIP());
myservo.attach(9);
myservo.write(1);
}
void loop() {
PhpocClient client = server.available();
if (client) {
String slideStr = client.readLine();
if(slideStr) {
slideValue = slideStr.substring(1).toInt();
myservo.write(slideValue);
Serial.print("Angle: ");
Serial.println(slideValue);
}
}
}
製造プロセス
- Web制御DMX照明システム:Arduino Megaマスターコントローラー
- Bluetooth操作のArduinoスパイボットを自作する
- スマート貯金箱を作ろう:Arduino Nanoでコインアクセプターを制御
- Web/モバイルアプリから家電を制御するスマートホームシステム
- Arduino電源制御センター:N-FET/P-FET/リレー/RTC搭載キット
- ArduinoでBluetooth経由でLEDを制御 – シンプルDIYガイド
- DIY Arduino加湿器コントローラー:リレーを用いた安全な高電圧制御
- Arduino Nanoとジョイスティックで2軸ステッピングモーターを制御:Visuinoで簡単プログラミング
- Arduino UNO + WiFiシールドで温度をWebリアルタイム表示 — シリアル出力だけでグラフ化
- ArduinoをWebパターンロックで保護 - 簡単セットアップとリモート制御
- ワイヤレスArduinoプログラミングシールド – HC-05 BluetoothでUSBケーブル不要に