空の写真リベンジ

投稿日:

 先週試したAdafruit製の「小型TTLシリアルjpegカメラ」での間欠撮影先週はどうしたわけか同じ画像ばかり撮れてしまい、失敗である。

 (チナミに、このカメラのメーカーの「Adafruit」という会社、有名なマッシモ・バンジのTEDの中で紹介されていたことに気付いた。)

https://youtube.com/watch?v=UoBUXOOdLXY%3Ft%3D5m50s

 気を取り直して、スケッチを直す。撮影時のカメラのステータスを確認し、撮れていなければ何回でも撮り続ける。

「while(撮れてない)撮る;」

……というわけだ。まあ、万が一ハードウェアエラーなどがあるとループが回り続けるので良くないが、ループが回り続けようが結局のところはモノとしては電源を入れ直す他にどうしようもないので、こんなものだろう。

 それと、撮影が終わったらカメラをそのつどリセットすることにした。また、既に存在するファイル名は避けるようにした。こういう時、Arduinoには書式文字列付きの「sprintf」がないので、少し不便だなと思う。

 夜明けから日没までの一日の空の雲を撮りたいので、天気予報を見て雨が降らぬ確信を持ってから、昨夜寝る前にベランダにカメラをセットしておいた。朝早く起きるより楽だからだ。夜のうちに1000枚くらい写真が撮れてしまうが、SDカードには余裕があるので大丈夫である。

 昼間は用事があるのでカメラの面倒は見れないが、放置しておけば淡々と写真は撮れていく。

IMG_3143

 で、撮れた写真をWindows Movie Makerに流し込むと、微速撮影動画の一丁上がりだ。

 スケッチは次のとおりである。

//
//  camera2Web.ino
//    27.07.20(月) 0850~
//    佐藤俊夫
//    Adafruit製小型TTLシリアルJPEGカメラ+ETHERNET SHIELD 2で
//    間欠撮影をし、Webでダウンロードできるようにする。
//
#include <Adafruit_VC0706.h>
#include <SPI.h>
#include <SD.h>
#include <SoftwareSerial.h>
#include <Ethernet2.h>

#define CHIPSELECT 4

SoftwareSerial CAMCONNECTION(2, 3);
Adafruit_VC0706 CAM = Adafruit_VC0706(&CAMCONNECTION);
const unsigned long int INTERVAL = 30L * 1000L;
byte MAC[] = {  0x90, 0xA2, 0xDA, 0x0F, 0xF6, 0x74 };
IPAddress IP(192, 168, 1, 129);
EthernetServer SERVER(80);
EthernetClient CLIENT;

void setup() {
  pinMode(10, OUTPUT);
  if(!SD.begin(CHIPSELECT)) return;
  if(!CAM.begin()) return;
  CAM.setImageSize(VC0706_320x240);
  Ethernet.begin(MAC, IP);
  SERVER.begin();
  delay(1000);
}

void loop() {
  static unsigned long int prevtime = 0;
  char c;
  String rstr = "";
  //  INTERVALおきに写真を撮る
  if(millis() >= prevtime + INTERVAL){
    prevtime = millis();
    takePicture();
  }
  //  Webサーバ
  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("IMG") >= 0){
      String filename = "DCIM/";
      char cfilename[17];
      filename.concat(rstr.substring(rstr.indexOf("IMG"), rstr.indexOf("JPG") + 3));
      filename.toCharArray(cfilename, 17);
      CLIENT.println("HTTP/1.1 200 OK");
      CLIENT.println("Content-Type: image/jpg");
      CLIENT.println("Connection: close");
      CLIENT.println();
      File img = SD.open(cfilename);
      while(img.available()){
        CLIENT.write(img.read());
      }
      img.close();
    }else{
      sendform();
    }
    rstr = "";
    delay(1);
    // close the connection:
    CLIENT.stop();
  }
  delay(20);
}

void sendform(){
  //  フォームを送る。
  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><head></head><body><center>");
  File dcim = SD.open("/DCIM");
  while(true) {
    File imgfile =  dcim.openNextFile();
    if(!imgfile){
      dcim.rewindDirectory();
      break;
    }
    CLIENT.write("<a href=\"");
    CLIENT.write(imgfile.name());
    CLIENT.write("\">");    
    CLIENT.write(imgfile.name());
    CLIENT.println("</a><br>");
    imgfile.close();    
  }
  dcim.close();
  CLIENT.println("</center></body></html>");
}

void takePicture(){
  static unsigned int pnum = 0;
  char filename[] = "DCIM/img0000.jpg";
  while(!CAM.takePicture());
  do{
    filename[8]  = '0' + pnum / 1000;
    filename[9]  = '0' + (pnum / 100) % 10;
    filename[10] = '0' + (pnum /  10) % 10;
    filename[11] = '0' + pnum % 10;
    pnum ++;
  }while(SD.exists(filename));
  if(pnum > 9999) pnum = 0;
  File imgFile = SD.open(filename, FILE_WRITE);
  uint16_t jpglen = CAM.frameLength();
  pinMode(8, OUTPUT);
  while (jpglen > 0) {
    uint8_t *buffer;
    uint8_t bytesToRead = min(32, jpglen);
    buffer = CAM.readPicture(bytesToRead);
    imgFile.write(buffer, bytesToRead);
    jpglen -= bytesToRead;
  }
  while(!CAM.reset());
  imgFile.close();
}




リモコンでLEDコントロール

投稿日:

 秋月電子で買ったリモコン、せっかくだから、これでLEDを点けたり消したりしてみよう。ただ単に点滅ではいまいち芸がないから、3色LEDの照度を多段階に変えたり、色をいろいろに変えたりしてみようではないか。

 買ってきたリモコン

 IR受信ライブラリを使ってみたが、どうも安定しない。リモコンからはコードが連続送出されるようなのだが、受信ごとに違う値が返ってきて困る。

 調べてみると、0xffffff、すなわち24ビットを超える値が返ってくるときはどうやら受信がうまく行っていないことがわかったので、これを排除してみたところ、うまくいくようだ。

 そこを通り抜けると、今度はどうも、使用するピンの組み合わせに約束があるようだ。はっきりとはわからないのだが、PWMを9・10・11、IRリモコン受信モジュールを12で使うと、さっぱりうまく行かない。

 いろいろと変えて、LEDのためのPWMをR・G・Bそれぞれ9・5・6に割り付けるとうまくいくことがわかった。……理由がなんだかよくわからないのだが(笑)。

 スケッチは結局こうなった。

//
//  IR2LED.ino
//    赤外線リモコン「DFR0107」(DFROBOT社)でLEDをコントロールする。
//    https://satotoshio.net/blog/?p=1490
//    27.7.11(土) 1755~
//    佐藤俊夫
//    
#include <IRremote.h>

const int LEDR = 9, LEDG = 5, LEDB = 6, IRR = 12;
IRrecv irrecv(IRR);
decode_results results;

void setup()
{
  irrecv.enableIRIn(); // Start the receiver
  pinMode(LEDR, OUTPUT);
  pinMode(LEDG, OUTPUT);
  pinMode(LEDB, OUTPUT);
  pinMode(IRR, INPUT);  
}

void loop() {
  if (irrecv.decode(&results)) {
    if(results.value <= 0xffffff && results.value > 0xfd0000){
      processValue(results.value);
    }      
    irrecv.resume(); // Receive the next value
  }
}

void processValue(long int value){
  static boolean ledOn = true;
  static unsigned int r = 255, g = 255, b = 255, combi = 0x07;
  switch(value){
    case 0xfd00ff:  //  vol+
      if(ledOn == true){
        ledOn = false;
      }else{
        ledOn = true;
      }
      break;
    case 0xfd807f:  //  vol-
      if(r < 255 - 10) r += 10;
      if(g < 255 - 10) g += 10;
      if(b < 255 - 10) b += 10;
      break;
    case 0xfd906f:
      if(r > 0 + 10) r -= 10;
      if(g > 0 + 10) g -= 10;
      if(b > 0 + 10) b -= 10;
      break;
    case 0xfd609f:  // >>|
      if(combi < 0x07){
        combi++;
      }else{
        combi = 0x01;
      }
      break;
    case 0xfd20df:  // |<<
      if(combi > 0x01){
        combi --;
      }else{
        combi = 0x07;
      }
      break;
    default:
      break;
  }
  controlLED(ledOn, r, g, b, combi);

}

void controlLED(const boolean on, const int r, const int g, const int b, const int combi){
  unsigned int ron = 0, gon = 0, bon = 0;
  
  ron = (0x04 & combi) > 0;
  gon = (0x02 & combi) > 0;
  bon = (0x01 & combi) > 0;
  delay(20);
  if(on){
    analogWrite(LEDR, (255 - r * ron));
    analogWrite(LEDG, (255 - g * gon));
    analogWrite(LEDB, (255 - b * bon));
  }else{
    analogWrite(LEDR, 255);
    analogWrite(LEDG, 255);
    analogWrite(LEDB, 255);
  }
}

ウェブ扇風機

投稿日:

 昨日試した扇風機遊びを少し進め、温度制御を付けてみたい。

 おあつらえ向きに、最初に買った「Seeedstudio SIDEKICK BASIC KIT」には、サーミスタが入っている。これで温度を検知して、温度ごとに扇風機の回転を制御するわけだ。値の変更などはETHERNET SHIELD 2をつなぎ、ウェブインターフェイスで行うと面白いだろう。

 以前にも試したが、しかし、このキット付属のサーミスタは情報が少なくて困る。そこで、ちゃんと計算してそれなりの温度測定をしてみたい。以前は測りたい温度近傍の特性値をテーブルから拾って2次式で近似しただけであった。

 スペックシートによれば、-55℃で10583.3Ω、+125℃で1.277Ωになる、とある。常温付近では0℃で190.07Ω、40℃で24.87Ωだ。もしサーミスタ直付けでArduinoから5V給電すれば、-55℃のときは0.47mAだが、0℃で26.3mA、40℃で201mAと、Arduinoに流せる電流の20mAを大きく逸脱してしまう。

 40度付近でも大丈夫なように、抵抗を付加しなければならない。40℃の時に流したい電流を5mAと仮定すれば、

IMG_3075

……というような計算で975Ωくらいつなげておけばよいということになる。

 しかし、手持ちの抵抗は、キットに入っていた330Ω・1kΩ・10kΩの3種類しかないから、これで賄うしかない。

 一番大きい抵抗は10kΩだから、これで計算しなおすと、40℃の時で

5V / (24.87Ω + 10kΩ) = 0.5mA

2番目は1kΩ、同じく40℃の時で

5V / (24.87Ω + 1kΩ) = 4.88mA

同様に3番目の330Ωだと

5V / (24.87Ω + 330Ω) = 14mA

…ということになる。間をとって、1kΩで回路を作ってみよう。

 次に、サーミスタは抵抗から温度を知る。一方、Arduinoは電圧からデジタル値を知る。したがって、電圧からまず抵抗値を知らなければその先の温度測定に進めない。

 先の電流調整用の1kΩ抵抗をR0として含め、現在のサーミスタの抵抗値を知るには、一般に式は次のようになろう。

IMG_3079

 これをソースコードに表せば、次のようになる。

  const float v0 = 5.0, r0 = 1000.0;  //  Arduino +5Vと電流調整抵抗1kΩ
  const int resolution = 1024;  //  アナログ入力の分解能
  int srcVal = 0;
  float vt = 0.0, rt = 0.0;
  
  srcVal = analogRead(THERMISTOR);
  vt = srcVal * (v0 / (resolution - 1));
  rt = (v0 * r0 - vt * r0) / vt;

 次に、知った抵抗値から温度を知る計算である。

 サーミスタの抵抗は対数特性を持っているので、関数は対数モデルになる。また、サーミスタの特性を表す重要な値は「B値」と呼ばれるものと、Ta(温度)、R0(抵抗)の3つで、それらはスペックシートに書いてある。

 TaとR0については、「温度がTaのとき、抵抗はR0になる」というものだ。私の手元のサーミスタは、スペックシートによると、

Ta = 25℃
R0 = 50kΩ

……とある。

 B値は「抵抗の対数と、温度の比」である。これもスペックシートに関係式が書いてある。

 これらのスペックシートの値と、知った抵抗値から温度を求める式は、次のようになる。

IMG_3080

 これを、先の抵抗値を知る部分と併せて、Arduinoで使える関数にすれば、このようになろうか。

float tempMesure(){
  const float B = 4350.0, Ta = 25.0, Rt0 = 50000.0;  //  MF11-503Kスペックシート記載
  const float K = 273.15;  //  熱力学温度の定数
  const float v0 = 5.0, r0 = 1000.0;  //  Arduino +5Vと電流調整抵抗1kΩ
  const int resolution = 1024;  //  アナログ入力の分解能
  int srcVal = 0;
  float vt = 0.0, rt = 0.0;
  
  srcVal = analogRead(THERMISTOR);
  vt = srcVal * (v0 / (resolution - 1));
  rt = (v0 * r0 - vt * r0) / vt;
  return(1.0 / (log(rt / Rt0) / B + 1.0 / (Ta + K)) - K);
}

 これを用いて、少し真面目っぽい温度測定スケッチを書けば、次のようになる。

//
//  thermistor2temp.ino
//    サーミスタでわりと真面目に温度を測る。
//    27.7.5(日)
//    佐藤俊夫
//
const int THERMISTOR = 1;

void setup() 
{ 
  Serial.begin(9600);
} 
 
void loop() 
{ 
  float t = 0.0;
  
  t = tempMesure();
  Serial.print("Temp = ");
  Serial.print(t);
  Serial.println("C");
  delay(500);
}

float tempMesure(){
  const float B = 4350.0, Ta = 25.0, Rt0 = 50000.0;  //  MF11-503Kスペックシート記載
  const float K = 273.15;  //  熱力学温度の定数
  const float v0 = 5.0, r0 = 1000.0;  //  Arduino +5Vと電流調整抵抗1kΩ
  const int resolution = 1024;  //  アナログ入力の分解能
  int srcVal = 0;
  float vt = 0.0, rt = 0.0;
  
  srcVal = analogRead(THERMISTOR);
  vt = srcVal * (v0 / (resolution - 1));
  rt = (v0 * r0 - vt * r0) / vt;
  return(1.0 / (log(rt / Rt0) / B + 1.0 / (Ta + K)) - K);
}

 次に、温度に応じて扇風機の回転数が変わるようにしよう。先日作った「ソリッドステート・リレーモジュール」を遺憾なく使用する。

ソリッドステート・リレーモジュール
IMG_3081

 このモジュールの内部には、秋月電子の「ソリッド・ステート・リレー(SSR)キット 25A(20A)タイプ」が組み付けてある。

 27℃で1/fゆらぎエフェクト、28℃で弱風、29℃で強風、とでもしてみようか。1/fゆらぎエフェクトには、先週書いた関数をそのままコピペする。

IMG_3083

//
//  thermistor2windFan.ino
//    サーミスタで温度を測り、扇風機を制御する。
//    27.7.5(日)
//    佐藤俊夫
//
const int THERMISTOR = 1, WINDFAN = 9;
void setup() 
{ 
  Serial.begin(9600);
  pinMode(WINDFAN, OUTPUT);
} 
 
void loop() 
{ 
  const int half = 128, full = 255;
  const float lowTemp = 27.0, midTemp = 28.0, highTemp = 29.0; 
  float t = 0.0, f = 0.0;
  
  t = tempMesure();
  Serial.print("temp=");
  Serial.println(t);
  if(t > highTemp){
    analogWrite(WINDFAN, full);
  }else
  if(t > midTemp){
    analogWrite(WINDFAN, half);
  }else
  if(t > lowTemp){
    f = f1Fluctuation();
    analogWrite(WINDFAN, f);
  }else{
    analogWrite(WINDFAN, 0);
  }
  delay(20);
}

float tempMesure(){
  const float B = 4350.0, Ta = 25.0, Rt0 = 50000.0;  //  MF11-503Kスペックシート記載
  const float K = 273.15;  //  熱力学温度の定数
  const float v0 = 5.0, r0 = 1000.0;  //  Arduino +5Vと電流調整抵抗1kΩ
  const int resolution = 1024;  //  アナログ入力の分解能
  int srcVal = 0;
  float vt = 0.0, rt = 0.0;
  
  srcVal = analogRead(THERMISTOR);
  vt = srcVal * (v0 / (resolution - 1));
  rt = (v0 * r0 - vt * r0) / vt;
  return(1.0 / (log(rt / Rt0) / B + 1.0 / (Ta + K)) - K);
}

int f1Fluctuation(){
  static float x = 0.1;
  if(x < 0.5){
    x = x + 2 * x * x;
  } 
  else {
    x = x - 2 * (1.0 - x) * (1.0 - x);
  }
  if(x < 0.05 || x > 0.995){
    x = random(10, 90) / 100.0;
  }
  return((int)(x * 255));
}

 回路図など、描くも愚かというか、こんな簡単なものである。

IMG_3084

 さて次に、ETHERNET SHIELD 2をつなぎ、この扇風機をWeb化する。昨日買っておいた「クリアランス確保用ピンソケット」が役に立つ。

IMG_3067

 組み付けるとこんな感じである。回路はネットにつながない場合と同じでよい。

IMG_3087

 こんなインターフェイスで動かす。

ネット扇風機インターフェイス

 設定した温度になると扇風機が回る。

IMG_3088

 スケッチはこのようになる。

//
//  web2thermistor_windFan.ino
//    サーミスタで温度を測り、扇風機を制御する。
//    27.7.5(日)
//    佐藤俊夫
//
#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;
const int THERMISTOR = 1, WINDFAN = 9;
//
void setup() 
{ 
  Ethernet.begin(mac, ip);
  SERVER.begin();
  Serial.begin(9600);
  pinMode(WINDFAN, OUTPUT);
} 
 
void loop() 
{ 
  const int half = 128, full = 255;
  static float lowTemp = 27.0, midTemp = 28.0, highTemp = 29.0; 
  float t = 0.0, f = 0.0;
  //  Webサーバの動作
  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("low=") >= 0){
      lowTemp = rstr.substring(rstr.indexOf("low=") + 4, rstr.indexOf("&green=")).toInt();
      midTemp = rstr.substring(rstr.indexOf("mid=") + 4, rstr.indexOf("&blue=")).toInt();
      highTemp = rstr.substring(rstr.indexOf("high=") + 5, rstr.indexOf("&end")).toInt();
    }
    rstr = "";
    sendform(lowTemp, midTemp, highTemp);
    delay(1);
    // close the connection:
    CLIENT.stop();
  }
  // 温度測定
  t = tempMesure();
  Serial.print("temp=");
  Serial.println(t);
  if(t > highTemp){
    analogWrite(WINDFAN, full);
    Serial.println("high");
  }else
  if(t > midTemp){
    analogWrite(WINDFAN, half);
  }else
  if(t > lowTemp){
    f = f1Fluctuation();
    analogWrite(WINDFAN, f);
  }else{
    analogWrite(WINDFAN, 0);
  }
  delay(20);
}

float tempMesure(){
  //  Seeedstudio SIDEKICK BASIC KIT付属のサーミスタ「MF11-503K」で
  //  温度を測る。
  const float B = 4350.0, Ta = 25.0, Rt0 = 50000.0;  //  MF11-503Kスペックシート記載
  const float K = 273.15;  //  熱力学温度の定数
  const float v0 = 5.0, r0 = 1000.0;  //  Arduino +5Vと電流調整抵抗1kΩ
  const int resolution = 1024;  //  アナログ入力の分解能
  int srcVal = 0;
  float vt = 0.0, rt = 0.0;
  
  srcVal = analogRead(THERMISTOR);
  vt = srcVal * (v0 / (resolution - 1));
  rt = (v0 * r0 - vt * r0) / vt;
  return(1.0 / (log(rt / Rt0) / B + 1.0 / (Ta + K)) - K);
}

int f1Fluctuation(){
  //  間欠カオス法により0~255の間で1/fゆらぎを生成して返す。
  static float x = 0.1;
  if(x < 0.5){
    x = x + 2 * x * x;
  } 
  else {
    x = x - 2 * (1.0 - x) * (1.0 - x);
  }
  if(x < 0.05 || x > 0.995){
    x = random(10, 90) / 100.0;
  }
  return((int)(x * 255));
}

void sendform(float lowTemp, float midTemp, float highTemp){
  //  フォームを送る。
  char* formFirstHalf[] = {
    "<html>",
    "  <head>",
    "    <meta charset=\"utf-8\">",
    "  </head>",
    "  <body bgcolor='#ddddff'>",
    "    <center>",
    "      <h1>Arduino ネット扇風機</h1>",
    "      <form method='GET'>",
    "        <table>",
    "          <tr>",
    "  	    <th>ゆらぎ送風温度</th>",
    "  	    <th>弱風温度</th>",
    "  	    <th>強風温度</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("           <input type='text' name='low' size='6em' value =");
  CLIENT.print(lowTemp);
  CLIENT.println(">");
  CLIENT.println("            </td>");
  CLIENT.println("            <td>");
  CLIENT.println("           <input type='text' name='mid' size='6em' value =");
  CLIENT.print(midTemp);
  CLIENT.println(">");
  CLIENT.println("            </td>");
  CLIENT.println("            <td>");
  CLIENT.println("           <input type='text' name='high' size='6em' value =");
  CLIENT.print(highTemp);
  CLIENT.println(">");
  CLIENT.println("            </td>");
  CLIENT.println("          </tr>");
  for(i = 0; i < 7; i++){
    CLIENT.println(formSecondHalf[i]);
  }

}






自然風もどき扇風機

投稿日:

 1000円ほどで買った安物の扇風機にArduinoその他をつなぎ、自然風もどきの送風機能を実装してみる。

 先週作った「AC100Vオン・オフモジュール」と、1/fゆらぎエフェクトをかけたランプのスケッチをそのまま使う。

 先週のLチカの、赤色LEDの線をそのままSSRの+5Vロジック・インへ、それから、Lチカはアノード・コモンのLEDを使ったためにArduinoの+5Vに接続していたから、これをGNDにつなぎかえてSSRの+-0Vへ入れる。

 これを試そうという方がいたら、使う扇風機についてご注意いただきたい。必ず、ここで使っているような、電子的な制御がかかっていない、AC100Vがモーターに直接かかるような、安物の簡素な扇風機を使わないといけない。

1/fゆらぎでLチカ

投稿日:

 さて、他の人もよくやっている「1/fゆらぎのLチカ」を、私もマネしてやってみる。

 こちらこちらのサイトを参考にさせていただく。よく使われるのはこの「間欠カオス法」だそうである。

間欠カオス法
0.0\leqq x\leqq 1.0として
x<0.5のときx=x+2x^2
x\geqq 0.5のときx=x-2(1-x)^2

 ただし、参考にさせていただいた各サイトによると、この間欠カオス法は、0.0付近や1.0付近に値が貼り付き易いので、0.0や1.0に近くなったら乱数を入れてやるとよいとのことである。実際に試すと、たしかに、長時間ちらつきがなくなったり、消えたままになったりしやすい。そこで、そうなったときに乱数を入れ、中間付近の適当な輝度で光らせるようにしてある。

 青色要素を少なくして、ろうそくの色合い風なちょっと黄色味がかった色にした。

//
//  f1Fluctuation.ino
//    1/fゆらぎでLチカ
//    佐藤俊夫
//    27.06.28(日)1700~
//
const int R = 9, G = 10, B = 11;
//
void setup() {
  pinMode(R, OUTPUT);
  pinMode(G, OUTPUT);
  pinMode(B, OUTPUT);
}

void loop() {
  int f = 0.0;
  
  f = f1Fluctuation();
  analogWrite(R, 255 - f);
  analogWrite(G, 255 - f);
  analogWrite(B, 255 - (f * 0.2));
  delay(20);
}


int f1Fluctuation(){
  static float x = 0.1;
  if(x < 0.5){
    x = x + 2 * x * x;
  } 
  else {
    x = x - 2 * (1.0 - x) * (1.0 - x);
  }
  if(x < 0.05 || x > 0.995){
    x = random(10, 90) / 100.0;
  }
  return((int)(x * 255));
}



どういうワケか蕎麦

投稿日:

 ある時たまたま「蕎麦打ち」の動画をYoutubeで見かけて、それを一度だけ見た。

 一度見ただけなのに、再生リコメンドに蕎麦打ち動画ばかり強迫神経症的に出てくるようになった(笑)。

 蕎麦打ちの動画はなぜか結構多く、全部見きれないほど大量にある。蕎麦打ちが趣味と言う人は多いらしい。

 もういっそのこと、と思い、面白そうなものを再生リストにしてみた。

今度は鬱陶しく

投稿日:

 今度はできるだけ鬱陶しく汚くLEDを光らせてみた。 見ていて疲れる。

//
//  badLight.ino
//    今度は3色LEDをできるだけ鬱陶しく汚く光らせたい。
//    佐藤俊夫
//    27.06.28(日)
//
const int R = 9, G = 10, B = 11;
//
void setup() {
  pinMode(R, OUTPUT);
  pinMode(G, OUTPUT);
  pinMode(B, OUTPUT);
}

void loop() {
  digitalWrite(R, random(2));
  digitalWrite(G, random(2));
  digitalWrite(B, random(2));
  delay(100);
}



美しくLチカ

投稿日:

 3色LEDにサインカーブでPWMをかけ、できるだけ美しく色変化をかけたい。実はこれは前にもやったが、もうちょっとコードを美しくしたいのであった。

 3項演算子があったらなあ……。

//
//  sinCurvePWM.ino
//    3色LEDをできるだけ幻想的に美しく光らせたい。
//    佐藤俊夫
//    27.06.27(土)
//
const int R = 9, G = 10, B = 11;
//
void setup() {
  pinMode(R, OUTPUT);
  pinMode(G, OUTPUT);
  pinMode(B, OUTPUT);
}

void loop() {
  static float thetaR = 0.0, thetaG = 0.0, thetaB = 0.0;
  const float PI2 = 2.0 * PI;
  const float Rtic = PI2 / 1000.0, Gtic = PI2 / 900.0, Btic = PI2 / 1100.0;
  
  thetaR += Rtic;
  thetaG += Gtic;
  thetaB += Btic;
  if(thetaR >= PI2) thetaR = 0.0;
  if(thetaG >= PI2) thetaG = 0.0;
  if(thetaB >= PI2) thetaB = 0.0;
  analogWrite(R, 255 - sin255(thetaR));
  analogWrite(G, 255 - sin255(thetaG));
  analogWrite(B, 255 - sin255(thetaB));
  delay(20);
}

int sin255(float theta){
  return((int)((sin(theta) + 1.0) * 127.5));
}



日々思い通りにはいかぬもの

投稿日:

 帰宅。

 今日は日本ITストラテジスト協会関東支部の月例会があったが、仕事と用事が重なり、行けなかった。今回の月例会のテーマ別ディスカッションもぜひ聞きたいと思っていた内容だったので、残念である。人生、なかなか、こうしたいああしたい、と、思ったようには運ばぬものだ。

 忙中有閑。仕事は朝に引けたから、帰りに秋葉原へ寄り、挨拶がわりにヨドバシのスタインウェイをちょっと弾く。

media-20150621

 いつものショパン3曲弾くなど。

 千石電商と秋月電子で少し買い物をする。2000円ほども使えばたっぷり楽しめる。我ながらなんとささやかなこと。

IMG_3029

 今回買ったもののうちでも、出色の傑作は、このたった250円の小さなキットだろう。

IMG_3030

 これは、TTL5VでAC100Vを扱うためのリレーなのだが、昔のリレーとは違って、機械電磁式ではなく、電子式の無接点で、部品点数も極めて少ない。しかも100Vで25Aも流せる。これは、この方のブログ記事で見て知ったのである。

 それから、もうひとつはこれだろう。Arduinoのシールド基盤に小さなブレッドボードを載せることができるもの。

IMG_3031

 帰宅してみると、先週作った「メールサーバお知らせランプ」、赤い点滅で、100通以上の未読がある。

 電子メールを読み出すと、動画のように緑色の点灯に変わる。

 さて、一週間、誤動作なく動いていた「メールサーバお知らせランプ」だが、さっさと回路をバラしてしまい、次の作品を作ることにする。

 秋葉原で買ってきたものを取り出し、半田鏝を温めて、なにやらごそごそと活動する。

IMG_3038
なにやら組み立てる

IMG_3039

 このちっぽけな基盤がAC100VをTTLで扱うためのキモである。

 これを、Arduinoにつなぐ。先日買った超音波センサを、今日買ったブレッドボードシールドに取り付け、回路はこんな具合にする。

IMG_3043

 スケッチを書く。こんな感じだ。

//
//  superSonic2AC100.ino
//    27.06.21(日) 1500~
//    SatoToshio
//      Arduino + 超音波センサ + ソリッドステートリレーで、
//      人が近接したら扇風機などを回す。
//
const int TRIG = 8, ECHO = 9, SSR = 10;
float ranges[10];
int arrcount = 0;
const float THRESHOLDRNAGE = 100.0;

void setup() {
  Serial.begin(9600);
  pinMode(TRIG,OUTPUT);
  pinMode(ECHO,INPUT);
  pinMode(SSR, OUTPUT);
  digitalWrite(SSR, LOW);
}

void loop() {
  float range = 0.0;
  delay(1000);
  range = ranging();
  ranges[arrcount++] = range;
  if(arrcount >= 9){
    arrcount = 0;
  }
  //  THRESHOLDRANGE以下が何割かを調べる。雑音除去のため。
  int rangecount = 0, i = 0;
  for(i = 0; i <= 9; i++){
    if(ranges[i] < THRESHOLDRNAGE){
      rangecount ++;
    }
  }
Serial.println(range);
Serial.println(rangecount);
  if(rangecount >= 6){
    digitalWrite(SSR, HIGH);
  }else{
    digitalWrite(SSR, LOW);
  }
}

float ranging(){
  float time = 0.0, range = 0.0;

  digitalWrite(TRIG,LOW);
  delayMicroseconds(1.1);
  digitalWrite(TRIG,HIGH);
  delayMicroseconds(1.1);
  digitalWrite(TRIG,LOW);
  time = pulseIn(ECHO,HIGH);
  if (time > 0.0) {
    range = (time / 2.0) * 340.0 * 100.0 / 1000000.0;
    if(range > 500.0){
      range = 500.0;
    }
    return(range);
  }else{
    return(9999.0);
  }
}

 で、これで何をするかというと、「人が近づくと部屋のミニ扇風機が回る」というのをやりたかったわけだ。暑いから(笑)。

IMG_3041

 組み付けたプロトはこんな感じだ。100円ショップで買ったアクリルの仕切りグッズにスペーサーで取り付けてある。

IMG_3042

 自分の部屋に入ると、扇風機が回り出し、涼しい。