目次
前
次
データ集計装置
温度計測装置は、常時温度を測定し、LCDに表示します。
温度計測装置は、単独での動作。
1日のうちの温度変化をグラフにするため
日付、時刻と温度データを集めておきます。
温度計測装置は、EEPROMにデータを格納して
いるので、データ集計装置を作成して情報を
取得。
データ集計装置に必要な機能を列挙すると、以下。
- 温度計測装置のEEPROMにアクセス
- 日付、時刻と温度データを取り出し
- ホストコンピュータからの要求で、温度データを転送
EEPROMは、温度計測装置から外せるようになっています。
EEPROM基板をArduinoに接続し、EEPROMのデータだけを
吸い上げれば、データ集計の元情報を取得できます。
秋月電子のArduino基板を使って、データ集計用の
専用装置を作成すればよいはず。
データ集計専用のArduinoスケッチを作成します。
端末ソフトでArduinoに接続したEEPROMから
データを取得するだけとします。
用意したコマンドは、以下。
- ? コマンドリスト表示
- D バッファ内容表示
- L EEPROMの情報をロード
このコマンドインタプリタを内蔵したスケッチは、以下。
/*
romtstx.ino
PORTB
PB5 (output) LED
PB4 (output) --
PB3 (output) --
PB2 (output) --
PB1 (output) --
PB0 (output) --
PORTC
PC5 (output) SCL
PC4 (output) SDA
PC3 (output) --
PC2 (output) --
PC1 (output) --
PC0 (output) --
*/
#include <Wire.h>
#define OFF 0
#define ON OFF+1
#define BSIZE 256
#define ROM_ADRS 0x50
#define LED_BIT 5
boolean uflag ;
/* variables */
char sbuf[4] ;
byte sindex ;
byte cmd ;
byte ydat[32];
byte zdat[BSIZE] ;
void update_trigger();
void show_help();
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void get_page_eeprom(word xadr);
void loadEEPROM();
void showBuffer();
char getHex(byte x);
void setup()
{
Serial.begin(9600);
rs_puts("Hello !");
crlf();
/* port value */
PORTB = 0x00 ;
PORTC = 0x00 ;
PORTD = 0x00 ;
/* port direction */
DDRB = 0xff ;
DDRC = 0xf0 ;
DDRD = 0xfe ;
/* flags */
uflag = OFF ;
/* enable IIC bus */
Wire.begin();
}
void loop()
{
/* command interpreter */
if ( uflag == ON ) {
/* clear flag */
uflag = OFF ;
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help(); }
/* show EEPROM context */
if ( cmd == 'D' ) { showBuffer(); }
/* load data from EEPROM */
if ( cmd == 'L' ) { loadEEPROM(); }
/* new line */
crlf() ;
}
}
void show_help()
{
rs_puts("? help") ; crlf();
rs_puts("D display buffer") ; crlf();
rs_puts("L load data") ; crlf();
}
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
void get_page_eeprom(word xadr)
{
byte adh ;
byte adl ;
/* separate address */
adl = xadr & 0xff ;
adh = (xadr >> 8) & 0x1f ;
/* write entry address */
Wire.beginTransmission(ROM_ADRS);
Wire.write(adh); /* upper address */
Wire.write(adl); /* lower address */
Wire.endTransmission();
/* get 32 bytes */
Wire.requestFrom(ROM_ADRS,32);
/* copy */
if ( Wire.available() ) {
for ( adl = 0 ; adl < 32 ; adl++ ) {
*(ydat+adl) = Wire.read();
}
}
/* show */
for ( adl = 0 ; adl < 32 ; adl++ ) {
adh = *(ydat+adl) ;
rs_putchar( getHex( (adh >> 4) & 15 ) );
rs_putchar( getHex( adh & 15 ) );
rs_putchar(' ');
if ( (adl % 16) == 15 ) { crlf(); }
}
/* wait */
delay(100);
}
void loadEEPROM()
{
word i ;
byte j ;
word xad ;
for ( i = 0 ; i < 256 ; i++ ) {
xad = (i << 5) ;
get_page_eeprom( xad ) ;
}
}
void showBuffer()
{
byte tmp ;
word ii ;
char msg[2] ;
for ( ii = 0 ; ii < BSIZE ; ii++ ) {
/* get */
tmp = *(zdat+ii) ;
/* lower */
*(msg+1) = getHex( tmp & 15 );
/* upper */
*(msg+0) = getHex( (tmp >> 4) & 15 );
/* impress */
rs_putchar( *(msg+0) );
rs_putchar( *(msg+1) );
rs_putchar( ' ' );
/* new line */
if ( (ii & 15) == 15 ) { crlf(); }
}
}
char getHex(byte x)
{
char result ;
/* default */
result = '0' ;
/* judge */
if ( 0 <= x && x <= 9 ) { result = x + '0' ; }
if ( x > 9 ) { result = x - 10 + 'A' ; }
return result ;
}
/* receive interrupt */
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
uflag = ON ;
}
}
}
コマンド'L'は、関数loadEEPROMに処理を一任しています。
関数loadEEPROMは、関数get_page_eepromを利用して
ページ単位(1ページ=32バイト)で配列にEEPROM
の情報を格納後、表示させます。
TeraTermで操作すると、次のようになります。
このままでは、ファイルに転送できないので
クリップボードを使わない方法で、ファイル
に情報を格納します。
コマンド'L'で表示している内容は、ログを取る
機能でファイルに転送。
ログでは、操作も記録されるので、次のようになります。
L
06 01 16 34 22 40 18 85 06 01 16 36 22 40 18 85
06 01 16 38 22 39 17 84 06 01 16 40 22 40 18 85
06 01 16 42 22 39 18 84 06 01 16 44 22 39 17 84
06 01 16 46 23 39 17 84 06 01 16 48 23 40 18 85
06 01 16 50 22 40 21 85 06 01 16 52 22 40 24 85
06 01 16 54 22 40 25 85 06 01 16 56 21 40 26 85
06 01 16 58 21 41 26 85 06 01 17 00 22 40 26 85
06 01 17 02 21 41 25 85 06 01 17 04 22 40 24 85
06 01 17 06 22 40 22 85 06 01 17 08 22 40 21 85
06 01 17 10 22 40 21 85 06 01 17 12 22 40 21 85
テキストファイルになっているので、テキストエディタで
操作は削除すれば、1レコード(8バイト)の情報が1行
に2つあるとわかります。
spreadsheetでグラフ描画がしたいので、CSV形式に
変換して、扱いやすくしていきます。
テキストデータの整形に利用するスクリプト言語では
AWKが最も使いやすいので、AWKスクリプトを作成して
いきます。
1行を1レコードに
1行を1レコードにするには、8フィールドのデータを
表示したなら、改行することで実現。
{
for ( i = 1 ; i < 17 ; i++ ) {
printf("%s ",$i)
if ( i == 8 || i == 16 ) printf("\n")
}
}
日付等の情報以外は表示しない
1フィールド目が、FFであれば、1レコード表示を
スキップすればよいはず。
{
if ( $1 != "FF" ) {
for ( i = 1 ; i < 17 ; i++ ) {
printf("%s ",$i)
if ( i == 8 || i == 16 ) printf("\n")
}
}
}
日付をわかりやすく見せる
1フィールドと2フィールドの間に、「/」を入れて対応。
{
if ( $1 != "FF" ) {
for ( i = 1 ; i < 17 ; i++ ) {
printf("%s",$i)
if ( i == 1 || i == 9 ) printf("/")
if ( i == 8 || i == 16 ) printf("\n")
}
}
}
1レコードは、8フィールドなので、2レコード目の
処理も入れてます。
時刻をわかりやすく見せる
3フィールドと4フィールドの間に、「:」を入れて対応。
{
if ( $1 != "FF" ) {
for ( i = 1 ; i < 17 ; i++ ) {
printf("%s",$i)
if ( i == 1 || i == 9 ) printf("/")
if ( i == 3 || i == 11 ) printf(":")
if ( i == 8 || i == 16 ) printf("\n")
}
}
}
1レコードは、8フィールドなので、2レコード目の
処理もORで追加してます。
CSV形式になるようにカンマを追加
日付、時刻を除外したフィールドに「,」を入れて対応。
{
if ( $1 != "FF" ) {
for ( i = 1 ; i < 17 ; i++ ) {
printf("%s",$i)
if ( i == 1 || i == 9 ) printf("/")
if ( i == 2 || i == 10 ) printf(",")
if ( i == 3 || i == 11 ) printf(":")
if ( 3 < i && i < 8 ) printf(",")
if ( 11 < i && i < 16 ) printf(",")
if ( i == 8 || i == 16 ) printf("\n")
}
}
}
このスクリプトを利用したときに、生成されるCSV形式ファイル
の内容は、以下となります。
06/01,16:34,22,40,18,85
06/01,16:36,22,40,18,85
06/01,16:38,22,39,17,84
06/01,16:40,22,40,18,85
06/01,16:42,22,39,18,84
06/01,16:44,22,39,17,84
06/01,16:46,23,39,17,84
06/01,16:48,23,40,18,85
06/01,16:50,22,40,21,85
照度値を生値に戻す
照度値は、1バイトに収容するために、実際の値の1/4に
しています。さらに16進数にしてあるので生値に戻しておき
ます。
16進数2けたは、文字列になっているので関数を用意し
それを呼び出して使うだけとします。
16進数を10進数に変換する関数は、if文で15回の判定を
使います。
function getValue(x)
{
result = 0
if ( x == "1" ) { result = 1 }
if ( x == "2" ) { result = 2 }
if ( x == "3" ) { result = 3 }
if ( x == "4" ) { result = 4 }
if ( x == "5" ) { result = 5 }
if ( x == "6" ) { result = 6 }
if ( x == "7" ) { result = 7 }
if ( x == "8" ) { result = 8 }
if ( x == "9" ) { result = 9 }
if ( x == "A" ) { result = 10 }
if ( x == "B" ) { result = 11 }
if ( x == "C" ) { result = 12 }
if ( x == "D" ) { result = 13 }
if ( x == "E" ) { result = 14 }
if ( x == "F" ) { result = 15 }
return result
}
文字列を分割するには、関数substrを使えばよいので
スクリプトは、次のようにまとめられます。
function getValue(x)
{
result = 0
if ( x == "1" ) { result = 1 }
if ( x == "2" ) { result = 2 }
if ( x == "3" ) { result = 3 }
if ( x == "4" ) { result = 4 }
if ( x == "5" ) { result = 5 }
if ( x == "6" ) { result = 6 }
if ( x == "7" ) { result = 7 }
if ( x == "8" ) { result = 8 }
if ( x == "9" ) { result = 9 }
if ( x == "A" ) { result = 10 }
if ( x == "B" ) { result = 11 }
if ( x == "C" ) { result = 12 }
if ( x == "D" ) { result = 13 }
if ( x == "E" ) { result = 14 }
if ( x == "F" ) { result = 15 }
return result
}
{
if ( $1 != "FF" ) {
for ( i = 1 ; i < 17 ; i++ ) {
if ( i != 8 && i != 16 ) {
printf("%s",$i)
}
if ( i == 1 || i == 9 ) printf("/")
if ( i == 2 || i == 10 ) printf(",")
if ( i == 3 || i == 11 ) printf(":")
if ( 3 < i && i < 8 ) printf(",")
if ( 11 < i && i < 16 ) printf(",")
if ( i == 8 || i == 16 ) {
num = getValue(substr($i,1,1)) * 16 + getValue(substr($i,2,1))
printf("%d\n",4*num)
}
}
}
}
このスクリプトを利用したときに、生成されるCSV形式ファイル
の内容は、以下となります。
06/01,16:34,22,40,18,532
06/01,16:36,22,40,18,532
06/01,16:38,22,39,17,528
06/01,16:40,22,40,18,532
06/01,16:42,22,39,18,528
06/01,16:44,22,39,17,528
CSV形式ファイルをspreadsheetに取り込んで
表示すると、次のようになります。
色づけした線は、次の物理量を表現
- 青 上段の温度変化
- 黄 下段の温度変化
- 緑 照度変化
グラフからわかることをリスト。
- 照度は、日没とともに一気に下がる。
- 上段の温度変化は少ない
- 下段の温度は日没とともに一気に下がる
- 上段と下段の温度差は10度近い
目次
前
次