今日、Arduino + ETHERNET SHIELD 2 で遊んでいて思ったのだが、同じGETの処理をするなら、
http://hogehoge.hoge/?start=10&hold=5000&release=50
……というような、汎用化したGET引数にすれば面白いかも、と思った。まあ、セキュリティ上よろしくないが。
オッサンは生きている。
今日、Arduino + ETHERNET SHIELD 2 で遊んでいて思ったのだが、同じGETの処理をするなら、
http://hogehoge.hoge/?start=10&hold=5000&release=50
……というような、汎用化したGET引数にすれば面白いかも、と思った。まあ、セキュリティ上よろしくないが。
先週少しやりかけて途中だった、Arduino UNO に ETHERNET SHIELD 2 をとりつけ、ウェブインターフェイスを作ってこれでLEDをコントロールするという遊びの続きをやる。
こんなふうなインターフェイスでコントロールすることにする。
で、ブレッドボードとArduinoはこんな感じ。「SeeedStudio SIDEKICK BASIC KIT」に入っていた3色LEDを使う。
スケッチはこんな感じで。ウェブ上で同じようなことを公開している方々は、ブラウザの出力を受けるのに、たいてい普通のポインタ文字列を使っておられるのだが、せっかくStringクラスがあるので、それを活用することにした。Stringクラスを使う欠点は多少メモリを浪費することだが、反面、余裕は十分あり、今回は別にメモリが足りないというわけでもないので、そうした。
// // Web2TriColorLED.ino // 27.06.07(日) 1800~ // 佐藤俊夫 // 3色LEDをウェブインターフェイスで制御 // ETHERNET SHIELD 2 使用 // #include <SPI.h> #include <Ethernet2.h> // byte mac[] = { 0x90, 0xA2, 0xDA, 0x0F, 0xF6, 0x74 }; IPAddress ip(192, 168, 1, 129); EthernetServer SERVER(80); EthernetClient CLIENT; int R = 0, G = 0, B = 0; const int LEDR = 3, LEDG = 5, LEDB = 6; // void setup() { Ethernet.begin(mac, ip); SERVER.begin(); pinMode(LEDR, OUTPUT); pinMode(LEDG, OUTPUT); pinMode(LEDB, OUTPUT); } void loop() { char c; String rstr = ""; CLIENT = SERVER.available(); if (CLIENT) { while (CLIENT.connected()) { if (CLIENT.available()) { c = CLIENT.read(); rstr += c; if(rstr.endsWith("\r\n")){ break; } } } if(rstr.indexOf("red=") >= 0){ R = rstr.substring(rstr.indexOf("red=") + 4, rstr.indexOf("&green=")).toInt(); G = rstr.substring(rstr.indexOf("green=") + 6, rstr.indexOf("&blue=")).toInt(); B = rstr.substring(rstr.indexOf("blue=") + 5, rstr.indexOf("&end")).toInt(); } analogWrite(LEDR, R); analogWrite(LEDG, G); analogWrite(LEDB, B); delay(10); rstr = ""; sendform(); // give the web browser time to receive the data delay(1); // close the connection: CLIENT.stop(); } } // void sendform(){ char* formFirstHalf[] = { "<html>", " <head>", " <meta charset=\"utf-8\">", " </head>", " <body>", " <center>", " <h1>Arduino 3色LEDをウェブインターフェイスで</h1>", " <form method='GET'>", " <table>", " <tr>", " <th style='background-color:red; color:white;'>赤</th>", " <th style='background-color:green;color:white;'>緑</th>", " <th style='background-color:blue; color:white;'>青</th>", " </tr>" }; // 14 num. char* formSecondHalf[] = { " </table>", " <input type='hidden' name='end'>", " <input type='submit' value='セット'>", " </form>", " </center>", " </body>", "</html>" }; // 7 num. int i = 0; CLIENT.println("HTTP/1.1 200 OK"); CLIENT.println("Content-Type: text/html"); CLIENT.println("Connection: close"); CLIENT.println(); CLIENT.println("<!DOCTYPE HTML>"); for(i = 0; i < 14; i++){ CLIENT.println(formFirstHalf[i]); } CLIENT.println(" <tr>"); CLIENT.println(" <td>"); CLIENT.println(" <select name='red'>"); for(i = 0; i <= 255; i++){ CLIENT.print(" <option value='"); CLIENT.print(i, DEC); if(i == R){ CLIENT.print("' selected>"); }else{ CLIENT.print("'>"); } CLIENT.print(255 - i, DEC); CLIENT.println("</option>"); } CLIENT.println(" </select>"); CLIENT.println(" </td>"); CLIENT.println(" <td>"); CLIENT.println(" <select name='green'>"); for(i = 0; i <= 255; i++){ CLIENT.print(" <option value='"); CLIENT.print(i, DEC); if(i == G){ CLIENT.print("' selected>"); }else{ CLIENT.print("'>"); } CLIENT.print(255 - i, DEC); CLIENT.println("</option>"); } CLIENT.println(" </select>"); CLIENT.println(" </td>"); CLIENT.println(" <td>"); CLIENT.println(" <select name='blue'>"); for(i = 0; i <= 255; i++){ CLIENT.print(" <option value='"); CLIENT.print(i, DEC); if(i == B){ CLIENT.print("' selected>"); }else{ CLIENT.print("'>"); } CLIENT.print(255 - i, DEC); CLIENT.println("</option>"); } CLIENT.println(" </select>"); CLIENT.println(" </td>"); CLIENT.println(" </tr>"); for(i = 0; i < 7; i++){ CLIENT.println(formSecondHalf[i]); } }
気を付けるところは、私が買った「SeeedStudio SIDEKICK BASIC KIT」というキットに入っている3色LEDは「アノードコモン(共通陽極)」というタイプで、プラスに5V、各色のコントロールはマイナスになる、という点だ。ここを間違えると思ったように動作しない。一番長い脚が共通陽極だ。封入の切り欠きを左にして、左から赤、アノード、緑、青の順に脚が並ぶ。
また、フォームから与える値も、アノードコモンであるゆえに、「0」が最も明るくなり、「255」が最も暗くなるので、インターフェイスを逆にするなど、多少工夫が必要だ。
それから、PWM(パルス幅変調)の機能を大いに活用し、いろいろな色が出せるようにした。この場合の注意点は、ETHERNET SHIELD 2を取り付けると、10番・11番・12番のピンが使えなくなるので、他のピンで出力しなければならないことだ。今回は3番・5番・6番を使った。
で、ウェブインターフェイスをタブレットにロードする。
動かしてみるとこんな感じで、いろんな色になって面白い。
うほほ、なんだかわからないが、韓国WIZnetにビシビシ紹介されている。嬉しいぞ。
Arduinoは内部抗争で分裂しているのだそうな。そういやあ、arduino.orgとarduino.ccの二つがあって、ダウンロードできるIDEのバージョンが微妙に違ったりするので、変だな、と思っていたのである。
私はと言うと、なんというか、2ちゃんねる用語でいうところの「今北産業」(笑)みたいなことなんで、なにがどうなんだか、さっぱりワカンネェw。
昨日、一定の距離に近づくとカメラのシャッターが切れるという装置を作ったが、ふと思いついて、同じハードウェアで距離計が作れるな、と思った。なぜかというと、無駄にサーボを使っているからだ。
50センチ以下の距離が測れればいいな、というわけで、サーボの0度から180度まで割り付ける。
5センチ18度、というわけで、何かスマホいじりをしている次女に
「お~い、智香っ、智香。分度器を貸せ」
と言ったら、
「ん~?分度器?……ナイ。」
とニベもない。中学生のクセして、ないワケあるかいっ!!……と思ったが、ガミガミ言うのも面倒くさい。
針の長さがだいたい8cmだから、
tan18° = x ÷ 8cm、x = tan18° × 8cm ≒ 2.6cm。
円周上2.6cm刻みで目盛りをふればよろしい。……って、なにムダな計算しとるんだ俺は。
まあよい、それで、動かしてみるとこんなふうになかなか面白い。
で、まあ、定規をあてて測ると、だいたい合ってる。
しかし、デジタルで採れている値を、ムダにアナログに直すという、はっはっは。
スケッチはこんな具合。
// // rangemeter.ino // 超音波センサで測距して、サーボで作ったメーターで表示する。 // 27.6.7(日) 1015~ // 佐藤俊夫 // #include <Servo.h> Servo meter; const int SERVO = 8; const int TRIG = 9; const int ECHO = 10; const int SERVO_MIN = 0; const int SERVO_MAX = 180; const float RANGE_MIN = 0.0; const float RANGE_MAX = 50.0; void setup() { meter.attach(SERVO); meter.write(SERVO_MIN); pinMode(TRIG,OUTPUT); pinMode(ECHO,INPUT); } void loop() { float range = 0.0; delay(1000); range = ranging(); if(range <= RANGE_MAX){ indication(range); }else{ indication(RANGE_MAX); } } void indication(float range){ int deg = 0; deg = map(range, RANGE_MIN, RANGE_MAX, SERVO_MIN, SERVO_MAX); meter.write(deg); } float ranging(){ float time = 0.0, range = 0.0; digitalWrite(TRIG,LOW); delayMicroseconds(1); digitalWrite(TRIG,HIGH); delayMicroseconds(1); digitalWrite(TRIG,LOW); time = pulseIn(ECHO,HIGH); if (time > 0.0) { range = (time / 2.0) * 340.0 * 100.0 / 1000000.0; return(range); }else{ return(9999.0); } }
それにしてもこのArduinoのIDE、エディタがviに差し変わらんものか。オッサンはviのほうが調子が出るのぢゃ。
それで、デジカメの電池がなくなるまでベランダ写真をとり、雲の流れる動画にした。
しかし、今日は良く晴れていて、雲があまりないので、いまいち雲が流れる感じはしないのであった(笑)。
昨日Arduinoを使って出来上がった「シャッターを切るやつ」、単純なものに作り直す。
今度は1分おきに間欠的にシャッターを切るものに変更して、これをベランダに持ち出し、南の空を1分おきに撮影する。
何をするかと言うと、雲が流れる動画を作りたいのである。
プログラムはとっても簡単。
// // サーボを間欠的に動かしてカメラのシャッターを切る。 // 佐藤俊夫 // 27.5.31(日)1020~ // #include <Servo.h> Servo shutter; const int SERVO = 9; void setup() { shutter.attach(SERVO); shutter.write(90); } void loop() { int i = 0; shutter.write(50); delay(5000); shutter.write(90); for(i = 0; i <= 60; i++){ delay(1000); } }
こうしてカメラをベランダに放置しておくと、無線SDカードで刻々と写真がタブレットに流し込まれてくるのもいとをかし、である。
Arduinoの参考書は、この本以外は特に必要ないのではないかと思われるほど、本当に手短に、しかも分かり易く、必要なことが述べられてある。
先週、Arduinoを使ってデジカメのシャッターを切る遊びがやりたくて、ソレノイドでやろうとしたら、ソレノイドの力不足でうまくいかなかった。
よく考えてみたら、最初に買った入門キットの中に小さいサーボがあり、これは力があるからシャッターぐらい押せるだろう、と思って工夫した。
そうしたら、うまくいった。こんな具合である。
ネットワーク経由で動くようになっており、そのプログラムは次の通りである。
// // WebServerでサーボを動かし、カメラのシャッターを切る。 // 佐藤俊夫 // 27.5.30(土)1300~ // #include <SPI.h> #include <Ethernet2.h> #include <Servo.h> byte mac[] = { 0x90, 0xA2, 0xDA, 0x0F, 0xF6, 0x74 }; IPAddress ip(192, 168, 1, 129); EthernetServer server(80); Servo shutter; const int SERVO = 9; void setup() { Ethernet.begin(mac, ip); server.begin(); shutter.attach(SERVO); shutter.write(90); } void loop() { String recvbuf; EthernetClient client = server.available(); if (client) { boolean currentLineIsBlank = true; while (client.connected()) { if (client.available()) { char c = client.read(); recvbuf += c; if (c == '\n' && currentLineIsBlank) { client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); client.println(); client.println("<!DOCTYPE HTML>"); client.println("<html>"); client.println("<head></head>"); client.println("<body>"); client.println("<center><h3>Drive solenoid.</h3>"); client.println("<hr>"); client.println("<form method=\"POST\">"); client.println("<input type=\"submit\" value=\"Do!\">"); client.println("</form>"); client.println("</center>"); client.println("</body>"); client.println("</html>"); break; } if (c == '\n') { currentLineIsBlank = true; if(recvbuf.indexOf("POST") == 0){ shutter.write(50); delay(5000); shutter.write(90); } recvbuf = ""; } else if (c != '\r') { currentLineIsBlank = false; } } } // give the web browser time to receive the data delay(1); client.stop(); } }