前の10件 | -
Arduinoで大気圧センサを使ってみる [Arduino]
大気圧センサを入手したので,Arduinoで動かしてみる.使用したのは,秋月電子の,「MPL115A2使用大気圧センサーモジュールキット(I2C) Ver2」.
ネットで調べると,Arduino用のライブラリが公開されていたので,ありがたく使わせてもらう.参照したのは,Arduinoで遊ぶページにある,「MPL115A2用ライブラリ」だ.
あとは,前回作った7セグLEDライブラリと組み合わせて,測定した大気圧を7セグLEDに表示してみる.
7セグLEDとの接続は前回と同じなので,大気圧センサとの接続部分のみ示そう.回路図はこんな感じ.
で,スケッチ.
測定値のばらつきが多いようなので,10回測定して平均値を採用してる.
あと,本来は高度に応じて補正を入れて,海面更正気圧を求めるべきだが,ひとまず測定値そのままにしている.
こちらの使用環境ではおおよそ5気圧ほど低くなる計算(高度からの換算値)で,補正後の値を気象庁の気圧値(たとえば東京はこんな感じ)と比較してもおおよそ一致しているので,測定は正しく行えているようである.
で,実際の動作状況はこんなかんじ.
大気圧測定ライブラリを利用すると,とても簡単に使える.作成者に感謝.
お試しあれ.
ネットで調べると,Arduino用のライブラリが公開されていたので,ありがたく使わせてもらう.参照したのは,Arduinoで遊ぶページにある,「MPL115A2用ライブラリ」だ.
あとは,前回作った7セグLEDライブラリと組み合わせて,測定した大気圧を7セグLEDに表示してみる.
7セグLEDとの接続は前回と同じなので,大気圧センサとの接続部分のみ示そう.回路図はこんな感じ.
で,スケッチ.
#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気圧ほど低くなる計算(高度からの換算値)で,補正後の値を気象庁の気圧値(たとえば東京はこんな感じ)と比較してもおおよそ一致しているので,測定は正しく行えているようである.
で,実際の動作状況はこんなかんじ.
大気圧測定ライブラリを利用すると,とても簡単に使える.作成者に感謝.
お試しあれ.
タグ:大気圧センサ
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Ωを採用.
実際に点灯させてみれば分かるが,室内で見ている限りこれで十分な視認性が得られる.
こんな感じで接続する.
あとは,Arduino側のスケッチだが,今回は,Adafruitが公開しているLibraryを参考に改変して,出力する桁数を指定可能な7セグLED点灯用ライブラリを作ってみた.
表示テスト用のスケッチとあわせて示そう.
まず,ライブラリだが,以下の3つのファイルからなる.
Led7segment.h
Led7segment.cpp
keywords.txt
で,適当なディレクトリに,Led7segmentディレクトリを作成し,上記3つのファイルを保存してやればよい.
順にコードを示そう.
まず,Led7segment.h
次に,Led7segment.cpp
最後に,keywords.txt
ダウンロードするなら,こちらから.
Led7segment.zip.jpg
ただし,So-netのブログでは,zip圧縮ファイルのアップロードができないため,zip圧縮してから拡張子に.jpgをつけて無理矢理アップロードしている.
なので.ダウンロードしたら.jpgを消してzipファイルとして展開してやってください.
また,ライブラリの追加方法は,「arduino ライブラリ 追加」等で検索してみてください.
で,表示テスト用のスケッチは,Led_7seg.ino.
Led7segmentライブラリの使い方は,Led7segment.hと,これを見れば分かると思う.
なお,このスケッチではMsTimer2ライブラリを使用しているので,別途追加してやってください.
この表示テスト用スケッチでは,
それでは,お試しあれ.
今回は,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Ωを採用.
実際に点灯させてみれば分かるが,室内で見ている限りこれで十分な視認性が得られる.
こんな感じで接続する.
あとは,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
久しぶりにov7670+Arduino+Processingを動かしてみる [Arduino]
ov7670を動かしていたのはずいぶん前で,その後は放置していたのだが,久しぶりに動かしてみた.コード公開のご要望をいただいたので,最新の状態を公開してみる.
VGAとQQVGAに対応していて,QQVGAは当初はRGB444だったが,今はRGB565で動かしている.
ただ,あらかじめ断っておくが,シリアル通信で画像データを取得している部分がボトルネックになっているようで,画像の更新頻度(フレームレート)はかなり遅い.リアルタイムは全然無理で,パラパラマンガ以下である.
Arduinoのシリアル通信のライブラリに手を入れると改善できるのではないかと思うのだが...
ボーレートから計算した通信スピードに対して実際が遅すぎで,どこかに大きなオーバーヘッドがのってるような気がする.
では,まずはArduino側のコード.ov7670.inoとov7670reg.hだ.
最初にov7670.ino.
次に,ov7670reg.h
次にProcessing側のコード.こちらは,CameraControl.pde,Button.pde,CheckBox.pde,Command.pde,TextBox.pdeから構成されているので,順に示そう.あと,Font(.vlwファイル)は別途生成してください.
では,CameraControl.pde.
Button.pde.
CheckBox.pde.
Command.pde.
TextBox.pde.
Processingで作ったアプリの使い方は...
Arduinoがつながっている状態で起動すると,VGAで画像の連続取得が自動的に動きます.
あとは以下の画像内の説明を見ながら使ってみてください.
では,お試しあれ.
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で画像の連続取得が自動的に動きます.
あとは以下の画像内の説明を見ながら使ってみてください.
では,お試しあれ.
SyntaxHighlighterの動きを確認してみる [雑記]
コードの表示にはなるべくSyntaxHighlighterを使うようにしているのだが,期待通りに表示されないことがあるので,いろいろ試してみる.
たとえば,前回のjavascriptのコードで,
というのがあるのだが,これをSyntaxHighlighterで表示させると,こうなる(以下はスクリーンショットの画像).
アンカータグが追加されてしまう.
auto-linksの設定が影響しているのかと思って,falseにしてみてもアンカータグは変わらない.
原因がわからない.
So-netブログでSyntaxHighlightを使う:おぼえがき:So-net blog
を参考にしてSo-netブログの設定を変えてみたのだが,それでも変わらない.
と思ってたら,記事の編集の,詳細設定に「URL自動リンク」というのがあって,リンクするになっていたのでリンクしないにしてみた.
どうやらこれが悪さをしていたようだ.
上記のブログで,「キーワード広告の表示が悪さをしている」って書いてあったのを読んでもしかしてと思ったら正解だった.うちはもとからキーワード広告はオフだったけど,似たような設定で探したら「URL自動リンク」に気がつけた.感謝.
毎回詳細設定で変更するのは面倒だ.
管理ページの[設定]->[記事の初期設定]->[URL自動リンク]に設定があるので,ここをリンクしないにしておく.
ちなみに,管理ページの[デザイン]->[テンプレート管理]->HTML編集のテンプレートは,こんな感じ.</body>の直前に以下のように挿入してる.
実際のSyntaxHighlighterの表示はこれ.
これが(アンカータグがなくて)ちゃんと表示されていればOKのはず.
もし,困っている人がいたら参考になるかも.
たとえば,前回のjavascriptのコードで,
var url = 'http://weather.livedoor.com/forecast/webservice/json/v1?city=' + city;
というのがあるのだが,これをSyntaxHighlighterで表示させると,こうなる(以下はスクリーンショットの画像).
アンカータグが追加されてしまう.
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のはず.
もし,困っている人がいたら参考になるかも.
Edisonに天気予報をしゃべらせてみる [edison]
前回,Open JTalkを使ってEdisonをしゃべらせることができるようになったので,今回は天気予報をしゃべらせてみる.
天気予報の情報はlivedoorの,お天気Webサービス - Weather Hacksからスクレイピングすることにする.
で,天気予報情報を取得したら,Open JTalkで合成音声を出力するようにしておく.
あとは,Edisonにスイッチをつけて,スイッチを押したら天気予報をしゃべるようにする.
今回は,node.jsでやってみた.
では,早速だがコードを示そう.
まず,main.js
次に,package.json
この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の内蔵プルアップを使用するので,単にスイッチをつなぐだけだ.
スイッチが押されたかどうかは割り込みで監視していて,押されたらあらかじめ生成してあるweather_info.wavを再生するようになっている.
最初はスイッチが押されたら天気予報を取得して,wather_info.wavを生成して再生するようにしようと思ったのだが,情報取得と音声生成の処理があるのでスイッチを押しても若干待たされる.というわけで,あらかじめwather_info.wavの作成まで定期的に行っておいて,スイッチが押されたら単に生成済みのweather_info.wavを再生するだけにしてある.これならスイッチを押したらすぐに天気予報をしゃべるようになる.
そしたら最後に,Edison起動時に自動実行するようにしておく.
まず,weather_infoのディレクトリに,start_weather_info.shというスクリプトを用意しておく.
で実行権限付与.
そしたら次に,/lib/systemd/system/下に,weather_info.serviceというファイルを作成してやる.
そしたら自動起動ONにして起動させてみる.
これでOK.
ただ,これで試してみると,Edisonが起動したときの実行1回目でエラーが出るようだ.どうやらWheather HacksにアクセスしようとするがDNSでエラーになっているっぽい.実行タイミングが早すぎる感じだ.weather_info.serviceのAfter=network.targetあたりを変更するといいような気がするのだが,具体的にどうすればよいのか分からない.
とりあえず,最初の1回はエラーになるが10分後の実行からは問題なく動作するので気にしないことにしておく.
というわけで,これで完了.
お試しあれ.
2015.05.02追記
SyntaxHighlighterを使ったらコードのvar url = の部分の表記がおかしくなったので修正.
天気予報の情報は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の内蔵プルアップを使用するので,単にスイッチをつなぐだけだ.
スイッチが押されたかどうかは割り込みで監視していて,押されたらあらかじめ生成してある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 = の部分の表記がおかしくなったので修正.
EdisonでOpen JTalkを使ってみる [edison]
前回,EdsionでUSB Audioを使えるようにしたので,今回はOpen JTalkを使ってEdisonにしゃべらせてみる.Open JTalkはフリーの日本語音声合成エンジンだ.
ではさっそくインストール.
まず,hts_engine APIをインストール.適当なディレクトリ(以下ではopenjtalk)を用意してダウンロード,展開してインストールしてやる.
次に,OpenJTalkのインストール
MMDAgentのサイトから音声データをダウンロード
Open JTalkの実行にはオプションが多いので,スクリプトを作成して実行する.
ちなみに,最後の-gはvolume(dB)を指定するオプションだ.
前回,EdisonにUSB Audioを接続したが,スピーカーが100均のパッシブスピーカーなので音がとても小さい.なので,Open JTalkの出力をあらかじめ上げておく.値を大きくすると出力の音量が大きくなる.あまり上げすぎると音が歪んでしまうので適当に調整してみてくださいな.
あとは作成したスクリプトに実行属性を設定して,
しゃべらせてみる.とりあえず「テストです」としゃべってもらうには,
これでa.wavファイルが作成される.
再生はうちの環境では
でOK.
お試しあれ.
ではさっそくインストール.
まず,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
EdisonでUSB Audioを使ってみる [edison]
Intel Edisonから音を出すために,USB Audioアダプタをつないでみる.
使ったのは,PLANEXのPL-US35APだ.
また,Edisonと接続する際,Intel Edison Breakout Board側のUSB端子はMicroBタイプ,USB AudioアダプタはAタイプなので,BUFFALOのBSMPC11C01BKを使ってつないでやる.
なお,EdisonにUSB機器をつなぐとUSBポートから電源供給できなくなるので,別途ACアダプタを使って電源供給してやる.
使ったのは,秋月の,
・ブレッドボード用DCジャックDIP化キット
・超小型スイッチングACアダプター12V1A 100V~240V GF12-US1210
で,ジャンパー線でEdisonのJ21に接続してやる.
あとはスピーカーだが,今回は100均のSeriaで売っていた,ZY-76というのを買ってみた.
そしたら,あとは接続するだけだ.
以前はEdisonでUSB Audioを使うにはカーネルのビルドが必要だったが,最新のYoctoイメージでは必要なくなった.最新のYocto complete imageは,ここからダウンロードできる.現時点では,edison-image-ww05-15.zipが最新のようだ.
では,動作テスト.
以下のサイトを参考にしながら適当なwavファイルを再生してみる.
・Intel Edisonを動かす(4) サウンド編 | IoT
すると,あっさりと動作した.
が,1点問題が...
ちょっとネットで調べれば分かるが,今回使った100均スピーカをアンプなしで使うと,音量がかなり小さい.USB Audioの出力を100%にしてなんとか聞こえる程度だ.
もう少し音量が得られるといいのだが...
とりあえず聞こえるので良しとしよう.
使ったのは,PLANEXの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
- 出版社/メーカー: バッファロー
- メディア: エレクトロニクス
なお,EdisonにUSB機器をつなぐとUSBポートから電源供給できなくなるので,別途ACアダプタを使って電源供給してやる.
使ったのは,秋月の,
・ブレッドボード用DCジャックDIP化キット
・超小型スイッチングACアダプター12V1A 100V~240V GF12-US1210
で,ジャンパー線でEdisonのJ21に接続してやる.
あとはスピーカーだが,今回は100均のSeriaで売っていた,ZY-76というのを買ってみた.
そしたら,あとは接続するだけだ.
以前はEdisonでUSB Audioを使うにはカーネルのビルドが必要だったが,最新のYoctoイメージでは必要なくなった.最新のYocto complete imageは,ここからダウンロードできる.現時点では,edison-image-ww05-15.zipが最新のようだ.
では,動作テスト.
以下のサイトを参考にしながら適当なwavファイルを再生してみる.
・Intel Edisonを動かす(4) サウンド編 | IoT
すると,あっさりと動作した.
が,1点問題が...
ちょっとネットで調べれば分かるが,今回使った100均スピーカをアンプなしで使うと,音量がかなり小さい.USB Audioの出力を100%にしてなんとか聞こえる程度だ.
もう少し音量が得られるといいのだが...
とりあえず聞こえるので良しとしよう.
タグ:USB Audio
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に送りたいので,以下のようにコマンドを入力してやる.
あとはコードだが,前回同様,適当なProjectを作って,package.jsonとmain.jsを以下のようにしてやる.
まず,package.json
次に,main.js
これをビルドして実行すると,IoT Analyticsに10秒おきに温度と湿度を送信するようになる.
で,しばらく動かしてみたときの結果は,こんな感じ.
送信したデータをグラフにしてくれる.あと,csvのエクスポートも可能だ.
非常にお手軽に温湿度を送ることができる.
お試しあれ.
今回は,その温湿度センサの値を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秒おきに温度と湿度を送信するようになる.
で,しばらく動かしてみたときの結果は,こんな感じ.
送信したデータをグラフにしてくれる.あと,csvのエクスポートも可能だ.
非常にお手軽に温湿度を送ることができる.
お試しあれ.
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
次に,main.js
そしたら,ビルドして,Edisonにアップロードして実行してやる.
そうすると,コンソールに温度と湿度が5秒おきに出力されるようになる.
AM2321はI2Cの通信シーケンスに癖があるのだが,とりあえず上記のコードで動作させることができるようだ.
MRAAライブラリがあるので,Arduinoで動かしたときと同じぐらい簡単.
お試しあれ.
今回は,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
Edisonに温湿度センサをつないでみる [edison]
Edisonに温湿度センサをつないでみた.
使用したのは,秋月電子で以前購入した,温湿度センサモジュールAM2321だ.
このセンサは,当初は中国語のデータシートしかなかったらしいが,今は英語のデータシートを確認することができる.
また,デジタル 温度・湿度センサー AM2321 を制御 - Neにマニュアルを日本語訳された方の情報があって,とても助かりました.
というわけで,センサの詳細は上記を参照してもらうとして...
このセンサ,買って手元に届いてから気がついたんだけど,ピン間隔が1.27mmになっていて,そのままではブレッドボードに挿せない.なので,ピンをやや曲げて間隔を広げつつ,ピンヘッダに半田付けしてやる.
半田付けがちょっと汚いが,こんな感じ.
これでブレッドボードに挿せる.
さて,ではEdisonとの接続だ.
AM2321は通信モードとしてI2Cか1-Wireが選べるんだけど,今回はI2Cを使うことにした.EdisonはIntel Edison Breakout Boardを使っているので,IOは1.8Vだ.なので以前の照度センサをつないでみたときと同様に,秋月のI2Cバス用双方向電圧レベル変換モジュール(PCA9306)を経由して接続してやる.
回路図はこんな感じになる.I2Cのレベル変換モジュールはジャンパカットが必要なので注意.
当初はこんな感じで接続していたが,AE-PCA9306ではVref2側を高電圧側にするべきである.詳細は,ここのコメント欄を参照.
なので,こちらの回路図のように接続すべきである.
で,接続するとこんな感じ.写真はAE-PCA9306のVref2側を低電圧側にしちゃったままのものだが,接続のイメージはつかめると思う.
Arduinoで動作させてみる.スケッチは,いろんなサイトを参考にしつつ作成した.これでシリアルに温度と湿度が5秒おきに送られてくる.
このセンサは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製だし.機会があれば試してみたいが,そんなにいくつも温湿度センサっていらないよなぁ...
使用したのは,秋月電子で以前購入した,温湿度センサモジュールAM2321だ.
このセンサは,当初は中国語のデータシートしかなかったらしいが,今は英語のデータシートを確認することができる.
また,デジタル 温度・湿度センサー AM2321 を制御 - Neにマニュアルを日本語訳された方の情報があって,とても助かりました.
というわけで,センサの詳細は上記を参照してもらうとして...
このセンサ,買って手元に届いてから気がついたんだけど,ピン間隔が1.27mmになっていて,そのままではブレッドボードに挿せない.なので,ピンをやや曲げて間隔を広げつつ,ピンヘッダに半田付けしてやる.
半田付けがちょっと汚いが,こんな感じ.
これでブレッドボードに挿せる.
さて,ではEdisonとの接続だ.
AM2321は通信モードとしてI2Cか1-Wireが選べるんだけど,今回はI2Cを使うことにした.EdisonはIntel Edison Breakout Boardを使っているので,IOは1.8Vだ.なので以前の照度センサをつないでみたときと同様に,秋月のI2Cバス用双方向電圧レベル変換モジュール(PCA9306)を経由して接続してやる.
回路図はこんな感じになる.I2Cのレベル変換モジュールはジャンパカットが必要なので注意.
当初はこんな感じで接続していたが,AE-PCA9306ではVref2側を高電圧側にするべきである.詳細は,ここのコメント欄を参照.
なので,こちらの回路図のように接続すべきである.
で,接続するとこんな感じ.写真はAE-PCA9306のVref2側を低電圧側にしちゃったままのものだが,接続のイメージはつかめると思う.
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製だし.機会があれば試してみたいが,そんなにいくつも温湿度センサっていらないよなぁ...
タグ:温湿度センサ
前の10件 | -