サーボコントロールのWeb汎用化

投稿日:

 今日、Arduino + ETHERNET SHIELD 2 で遊んでいて思ったのだが、同じGETの処理をするなら、

http://hogehoge.hoge/?start=10&hold=5000&release=50

……というような、汎用化したGET引数にすれば面白いかも、と思った。まあ、セキュリティ上よろしくないが。

LEDをネットにつないでコントロール

投稿日:

 先週少しやりかけて途中だった、Arduino UNO に ETHERNET SHIELD 2 をとりつけ、ウェブインターフェイスを作ってこれでLEDをコントロールするという遊びの続きをやる。

 こんなふうなインターフェイスでコントロールすることにする。

名称未設定 1

 で、ブレッドボードとArduinoはこんな感じ。「SeeedStudio SIDEKICK BASIC KIT」に入っていた3色LEDを使う。

IMG_3004

 スケッチはこんな感じで。ウェブ上で同じようなことを公開している方々は、ブラウザの出力を受けるのに、たいてい普通のポインタ文字列を使っておられるのだが、せっかく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番を使った。

 で、ウェブインターフェイスをタブレットにロードする。

IMG_3005

 動かしてみるとこんな感じで、いろんな色になって面白い。


分裂

投稿日:

 Arduinoは内部抗争で分裂しているのだそうな。そういやあ、arduino.orgとarduino.ccの二つがあって、ダウンロードできるIDEのバージョンが微妙に違ったりするので、変だな、と思っていたのである。

 私はと言うと、なんというか、2ちゃんねる用語でいうところの「今北産業」(笑)みたいなことなんで、なにがどうなんだか、さっぱりワカンネェw。

無駄の多い距離計(笑)

投稿日:

 昨日、一定の距離に近づくとカメラのシャッターが切れるという装置を作ったが、ふと思いついて、同じハードウェアで距離計が作れるな、と思った。なぜかというと、無駄にサーボを使っているからだ。

 50センチ以下の距離が測れればいいな、というわけで、サーボの0度から180度まで割り付ける。

 5センチ18度、というわけで、何かスマホいじりをしている次女に

「お~い、智香っ、智香。分度器を貸せ」

と言ったら、

「ん~?分度器?……ナイ。」

とニベもない。中学生のクセして、ないワケあるかいっ!!……と思ったが、ガミガミ言うのも面倒くさい。

 針の長さがだいたい8cmだから、

tan18° = x ÷ 8cm、x = tan18° × 8cm ≒ 2.6cm。

 円周上2.6cm刻みで目盛りをふればよろしい。……って、なにムダな計算しとるんだ俺は。

IMG_2999

 まあよい、それで、動かしてみるとこんなふうになかなか面白い。

 で、まあ、定規をあてて測ると、だいたい合ってる。

IMG_2996

 しかし、デジタルで採れている値を、ムダにアナログに直すという、はっはっは。

 スケッチはこんな具合。

//
//  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を使って出来上がった「シャッターを切るやつ」、単純なものに作り直す。

 今度は1分おきに間欠的にシャッターを切るものに変更して、これをベランダに持ち出し、南の空を1分おきに撮影する。

media-20150531

 何をするかと言うと、雲が流れる動画を作りたいのである。

 プログラムはとっても簡単。

//
//  サーボを間欠的に動かしてカメラのシャッターを切る。
//    佐藤俊夫
//    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の参考書は、この本以外は特に必要ないのではないかと思われるほど、本当に手短に、しかも分かり易く、必要なことが述べられてある。



カメラのシャッターをネット経由で切る

投稿日:

 先週、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();
  }
}