SSブログ
前の10件 | -

Arduinoで大気圧センサを使ってみる [Arduino]

大気圧センサを入手したので,Arduinoで動かしてみる.使用したのは,秋月電子の,「MPL115A2使用大気圧センサーモジュールキット(I2C) Ver2」.

ネットで調べると,Arduino用のライブラリが公開されていたので,ありがたく使わせてもらう.参照したのは,Arduinoで遊ぶページにある,「MPL115A2用ライブラリ」だ.

あとは,前回作った7セグLEDライブラリと組み合わせて,測定した大気圧を7セグLEDに表示してみる.

7セグLEDとの接続は前回と同じなので,大気圧センサとの接続部分のみ示そう.回路図はこんな感じ.
pressure_sensor_回路図.png

で,スケッチ.
#include <Wire.h>
#include <MPL115A2.h>
#include <MsTimer2.h>
#include <Led7segment.h>

const int anode_pins[] = {
    12, // A
    8,  // B
    5,  // C
    3,  // D
    2,  // E
    11, // F
    6,  // G
    4,  // DP
};

const int cathode_pins[] = {
    13, // DIG.1
    10, // DIG.2
    9,  // DIG.3
    7,  // DIG.4
};

const int ANODE_PINS_NUM   = sizeof(anode_pins)   / sizeof(anode_pins[0]);
const int CATHODE_PINS_NUM = sizeof(cathode_pins) / sizeof(cathode_pins[0]);

unsigned int display_buffer[CATHODE_PINS_NUM] = {0};

void write_1digit(const unsigned char bitmap)
{
    for (int i = 0; i < ANODE_PINS_NUM; i++) {
        digitalWrite(anode_pins[i], bitmap & (1 << i) ? HIGH : LOW);
    }
}

void clear()
{
    for (int i = 0; i < ANODE_PINS_NUM; i++) {
        digitalWrite(anode_pins[i], LOW);
    }
}

void write()
{
    for (int i = 0; i < CATHODE_PINS_NUM; i++) {
        digitalWrite(cathode_pins[i], LOW);
        write_1digit(display_buffer[i]);
        delayMicroseconds(100);
        clear();
        digitalWrite(cathode_pins[i], HIGH);
    }
}

void print_7seg(unsigned char *buff)
{
    noInterrupts();
    for (int i = 0; i < CATHODE_PINS_NUM; i++) {
        display_buffer[i] = buff[i];
    }
    interrupts();
}

void setup()
{
    for (int i = 0; i < ANODE_PINS_NUM; i++) {
        pinMode(anode_pins[i], OUTPUT);
    }

    for (int i = 0; i < CATHODE_PINS_NUM; i++) {
        pinMode(cathode_pins[i], OUTPUT);
        digitalWrite(cathode_pins[i], HIGH);
    }
    
    MsTimer2::set(1, write);
    MsTimer2::start();
    
    MPL115A2.begin();
}

void loop()
{
    unsigned char buff[CATHODE_PINS_NUM] = {0};
    const unsigned int numeric_digits = CATHODE_PINS_NUM;
    const unsigned int frac_digits = 3;

    Led7segment led;
    
    double pressure = 0.0;
    for (int i = 0; i < 10; i++) {
      pressure += MPL115A2.read();
      delay(100);
    }
    
    pressure /= 10.0;
    led.print(buff, pressure, numeric_digits, frac_digits);
    print_7seg(buff);
}


測定値のばらつきが多いようなので,10回測定して平均値を採用してる.
あと,本来は高度に応じて補正を入れて,海面更正気圧を求めるべきだが,ひとまず測定値そのままにしている.
こちらの使用環境ではおおよそ5気圧ほど低くなる計算(高度からの換算値)で,補正後の値を気象庁の気圧値(たとえば東京はこんな感じ)と比較してもおおよそ一致しているので,測定は正しく行えているようである.

で,実際の動作状況はこんなかんじ.
DSC_0214.jpg

大気圧測定ライブラリを利用すると,とても簡単に使える.作成者に感謝.
お試しあれ.

nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

Arduinoで7セグLEDを使ってみる [Arduino]

秋月電子の,「ダイナミック接続4桁高輝度赤色7セグメントLED表示器 カソードコモン カソード共通接続」OSL40562-LRを入手したので,Arduinoで動かしてみる.

今回は,ArduinoのIOピンで直接制御してみる.高輝度で点灯させるにはトランジスタを併用してLEDへの電流を確保する必要があるが,ひとまずIOピンから出力できる電流量で点灯させてみる.

Arduino Unoでは,IOピンの定格電流は40mAのようなので,これを前提に考えると...

OSL40562-LRの順方向電圧はtyp2Vだが,Minが1.8Vなのでそれで計算する.
ArduinoのIOは5Vなので,
5V - 1.8 V = 3.2V
小数点も含めると8個のLEDがあるので,Totalで30mAぐらいに収めるとすると,1LEDあたり,
30mA / 8 = 3.75mA
3.2V / 3.75mA = 0.85KΩ
ということになる.

というわけで,手元にある抵抗から近いものを探して820Ωを採用.
実際に点灯させてみれば分かるが,室内で見ている限りこれで十分な視認性が得られる.

こんな感じで接続する.
7segLed_回路図.png

あとは,Arduino側のスケッチだが,今回は,Adafruitが公開しているLibraryを参考に改変して,出力する桁数を指定可能な7セグLED点灯用ライブラリを作ってみた.

表示テスト用のスケッチとあわせて示そう.

まず,ライブラリだが,以下の3つのファイルからなる.
Led7segment.h
Led7segment.cpp
keywords.txt
で,適当なディレクトリに,Led7segmentディレクトリを作成し,上記3つのファイルを保存してやればよい.

順にコードを示そう.

まず,Led7segment.h
/*
 * 数値を7セグLED用向けビットマップ値に変換する
 */

#ifndef LED_7SEGMENT_H
#define LED_7SEGMENT_H

class Led7segment
{
public:
    void print(unsigned char *buff, double value,
               unsigned int numeric_digits, unsigned int frac_digits = 1); 
    /*
     * buff           : 格納先バッファ(呼び出し元で必要なサイズ分を用意すること)
     * value          : 変換する値
     * numeric_digits : 出力する桁数(-符号含む桁数."."は含まない).例えば出力が"12.34"なら4.
     * frac_digits    : 出力する小数点以下桁数(桁数不足で表示できない場合は丸められる)
     */

private:
    void write(unsigned char *buff, unsigned int x, unsigned int num, bool is_dot = false);
    void write_minus(unsigned char *buff, int display_index);
    void write_space(unsigned char *buff, int display_index);
    void write_error(unsigned char *buff, int display_index);
};

#endif // LED_7SEGMENT_H


次に,Led7segment.cpp
/*
 * This is a library for 7segment LED OSL40562-LR.
 * 
 * function Led7segment::print were copied and modified from
 * Adafruit-LED-Backpack-Library(original function: Adafruit_7segment::printFloat).
 * https://github.com/adafruit/Adafruit-LED-Backpack-Library
 */
/*************************************************** 
  This is a library for our I2C LED Backpacks
  Designed specifically to work with the Adafruit LED Matrix backpacks 
  ----> http://www.adafruit.com/products/
  ----> http://www.adafruit.com/products/
  These displays use I2C to communicate, 2 pins are required to 
  interface. There are multiple selectable I2C addresses. For backpacks
  with 2 Address Select pins: 0x70, 0x71, 0x72 or 0x73. For backpacks
  with 3 Address Select pins: 0x70 thru 0x77
  Adafruit invests time and resources providing this open source code, 
  please support Adafruit and open-source hardware by purchasing 
  products from Adafruit!
  Written by Limor Fried/Ladyada for Adafruit Industries.  
  MIT license, all text above must be included in any redistribution
  
  https://github.com/adafruit/Adafruit-LED-Backpack-Library/blob/master/license.txt
 ****************************************************/

#include <math.h>
#include "Led7segment.h"

namespace {
const unsigned char digits_table[] = {
   /*   _____
    *  |  A  |
    * F|     |B
    *  |_____|
    *  |  G  |
    * E|     |C
    *  |_____|  o DP
    *     D
    */
            /*    DP G F E D C B A */
    0x3F,   /* 0 : 0 0 1 1 1 1 1 1 */
    0x06,   /* 1 : 0 0 0 0 0 1 1 0 */
    0x5B,   /* 2 : 0 1 0 1 1 0 1 1 */
    0x4F,   /* 3 : 0 1 0 0 1 1 1 1 */
    0x66,   /* 4 : 0 1 1 0 0 1 1 0 */
    0x6D,   /* 5 : 0 1 1 0 1 1 0 1 */
    0x7D,   /* 6 : 0 1 1 1 1 1 0 1 */
    0x07,   /* 7 : 0 0 1 0 0 1 1 1 */
    0x7F,   /* 8 : 0 1 1 1 1 1 1 1 */
    0x6F,   /* 9 : 0 1 1 0 1 1 1 1 */
    0x00,   /*   : 0 0 0 0 0 0 0 0 */
    0x40,   /* - : 0 1 0 0 0 0 0 0 */
    0x80,   /* . : 1 0 0 0 0 0 0 0 */
};

const int SPACE_INDEX = 10;
const int MINUS_INDEX = 11;
const int DOT_INDEX   = 12;

} // namespace

void Led7segment::print(unsigned char *buff, double value,
                        unsigned int numeric_digits, unsigned int frac_digits) 
{ 
    const unsigned int base = 10;
    bool is_minus = false;

    int display_index = numeric_digits - 1;

    if (value < 0) {
        is_minus = true;
        numeric_digits--;
        value *= -1;
    }

    if (numeric_digits <= frac_digits) {
        frac_digits = numeric_digits - 1;
    }

    unsigned long to_int_factor = pow(base, frac_digits) + 0.5;
    unsigned long display_value = value * to_int_factor + 0.5;
    unsigned long max_limit = pow(base, numeric_digits) + 0.5;

    while (display_value >= max_limit) {
        to_int_factor /= base;
        display_value = value * to_int_factor + 0.5;
        frac_digits--;
    }

    if (to_int_factor != 0) {
        if (display_value != 0) {
            for (int i = 0; (display_value != 0) || (i <= frac_digits); i++) {
                const bool is_dot = (frac_digits != 0) && (i == frac_digits);
                write(buff, display_index--, display_value % base, is_dot);

                display_value /= base;
            }
        }
        else {
            for (int i = 0; i <= frac_digits; i++) {
                const bool is_dot = (frac_digits != 0) && (i == frac_digits);
                write(buff, display_index--, 0, is_dot);
            }
        }

        if (is_minus == true) {
            write_minus(buff, display_index--);
        }

        while (display_index >= 0) {
            write_space(buff, display_index--);
        }
    }
    else {
        while (display_index >= 0) {
            write_error(buff, display_index--);
        }
    }
}

void Led7segment::write(unsigned char *buff, unsigned int i, unsigned int num, bool is_dot)
{
    if (is_dot == true) {
        buff[i] = digits_table[num] | digits_table[DOT_INDEX];
    }
    else {
        buff[i] = digits_table[num];
    }
}

void Led7segment::write_minus(unsigned char *buff, int display_index)
{
    write(buff, display_index, MINUS_INDEX);
}

void Led7segment::write_space(unsigned char *buff, int display_index)
{
    write(buff, display_index, SPACE_INDEX);
}

void Led7segment::write_error(unsigned char *buff, int display_index)
{
    write(buff, display_index, MINUS_INDEX);
}


最後に,keywords.txt
Led7segment	KEYWORD1
print	KEYWORD2


ダウンロードするなら,こちらから.
Led7segment.zip.jpg
ただし,So-netのブログでは,zip圧縮ファイルのアップロードができないため,zip圧縮してから拡張子に.jpgをつけて無理矢理アップロードしている.
なので.ダウンロードしたら.jpgを消してzipファイルとして展開してやってください.

また,ライブラリの追加方法は,「arduino ライブラリ 追加」等で検索してみてください.

で,表示テスト用のスケッチは,Led_7seg.ino.
Led7segmentライブラリの使い方は,Led7segment.hと,これを見れば分かると思う.
なお,このスケッチではMsTimer2ライブラリを使用しているので,別途追加してやってください.
#include <MsTimer2.h>
#include <Led7segment.h>

const int anode_pins[] = {
    12, // A
    8,  // B
    5,  // C
    3,  // D
    2,  // E
    11, // F
    6,  // G
    4,  // DP
};

const int cathode_pins[] = {
    13, // DIG.1
    10, // DIG.2
    9,  // DIG.3
    7,  // DIG.4
};

const int ANODE_PINS_NUM   = sizeof(anode_pins)   / sizeof(anode_pins[0]);
const int CATHODE_PINS_NUM = sizeof(cathode_pins) / sizeof(cathode_pins[0]);

unsigned int display_buffer[CATHODE_PINS_NUM] = {0};

void write_1digit(const unsigned char bitmap)
{
    for (int i = 0; i < ANODE_PINS_NUM; i++) {
        digitalWrite(anode_pins[i], bitmap & (1 << i) ? HIGH : LOW);
    }
}

void clear()
{
    for (int i = 0; i < ANODE_PINS_NUM; i++) {
        digitalWrite(anode_pins[i], LOW);
    }
}

void write()
{
    for (int i = 0; i < CATHODE_PINS_NUM; i++) {
        digitalWrite(cathode_pins[i], LOW);
        write_1digit(display_buffer[i]);
        delayMicroseconds(100);
        clear();
        digitalWrite(cathode_pins[i], HIGH);
    }
}

void print_7seg(unsigned char *buff)
{
    noInterrupts();
    for (int i = 0; i < CATHODE_PINS_NUM; i++) {
        display_buffer[i] = buff[i];
    }
    interrupts();
}

void setup()
{
    for (int i = 0; i < ANODE_PINS_NUM; i++) {
        pinMode(anode_pins[i], OUTPUT);
    }

    for (int i = 0; i < CATHODE_PINS_NUM; i++) {
        pinMode(cathode_pins[i], OUTPUT);
        digitalWrite(cathode_pins[i], HIGH);
    }
    
    MsTimer2::set(1, write);
    MsTimer2::start();
}

void loop()
{
    unsigned char buff[CATHODE_PINS_NUM] = {0};
    const unsigned int numeric_digits = CATHODE_PINS_NUM;
    const unsigned int frac_digits = 3;

    Led7segment led;

    for (long i = -1000; i <= 10000; i++) {
        double d = i;
        led.print(buff, d, numeric_digits, frac_digits);
        print_7seg(buff);
        delay(100);
    }
    
    for (long i = -1000; i <= 10000; i++) {
        double d = i / 10.0;
        led.print(buff, d, numeric_digits, frac_digits);
        print_7seg(buff);
        delay(100);
    }
    
    for (long i = -1000; i <= 10000; i++) {
        double d = i / 100.0;
        led.print(buff, d, numeric_digits, frac_digits);
        print_7seg(buff);
        delay(100);
    }
    
    for (long i = -1000; i <= 10000; i++) {
        double d = i / 1000.0;
        led.print(buff, d, numeric_digits, frac_digits);
        print_7seg(buff);
        delay(100);
    }
}


この表示テスト用スケッチでは,
・-1000  〜 10000  まで,1    ずつ
・-100.0 〜 1000.0 まで,0.1  ずつ
・-10.00 〜 100.00 まで,0.01 ずつ
・-1.000 〜 10.000 まで,0.001ずつ
順に表示する動作になっていて,桁数が足りなくて表示できない値は「----」と表示されます.

それでは,お試しあれ.

タグ:7セグLED
nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

久しぶりにov7670+Arduino+Processingを動かしてみる [Arduino]

ov7670を動かしていたのはずいぶん前で,その後は放置していたのだが,久しぶりに動かしてみた.コード公開のご要望をいただいたので,最新の状態を公開してみる.

VGAとQQVGAに対応していて,QQVGAは当初はRGB444だったが,今はRGB565で動かしている.

ただ,あらかじめ断っておくが,シリアル通信で画像データを取得している部分がボトルネックになっているようで,画像の更新頻度(フレームレート)はかなり遅い.リアルタイムは全然無理で,パラパラマンガ以下である.
Arduinoのシリアル通信のライブラリに手を入れると改善できるのではないかと思うのだが...
ボーレートから計算した通信スピードに対して実際が遅すぎで,どこかに大きなオーバーヘッドがのってるような気がする.

では,まずはArduino側のコード.ov7670.inoとov7670reg.hだ.
最初にov7670.ino.
#include <avr/io.h>
#include <Wire.h>
#include "ov7670reg.h"

// Pin
const int WEN   = 14; // PC0
const int RRST  = 15; // PC1
const int OE    = 16; // PC2
const int RCLK  = 17; // PC3
const int VSYNC =  2; // PD2 INT0
const int D0    =  8; // PB0
const int D1    =  9; // PB1
const int D2    = 10; // PB2
const int D3    = 11; // PB3
const int D4    =  4; // PD4
const int D5    =  5; // PD5
const int D6    =  6; // PD6
const int D7    =  7; // PD7

volatile boolean isWriteEnable = false;

const int BAYER_VGA    = 0;
const int RGB565_QQVGA = 1;
int format = BAYER_VGA;

void setPinMode()
{
  pinMode(WEN,   OUTPUT);
  pinMode(RRST,  OUTPUT);
  pinMode(OE,    OUTPUT);
  pinMode(RCLK,  OUTPUT);
  pinMode(D0,    INPUT );
  pinMode(D1,    INPUT );
  pinMode(D2,    INPUT );
  pinMode(D3,    INPUT );
  pinMode(D4,    INPUT );
  pinMode(D5,    INPUT );
  pinMode(D6,    INPUT );
  pinMode(D7,    INPUT );

  attachInterrupt(0, setWriteEnable, FALLING);

  digitalWrite(WEN,  LOW);
  digitalWrite(RRST, HIGH);
  digitalWrite(OE,   HIGH);
  digitalWrite(RCLK, HIGH);
}

void setWriteEnable()
{
  if (isWriteEnable) {
    digitalWrite(WEN, HIGH);
    isWriteEnable = false;
  } else {
    digitalWrite(WEN, LOW);
  }
}

void initFifo()
{
  /*
   *        __ __    __       __ __    __
   * RCLK        |__|  |__ __|     |__|  
   *        __             __ __ __ __ __
   * RRST     |__ __ __ __|              
   *        __ __ __ __ __ __ __         
   * OE                         |__ __ __
   */
                                            // RCLK  RRST   OE
  PORTC |=  _BV(3) |  _BV(1) |  _BV(2);     // H     H      H
  PORTC &=           ~_BV(1);               // H     L      H
  PORTC &= ~_BV(3);                         // L     L      H
  PORTC |=  _BV(3);                         // H     L      H
  PORTC &= ~_BV(3);                         // L     L      H
  PORTC |=            _BV(1);               // L     H      H
  PORTC |=  _BV(3);                         // H     H      H
  PORTC &=                     ~_BV(2);     // H     H      L
  PORTC &= ~_BV(3);                         // L     H      L
  PORTC |=  _BV(3);                         // H     H      L
}

const int QQVGA_HSIZE = 160;
const int QQVGA_VSIZE = 120;
const int QQVGA_DATABYTE = 2;
const int VGA_HSIZE = 640;
const int VGA_VSIZE = 480;
const int VGA_DATABYTE = 1;

int hsize = VGA_HSIZE;
int vsize = VGA_VSIZE;
int databyte = VGA_DATABYTE;

void setDataFormat()
{
  switch (format) {
    case BAYER_VGA:
      hsize    = VGA_HSIZE;
      vsize    = VGA_VSIZE;
      databyte = VGA_DATABYTE;
      break;
    case RGB565_QQVGA:
      hsize    = QQVGA_HSIZE;
      vsize    = QQVGA_VSIZE;
      databyte = QQVGA_DATABYTE;
      break;
    default:
      ;
      break;
  }
}

void readFifo()
{
  const char HEX_TABLE[] = "0123456789ABCDEF";

  byte dh, dl;

  for (int v = 0; v < vsize; v++) {
    for (int h = 0; h < hsize; h++) {
      for (int d = 0; d < databyte; d++) {
        PORTC |= _BV(3);    // RCLK HIGH
        delayMicroseconds(1);

        dh = PIND >> 4;
        dl = PINB & 0x0F;

        PORTC &= ~_BV(3);   // RCLK LOW

        Serial.print(HEX_TABLE[dh]);
        Serial.print(HEX_TABLE[dl]);
      }
    }
  }

  // read end
  PORTC &= ~_BV(3); // RCLK LOW
  PORTC |= _BV(2);  // OE   HIGH
  PORTC |= _BV(3);  // RCLK HIGH
}

void writeRegister(int address, int data)
{
  const int I2C_WRITE_ADDR = 0x42;

  Wire.beginTransmission(I2C_WRITE_ADDR >> 1);
  Wire.write(address);
  Wire.write(data);
  Wire.endTransmission();
}

int readRegister(int address)
{
  const int I2C_READ_ADDR = 0x43;

  Wire.beginTransmission(I2C_READ_ADDR >> 1);
  Wire.write(address);
  Wire.endTransmission();

  Wire.requestFrom(I2C_READ_ADDR >> 1, 1);
  return Wire.read();
}

void resetCamera()
{
  writeRegister(REG_COM7, COM7_RESET);
  delay(200);
}

void initRegister()
{
    initDefaultRegister();
    initVsyncPolarity();

    switch (format) {
      case BAYER_VGA:
        initBayerRGB();
        initVGA();
        break;
      case RGB565_QQVGA:
        initRGB565();
        initQQVGA();
        break;
      default:
        ;
        break;
    }
}

void initBayerRGB()
{
    /*
     * GBGBGBGBGB
     * RGRGRGRGRG
     * GBGBGBGBGB
     * RGRGRGRGRG
     */
    int reg_com7 = readRegister(REG_COM7);
    writeRegister(REG_COM7, reg_com7 | COM7_PBAYER);

    writeRegister(REG_RGB444, RGB444_DISABLE);
    writeRegister(REG_COM15,  COM15_R00FF);

    writeRegister(REG_COM13, 0x08);     /* No gamma, magic rsvd bit */
    writeRegister(REG_COM16, 0x3d);     /* Edge enhancement, denoise */
    writeRegister(REG_REG76, 0xe1);     /* Pix correction, magic rsvd */

    writeRegister(REG_TSLB,  0x04);
}

void initRGB444()
{
    int reg_com7 = readRegister(REG_COM7);
    writeRegister(REG_COM7, reg_com7 | COM7_RGB);

    writeRegister(REG_RGB444, RGB444_ENABLE | RGB444_XBGR);
    writeRegister(REG_COM15,  COM15_R01FE | COM15_RGB444);

    writeRegister(REG_COM1,   0x40);    /* Magic reserved bit */
    writeRegister(REG_COM9,   0x38);    /* 16x gain ceiling; 0x8 is reserved bit */
    writeRegister(0x4f,       0xb3);    /* "matrix coefficient 1" */
    writeRegister(0x50,       0xb3);    /* "matrix coefficient 2" */
    writeRegister(0x51,       0x00);    /* vb */
    writeRegister(0x52,       0x3d);    /* "matrix coefficient 4" */
    writeRegister(0x53,       0xa7);    /* "matrix coefficient 5" */
    writeRegister(0x54,       0xe4);    /* "matrix coefficient 6" */
    writeRegister(REG_COM13,  COM13_GAMMA | COM13_UVSAT | 0x2); /* Magic rsvd bit */

    writeRegister(REG_TSLB,   0x04);
}

void initRGB565()
{
    int reg_com7 = readRegister(REG_COM7);
    writeRegister(REG_COM7, reg_com7 | COM7_RGB);

    writeRegister(REG_RGB444, RGB444_DISABLE);
    writeRegister(REG_COM15,  COM15_R00FF | COM15_RGB565);

    writeRegister(REG_COM1,  0x00);
    writeRegister(REG_COM9,  0x38);     /* 16x gain ceiling; 0x8 is reserved bit */
    writeRegister(0x4f,      0xb3);     /* "matrix coefficient 1" */
    writeRegister(0x50,      0xb3);     /* "matrix coefficient 2" */
    writeRegister(0x51,      0x00);     /* vb */
    writeRegister(0x52,      0x3d);     /* "matrix coefficient 4" */
    writeRegister(0x53,      0xa7);     /* "matrix coefficient 5" */
    writeRegister(0x54,      0xe4);     /* "matrix coefficient 6" */
    writeRegister(REG_COM13, COM13_GAMMA | COM13_UVSAT);

    writeRegister(REG_TSLB,  0x04);
}

void initVGA()
{
    writeRegister(REG_HSTART, HSTART_VGA);
    writeRegister(REG_HSTOP,  HSTOP_VGA);
    writeRegister(REG_HREF,   HREF_VGA);
    writeRegister(REG_VSTART, VSTART_VGA);
    writeRegister(REG_VSTOP,  VSTOP_VGA);
    writeRegister(REG_VREF,   VREF_VGA);
    writeRegister(REG_COM3,   COM3_VGA);
    writeRegister(REG_COM14,  COM14_VGA);
 
    writeRegister(REG_SCALING_XSC,        SCALING_XSC_VGA);
    writeRegister(REG_SCALING_YSC,        SCALING_YSC_VGA);
    writeRegister(REG_SCALING_DCWCTR,     SCALING_DCWCTR_VGA);
    writeRegister(REG_SCALING_PCLK_DIV,   SCALING_PCLK_DIV_VGA);
    writeRegister(REG_SCALING_PCLK_DELAY, SCALING_PCLK_DELAY_VGA);
}

void initQQVGA()
{
    writeRegister(REG_HSTART, HSTART_QQVGA);
    writeRegister(REG_HSTOP,  HSTOP_QQVGA);
    writeRegister(REG_HREF,   HREF_QQVGA);
    writeRegister(REG_VSTART, VSTART_QQVGA);
    writeRegister(REG_VSTOP,  VSTOP_QQVGA);
    writeRegister(REG_VREF,   VREF_QQVGA);
    writeRegister(REG_COM3,   COM3_QQVGA);
    writeRegister(REG_COM14,  COM14_QQVGA);

    writeRegister(REG_SCALING_XSC,        SCALING_XSC_QQVGA);
    writeRegister(REG_SCALING_YSC,        SCALING_YSC_QQVGA);
    writeRegister(REG_SCALING_DCWCTR,     SCALING_DCWCTR_QQVGA);
    writeRegister(REG_SCALING_PCLK_DIV,   SCALING_PCLK_DIV_QQVGA);
    writeRegister(REG_SCALING_PCLK_DELAY, SCALING_PCLK_DELAY_QQVGA);
}

void initVsyncPolarity()
{
    writeRegister(REG_COM10, COM10_VS_NEG);
}

void initDefaultRegister()
{
    writeRegister(REG_CLKRC,   0x01);
    writeRegister(REG_COM7,    0x00);   /* VGA */

    /*
     * Set the hardware window.  These values from OV don't entirely
     * make sense - hstop is less than hstart.  But they work...
     */
    writeRegister(REG_HSTART,  0x13);
    writeRegister(REG_HSTOP,   0x01);
    writeRegister(REG_HREF,    0xb6);
    writeRegister(REG_VSTART,  0x02);
    writeRegister(REG_VSTOP,   0x7a);
    writeRegister(REG_VREF,    0x0a);

    writeRegister(REG_COM3,    0x00);
    writeRegister(REG_COM14,   0x00);

    /* Mystery scaling numbers */
    writeRegister(0x70,        0x3a);
    writeRegister(0x71,        0x35);
    writeRegister(0x72,        0x11);
    writeRegister(0x73,        0xf0);
    writeRegister(0xa2,        0x02);
    writeRegister(REG_COM10,   0x00);

    /* Gamma curve values */
    writeRegister(0x7a,        0x20);
    writeRegister(0x7b,        0x10);
    writeRegister(0x7c,        0x1e);
    writeRegister(0x7d,        0x35);
    writeRegister(0x7e,        0x5a);
    writeRegister(0x7f,        0x69);
    writeRegister(0x80,        0x76);
    writeRegister(0x81,        0x80);
    writeRegister(0x82,        0x88);
    writeRegister(0x83,        0x8f);
    writeRegister(0x84,        0x96);
    writeRegister(0x85,        0xa3);
    writeRegister(0x86,        0xaf);
    writeRegister(0x87,        0xc4);
    writeRegister(0x88,        0xd7);
    writeRegister(0x89,        0xe8);

    /* AGC and AEC parameters.  Note we start by disabling those features,
    then turn them only after tweaking the values. */
    writeRegister(REG_COM8,    COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT);
    writeRegister(REG_GAIN,    0x00);
    writeRegister(REG_AECH,    0x00);
    writeRegister(REG_COM4,    0x40);   /* magic reserved bit */
    writeRegister(REG_COM9,    0x18);   /* 4x gain + magic rsvd bit */
    writeRegister(REG_BD50MAX, 0x05);
    writeRegister(REG_BD60MAX, 0x07);
    writeRegister(REG_AEW,     0x95);
    writeRegister(REG_AEB,     0x33);
    writeRegister(REG_VPT,     0xe3);
    writeRegister(REG_HAECC1,  0x78);
    writeRegister(REG_HAECC2,  0x68);
    writeRegister(0xa1,        0x03);   /* magic */
    writeRegister(REG_HAECC3,  0xd8);
    writeRegister(REG_HAECC4,  0xd8);
    writeRegister(REG_HAECC5,  0xf0);
    writeRegister(REG_HAECC6,  0x90);
    writeRegister(REG_HAECC7,  0x94);
    writeRegister(REG_COM8,    COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT | COM8_AGC | COM8_AEC);

    /* Almost all of these are magic "reserved" values. */
    writeRegister(REG_COM5,    0x61);
    writeRegister(REG_COM6,    0x4b);
    writeRegister(0x16,        0x02);
    writeRegister(REG_MVFP,    0x37);
    writeRegister(0x21,        0x02);
    writeRegister(0x22,        0x91);
    writeRegister(0x29,        0x07);
    writeRegister(0x33,        0x0b);
    writeRegister(0x35,        0x0b);
    writeRegister(0x37,        0x1d);
    writeRegister(0x38,        0x71);
    writeRegister(0x39,        0x2a);
    writeRegister(REG_COM12,   0x78);
    writeRegister(0x4d,        0x40);
    writeRegister(0x4e,        0x20);
    writeRegister(REG_GFIX,    0x00);
    writeRegister(0x6b,        0x4a);
    writeRegister(0x74,        0x10);
    writeRegister(0x8d,        0x4f);
    writeRegister(0x8e,        0x00);
    writeRegister(0x8f,        0x00);
    writeRegister(0x90,        0x00);
    writeRegister(0x91,        0x00);
    writeRegister(0x96,        0x00);
    writeRegister(0x9a,        0x00);
    writeRegister(0xb0,        0x84);
    writeRegister(0xb1,        0x0c);
    writeRegister(0xb2,        0x0e);
    writeRegister(0xb3,        0x82);
    writeRegister(0xb8,        0x0a);

    /* More reserved magic, some of which tweaks white balance */
    writeRegister(0x43,        0x0a);
    writeRegister(0x44,        0xf0);
    writeRegister(0x45,        0x34);
    writeRegister(0x46,        0x58);
    writeRegister(0x47,        0x28);
    writeRegister(0x48,        0x3a);
    writeRegister(0x59,        0x88);
    writeRegister(0x5a,        0x88);
    writeRegister(0x5b,        0x44);
    writeRegister(0x5c,        0x67);
    writeRegister(0x5d,        0x49);
    writeRegister(0x5e,        0x0e);
    writeRegister(0x6c,        0x0a);
    writeRegister(0x6d,        0x55);
    writeRegister(0x6e,        0x11);
    writeRegister(0x6f,        0x9f);   /* "9e for advance AWB" */
    writeRegister(0x6a,        0x40);
    writeRegister(REG_BLUE,    0x40);
    writeRegister(REG_RED,     0x60);
    writeRegister(REG_COM8,    COM8_FASTAEC | COM8_AECSTEP | COM8_BFILT | COM8_AGC | COM8_AEC | COM8_AWB);

    /* Matrix coefficients */
    writeRegister(0x4f,        0x80);
    writeRegister(0x50,        0x80);
    writeRegister(0x51,        0x00);
    writeRegister(0x52,        0x22);
    writeRegister(0x53,        0x5e);
    writeRegister(0x54,        0x80);
    writeRegister(0x58,        0x9e);

    writeRegister(REG_COM16,   COM16_AWBGAIN); 
    writeRegister(REG_EDGE,    0x00);
    writeRegister(0x75,        0x05);
    writeRegister(0x76,        0xe1);
    writeRegister(0x4c,        0x00);
    writeRegister(0x77,        0x01);
    writeRegister(REG_COM13,   0xc3);
    writeRegister(0x4b,        0x09);
    writeRegister(0xc9,        0x60);
    writeRegister(REG_COM16,   0x38);
    writeRegister(0x56,        0x40);

    writeRegister(0x34,        0x11);
    writeRegister(REG_COM11,   COM11_EXP | COM11_HZAUTO);
    writeRegister(0xa4,        0x88);
    writeRegister(0x96,        0x00);
    writeRegister(0x97,        0x30);
    writeRegister(0x98,        0x20);
    writeRegister(0x99,        0x30);
    writeRegister(0x9a,        0x84);
    writeRegister(0x9b,        0x29);
    writeRegister(0x9c,        0x03);
    writeRegister(0x9d,        0x4c);
    writeRegister(0x9e,        0x3f);
    writeRegister(0x78,        0x04);

    /* Extra-weird stuff.  Some sort of multiplexor register */
    writeRegister(0x79,        0x01);
    writeRegister(0xc8,        0xf0);
    writeRegister(0x79,        0x0f);
    writeRegister(0xc8,        0x00);
    writeRegister(0x79,        0x10);
    writeRegister(0xc8,        0x7e);
    writeRegister(0x79,        0x0a);
    writeRegister(0xc8,        0x80);
    writeRegister(0x79,        0x0b);
    writeRegister(0xc8,        0x01);
    writeRegister(0x79,        0x0c);
    writeRegister(0xc8,        0x0f);
    writeRegister(0x79,        0x0d);
    writeRegister(0xc8,        0x20);
    writeRegister(0x79,        0x09);
    writeRegister(0xc8,        0x80);
    writeRegister(0x79,        0x02);
    writeRegister(0xc8,        0xc0);
    writeRegister(0x79,        0x03);
    writeRegister(0xc8,        0x40);
    writeRegister(0x79,        0x05);
    writeRegister(0xc8,        0x30);
    writeRegister(0x79,        0x26);
}

void printData(int address, int data)
{
  char temp[16];
  snprintf(temp, sizeof(temp), "addr=%02X data=%02X", address, data);
  Serial.println(temp);
}

void receiveCommand(char *buff)
{
  char c = '\0';
  while (c != '\r' ) {
    if (Serial.available() > 0) {
      c = Serial.read();
      Serial.print(c);
      *buff++ = c;
    }
  }
  *buff = '\0';
  Serial.println("");
}

void setup()
{
  Serial.begin(2000000);
  Wire.begin(); 

  printMenu();

  setPinMode();
  resetCamera();
  initRegister();
}

void loop()
{
  int address  = 0;
  int data = 0;

  char buff[256];
  char *buff_ptr;
  char *cmd_ptr;
  char *arg1_ptr;
  char *arg2_ptr;
  const char DELIMITTER[] = " \t";

  Serial.print("> ");
  receiveCommand(buff);

  //  trim white space
  buff_ptr = buff;
  while ( (*buff_ptr == ' ') || (*buff_ptr == '\t') || (*buff_ptr == '\n') ) {
    buff_ptr++;
  }

  cmd_ptr = strtok(buff_ptr, DELIMITTER);

  switch (tolower(*cmd_ptr)) {
    case 'w':
      arg1_ptr = strtok(NULL, DELIMITTER);
      arg2_ptr = strtok(NULL, DELIMITTER);

      if ( (arg1_ptr != NULL) && (arg2_ptr != NULL) ) {
        address = hex2dec(arg1_ptr);
        data    = hex2dec(arg2_ptr);

        printData(address, data);
        writeRegister(address, data);

        delay(10);

      } else {
        Serial.println("Syntax Error");
      }

      break;
    case 'r':
      arg1_ptr = strtok(NULL, DELIMITTER);

      if (arg1_ptr != NULL) {
        address = hex2dec(arg1_ptr);
        data = readRegister(address);

        printData(address, data);

        delay(50);

      } else {
        Serial.println("Syntax Error");
      }
      break;

    case 'f':
      arg1_ptr = strtok(NULL, DELIMITTER);

      if (arg1_ptr != NULL) {
        format = hex2dec(arg1_ptr);
        Serial.print("Format ");
        Serial.println(format);

        setDataFormat();
        resetCamera();
        initRegister();

      } else {
        Serial.println("Syntax Error");
      }
      break;

    case 'd':
      Serial.println("Dump");
      initFifo();
      readFifo();
      break;

    case 's':
      Serial.println("Start");
      isWriteEnable = true;
      break;

    case 'h':
      printHelp();
      break;

    default:
      Serial.println("Syntax Error");
      break;
  }

  Serial.println("");
}

void printMenu()
{
  Serial.println("------------------------------");
  Serial.println(" OV7670 Camera Debugger");
  Serial.println("------------------------------");
  Serial.println("");
}

void printHelp()
{
  Serial.println("WRITE :w [address] [data]");
  Serial.println("READ  :r [address]");
  Serial.println("FORMAT:f [mode]  0:VGA 1:QQVGA");
  Serial.println("START :s");
  Serial.println("DUMP  :d");
  Serial.println("HELP  :h");
}

int hex2dec(char *str)
{
  return strtol(str, NULL, 16);
}


次に,ov7670reg.h
/* VGA setting */
#define HSTART_VGA               0x13
#define HSTOP_VGA                0x01
#define HREF_VGA                 0x36
#define VSTART_VGA               0x02
#define VSTOP_VGA                0x7a
#define VREF_VGA                 0x0a
#define COM3_VGA                 0x00
#define COM14_VGA                0x00
#define SCALING_XSC_VGA          0x3a
#define SCALING_YSC_VGA          0x35
#define SCALING_DCWCTR_VGA       0x11
#define SCALING_PCLK_DIV_VGA     0xf0
#define SCALING_PCLK_DELAY_VGA   0x02

/* QQVGA setting */
#define HSTART_QQVGA             0x16
#define HSTOP_QQVGA              0x04
#define HREF_QQVGA               0xa4
#define VSTART_QQVGA             0x02
#define VSTOP_QQVGA              0x7a
#define VREF_QQVGA               0x0a
#define COM3_QQVGA               0x04
#define COM14_QQVGA              0x1a
#define SCALING_XSC_QQVGA        0x3a
#define SCALING_YSC_QQVGA        0x35
#define SCALING_DCWCTR_QQVGA     0x22
#define SCALING_PCLK_DIV_QQVGA   0xf2
#define SCALING_PCLK_DELAY_QQVGA 0x02

/* Registers */
#define REG_GAIN        0x00    /* Gain lower 8 bits (rest in vref) */
#define REG_BLUE        0x01    /* blue gain */
#define REG_RED         0x02    /* red gain */
#define REG_VREF        0x03    /* Pieces of GAIN, VSTART, VSTOP */
#define REG_COM1        0x04    /* Control 1 */
#define  COM1_CCIR656     0x40  /* CCIR656 enable */
#define REG_BAVE        0x05    /* U/B Average level */
#define REG_GbAVE       0x06    /* Y/Gb Average level */
#define REG_AECHH       0x07    /* AEC MS 5 bits */
#define REG_RAVE        0x08    /* V/R Average level */
#define REG_COM2        0x09    /* Control 2 */
#define  COM2_SSLEEP      0x10  /* Soft sleep mode */
#define REG_PID         0x0a    /* Product ID MSB */
#define REG_VER         0x0b    /* Product ID LSB */
#define REG_COM3        0x0c    /* Control 3 */
#define  COM3_SWAP        0x40    /* Byte swap */
#define  COM3_SCALEEN     0x08    /* Enable scaling */
#define  COM3_DCWEN       0x04    /* Enable downsamp/crop/window */
#define REG_COM4        0x0d    /* Control 4 */
#define REG_COM5        0x0e    /* All "reserved" */
#define REG_COM6        0x0f    /* Control 6 */
#define REG_AECH        0x10    /* More bits of AEC value */
#define REG_CLKRC       0x11    /* Clocl control */
#define   CLK_EXT         0x40    /* Use external clock directly */
#define   CLK_SCALE       0x3f    /* Mask for internal clock scale */
#define REG_COM7        0x12    /* Control 7 */
#define   COM7_RESET      0x80    /* Register reset */
#define   COM7_FMT_MASK   0x38
#define   COM7_FMT_VGA    0x00
#define   COM7_FMT_CIF    0x20    /* CIF format */
#define   COM7_FMT_QVGA   0x10    /* QVGA format */
#define   COM7_FMT_QCIF   0x08    /* QCIF format */
#define   COM7_RGB        0x04    /* bits 0 and 2 - RGB format */
#define   COM7_YUV        0x00    /* YUV */
#define   COM7_BAYER      0x01    /* Bayer format */
#define   COM7_PBAYER     0x05    /* "Processed bayer" */
#define REG_COM8        0x13    /* Control 8 */
#define   COM8_FASTAEC    0x80    /* Enable fast AGC/AEC */
#define   COM8_AECSTEP    0x40    /* Unlimited AEC step size */
#define   COM8_BFILT      0x20    /* Band filter enable */
#define   COM8_AGC        0x04    /* Auto gain enable */
#define   COM8_AWB        0x02    /* White balance enable */
#define   COM8_AEC        0x01    /* Auto exposure enable */
#define REG_COM9        0x14    /* Control 9  - gain ceiling */
#define REG_COM10       0x15    /* Control 10 */
#define   COM10_HSYNC     0x40    /* HSYNC instead of HREF */
#define   COM10_PCLK_HB   0x20    /* Suppress PCLK on horiz blank */
#define   COM10_HREF_REV  0x08    /* Reverse HREF */
#define   COM10_VS_LEAD   0x04    /* VSYNC on clock leading edge */
#define   COM10_VS_NEG    0x02    /* VSYNC negative */
#define   COM10_HS_NEG    0x01    /* HSYNC negative */
#define REG_HSTART      0x17    /* Horiz start high bits */
#define REG_HSTOP       0x18    /* Horiz stop high bits */
#define REG_VSTART      0x19    /* Vert start high bits */
#define REG_VSTOP       0x1a    /* Vert stop high bits */
#define REG_PSHFT       0x1b    /* Pixel delay after HREF */
#define REG_MIDH        0x1c    /* Manuf. ID high */
#define REG_MIDL        0x1d    /* Manuf. ID low */
#define REG_MVFP        0x1e    /* Mirror / vflip */
#define   MVFP_MIRROR     0x20    /* Mirror image */
#define   MVFP_FLIP       0x10    /* Vertical flip */

#define REG_AEW         0x24    /* AGC upper limit */
#define REG_AEB         0x25    /* AGC lower limit */
#define REG_VPT         0x26    /* AGC/AEC fast mode op region */
#define REG_HSYST       0x30    /* HSYNC rising edge delay */
#define REG_HSYEN       0x31    /* HSYNC falling edge delay */
#define REG_HREF        0x32    /* HREF pieces */
#define REG_TSLB        0x3a    /* lots of stuff */
#define   TSLB_YLAST      0x04    /* UYVY or VYUY - see com13 */
#define REG_COM11       0x3b    /* Control 11 */
#define   COM11_NIGHT     0x80    /* NIght mode enable */
#define   COM11_NMFR      0x60    /* Two bit NM frame rate */
#define   COM11_HZAUTO    0x10    /* Auto detect 50/60 Hz */
#define   COM11_50HZ      0x08    /* Manual 50Hz select */
#define   COM11_EXP       0x02
#define REG_COM12       0x3c    /* Control 12 */
#define   COM12_HREF      0x80    /* HREF always */
#define REG_COM13       0x3d    /* Control 13 */
#define   COM13_GAMMA     0x80    /* Gamma enable */
#define   COM13_UVSAT     0x40    /* UV saturation auto adjustment */
#define   COM13_UVSWAP    0x01    /* V before U - w/TSLB */
#define REG_COM14       0x3e    /* Control 14 */
#define   COM14_DCWEN     0x10    /* DCW/PCLK-scale enable */
#define REG_EDGE        0x3f    /* Edge enhancement factor */
#define REG_COM15       0x40    /* Control 15 */
#define   COM15_R10F0     0x00    /* Data range 10 to F0 */
#define   COM15_R01FE     0x80    /*            01 to FE */
#define   COM15_R00FF     0xc0    /*            00 to FF */
#define   COM15_RGB444    0x10    /* RGB444 output */
#define   COM15_RGB565    0x10    /* RGB565 output */
#define   COM15_RGB555    0x30    /* RGB555 output */
#define REG_COM16       0x41    /* Control 16 */
#define   COM16_AWBGAIN   0x08    /* AWB gain enable */
#define REG_COM17       0x42    /* Control 17 */
#define   COM17_AECWIN    0xc0    /* AEC window - must match COM4 */
#define   COM17_CBAR      0x08    /* DSP Color bar */

/*
 * This matrix defines how the colors are generated, must be
 * tweaked to adjust hue and saturation.
 *
 * Order: v-red, v-green, v-blue, u-red, u-green, u-blue
 *
 * They are nine-bit signed quantities, with the sign bit
 * stored in 0x58.  Sign for v-red is bit 0, and up from there.
 */
#define REG_CMATRIX_BASE 0x4f
#define   CMATRIX_LEN 6
#define REG_CMATRIX_SIGN 0x58


#define REG_BRIGHT      0x55    /* Brightness */
#define REG_CONTRAS     0x56    /* Contrast control */

#define REG_GFIX        0x69    /* Fix gain control */

#define REG_DBLV        0x6b    /* PLL control an debugging */
#define   DBLV_BYPASS     0x00    /* Bypass PLL */
#define   DBLV_X4         0x01    /* clock x4 */
#define   DBLV_X6         0x10    /* clock x6 */
#define   DBLV_X8         0x11    /* clock x8 */

#define REG_SCALING_XSC             0x70
#define REG_SCALING_YSC             0x71
#define REG_SCALING_DCWCTR          0x72
#define REG_SCALING_PCLK_DIV        0x73

#define REG_REG76       0x76    /* OV's name */
#define   R76_BLKPCOR     0x80    /* Black pixel correction enable */
#define   R76_WHTPCOR     0x40    /* White pixel correction enable */

#define REG_RGB444      0x8c    /* RGB 444 control */
#define   RGB444_ENABLE   0x02    /* Turn on  RGB444, overrides 5x5 */
#define   RGB444_DISABLE  0x00    /* Turn off RGB444, overrides 5x5 */
#define   RGB444_BGRX     0x01    /* Empty nibble at end */
#define   RGB444_XBGR     0x00    /* Empty nibble at start */

#define REG_HAECC1      0x9f    /* Hist AEC/AGC control 1 */
#define REG_HAECC2      0xa0    /* Hist AEC/AGC control 2 */

#define REG_SCALING_PCLK_DELAY      0xa2

#define REG_BD50MAX     0xa5    /* 50hz banding step limit */
#define REG_HAECC3      0xa6    /* Hist AEC/AGC control 3 */
#define REG_HAECC4      0xa7    /* Hist AEC/AGC control 4 */
#define REG_HAECC5      0xa8    /* Hist AEC/AGC control 5 */
#define REG_HAECC6      0xa9    /* Hist AEC/AGC control 6 */
#define REG_HAECC7      0xaa    /* Hist AEC/AGC control 7 */
#define REG_BD60MAX     0xab    /* 60hz banding step limit */


次にProcessing側のコード.こちらは,CameraControl.pde,Button.pde,CheckBox.pde,Command.pde,TextBox.pdeから構成されているので,順に示そう.あと,Font(.vlwファイル)は別途生成してください.

では,CameraControl.pde.
import processing.serial.*;
import java.awt.*;

Serial port;

PFont fontButton;
PFont fontCheckBox;
PFont fontData;
Button startButton;
Button dumpButton;
Button readButton;
Button writeButton;
Button formatVGAButton;
Button formatQQVGAButton;
Button continuousButton;
Button singleButton;

CheckBox saveCheckBox;

String readResult  = "addr=   data=  ";
String writeResult = "addr=   data=  ";
String csStatus    = "";

TextBox readAddress;
TextBox writeAddress;
TextBox writeData;
TextBox intervalSec;

PImage img;

static final int FRAME_RATE = 10;
int frame_counter = 0;
static final int TIMEOUT_THRESHOLD_SEC = 30;
int timeout_counter = 0;

static final int VGA_WIDTH    = 640;
static final int VGA_HEIGHT   = 480;
static final int QQVGA_WIDTH  = 160;
static final int QQVGA_HEIGHT = 120;

int width  = VGA_WIDTH;
int height = VGA_HEIGHT;
int[] r = new int[VGA_WIDTH * VGA_HEIGHT];
int[] g = new int[VGA_WIDTH * VGA_HEIGHT];
int[] b = new int[VGA_WIDTH * VGA_HEIGHT];

static final int VGA   = 0;
static final int QQVGA = 1;
int format = VGA;

static final int BAYER_RGGB = 0;
static final int BAYER_GRBG = 1;
static final int BAYER_GBRG = 2;
static final int BAYER_BGGR = 3;

static final int MODE_INIT   = -1;
static final int MODE_NONE   = 0;
static final int MODE_START  = 1;
static final int MODE_DUMP   = 2;
static final int MODE_READ   = 3;
static final int MODE_WRITE  = 4;
static final int MODE_FORMAT = 5;

int cmd_mode = MODE_INIT;
boolean is_dump_ready = false;

boolean isContinuousMode = true;

Command read_cmd;
Command write_cmd;
Command format_cmd;
Command start_cmd;
Command dump_cmd;

void drawButtons()
{
  startButton.draw();
  dumpButton.draw();
  readButton.draw();
  writeButton.draw();
  formatVGAButton.draw();
  formatQQVGAButton.draw();
  continuousButton.draw();
  singleButton.draw();
}

void drawCheckBox()
{
  saveCheckBox.draw();
}

void setup()
{
  println(Serial.list());
  String arduinoPort = Serial.list()[2];
  port = new Serial(this, arduinoPort, 2000000);
  port.bufferUntil('\n');

  frameRate(FRAME_RATE);
  size(880, 560);
  colorMode(RGB, 255);

  fontButton   = loadFont("Helvetica-Bold-14.vlw");
  fontCheckBox = loadFont("Monaco-12.vlw");  
  fontData     = loadFont("Monaco-12.vlw");
  
  startButton = new Button(680+10,  10, 80, 24, "Start");
  dumpButton  = new Button(680+10,  40, 80, 24, "Dump");
  readButton  = new Button(680+10, 100, 80, 24, "Read");
  writeButton = new Button(680+10, 160, 80, 24, "Write");
  
  formatVGAButton   = new Button(680+10, 240, 80, 24, "VGA");
  formatQQVGAButton = new Button(680+10, 270, 80, 24, "QQVGA");
  
  continuousButton = new Button(680+10, 350, 30, 24, "C");
  singleButton     = new Button(680+50, 350, 30, 24, "S");
  
  saveCheckBox = new CheckBox(680+10, 420, false, "Save Image");
  
  setLayout(null);
  readAddress  = new TextBox(680+100, 100, 30, 24, "01");
  writeAddress = new TextBox(680+100, 160, 30, 24, "80");
  writeData    = new TextBox(680+135, 160, 30, 24, "ff");
  intervalSec  = new TextBox(680+10,  440, 30, 24, "0");
  
  for (int i = 0; i < width*height; i++) {
    r[i] = 0;
    g[i] = 90;
    b[i] = 102;
  }
  img = createImage(width, height, RGB);
  img.loadPixels();
  for (int i = 0; i < img.pixels.length; i++) {
    img.pixels[i] = color(r[i], g[i], b[i]);
  }
  
  read_cmd   = new Command();
  write_cmd  = new Command();
  format_cmd = new Command();
  start_cmd  = new Command();
  dump_cmd   = new Command();
}

void setImageFormat()
{
  switch (format) {
    case VGA:
      width  = VGA_WIDTH;
      height = VGA_HEIGHT;
      img = createImage(width, height, RGB);
      img.loadPixels();
      break;
    case QQVGA:
      width  = QQVGA_WIDTH;
      height = QQVGA_HEIGHT;
      img = createImage(width, height, RGB);
      img.loadPixels();
      break;
    default:
      ;
      break;
  }
}

boolean isCmdRunning()
{
  boolean is_cmd_running;

  if (cmd_mode == MODE_NONE) {
    timeout_counter = 0;
    is_cmd_running = false;
  }
  else {
    timeout_counter++;
    if (timeout_counter > TIMEOUT_THRESHOLD_SEC * FRAME_RATE) {
      timeout_counter = 0;
      is_dump_ready = false;
      cmd_mode = MODE_NONE;
      is_cmd_running = false;
      println("Timeout detect!");
    }
    else {
      is_cmd_running = true;
    }
  }

  return is_cmd_running;
}

void execStartCmd()
{
  cmd_mode = MODE_START;
  port.write("s\r\n");
}

void execDumpCmd()
{
  cmd_mode = MODE_DUMP;
  port.write("d\r\n");
}

void execReadCmd(String address)
{
  cmd_mode = MODE_READ;
  port.write("r " + address + "\r\n");
}

void execWriteCmd(String address, String data)
{
  cmd_mode = MODE_WRITE;
  port.write("w " + address + " " + data + "\r\n");
}

void execFormatCmd()
{
  cmd_mode = MODE_FORMAT;
  format = format_cmd.format;
  setImageFormat();
  port.write("f " + format + "\r\n");
}

void draw()
{
  background(196);
  drawButtons();
  drawCheckBox();
  
  textFont(fontData, 12);
  textAlign(LEFT);
  fill(255);
  text(readResult,  680+10, 140);
  text(writeResult, 680+10, 200);

  if (isContinuousMode == true) {
    csStatus = "continuous";
  } else {
    csStatus = "single";
  }
  text(csStatus, 680+90, 370);

  text("sec (StartInterval)", 680+45, 460);
  
  image(img, 10, 10, 640, 480);
  
  if (isCmdRunning() == true) {
    return;
  }

  if (read_cmd.request == true) {
    execReadCmd(read_cmd.address);
    read_cmd.request = false;
  }
  else if (write_cmd.request == true) {
    execWriteCmd(write_cmd.address, write_cmd.data);
    write_cmd.request = false;
  }
  else if (format_cmd.request == true) {
    execFormatCmd();
    format_cmd.request = false;
  }
  else if (start_cmd.request == true) {
    execStartCmd();
    start_cmd.request = false;

    if (isContinuousMode == true) {
      dump_cmd.request = true;
    }
  }
  else if (dump_cmd.request == true) {
    execDumpCmd();
    dump_cmd.request = false;
  }
  else {
    if (isContinuousMode == true) {
      int interval_sec = int(intervalSec.getText());
      if (frame_counter > interval_sec * FRAME_RATE) {
        frame_counter = 0;
        start_cmd.request = true;
      }
    } else {
      frame_counter = 0;
    }
  }
  
  frame_counter++;
}

void mouseClicked()
{
  if (startButton.isMouseOn()) {
   start_cmd.request = true;
  }

  if (dumpButton.isMouseOn()) {
   dump_cmd.request = true;
  }

  if (readButton.isMouseOn()) {
    read_cmd.address = readAddress.getText();
    read_cmd.request = true;
  }

  if (writeButton.isMouseOn()) {
    write_cmd.address = writeAddress.getText();
    write_cmd.data    = writeData.getText();
    write_cmd.request = true;
  }
  
  if (formatVGAButton.isMouseOn()) {
    format_cmd.format = VGA;
    format_cmd.request = true;
  }

  if (formatQQVGAButton.isMouseOn()) {
    format_cmd.format = QQVGA;
    format_cmd.request = true;
  }

  if (continuousButton.isMouseOn()) {
    isContinuousMode = true;
  }

  if (singleButton.isMouseOn()) {
    isContinuousMode = false;
  }
  
  if (saveCheckBox.isMouseOn()) {
    saveCheckBox.click();
  }
}

void updateRGBData(String string)
{
  switch (format) {
    case VGA:
      updateRGBDataBayerVGA(BAYER_RGGB, string);
      break;
    case QQVGA:
      updateRGBDataRGB565QQVGA(string);
      break;
    default:
      ;
      break;
  }

  for (int i = 0; i < width * height; i++) {
    img.pixels[i] = color(r[i], g[i], b[i]);
  }
  img.updatePixels();

  if (saveCheckBox.is_checked) {
    img.save("image/" + getDateString());
  }
}


/*
 * BayerPattern
 *     BAYER_RGGB    BAYER_GRBG    BAYER_GBRG    BAYER_BGGR
 * 
 *     RGRGRGRGRG    GRGRGRGRGR    GBGBGBGBGB    BGBGBGBGBG
 *     GBGBGBGBGB    BGBGBGBGBG    RGRGRGRGRG    GRGRGRGRGR
 *     RGRGRGRGRG    GRGRGRGRGR    GBGBGBGBGB    BGBGBGBGBG
 *     GBGBGBGBGB    BGBGBGBGBG    RGRGRGRGRG    GRGRGRGRGR
 */
void updateRGBDataBayerVGA(int pattern, String string)
{
    int d0, d1, d2, d3, d4, d5, d6, d7, d8;
    int i0, i1, i2, i3, i4, i5, i6, i7, i8;
    /*
     *    data       index
     *  d0 d1 d2    i0 i1 i2
     *  d3 d4 d5    i3 i4 i5
     *  d6 d7 d8    i6 i7 i8
     */
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            i0 = (x - 1) + (y - 1) * width;
            i1 = (x    ) + (y - 1) * width;
            i2 = (x + 1) + (y - 1) * width;
            i3 = (x - 1) + (y    ) * width;
            i4 = (x    ) + (y    ) * width;
            i5 = (x + 1) + (y    ) * width;
            i6 = (x - 1) + (y + 1) * width;
            i7 = (x    ) + (y + 1) * width;
            i8 = (x + 1) + (y + 1) * width;

            if (y == 0) {
                i0 = i6;
                i1 = i7;
                i2 = i8;
            } else if (y == height - 1) {
                i6 = i0;
                i7 = i1;
                i8 = i2;
            }
            if (x == 0) {
                i0 = i2;
                i3 = i5;
                i6 = i8;
            } else if (x == width - 1) {
                i2 = i0;
                i5 = i3;
                i8 = i6;
            }

            d0 = unhex( string.substring(i0 * 2, i0 * 2 + 2) );
            d1 = unhex( string.substring(i1 * 2, i1 * 2 + 2) );
            d2 = unhex( string.substring(i2 * 2, i2 * 2 + 2) );
            d3 = unhex( string.substring(i3 * 2, i3 * 2 + 2) );
            d4 = unhex( string.substring(i4 * 2, i4 * 2 + 2) );
            d5 = unhex( string.substring(i5 * 2, i5 * 2 + 2) );
            d6 = unhex( string.substring(i6 * 2, i6 * 2 + 2) );
            d7 = unhex( string.substring(i7 * 2, i7 * 2 + 2) );
            d8 = unhex( string.substring(i8 * 2, i8 * 2 + 2) );

            /*
             *              x/y   
             *  BAYER_RGGB  0/0  1/0  0/1  1/1
             *  BAYER_GRBG  1/0  0/0  1/1  0/1
             *  BAYER_GBRG  0/1  1/1  0/0  1/0                          
             *  BAYER_BGGR  1/1  0/1  1/0  0/0          data       index
             *              BGB  GBG  GRG  RGR        d0 d1 d2    i0 i1 i2
             *  Pixel       GRG  RGR  BGB  GBG        d3 d4 d5    i3 i4 i5
             *              BGB  GBG  GRG  RGR        d6 d7 d8    i6 i7 i8
             */

            if ( ( (pattern == BAYER_RGGB) && ( ((x&1) == 0) && ((y&1) == 0) ) )
              || ( (pattern == BAYER_GRBG) && ( ((x&1) == 1) && ((y&1) == 0) ) )
              || ( (pattern == BAYER_GBRG) && ( ((x&1) == 0) && ((y&1) == 1) ) )
              || ( (pattern == BAYER_BGGR) && ( ((x&1) == 1) && ((y&1) == 1) ) ) ) {
                r[i4] = d4;
                g[i4] = (d1 + d3 + d5 + d7) / 4;
                b[i4] = (d0 + d2 + d6 + d8) / 4;
            }
            else if ( ( (pattern == BAYER_RGGB) && ( ((x&1) == 1) && ((y&1) == 0) ) )
                   || ( (pattern == BAYER_GRBG) && ( ((x&1) == 0) && ((y&1) == 0) ) )
                   || ( (pattern == BAYER_GBRG) && ( ((x&1) == 1) && ((y&1) == 1) ) )
                   || ( (pattern == BAYER_BGGR) && ( ((x&1) == 0) && ((y&1) == 1) ) ) ) {
                r[i4] = (d3 + d5) / 2;
                g[i4] = (d4 * 4 + d0 + d2 + d6 + d8) / 8;
                b[i4] = (d1 + d7) / 2;
            }
            else if ( ( (pattern == BAYER_RGGB) && ( ((x&1) == 0) && ((y&1) == 1) ) )
                   || ( (pattern == BAYER_GRBG) && ( ((x&1) == 1) && ((y&1) == 1) ) )
                   || ( (pattern == BAYER_GBRG) && ( ((x&1) == 0) && ((y&1) == 0) ) )
                   || ( (pattern == BAYER_BGGR) && ( ((x&1) == 1) && ((y&1) == 0) ) ) ) {
                r[i4] = (d1 + d7) / 2;
                g[i4] = (d4 * 4 + d0 + d2 + d6 + d8) / 8;
                b[i4] = (d3 + d5) / 2;
            }
            else if ( ( (pattern == BAYER_RGGB) && ( ((x&1) == 1) && ((y&1) == 1) ) )
                   || ( (pattern == BAYER_GRBG) && ( ((x&1) == 0) && ((y&1) == 1) ) )
                   || ( (pattern == BAYER_GBRG) && ( ((x&1) == 1) && ((y&1) == 0) ) )
                   || ( (pattern == BAYER_BGGR) && ( ((x&1) == 0) && ((y&1) == 0) ) ) ) {
                r[i4] = (d0 + d2 + d6 + d8) / 4;
                g[i4] = (d1 + d3 + d5 + d7) / 4;
                b[i4] = d4;
            }
        }
    }
}

void updateRGBDataRGB444QQVGA(String string)
{
  // xBGR
  for (int i = 0; i < width * height; i++) {
    r[i] = unhex(String.valueOf(string.charAt(4*i+3))) *16;
    g[i] = unhex(String.valueOf(string.charAt(4*i+2))) *16;
    b[i] = unhex(String.valueOf(string.charAt(4*i+1))) *16;
  }
}

void updateRGBDataRGB565QQVGA(String string)
{
  /*
   * [   dh  ] [   dl  ]
   * 7654 3210 7654 3210
   * [ R  ][  G  ][ B  ]
   */
  for (int i = 0; i < width * height; i++) {
    int dl = unhex( string.substring(i * 4,     i * 4 + 2    ) );
    int dh = unhex( string.substring(i * 4 + 2, i * 4 + 2 + 2) );

    r[i] = (dh & 0xF8);
    g[i] = ((dh & 0x07) << 5) | ((dl & 0xE0) >> 3);
    b[i] = (dl & 0x1F) << 3;
  }
}

void serialEvent(Serial port)
{
  String inString = port.readStringUntil('\n');
  if (inString != null) {
    inString = trim(inString);

//    println(inString);

    switch (cmd_mode) {
      case MODE_INIT:
        if (inString.equals("OV7670 Camera Debugger") == true) {
          cmd_mode = MODE_NONE;
        }
        break;
        
      case MODE_START:
        if (inString.equals("Start") == true) {
          cmd_mode = MODE_NONE;
        }
        break;
      
      case MODE_DUMP:
        if (is_dump_ready == true) {
          updateRGBData(inString);

          is_dump_ready = false;
          cmd_mode = MODE_NONE;
        }
        if (inString.equals("Dump") == true) {
          is_dump_ready = true;
        }
        break;
        
      case MODE_READ:
        if (inString.length() >= 5
         && inString.substring(0, 5).equals("addr=") == true) {
          readResult = new String(inString);
          cmd_mode = MODE_NONE;
        }
        break;
      
      case MODE_WRITE:
        if (inString.length() >= 5
         && inString.substring(0, 5).equals("addr=") == true) {
          writeResult = new String(inString);
          cmd_mode = MODE_NONE;
        }
        break;
      
      case MODE_FORMAT:
        if (inString.length() >= 6
         && inString.substring(0, 6).equals("Format") == true) {
          cmd_mode = MODE_NONE;
        }
        break;

      default:
        break;
    }
  }
}

String getDateString() {
    int year  = year();
    int month = month();
    int day   = day();
    int hour  = hour();
    int minute = minute();
    int second = second();
    
    String date = nf(year, 4) + nf(month, 2) + nf(day, 2) + nf(hour, 2) + nf(minute, 2) + nf(second, 2);
    return date;
}


Button.pde.
class Button {
  int x;
  int y;
  int w;
  int h;
  String str;

  Button(int x, int y, int w, int h, String str) {
    this.x = x;
    this.y = y;
    this.w = w;
    this.h = h;
    this.str = str;
  }

  boolean isMouseOn() {
    return ( (this.x <= mouseX && mouseX <= this.x + this.w)
          && (this.y <= mouseY && mouseY <= this.y + this.h) );
  }

  void draw() {
    noStroke();
    color c = color(0, 255, 0);
    float alpha = 0.8;
    if (this.isMouseOn()) {
      fill(c);
    } else {
      fill(red(c) * alpha, green(c) * alpha, blue(c)  * alpha, alpha(c));
    }
    rect(this.x, this.y, this.w, this.h);

    int fontSize = 14;
    textFont(fontButton, fontSize);
    textAlign(CENTER);
    fill(0);
    text(this.str, this.x + this.w/2, this.y + this.h - fontSize/2);
  }
};


CheckBox.pde.
class CheckBox {
  int x;
  int y;
  boolean is_checked;
  String str;

  private static final int SIZE = 10;

  CheckBox(int x, int y, boolean is_checked, String str) {
    this.x = x;
    this.y = y;
    this.is_checked = is_checked;
    this.str = str;
  }

  void draw() {
    stroke(255);
    color c = color(0, 255, 0);
    float alpha = 0.8;

    if (this.isMouseOn()) {
      fill(c);
    } else {
      fill(red(c) * alpha, green(c) * alpha, blue(c)  * alpha, alpha(c));
    }
    rect(x, y, SIZE, SIZE);

    if (is_checked) {
      line(x, y,        x + SIZE, y + SIZE);
      line(x, y + SIZE, x + SIZE, y       );
    }

    int fontSize = 12;
    textFont(fontCheckBox, fontSize);
    textAlign(LEFT, CENTER);
    fill(255);
    text(this.str, this.x + SIZE * 2, this.y + SIZE/2);
  }

  void click() {
    if (isMouseOn()) {
      is_checked = !is_checked;
    }
  }

  boolean isMouseOn() {
    return ( (this.x <= mouseX && mouseX <= this.x + SIZE)
          && (this.y <= mouseY && mouseY <= this.y + SIZE) );
  }
};


Command.pde.
class Command {
  boolean request;
  String address;
  String data;
  int format;

  Command() {
    request = false;
    address = "";
    data    = "";
    format  = VGA;
  }
};


TextBox.pde.
class TextBox {
  TextField tf;

  TextBox(int x, int y, int w, int h, String str) {
    tf = new TextField(str);
    tf.setFont(new Font("monaco", Font.PLAIN, 12));
    tf.setBounds(x, y, w, h);
    add(tf);
  }
  
  String getText() {
    return tf.getText();
  }
};


Processingで作ったアプリの使い方は...
Arduinoがつながっている状態で起動すると,VGAで画像の連続取得が自動的に動きます.
あとは以下の画像内の説明を見ながら使ってみてください.使い方.png

では,お試しあれ.

nice!(1)  トラックバック(0) 
共通テーマ:日記・雑感

SyntaxHighlighterの動きを確認してみる [雑記]

コードの表示にはなるべくSyntaxHighlighterを使うようにしているのだが,期待通りに表示されないことがあるので,いろいろ試してみる.

たとえば,前回のjavascriptのコードで,
var url = 'http://weather.livedoor.com/forecast/webservice/json/v1?city=' + city;

というのがあるのだが,これをSyntaxHighlighterで表示させると,こうなる(以下はスクリーンショットの画像).
スクリーンショット 2015-05-02 10.53.01.png
アンカータグが追加されてしまう.

auto-linksの設定が影響しているのかと思って,falseにしてみてもアンカータグは変わらない.

原因がわからない.

So-netブログでSyntaxHighlightを使う:おぼえがき:So-net blog
を参考にしてSo-netブログの設定を変えてみたのだが,それでも変わらない.

と思ってたら,記事の編集の,詳細設定に「URL自動リンク」というのがあって,リンクするになっていたのでリンクしないにしてみた.
どうやらこれが悪さをしていたようだ.
上記のブログで,「キーワード広告の表示が悪さをしている」って書いてあったのを読んでもしかしてと思ったら正解だった.うちはもとからキーワード広告はオフだったけど,似たような設定で探したら「URL自動リンク」に気がつけた.感謝.
毎回詳細設定で変更するのは面倒だ.
管理ページの[設定]->[記事の初期設定]->[URL自動リンク]に設定があるので,ここをリンクしないにしておく.

ちなみに,管理ページの[デザイン]->[テンプレート管理]->HTML編集のテンプレートは,こんな感じ.</body>の直前に以下のように挿入してる.
<!-- Syntax Highlighter -->
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shCoreDefault.css" rel="stylesheet" type="text/css">
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js" type="text/javascript"></script>
<script type="text/javascript">
SyntaxHighlighter.autoloader(
'applescript            http://alexgorbatchev.com/pub/sh/current/scripts/shBrushAppleScript.js',
'actionscript3 as3      http://alexgorbatchev.com/pub/sh/current/scripts/shBrushAS3.js',
'bash shell             http://alexgorbatchev.com/pub/sh/current/scripts/shBrushBash.js',
'coldfusion cf          http://alexgorbatchev.com/pub/sh/current/scripts/shBrushColdFusion.js',
'cpp c                  http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js',
'c# c-sharp csharp      http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCSharp.js',
'css                    http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js',
'delphi pascal          http://alexgorbatchev.com/pub/sh/current/scripts/shBrushDelphi.js',
'diff patch pas         http://alexgorbatchev.com/pub/sh/current/scripts/shBrushDiff.js',
'erl erlang             http://alexgorbatchev.com/pub/sh/current/scripts/shBrushErlang.js',
'groovy                 http://alexgorbatchev.com/pub/sh/current/scripts/shBrushGroovy.js',
'java                   http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js',
'jfx javafx             http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJavaFX.js',
'js jscript javascript  http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js',
'perl pl                http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPerl.js',
'php                    http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPhp.js',
'text plain             http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPlain.js',
'py python              http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js',
'ruby rails ror rb      http://alexgorbatchev.com/pub/sh/current/scripts/shBrushRuby.js',
'sass scss              http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSass.js',
'scala                  http://alexgorbatchev.com/pub/sh/current/scripts/shBrushScala.js',
'sql                    http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js',
'vb vbnet               http://alexgorbatchev.com/pub/sh/current/scripts/shBrushVb.js',
'xml xhtml xslt html    http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js'
);
SyntaxHighlighter.all();
SyntaxHighlighter.defaults['toolbar'] = false;
SyntaxHighlighter.defaults['auto-links'] = false;
</script>
<style type="text/css">
/*--- Syntax Highlighter Scripts Generator 縦スクロールを出さない ---*/
.syntaxhighlighter {
overflow-y: hidden !important;
padding: 1px !important;
}
</style>
<!-- Syntax Highlighter -->

</body>


実際のSyntaxHighlighterの表示はこれ.
var url = 'http://weather.livedoor.com/forecast/webservice/json/v1?city=' + city;


これが(アンカータグがなくて)ちゃんと表示されていればOKのはず.

もし,困っている人がいたら参考になるかも.

nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

Edisonに天気予報をしゃべらせてみる [edison]

前回Open JTalkを使ってEdisonをしゃべらせることができるようになったので,今回は天気予報をしゃべらせてみる.

天気予報の情報はlivedoorの,お天気Webサービス - Weather Hacksからスクレイピングすることにする.

で,天気予報情報を取得したら,Open JTalkで合成音声を出力するようにしておく.
あとは,Edisonにスイッチをつけて,スイッチを押したら天気予報をしゃべるようにする.

今回は,node.jsでやってみた.

では,早速だがコードを示そう.

まず,main.js
var http = require('http');
var exec = require('child_process').exec;
var mraa = require('mraa');

var city = '140010';
var url = 'http://weather.livedoor.com/forecast/webservice/json/v1?city=' + city;
var weather_info = '';

var waveFilePath = '/tmp/';
var waveFileName = 'weather_info.wav';

var createWetherInfo = function(data) {
    var location_city       = data.location.city;
    var forecasts_dateLabel = data.forecasts[0].dateLabel;
    var forecasts_telop     = data.forecasts[0].telop;

    var forecasts_temp_max = '-';
    if (data.forecasts[0].temperature.max != null) {
        forecasts_temp_max  = data.forecasts[0].temperature.max.celsius;
    }

    var forecasts_temp_min = '-';
    if (data.forecasts[0].temperature.min != null) {
        forecasts_temp_min  = data.forecasts[0].temperature.min.celsius;
    }

    weather_info = location_city + "の"
                 + forecasts_dateLabel + "の天気は,"
                 + forecasts_telop + "でしょう.";
    if (forecasts_temp_max != '-') {
        weather_info += "最高気温は" + forecasts_temp_max + "度です.";
    }
    if (forecasts_temp_min != '-') {
        weather_info += "最低気温は" + forecasts_temp_min + "度です.";
    }
};

var createWaveFile = function() {
    var openjtalkShellCmd = '/home/root/openjtalk/openjtalk.sh';
    var cmd = openjtalkShellCmd + ' ' + weather_info + ' ' + waveFilePath + waveFileName;
    exec(cmd, function(err, stdout, stderr) {
        if (err) {
            console.log(err);
            console.log(err.code);
            console.log(err.signal);
        }
    });
};

var callback = function(data) {
    createWetherInfo(data);
    console.log(weather_info);

    createWaveFile();
};

var playWaveFile = function() {
    var cmd = 'aplay -D plughw:1 ' + waveFilePath + waveFileName;
    exec(cmd, function(err, stdout, stderr) {
        if (err) {
            console.log(err);
            console.log(err.code);
            console.log(err.signal);
        }
    });
};

x = new mraa.Gpio(14);
x.dir(mraa.DIR_IN);
x.mode(mraa.MODE_PULLUP);
x.isr(mraa.EDGE_FALLING, playWaveFile);

weatherInfo(callback);
setInterval(function() {
    weatherInfo(callback);
}, 10 * 60 * 1000);


function weatherInfo(callback) {
    http.get(url, function(res) {
        var body = '';
        res.setEncoding('utf8');

        res.on('data', function(chunk) {
            body += chunk;
        });

        res.on('end', function() {
            callback(JSON.parse(body));
        });

    }).on('error', function(e) {
        console.log(e.message);
    });
}


次に,package.json
{
  "name": "weather_info",
  "description": "",
  "version": "0.0.0",
  "main": "main.js",
  "engines": {
    "node": ">=0.10.0"
  },
  "dependencies": {
  }
}


この2つのファイルを,たとえばホームディレクトリ下にweather_infoディレクトリを作成して保存しておく.
なお,Open JTalkは前回のとおり,ホームディレクトリ下のopenjtalkディレクトリにインストールされてる前提で書かれているので,もし変更してるなら環境に合わせて変更が必要だ.

これで,10分おきにWeather Hacksから天気予報を取得してくる.上記のコードでは横浜の天気予報を取得するようになっている.main.js:5でvar city = '140010';としているが,これはWeather Hacksのcityパラメータそのものだ.なので地域を変更したければこの値を変更すればよい.定義はWeather Hacksのページに情報がある.

天気予報の合成音声は,取得した天気予報情報のうち,天気と最高気温と最低気温から,
「横浜の今日の天気は,XXXでしょう.最高気温はYY度です.最低気温はZZ度です.」
といった感じで生成され,/tmp/weather_info.wavとして保存されるようにしてある.最高気温と最低気温は(データが有効なときだけ)出力されるので,天気だけしかしゃべらないときもある.

Edisonにつなぐスイッチは,(回路図にするほどのこともないが)以下のように接続してやる.Edisonの内蔵プルアップを使用するので,単にスイッチをつなぐだけだ.
switch_回路図.png

スイッチが押されたかどうかは割り込みで監視していて,押されたらあらかじめ生成してあるweather_info.wavを再生するようになっている.

最初はスイッチが押されたら天気予報を取得して,wather_info.wavを生成して再生するようにしようと思ったのだが,情報取得と音声生成の処理があるのでスイッチを押しても若干待たされる.というわけで,あらかじめwather_info.wavの作成まで定期的に行っておいて,スイッチが押されたら単に生成済みのweather_info.wavを再生するだけにしてある.これならスイッチを押したらすぐに天気予報をしゃべるようになる.

そしたら最後に,Edison起動時に自動実行するようにしておく.
まず,weather_infoのディレクトリに,start_weather_info.shというスクリプトを用意しておく.
#!/bin/sh

/usr/bin/node /home/root/weather_info/main.js


で実行権限付与.
chmod 755 start_weather_info.sh


そしたら次に,/lib/systemd/system/下に,weather_info.serviceというファイルを作成してやる.
[Unit]
Description=Weather_Info
After=network.target

[Service]
Environment="NODE_PATH=/usr/lib/node_modules"
Environment="NODE_ENV=production"
Environment="HOME=/home/root"
ExecStart=/home/root/weather_info/start_weather_info.sh

[Install]
WantedBy=multi-user.target


そしたら自動起動ONにして起動させてみる.
systemctl enable weather_info.service
systemctl start weather_info.service


これでOK.

ただ,これで試してみると,Edisonが起動したときの実行1回目でエラーが出るようだ.どうやらWheather HacksにアクセスしようとするがDNSでエラーになっているっぽい.実行タイミングが早すぎる感じだ.weather_info.serviceのAfter=network.targetあたりを変更するといいような気がするのだが,具体的にどうすればよいのか分からない.
とりあえず,最初の1回はエラーになるが10分後の実行からは問題なく動作するので気にしないことにしておく.

というわけで,これで完了.
お試しあれ.

2015.05.02追記
SyntaxHighlighterを使ったらコードのvar url = の部分の表記がおかしくなったので修正.

nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

EdisonでOpen JTalkを使ってみる [edison]

前回,EdsionでUSB Audioを使えるようにしたので,今回はOpen JTalkを使ってEdisonにしゃべらせてみる.Open JTalkはフリーの日本語音声合成エンジンだ.

ではさっそくインストール.

まず,hts_engine APIをインストール.適当なディレクトリ(以下ではopenjtalk)を用意してダウンロード,展開してインストールしてやる.
cd ~/
mkdir openjtalk
cd openjtalk/
wget http://downloads.sourceforge.net/hts-engine/hts_engine_API-1.09.tar.gz
tar xzvf hts_engine_API-1.09.tar.gz
cd hts_engine_API-1.09/
./configure
make
make install

次に,OpenJTalkのインストール
cd ~/openjtalk/
wget http://downloads.sourceforge.net/open-jtalk/open_jtalk-1.08.tar.gz
wget http://downloads.sourceforge.net/open-jtalk/open_jtalk_dic_utf_8-1.08.tar.gz
wget http://downloads.sourceforge.net/open-jtalk/hts_voice_nitech_jp_atr503_m001-1.05.tar.gz

tar xzvf open_jtalk-1.08.tar.gz
cd open_jtalk-1.08/
./configure \
     --with-hts-engine-header-path=/usr/local/include \
     --with-hts-engine-library-path=/usr/local/lib \
     --with-charset=utf-8
make
make install

cd ~/openjtalk/
tar xzvf open_jtalk_dic_utf_8-1.08.tar.gz
tar xzvf hts_voice_nitech_jp_atr503_m001-1.05.tar.gz

MMDAgentのサイトから音声データをダウンロード
wget http://sourceforge.net/projects/mmdagent/files/MMDAgent_Example/MMDAgent_Example-1.4/MMDAgent_Example-1.4.zip
unzip MMDAgent_Example-1.4.zip


Open JTalkの実行にはオプションが多いので,スクリプトを作成して実行する.
vi openjtalk.sh

#!/bin/sh

OPENJTALK_DIR=/home/root/openjtalk
VOICE=$OPENJTALK_DIR/MMDAgent_Example-1.4/Voice/mei/mei_normal.htsvoice
DIC=$OPENJTALK_DIR/open_jtalk_dic_utf_8-1.08
echo $1 | open_jtalk \
-m  $VOICE \
-x  $DIC \
-ow $2 \
-g 9

ちなみに,最後の-gはvolume(dB)を指定するオプションだ.
前回,EdisonにUSB Audioを接続したが,スピーカーが100均のパッシブスピーカーなので音がとても小さい.なので,Open JTalkの出力をあらかじめ上げておく.値を大きくすると出力の音量が大きくなる.あまり上げすぎると音が歪んでしまうので適当に調整してみてくださいな.

あとは作成したスクリプトに実行属性を設定して,
chmod 744 openjtalk.sh

しゃべらせてみる.とりあえず「テストです」としゃべってもらうには,
./openjtalk.sh "テストです" a.wav

これでa.wavファイルが作成される.

再生はうちの環境では
aplay -D plughw:1 a.wav

でOK.

お試しあれ.

タグ:openjtalk
nice!(1)  トラックバック(0) 
共通テーマ:日記・雑感

EdisonでUSB Audioを使ってみる [edison]

Intel Edisonから音を出すために,USB Audioアダプタをつないでみる.
使ったのは,PLANEXのPL-US35APだ.

PLANEX USB→3.5mmヘッドホン/マイク端子 USBオーディオ変換アダプタ PL-US35AP

PLANEX USB→3.5mmヘッドホン/マイク端子 USBオーディオ変換アダプタ PL-US35AP

  • 出版社/メーカー: プラネックス
  • メディア: Personal Computers

また,Edisonと接続する際,Intel Edison Breakout Board側のUSB端子はMicroBタイプ,USB AudioアダプタはAタイプなので,BUFFALOのBSMPC11C01BKを使ってつないでやる.

iBUFFALO USB(microB to A)変換アダプター ブラック BSMPC11C01BK

iBUFFALO USB(microB to A)変換アダプター ブラック BSMPC11C01BK

  • 出版社/メーカー: バッファロー
  • メディア: エレクトロニクス


なお,EdisonにUSB機器をつなぐとUSBポートから電源供給できなくなるので,別途ACアダプタを使って電源供給してやる.
使ったのは,秋月の,
ブレッドボード用DCジャックDIP化キット
超小型スイッチングACアダプター12V1A 100V~240V GF12-US1210
で,ジャンパー線でEdisonのJ21に接続してやる.

あとはスピーカーだが,今回は100均のSeriaで売っていた,ZY-76というのを買ってみた.
ZY-76.jpg

そしたら,あとは接続するだけだ.

以前はEdisonでUSB Audioを使うにはカーネルのビルドが必要だったが,最新のYoctoイメージでは必要なくなった.最新のYocto complete imageは,ここからダウンロードできる.現時点では,edison-image-ww05-15.zipが最新のようだ.

では,動作テスト.
以下のサイトを参考にしながら適当なwavファイルを再生してみる.
Intel Edisonを動かす(4) サウンド編 | IoT

すると,あっさりと動作した.
が,1点問題が...

ちょっとネットで調べれば分かるが,今回使った100均スピーカをアンプなしで使うと,音量がかなり小さい.USB Audioの出力を100%にしてなんとか聞こえる程度だ.
もう少し音量が得られるといいのだが...
とりあえず聞こえるので良しとしよう.

タグ:USB Audio
nice!(1)  トラックバック(0) 
共通テーマ:日記・雑感

EdisonでIoT Analyticsを使ってみる [edison]

前回,Intel XDK IoT Editionで温湿度センサAM2321を使ってみた.
今回は,その温湿度センサの値をIoT Analyticsへ飛ばしてみる.

IoT Analyticsがどんなものであるかとか,必要な設定は下記サイトが詳しい.
21: Intel Edison実践編 (1) 〜 センサーデータを受け取るSaaS「IoT Analytics」
22: Intel Edison実践編 (2) 〜 EdisonからIoT Analyticsにデータを送る
23: Intel Edison実践編 (3) 〜 XDKで開発したプログラムからiotkit-agentにデータを送る

というわけで,上記サイトを参考に,ほぼそのままやってみる.ただ,今回は温度と湿度をIoT Analyticsに送りたいので,以下のようにコマンドを入力してやる.
iotkit-admin register temperature temperature.v1.0
iotkit-admin register humidity humidity.v1.0


あとはコードだが,前回同様,適当なProjectを作って,package.jsonとmain.jsを以下のようにしてやる.

まず,package.json
{
  "name": "iot_analytics_test",
  "description": "",
  "version": "0.0.0",
  "main": "main.js",
  "engines": {
    "node": ">=0.10.0"
  },
  "dependencies": {
      "sleep": "*"
  }
}

次に,main.js
var dgram = require('dgram');
var client = dgram.createSocket('udp4');
var udpOptions = {
    host : '127.0.0.1',
    port : 41234
};
 
function sendObservation(name, value, on){
    var msg = JSON.stringify({
        n: name,
        v: value,
        on: on
    });
 
    var sentMsg = new Buffer(msg);
    console.log("Sending observation: " + sentMsg);
    client.send(sentMsg, 0, sentMsg.length, udpOptions.port, udpOptions.host);
}

var mraa = require('mraa');
console.log('MRAA Version: ' + mraa.getVersion());

var sleep = require('sleep');

function crc16(buf, length)
{
    var crc = 0xFFFF;
    for (var j = 0; j < length; j++) {
        crc ^= buf[j];
        for (var i = 0; i < 8; i++) {
            if (crc & 0x01) {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

function Am2321(bus, address, bufsize) {
    this.x = new mraa.I2c(bus);
    this.x.address(address);
    this.buff = new Buffer(bufsize);
    
    this.wakeupSensor = function() {
        this.x.writeReg(0, 0);
    }
    
    this.sendCommand = function() {
        get_command = new Buffer([0x03, 0x00, 0x04]);
        this.x.write(get_command);
        sleep.usleep(1500);
    }
    
    this.receiveData = function() {
        this.buff = this.x.read(8);
    }
    
    this.checkCrc = function() {
        var crc = this.buff[6] + this.buff[7] * 256;
        return crc == crc16(this.buff, 6);
    }
    
    this.checkRecieveData = function() {
        return (this.buff[0] == 0x03) && (this.buff[1] == 0x04);
    }

    this.getHumidity = function() {
        var high = this.buff[2];
        var low  = this.buff[3];
        return (high * 256 + low) / 10;
    }

    this.getTemperature = function() {
        var temperature = 0;
        var high = this.buff[4];
        var low  = this.buff[5];

        if (high & 0x80) {
            temperature = -1 * (high & 0x7F) * 256 + low;
        }
        else {
            temperature = high * 256 + low;
        }
        return temperature / 10;
    }
}

var sensor = new Am2321(6, 0x5C, 8);
periodicActivity();

function periodicActivity()
{
    sensor.wakeupSensor();
    sensor.sendCommand();
    sensor.receiveData(); 

    if (sensor.checkCrc() == true) {
        if (sensor.checkRecieveData() == true) {
            var humidity = sensor.getHumidity();
            var temperature = sensor.getTemperature();
            console.log("%d %%, %d C", humidity, temperature);
            
            var date = new Date();
            sendObservation('humidity',    humidity,    date.getTime());
            sendObservation('temperature', temperature, date.getTime());
        }
    }

    setTimeout(periodicActivity,10000);
}


これをビルドして実行すると,IoT Analyticsに10秒おきに温度と湿度を送信するようになる.

で,しばらく動かしてみたときの結果は,こんな感じ.
IoT Analytics Graph.png
送信したデータをグラフにしてくれる.あと,csvのエクスポートも可能だ.

非常にお手軽に温湿度を送ることができる.
お試しあれ.

nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

Intel XDK IoT Editionを使って温湿度センサを動かしてみる [edison]

前回,EdisonでArduino IDEから動かしてみた.
今回は,Intel XDK IoT Editionを使って温湿度センサを動かしてみる.

というわけで,まず,Intel XDK IoT Editionのインストール.
インストール手順とか,Intel XDK IoT Editionの使い方等は,詳しく書かれているサイトがいくつもあるので,そちらを参照下さいな.
たとえば...
Edisonを開封してからnode.jsでIOポート制御するまで - Qiita
Intel EdisonでIntel XDK for IoTを使う - Qiita
[Edison]Intel XDK IoT Edisonをつかってプログラムのビルドをしてみる | Developers.IO
あたりが参考になるかと.

で,温湿度センサを動かしてみる.温湿度センサは前回使った,AM2321だ.接続(回路図)等は前回から変わってない.

というわけで,早速コードを示そう.
適当なProjectを作って,package.jsonとmain.jsを以下のようにしてやる.

まず,package.json
{
  "name": "TemperatureHumiditySensor",
  "description": "",
  "version": "0.0.0",
  "main": "main.js",
  "engines": {
    "node": ">=0.10.0"
  },
  "dependencies": {
      "sleep": "*"
  }
}

次に,main.js
var mraa = require('mraa');
console.log('MRAA Version: ' + mraa.getVersion());

var sleep = require('sleep');

function crc16(buf, length)
{
    var crc = 0xFFFF;
    for (var j = 0; j < length; j++) {
        crc ^= buf[j];
        for (var i = 0; i < 8; i++) {
            if (crc & 0x01) {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

function Am2321(bus, address, bufsize) {
    this.x = new mraa.I2c(bus);
    this.x.address(address);
    this.buff = new Buffer(bufsize);
    
    this.wakeupSensor = function() {
        this.x.writeReg(0, 0);
    }
    
    this.sendCommand = function() {
        get_command = new Buffer([0x03, 0x00, 0x04]);
        this.x.write(get_command);
        sleep.usleep(1500);
    }
    
    this.receiveData = function() {
        this.buff = this.x.read(8);
    }
    
    this.checkCrc = function() {
        var crc = this.buff[6] + this.buff[7] * 256;
        return crc == crc16(this.buff, 6);
    }
    
    this.checkRecieveData = function() {
        return (this.buff[0] == 0x03) && (this.buff[1] == 0x04);
    }

    this.getHumidity = function() {
        var high = this.buff[2];
        var low  = this.buff[3];
        return (high * 256 + low) / 10;
    }

    this.getTemperature = function() {
        var temperature = 0;
        var high = this.buff[4];
        var low  = this.buff[5];

        if (high & 0x80) {
            temperature = -1 * (high & 0x7F) * 256 + low;
        }
        else {
            temperature = high * 256 + low;
        }
        return temperature / 10;
    }
}

var sensor = new Am2321(6, 0x5C, 8);
periodicActivity();

function periodicActivity()
{
    sensor.wakeupSensor();
    sensor.sendCommand();
    sensor.receiveData(); 

    if (sensor.checkCrc() == true) {
        if (sensor.checkRecieveData() == true) {
            var humidity = sensor.getHumidity();
            var temperature = sensor.getTemperature();
            console.log("%d %%, %d C", humidity, temperature);
        }
    }

    setTimeout(periodicActivity,5000);
}

そしたら,ビルドして,Edisonにアップロードして実行してやる.
そうすると,コンソールに温度と湿度が5秒おきに出力されるようになる.

AM2321はI2Cの通信シーケンスに癖があるのだが,とりあえず上記のコードで動作させることができるようだ.

MRAAライブラリがあるので,Arduinoで動かしたときと同じぐらい簡単.
お試しあれ.

タグ:XDK
nice!(0)  トラックバック(0) 
共通テーマ:日記・雑感

Edisonに温湿度センサをつないでみる [edison]

Edisonに温湿度センサをつないでみた.
使用したのは,秋月電子で以前購入した,温湿度センサモジュールAM2321だ.
このセンサは,当初は中国語のデータシートしかなかったらしいが,今は英語のデータシートを確認することができる.
また,デジタル 温度・湿度センサー AM2321 を制御 - Neにマニュアルを日本語訳された方の情報があって,とても助かりました.

というわけで,センサの詳細は上記を参照してもらうとして...
このセンサ,買って手元に届いてから気がついたんだけど,ピン間隔が1.27mmになっていて,そのままではブレッドボードに挿せない.なので,ピンをやや曲げて間隔を広げつつ,ピンヘッダに半田付けしてやる.
半田付けがちょっと汚いが,こんな感じ.
AM2321-1.jpg
これでブレッドボードに挿せる.

さて,ではEdisonとの接続だ.
AM2321は通信モードとしてI2Cか1-Wireが選べるんだけど,今回はI2Cを使うことにした.EdisonはIntel Edison Breakout Boardを使っているので,IOは1.8Vだ.なので以前の照度センサをつないでみたときと同様に,秋月のI2Cバス用双方向電圧レベル変換モジュール(PCA9306)を経由して接続してやる.

回路図はこんな感じになる.I2Cのレベル変換モジュールはジャンパカットが必要なので注意.
当初はこんな感じで接続していたが,AE-PCA9306ではVref2側を高電圧側にするべきである.詳細は,ここのコメント欄を参照.
temperature_humidity_sensor_ng.png
なので,こちらの回路図のように接続すべきである.
temperature_humidity_sensor.png

で,接続するとこんな感じ.写真はAE-PCA9306のVref2側を低電圧側にしちゃったままのものだが,接続のイメージはつかめると思う.
AM2321-2.jpg

Arduinoで動作させてみる.スケッチは,いろんなサイトを参考にしつつ作成した.これでシリアルに温度と湿度が5秒おきに送られてくる.
#include <Wire.h>

#define I2C_AM2321_ADDRESS                0x5C
#define REGISTER_ADDRESS_HUMIDITY_HIGH    0x00
#define REGISTER_ADDRESS_HUMIDITY_LOW     0x01
#define REGISTER_ADDRESS_TEMPERATURE_HIGH 0x02
#define REGISTER_ADDRESS_TEMPERATURE_LOW  0x03
#define READ_REGISTER_LENGTH              0x04
#define FUNCTION_CODE_READ_REGISTER       0x03

enum BUFF_INDEX {
    INDEX_FUNCTION_CODE = 0,
    INDEX_RETURN_BYTE,
    INDEX_HUMIDITY_HIGH,
    INDEX_HUMIDITY_LOW,
    INDEX_TEMPERATURE_HIGH,
    INDEX_TEMPERATURE_LOW,
    INDEX_CRC_LOW,
    INDEX_CRC_HIGH,
    INDEX_MAX,
};
    
void wakeupSensor()
{
    Wire.beginTransmission(I2C_AM2321_ADDRESS);
    Wire.write(0x00);
    Wire.endTransmission();
}

void sendReadCommand()
{
    Wire.beginTransmission(I2C_AM2321_ADDRESS);
    Wire.write(FUNCTION_CODE_READ_REGISTER);
    Wire.write(REGISTER_ADDRESS_HUMIDITY_HIGH);
    Wire.write(READ_REGISTER_LENGTH);
    Wire.endTransmission();

    delayMicroseconds(1500);
}

void receiveData(byte (&buff)[INDEX_MAX])
{
    Wire.requestFrom(I2C_AM2321_ADDRESS, INDEX_MAX);
    int index = 0;
    while (Wire.available()) {
        buff[index++] = Wire.read();
    }
}

unsigned short crc16(unsigned char *ptr, int length)
{
    unsigned short crc = 0xFFFF;

    while (length--) {
        crc ^= *ptr++;
        for (int i = 0; i < 8; i++) {
            if (crc & 0x01) {
                crc >>= 1;
                crc ^= 0xA001;
            }
            else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

bool checkCrc(byte (&buff)[INDEX_MAX])
{
    unsigned short crc = 0;
    crc  = buff[INDEX_CRC_LOW];
    crc |= buff[INDEX_CRC_HIGH] << 8;

    return (crc == crc16(buff, 2 + READ_REGISTER_LENGTH));
}

bool checkReceiveData(byte (&buff)[INDEX_MAX])
{
    return ( (buff[INDEX_FUNCTION_CODE] == FUNCTION_CODE_READ_REGISTER)
          && (buff[INDEX_RETURN_BYTE]   == READ_REGISTER_LENGTH) );
}

bool readAm2321(byte (&buff)[INDEX_MAX])
{
    wakeupSensor();
    sendReadCommand();
    receiveData(buff);
    if (!checkCrc(buff)) {
        return false;
    }
    return checkReceiveData(buff);
}

signed int convertHumidity(byte high, byte low)
{
    return high * 256 + low;
}

signed int convertTemperature(byte high, byte low)
{
    signed int temperature = (high & 0x7F) * 256 + low;
    temperature *= (high & 0x80) ? -1 : 1;
    return temperature;
}

void displayHumidityAndTemperature(signed int humidity, signed int temperature)
{
    Serial.print(temperature / 10);
    Serial.print(".");
    Serial.print(temperature % 10);
    Serial.print("C");
    Serial.print(", ");

    Serial.print(humidity / 10);
    Serial.print(".");
    Serial.print(humidity % 10);
    Serial.print("%");
    Serial.println();
}

void setup()
{
    Serial.begin(9600);
    Wire.begin();
}

void loop()
{
    byte buff[INDEX_MAX] = {0};
    bool result = readAm2321(buff);

    if (result == true) {
        signed int humidity = convertHumidity(buff[INDEX_HUMIDITY_HIGH],
                                              buff[INDEX_HUMIDITY_LOW]);
        signed int temperature = convertTemperature(buff[INDEX_TEMPERATURE_HIGH],
                                                    buff[INDEX_TEMPERATURE_LOW]);

        displayHumidityAndTemperature(humidity, temperature);
    }

    delay(5000);
}


このセンサはI2Cの通信シーケンスに癖があって,WakeUpが必要だったり,アドレス送信後にWaitが必要だったりする.なお、上のスケッチでは,アドレス送信後のWaitはArduinoのWireライブラリではできない(できるかもしれないがやり方が分からない)ので対応してない.

あと,ときどき,受信データByte数が0で返ってくることがある.エラーは返ってこないしCRCチェックも正常なのだが.なので,Arduino側で4Byte受信したかどうかのエラーチェックもやっている.

それから,ときどき湿度値が3%ぐらい落ち込むことがある.

センサーの湿度の値が: さとうしゅうの 日々平安 2.37
で書かれてる現象と同じっぽい.I2C通信するとき,AM2321仕様にある通信シーケンス(Wait等)を守っていないからなのかなとも思ったが,

Arduino/みんな試作機/温湿度計 - 講義のページ - MyDNS.JP
に書かれてる「動作確認-2-」の測定例を見ても湿度値が落ち込んでいるところがある.こちらはI2Cではなく1-Wireで通信した結果のようなので,通信シーケンスに問題があるのではなさそうだ.

AM2321側が何か問題があるのではないかと思うのだが,詳細はよくわからない.
まぁあまり気にしないことにする.

というわけで若干動作が怪しいところがある気がするが,お手軽に使えるのでお試しあれ.

あーでも,秋月にHDC1000使用 温湿度センサーモジュールってのがあって,こっちも試してみたい.AM2321を購入したときにはなかったんだよなぁ.こっちはピン間隔が2.54mmピッチだし,TI製だし.機会があれば試してみたいが,そんなにいくつも温湿度センサっていらないよなぁ...
nice!(2)  トラックバック(0) 
共通テーマ:日記・雑感
前の10件 | -

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。