目次

温度表示装置量産

 温度表示装置の試作品ができたので、ビニルハウス1棟に
 3装置必要との要求が来たので、10個を量産します。

 Arduinoは、数種類販売されていますが
 試作装置よりも、安価に入手できる基板
 を選定しなければなりません。

 秋月電子のサイトで探すと、次の Arduino Pro Mini が
 用意されていました。



 プロトタイプの開発で、LCDは1行16桁で
 温度表示ができるとわかったので、1行で
 表示するデバイスを探しました。




 実運用で1行16桁と2行16桁のどちらがよいか
 判断しますが、デバイスの有無は知っておいて
 損はないと調べておきました。

 温度センサーは、次のように容器に入れ、吊り下げ
 られるようにしています。



 マイコンと電池は、タッパーに入れて使います。




 どのセンサーを接続するのかが、一目でわかる
 ように、ドットスティックをつけてます。

 センサー接続には、フォンジャックを利用。



 センサーのケーブルには、フォンプラグを接続し
 マイコンとの着脱が簡単になるようにします。




 先頭、中段、根元は、Vcc、Vout、GNDと
 半田付けし、マイコンとの接続に戸惑う
 ことがないようにします。

 温度センサーは、高さで測定地点を変えるので
 センサーを入れる容器のキャップ色で、高さを
 指定できるようにしておきます。



 電源は、ニッケル水素電池を8本使うことに。



 鉛蓄電池では、専用充電器を用意しなければならず
 充電器をホームセンターでも入手できる2次電池を
 選択します。

 24時間の温度計測をしたいと要望が来たので
 リアルタイムクロックを実装。

 IICバスで接続できるリアルタイムクロックを
 選びました。



 温度と日照の相関を知るために、ビニルハウス
 内部が明るいのか暗いのかを、照度センサーで
 判断できるようにしておきます。



 すべての部品が揃ったので、ブロック図で
 接続を確認。



 照度センサーを使うと、ビニルハウス内の
 温度上昇との相関を知ることができます。

 リアルタイムクロックを用意したので、2分ごとに
 日付、時刻、温度、明るさをEEPROMに保存。

 EEPROMの容量が、8kバイトであれば1レコード
 =8バイトとすると、約35時間の計測で1日半
 のデータ保存が可能になります。

 Arduinoスケッチは、以下。

/*
  thermoa.ino

  Pin assignment

  PORTB
    PB5 (output) LED
    PB4 (output) --
    PB3 (output) --
    PB2 (output) --
    PB1 (output) --
    PB0 (output) --
 
  PORTC
    PC5 (output) SCL(IIC)
    PC4 (output) SDA(IIC)
    PC3 (output) light sensor
    PC2 (output) sensor 2(TOP)
    PC1 (output) sensor 1(MIDDLE)
    PC0 (output) sensor 0(BOTTOM)
 
  PORTD
    PD7 (output) D7
    PD6 (output) D6
    PD5 (output) D5
    PD4 (output) D4
    PD3 (output) E
    PD2 (output) RS
    PD1 (output) TxD
    PD0 (input)  RxD
*/
#include <MsTimer2.h>
#include <LiquidCrystal.h>
#include <Wire.h>

#define OFF 0
#define ON  OFF+1

/* pin assignment */
#define BOTTOM 0
#define MIDDLE 1
#define TOP    2
#define LAST   3

#define LED_BIT 5

/* Real Time Clock address */
#define ROM_ADRS  0x50
#define RTC_ADRS  0x32

#define XINTERVAL 1500
#define XLIMIT    35
#define YLIMIT    170
#define RSIZE     8
#define SYS_ADRS  0x1f80

#define BSIZE     256

/* LCD rs , e , D4 , D5 , D6 , D7 */
LiquidCrystal lcd( 2, 3, 4, 5, 6, 7 );

/* function prototype */
void update_trigger();
void show_help();
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
byte getHex(char x);
void rs_print(int x);
void makeLCDString();
void oneShot();
void put_light_dark();
byte convBCD2HEX(byte x);
byte convHEX2BCD(byte x);
void putDateTime();
void setDateTime();
void initRtc();
void showRtc();
void getDateTime();
void saveInfo();
void put_eeprom(word xadr,byte xdat);
byte get_eeprom(word xadr);
void updateSflag();

/* variables */
boolean tflag ;
boolean eflag ;
boolean uflag ;
boolean wflag ;
boolean oflag ;
boolean dflag ; /* day or night */
boolean sflag ; /* store flag */

char sbuf[16] ;
byte sindex ;

byte cmd ;
byte xcnt ;
int  adv ;
int  tv ;
int  temp[4] ;
byte tempbcd[3] ;
char lineU[17];
char lineL[17];
byte prv ;
word rom_address ;
byte xah ;
byte xal ;
char daytime[8];
byte xdatx[6];

void setup()
{
  /* initialize serial */
  Serial.begin(9600);
  sindex = 0 ;
  rs_puts("Hello !");
  crlf();
  /* clear flags */
  tflag = OFF ;
  eflag = OFF ;
  uflag = OFF ;
  wflag = OFF ;
  oflag = ON ;
  dflag = OFF ;
  sflag = OFF ;
  /* initialize port values */
  PORTB = 0x00 ;
  PORTC = 0x00 ;
  PORTD = 0x00 ;
  /* initialize port direction */
  DDRB = 0xff ;
  DDRC = 0x00 ;
  DDRD = 0xfe ;
  /* variables */
  xcnt = 0 ;
  /* 1500ms period */
  MsTimer2::set(XINTERVAL,update_trigger);
  /* enable */ 
  MsTimer2::start();
  /* initialize LCD */
  lcd.begin( 16, 2 );
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Measure");
  lcd.setCursor(0, 1);
  lcd.print("Hello, world!");
  /* LCD string default */
  for ( byte ii = 0 ; ii < 16 ; ii++ ) {
    *(lineL+ii) = ' ' ;
    *(lineU+ii) = ' ' ;
  }
  *(lineL+16) = '\0';
  *(lineU+ 0) = 'T';
  *(lineU+ 4) = '|';
  *(lineU+ 5) = 'M';
  *(lineU+ 9) = '|';
  *(lineU+10) = 'B';
  *(lineU+14) = '|';
  *(lineU+15) = 'N';
  *(lineU+16) = '\0';
  /* Real Time Clock configration */
  Wire.begin();
  initRtc();
  /* others */
  prv = 70 ;
  rom_address = 0;
}

void loop()
{
  /* one time handling */
  if ( oflag == ON ) {
    /* clear flag */
    oflag = OFF ;
    /* default */
    lcd.setCursor(0,1);
    lcd.print("Namara Firm    ");
  }
  /* command interpreter */
  if ( uflag == ON ) {
    /* clear flag */
    uflag = OFF ;
    /* get command */
    cmd = *(sbuf+0) ;
    /* help */
    if ( cmd == '?' ) { show_help(); }
    /* enable */
    if ( cmd == 'G' ) { eflag = ON ; }
    /* disable */
    if ( cmd == 'g' ) { eflag = OFF; }
    /* one shot */
    if ( cmd == 'T' ) { oneShot(); }
    /* adjust real time clock */
    if ( cmd == 'A' ) { setDateTime(); }
    /* show Months Days hour minute */
    if ( cmd == 'a' ) { showRtc(); }
    /* new line */
    crlf() ;
  }
  /* timer handling */
  if ( tflag == ON ) {
    /* clear flag */
    tflag = OFF ;
    /* get current Date and Time */
    getDateTime();
    /* light or dark */
    put_light_dark();
    /* get thermometer */
    for ( int ii = TOP ; ii > -1 ; ii-- ) {
      /* get data */
      adv = analogRead(ii);
      /* convert (voltage) */
      tv = map(adv,0,1023,0,5000); 
      /* convert (temparature) */
      *(temp+ii) = map(tv,300,1600,-30,100);
      /* show ( LCD ) */
      makeLCDString();
      lcd.setCursor(0,0); lcd.print( lineU );
      lcd.setCursor(0,1); lcd.print( lineL );
    }
    /* show ( serial )*/
    if ( eflag == ON ) { oneShot(); }
    /* judge  warning */
    wflag = OFF ;
    if ( *(temp+BOTTOM) > XLIMIT ) { wflag = ON ; }
    if ( *(temp+MIDDLE) > XLIMIT ) { wflag = ON ; }
    if ( *(temp+TOP)    > XLIMIT ) { wflag = ON ; }
    /* convert BCD */
    for ( byte i = 0 ; i < 3 ; i++ ) {
      *(tempbcd + i) = convHEX2BCD( *(temp+i) );
    }
    /* */
    updateSflag();
  }
  /* store data to EEPROM every 2 minutes */
  saveInfo();
}

void update_trigger()
{
  /* set flag */
  tflag = ON ;
  /* inrement */
  xcnt++ ;
  /* flashing */
  PORTB &= ~(1 << LED_BIT) ;
  if ( wflag == ON ) {
    if ( xcnt & ON ) { PORTB |= (1 << LED_BIT) ; }
  }
}

void show_help()
{
  rs_puts("? help")             ; crlf();
  rs_puts("G enable")           ; crlf();
  rs_puts("g disable")          ; crlf();
  rs_puts("T one shot")         ; crlf();
  rs_puts("A set date time")    ; crlf();
  rs_puts("usage:AYYMMDDhhmmss"); crlf();
  rs_puts("a show date time")   ; 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');
}

byte getHex(char x)
{
  byte result ;
  /* default */
  result = 0 ;
  /* judge */
  if ( '0' <= x && x <= '9' ) { result = x - '0' ; }
  if ( 'A' <= x && x <= 'F' ) { result = x - 'A' + 10 ; }
  if ( 'a' <= x && x <= 'f' ) { result = x - 'a' + 10 ; }
  
  return result ;
}

void rs_print(int x)
{
  char msg[5] ;
  int  tmp ;
  /* default */
  *(msg+4) = '\0' ;
  /* sign */
  *(msg+0) = ' ' ;
  tmp = x ;
  if ( x < 0 ) {
    tmp *= (-1) ;
    *(msg+0) = '-' ; 
  }
  /* generate code */
  *(msg+3) = tmp % 10 + '0' ; tmp /= 10 ;
  *(msg+2) = tmp % 10 + '0' ; tmp /= 10 ;
  *(msg+1) = tmp % 10 + '0' ;
  /* zero surpress */
  if ( *(msg+1) == '0' ) {
    *(msg+1) = ' ' ;
    if ( *(msg+2) == '0' ) { *(msg+2) = ' ' ; }
  }
  /* show */
  rs_puts( msg );
}

void makeLCDString()
{
  int tmp ;
  byte i ;
  byte j ;
  /* upper */
  for ( i = BOTTOM ; i < LAST ; i++ ) {
    /* get value */
    tmp = *(temp+TOP-i) ;
    /* calculate offset */
    j = (i << 2) + i ;
    /* sign handling */
    *(lineU+1+j) = ' ' ;
    if ( tmp < 0 ) {
      *(lineU+1+j) = '-' ;
      tmp *= (-1); 
    }
    /* x1 */
    *(lineU+3+j) = tmp % 10 + '0' ;
    /* x10 */
    tmp /= 10 ;
    *(lineU+2+j) = tmp % 10 + '0' ;
  }
  /* day or night */
  *(lineU+15) = '_' ;
  if ( dflag ) { *(lineU+15) = '*' ; }
  /* lower */
  for ( i = 0 ; i < 6 ; i++ ) {
    tmp =  *(xdatx+i) ;
    j = i + i ;
    *(lineL+j+0) = ((tmp >> 4) & 15) + '0' ;
    *(lineL+j+1) = (tmp & 15) + '0' ;
  }
}

void oneShot()
{
  rs_putchar('T'); rs_print( *(temp+TOP)    ); rs_puts(" | ");
  rs_putchar('M'); rs_print( *(temp+MIDDLE) ); rs_puts(" | ");
  rs_putchar('B'); rs_print( *(temp+BOTTOM) ); rs_puts(" | ");
  rs_print( *(temp+LAST) ); crlf();
  /* showRtc(); */
}

byte convBCD2HEX(byte x)
{
  byte result ;

  /* lower */
  result = (x & 15) ;
  /* upper */
  result += (((x >> 4) & 15) * 10);

  return result ;
}

byte convHEX2BCD(byte x)
{
  byte result ;

  /* lower */
  result = (x % 10);
  /* upper */
  result += ((x / 10) << 4);

  return result ;
}

void put_light_dark()
{
  /* get state */
  adv = analogRead( LAST );
  *(temp+3) = adv ;
  /* default (night) */
  dflag = OFF ;
  /* day */
  if ( adv > YLIMIT ) { dflag = ON ; }
}

void putDateTime()
{
  Wire.beginTransmission(RTC_ADRS);
  Wire.write(0x00); /* start register addess */
  Wire.write( *(xdatx+5) );
  Wire.write( *(xdatx+4) );
  Wire.write( *(xdatx+3) );
  Wire.write(0x00); /* Sunday */
  Wire.write( *(xdatx+2) );
  Wire.write( *(xdatx+1) );
  Wire.write( *(xdatx+0) );
  Wire.endTransmission();
  /* wait */
  delay(1);
}

void setDateTime()
{
  byte ptr ;
  byte ii ;
  byte xdt[6] ;
  byte xtmp ;
  word yadr ;
  /* set base pointer */
  ptr = 1 ;
  /* generate BCD code */
  for ( ii = 0 ; ii < 6 ; ii++ ) {
    /* x10 */
    xtmp = getHex( *(sbuf+ptr) ) ;
    xtmp *= 10 ;
    ptr++ ;
    /* x1 */
    xtmp += getHex( *(sbuf+ptr) );
    *(xdt+ii) = convHEX2BCD( xtmp );
    ptr++ ;
    /* copy */
    *(xdatx+ii) = *(xdt+ii) ;
  }
  /* store RTC */
  putDateTime() ;
  /* store EEPROM */
  yadr = SYS_ADRS ;
  for ( ii = 0 ; ii < 6 ; ii++ ) {
    put_eeprom(yadr,*(xdatx+ii));
    yadr++ ;
  }
  /* set ROM entry addess */
  rom_address = 0 ;
}

void initRtc()
{
  /* start condition */
  Wire.beginTransmission(RTC_ADRS);
  Wire.write(0xE0);
  Wire.write(0x20); /* 24hour Mode */
  Wire.write(0x00); /* PON Clear */
  /* stop condition */
  Wire.endTransmission();
  /* wait */
  delay(1);
  /* initialize */
  *(xdatx+0) = 0x18 ;
  *(xdatx+1) = 0x01 ;
  *(xdatx+2) = 0x01 ;
  *(xdatx+3) = 0x00 ;
  *(xdatx+4) = 0x00 ;
  *(xdatx+5) = 0x00 ;
  /* store */
  putDateTime();
}

void showRtc()
{
  byte ii ;
  byte tmp ;
  byte tmpH ;
  byte tmpL ;
  /* show */
  for ( ii = 0 ; ii < 6 ; ii++ ) {
    tmp = *(xdatx+ii) ;
    tmpL = (tmp & 15 ) + '0' ;
    tmp >>= 4 ;
    tmpH = tmp + '0' ;
    rs_putchar( tmpH );
    rs_putchar( tmpL );
    if ( ii < 2  ) { rs_putchar('-'); }
    if ( ii == 2 ) { rs_putchar(' '); }
    if ( ii == 3 ) { rs_putchar(':'); }
    if ( ii == 4 ) { rs_putchar(':'); }
  }
  crlf();
}

void getDateTime()
{
  Wire.requestFrom(RTC_ADRS,8);
  Wire.read(); /* 0x0f (fixed dummy) */
  *(xdatx+5) = Wire.read(); /* ss */
  *(xdatx+4) = Wire.read(); /* mm */
  *(xdatx+3) = Wire.read(); /* hh */
  Wire.read(); /* weekday */
  *(xdatx+2) = Wire.read(); /* DD */
  *(xdatx+1) = Wire.read(); /* MM */
  *(xdatx+0) = Wire.read(); /* YY */
}

void saveInfo()
{
  byte xtmp[8];
  word yadr ;
  byte ii ;
  byte mm ;
  /* copy */
  *(xtmp+0) = *(xdatx+1);      /* MM */
  *(xtmp+1) = *(xdatx+2);      /* DD */
  *(xtmp+2) = *(xdatx+3);      /* hh */
  *(xtmp+3) = *(xdatx+4);      /* mm */
  *(xtmp+4) = *(tempbcd+2);    /* TOP */
  *(xtmp+5) = *(tempbcd+1);    /* MIDDLE */
  *(xtmp+6) = *(tempbcd+0);    /* BOTTOM */
  *(xtmp+7) = *(temp+3) >> 2 ; /* photo  */
  /* get minutes from Real Time Clock */
  mm = convBCD2HEX( *(xdatx+4) ) ;
  /* judge */
  if ( prv != mm ) {
    /* update */
    prv = mm ;
    /* ? 0x00 0x02 0x04 ... 0x58 */
    if ( (mm & ON) == 0 ) {
      /* debug */
      rs_putchar('='); crlf();
      /* generate address */
      yadr = rom_address ;
      /* store */
      for ( ii = 0 ; ii < 8 ; ii++ ) {
        /* put 1 byte */
        put_eeprom( yadr , *(xtmp+ii) );
        /* update address */
        yadr++ ; 
      }
      /* update ROM address */
      rom_address += RSIZE ;
      /* roll over reset */
      if ( rom_address > SYS_ADRS ) { rom_address = 0 ; }
    }
  }
}

void put_eeprom(word xadr,byte xdat)
{
  byte adh ;
  byte adl ;
  byte xx ;
  xx = xdat ;
  /* separate address */
  adl = xadr & 0xff ;
  adh = (xadr >> 8) & 0x1f ;
  /* write 1 byte */
  Wire.beginTransmission(ROM_ADRS);
  Wire.write(adh); /* upper address */
  Wire.write(adl); /* lower address */
  Wire.write(xx ); /* data */
  Wire.endTransmission();
  /* wait 5ms */
  delay(5);
}

byte get_eeprom(word xadr)
{
  byte adh ;
  byte adl ;
  byte result ;
  /* default */
  result = BSIZE ;
  /* separate address */
  adl = xadr & 0xff ;
  adh = (xadr >> 8) & 0x1f ;
  /* impress address */
  Wire.beginTransmission(ROM_ADRS);
  Wire.write(adh); /* upper address */
  Wire.write(adl); /* lower address */
  Wire.endTransmission();
  /* read 1 byte */
  Wire.requestFrom(ROM_ADRS,1);
  if ( Wire.available() ) { result = Wire.read() ; }

  return result ;
}

void updateSflag()
{
  byte mm ;
  byte target ;
  /* judge */
  if ( sflag == ON ) return ;
  /* update */
  /* get start time */
  mm = convBCD2HEX( get_eeprom( SYS_ADRS + 4 ) ) ;
  /* calculate */
  target = mm + 5 ;
  if ( target >= 60 ) { target %= 60 ; }
  /* get current time */
  mm = convBCD2HEX( *(xdatx+4) ) ;
  /* judge */
  if ( abs(target - mm) >= 5 ) { sflag = ON ; }
}

/* 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 ;
    }
  }
}

 EEPROMへ格納する情報は、フォーマットを次のようにしました。

 月、日、時、分、3点の温度値、日照換算値

 日照換算値は、実際の値の1/4にしています。
 日照換算値を除いた7データは、BCD形式での
 格納で、1バイト内に値が入る仕様。

 実際のデータは、16進数で次のように並べられます。

05 20 14 22 24 25 25 80 05 20 14 24 24 25 25 81
05 20 14 26 24 25 25 81 05 20 14 28 24 25 25 80

 8バイトで1レコードにし、1行16バイトとすると
 2レコードが見えるようになります。

 EEPROMに8kバイト品を使っているので
 2分周期で1レコードを格納。

 コマンドは、以下を用意。

 コマンドは、端末ソフトを利用すれば
 操作できます。

 LCDは、2行16桁仕様のデバイスでは
  上段 − 計測温度値と日照状態を表示
  下段 − 年月日時分秒を表示
 とします。

 次のシーケンスを使います。
  1. 温度センサー、LCD、RTCとEEPROMを接続
  2. 電源投入
  3. LCDに年月日で、000101と表示されるのを確認
  4. 'A'コマンドで、年月日時分秒を設定
  5. 電源を入れたままで、ビニルハウスの中に設置
 測定中止は、電源を落とすだけ。  動作状態を端末ソフトでモニタすると、以下。  RTC(Real Time Clock)とEEPROMは、一枚の基板に  実装して、着脱しやすくしておきました。

目次

inserted by FC2 system