目次

ZigBeeネットワーク構築

 ビニルハウス、選果場、休息用家屋は、それなりに
 離れているので、センサーネットワークを構築して
 温度をリモート監視することが、本システムの要件
 にありました。

 センサーネットワークとして、次の3種を比較検討。

 これらのネットワークを構築に必要なモジュールで
 手持ちの調べてみます。

 Wifi

  Wi−FiモジュールESP−WROOM−02
  (¥650)



 Bluetooth

  マイクロチップBluetoothモジュールRN42−I/RM
  (¥1、650)



 ZigBee

  XBee ZB S2Cモジュール ワイヤーアンテナタイプ
  (¥2、500)
  <長距離の通信では、ワイヤーアンテナタイプが必須>



 Wifiは、通信が途切れることがあり選択肢から外します。
 Bluetoothは、室内通信が主体なので、こちらも選択肢
 より除外。

 屋外での通信が可能となると、ZigBeeしか残らないことに。

 ZigBeeの電波到達距離は60m程度なので、Personal Computer
 がある休息家屋とビニルハウスの端までは、充分カバー可能。

 ネットワークトポロジーは、次のようにスター型に近くなります。



 ZigBeeのネットワークには、coordinator、router、end deviceの
 3種のノードが存在します。

 coordinatorは、ネットワークのリーダとして機能し
 routerは、coordinatorと通信する他に、通信不能な
 ノード(router、end device)の情報交換の中継役を担当
 します。

 今回のZigBeeネットワークは、coordinatorにPersonal Computerを
 接続して使います。




 ZigBeeネットワークのノードであるXBeeには、シリアル
 インタフェースと無線が具備されているので、遠隔にある
 router、end deviceとの通信は、無線を利用。

 router、end deviceには、Arduinoを接続して
 XBeeを無線/シリアル変換器として使います。

 システムイメージは、以下。



 Arduinoスケッチには、シリアルで情報を取得する
 コマンドが用意されています。
 TeraTermでの操作では、次のようにデータを返して
 きます。




 Personal Computerのアプリケーションは、2分周期で
 ビニルハウス内にある温度測定装置からデータを取得
 して、ファイルに保存するとともにグラフを描画する
 機能を持てばよいとします。

 ビニルハウス内の温度計測装置では、ArduinoにXBeeを
 接続するとともに、電源を強化しておきます。

 XBeeは、送信と受信で50mAほどの電流を必要とするので
 2次電池では、1日いっぱいの計測ができません。

 電源は、5000mAhの容量をもつ、モバイルバッテリーを利用。



 モバイルバッテリーは負荷が軽いとき、自動でパワーオフ
 してしまうので、2秒周期で1秒間だけ電流を5mAだけ流す
 アダプタを入れます。



 ワンチップマイコンPIC12F1501を利用しました。

 ArduinoとXBeeを1枚の基板に実装すると、以下。



 モバイルバッテリーと並べても、A4用紙の面積に
 収まります。

 ZigBeeのネットワークを構築する前に
 トランスペアラントモードで2台1組
 による通信ができることを確認します。

 トポロジーは、以下。



 時間ごとのデータ取得と保存は、Personal Computerの
 アプリケーションが担うとして、Arduinoのスケッチは
 次のようにしました。

/*
  thermog.ino

  Pin assignment

  PORTB
    PB5 (output) LED
    PB4 (output) --
    PB3 (output) --
    PB2 (output) --
    PB1 (output) --
    PB0 (output) --
 
  PORTC
    PC5 (output)
    PC4 (output)
    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>

#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

#define XINTERVAL 1500
#define XLIMIT    35
#define YLIMIT    170
#define RSIZE     8

#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 ledProc(byte x);
void put_light_dark();
byte convBCD2HEX(byte x);
byte convHEX2BCD(byte x);

/* variables */
boolean tflag ;
boolean uflag ;
boolean oflag ;
boolean dflag ; /* day or night */

char sbuf[8] ;
byte sindex ;

byte cmd ;
int  adv ;
int  tv ;
int  temp[4] ;
byte tempbcd[3] ;
char lineU[17];
char lineL[17];

void setup()
{
  /* initialize serial */
  Serial.begin(9600);
  sindex = 0 ;
  rs_puts("Hello !");
  crlf();
  /* clear flags */
  tflag = OFF ;
  uflag = OFF ;
  oflag = ON ;
  dflag = OFF ;
  /* initialize port values */
  PORTB = 0x00 ;
  PORTC = 0x00 ;
  PORTD = 0x00 ;
  /* initialize port direction */
  DDRB = 0xff ;
  DDRC = 0x00 ;
  DDRD = 0xfe ;
  /* 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';
}

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(); }
    /* one shot */
    if ( cmd == 'T' ) { oneShot(); }
    /* turn on LED */
    if ( cmd == 'L' ) { ledProc(1); }
    /* turn off LED */
    if ( cmd == 'l' ) { ledProc(0); }
    /* new line */
    crlf() ;
  }
  /* timer handling */
  if ( tflag == ON ) {
    /* clear flag */
    tflag = OFF ;
    /* 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 );
    }
  }
}

void update_trigger()
{
  /* set flag */
  tflag = ON ;
}

void show_help()
{
  rs_puts("? help")        ; crlf();
  rs_puts("T one shot")    ; crlf();
  rs_puts("L turn on LED") ; crlf();
  rs_puts("l turn off LED"); 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 ;
  /* lower */
  for ( i = 0 ; i < 16 ; i++ ) {
    *(lineL+i) = ' ' ;
  }
  /* 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) = '*' ; }
}

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

void ledProc(byte x)
{
  /* turn off */
  PORTB &= ~(1 << LED_BIT);
  /* judge */
  if ( x ) { PORTB |= (1 << LED_BIT); }
}

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

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

 プログラム容量は、30%(最大容量16kバイト)となってます。

 Personal Computerのアプリケーションで、設定温度を超えたとき
 警報を出します。Arduinoスケッチは、警報を出す機能を削除して
 あります。

 Personal Computerには、RaspberryPiを利用します。



 表示器を使い、GPIOでシリアルを使ってXBeeに接続。

 RaspberryPiの選定理由は、以下。

 RaspberryPiはUnixOSを採用しているので、周期的な
 動作は、cronでスクリプトを呼び出し実現できます。

 crontabに設定する内容は、以下。

0-58/2 * * * * * /home/pi/adatalog.sh

 シェルスクリプト adatalog.sh を使います。
 2分ごとに動作シーケンスを実現する仕様を
 考えました。

 シェルスクリプトの動作シーケンスは、以下。
  1. 温湿度計測装置からデータを吸い上げ(XBee経由)
  2. データをテキストファイルに追加書込み
  3. テキストファイルをCSV形式にしてコピー
  4. テキストファイルのデータから表示器にグラフ描画
  5. 設定温度を超えていれば、管理者にメールを発信
  6. ログファイル更新
 グラフ描画ではgnuplotを利用し、それ以外は  PythonかBシェルのコードで対応します。  シーケンスを図示すると、次のようになります。  データをテキストファイルに保存後、CSV形式の  ファイルを作成しておくと、後でspreadsheetを  使ったデータ整理とグラフ描画が可能に。  ログは、交信記録日誌で温湿度記録時間と  緊急警報メールの送信日時を保存します。  XBeeは、送信時に電力を食うので、次のように  DTB(Data Transfer Box)とArduinoを接続して  2台のXBeeでの通信を担当する仕様としました。  DTBをArduinoで実現する場合のスケッチは、以下。 /* dtb.ino Data Transfer Box Pin assignment PORTB PB5 (output) TxD2 PB4 (input) RxD2 PB3 (output) TxD1 PB2 (input) RxD1 PB1 (output) TxD0 PB0 (input) RxD0 PORTC PC5 (output) -- PC4 (output) -- PC3 (output) -- PC2 (output) -- PC1 (output) -- PC0 (output) -- PORTD PD7 (output) PD6 (output) PD5 (output) PD4 (output) PD3 (output) PD2 (output) PD1 (output) TxD PD0 (input) RxD */ #include <MsTimer2.h> #include <SoftwareSerial.h> SoftwareSerial ss_serz( 8, 9); /* rx, tx */ SoftwareSerial ss_sero(10,11); /* rx, tx */ SoftwareSerial ss_sert(12,13); /* rx, tx */ #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 #define XINTERVAL 2000 #define RSIZE 8 #define BSIZE 256 /* function prototype */ void update_trigger(); void show_help(); void rs_putchar(char x); void rs_puts(char *ptr); void crlf(); void oneShot(byte x); void init_ssbuf(); void perform(byte x); void performCH0(); void performCH1(); void performCH2(); /* variables */ boolean tflag ; boolean eflag ; boolean uflag ; char sbuf[8] ; byte sindex ; byte state ; byte cmd ; byte xcnt ; byte ii ; char rcvz[20] ; char rcvo[20] ; char rcvt[32] ; void setup() { /* initialize serial */ Serial.begin(9600); sindex = 0 ; rs_puts("Hello !"); crlf(); /* clear flags */ tflag = OFF ; eflag = OFF ; uflag = OFF ; /* initialize port values */ PORTB = 0x00 ; PORTC = 0x00 ; PORTD = 0x00 ; /* initialize port direction */ DDRB = 0xea ; DDRC = 0xff ; DDRD = 0xfe ; /* enable each software serial I/F */ ss_serz.begin(9600); ss_sero.begin(9600); ss_sert.begin(9600); /* initialize buffer */ init_ssbuf(); /* variables */ xcnt = 0 ; state = 0 ; /* 1500ms period */ MsTimer2::set(XINTERVAL,update_trigger); /* enable */ MsTimer2::start(); } void loop() { /* command interpreter */ if ( uflag == ON ) { /* clear flag */ uflag = OFF ; /* get command */ cmd = *(sbuf+0) ; /* help */ if ( cmd == '?' ) { show_help(); } /* one shot ch0 */ if ( cmd == 'Z' ) { oneShot(0); } /* one shot ch1 */ if ( cmd == 'O' ) { oneShot(1); } /* one shot ch2 */ if ( cmd == 'T' ) { oneShot(2); } } /* timer handling */ if ( tflag == ON ) { /* clear flag */ tflag = OFF ; /* handling */ perform( state ); /* update state */ state++ ; if ( state == 3 ) { state = 0 ; } } } void update_trigger() { /* set flag */ if ( xcnt & ON ) { tflag = ON ; } /* inrement */ xcnt++ ; } void show_help() { rs_puts("? help") ; crlf(); rs_puts("Z one shot ch0") ; crlf(); rs_puts("O one shot ch1") ; crlf(); rs_puts("T one shot ch2") ; 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 oneShot(byte x) { /* skip if no correct condition */ if ( x > 2 ) return ; /* channel 0 */ if ( x == 0 ) { rs_puts( rcvz ); } /* channel 1 */ if ( x == 1 ) { rs_puts( rcvo ); } /* channel 2 */ if ( x == 2 ) { rs_puts( rcvt ); } /* new line */ crlf(); } void perform(byte x) { if ( x == 0 ) { performCH0(); } if ( x == 1 ) { performCH1(); } if ( x == 2 ) { performCH2(); } } void performCH0() { char xch ; /* clear buffer */ for ( ii = 0 ; ii < 20 ; ii++ ) { *(rcvz+ii) = '\0'; } /* enable receive */ ss_serz.listen(); /* send command */ ss_serz.println("T"); /* get strings */ for ( ii = 0 ; ii < 19 ; ii++ ) { if ( ss_serz.available() > 0 ) { xch = ss_serz.read(); *(rcvz+ii) = xch ; if ( xch == 0x0d ) { *(rcvz+ii) = '.' ; } } delay(1); } } void performCH1() { char xch ; /* clear buffer */ for ( ii = 0 ; ii < 20 ; ii++ ) { *(rcvo+ii) = '\0'; } /* enable receive */ ss_sero.listen(); /* send command */ ss_sero.println("T"); /* get strings */ for ( ii = 0 ; ii < 19 ; ii++ ) { if ( ss_sero.available() > 0 ) { xch = ss_sero.read(); *(rcvo+ii) = xch ; if ( xch == 0x0d ) { *(rcvo+ii) = '.' ; } } delay(1); } } void performCH2() { char xch ; /* clear buffer */ for ( ii = 0 ; ii < 32 ; ii++ ) { *(rcvt+ii) = '\0'; } /* enable receive */ ss_sert.listen(); /* send command */ ss_sert.println("T"); /* get strings */ for ( ii = 0 ; ii < 31 ; ii++ ) { if ( ss_sert.available() > 0 ) { xch = ss_sert.read(); *(rcvt+ii) = xch ; if ( xch == 0x0d ) { *(rcvt+ii) = '.' ; } } delay(1); } } void init_ssbuf() { *(rcvz+0) = 'Z' ; *(rcvz+1) = 'z' ; *(rcvz+2) = '.' ; *(rcvz+3) = '\0' ; *(rcvo+0) = 'O' ; *(rcvo+1) = 'o' ; *(rcvo+2) = '.' ; *(rcvo+3) = '\0' ; *(rcvt+0) = 'T' ; *(rcvt+1) = 't' ; *(rcvt+2) = '.' ; *(rcvt+3) = '\0' ; } /* 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 ; } } }  ソフトウエアシリアルインタフェースを利用  しているので、ハードウエアの方では受信に  割込みを使えますが、ソフトウエアの場合は  コマンドを送ってから、受信待ちをします。  3チャネル分の温度計測装置、温湿度計測装置と  接続できます。温湿度計測装置は、チャネル2に  固定。  チャネルごとに専用受信バッファを用意し、通信で  各チャネルの温度湿度データ要求があったときだけ  内容を公開しています。  温度計測装置のスケッチは、以下。 /* themfx.ino Pin assignment PORTB PB5 (output) LED PB4 (output) -- PB3 (output) -- PB2 (output) -- PB1 (output) -- PB0 (output) -- PORTC PC5 (output) PC4 (output) 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 <SoftwareSerial.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 #define XINTERVAL 1500 #define XLIMIT 35 #define YLIMIT 170 #define RSIZE 8 #define BSIZE 256 /* LCD rs , e , D4 , D5 , D6 , D7 */ LiquidCrystal lcd( 2, 3, 4, 5, 6, 7 ); SoftwareSerial sw_ser( 8, 9); /* rx, tx */ /* function prototype */ void update_trigger(); void show_help(); void rs_putchar(char x); void rs_puts(char *ptr); void crlf(); void convert_digit(); void makeLCDString(); void oneShot(); void oneShotX(); void put_light_dark(); /* variables */ boolean tflag ; boolean uflag ; boolean oflag ; boolean dflag ; /* day or night */ boolean lflag ; char sbuf[4] ; byte sindex ; byte cmd ; int adv ; int tv ; int temp[4] ; char lineU[17]; char tbuf[17]; void setup() { /* initialize serial */ Serial.begin(9600); sindex = 0 ; rs_puts("Hello !"); crlf(); sw_ser.begin(9600); sw_ser.listen(); /* clear flags */ tflag = OFF ; uflag = OFF ; oflag = ON ; dflag = OFF ; lflag = OFF ; /* initialize port values */ PORTB = 0x00 ; PORTC = 0x00 ; PORTD = 0x00 ; /* initialize port direction */ DDRB = 0xfe ; DDRC = 0x00 ; DDRD = 0xfe ; /* 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++ ) { *(lineU+ii) = ' ' ; } *(lineU+ 0) = 'T'; *(lineU+ 4) = '|'; *(lineU+ 5) = 'M'; *(lineU+ 9) = '|'; *(lineU+10) = 'B'; *(lineU+14) = '|'; *(lineU+15) = 'N'; *(lineU+16) = '\0'; } void loop() { char xch ; /* one time handling */ if ( oflag == ON ) { /* clear flag */ oflag = OFF ; /* default */ lcd.setCursor(0,1); lcd.print("Namara Firm "); lflag = ON ; } /* command interpreter */ if ( uflag == ON ) { /* clear flag */ uflag = OFF ; /* get command */ cmd = *(sbuf+0) ; /* help */ if ( cmd == '?' ) { show_help(); } /* one shot */ if ( cmd == 'T' ) { oneShot(); } /* new line */ crlf() ; } /* timer handling */ if ( tflag == ON ) { /* clear flag */ tflag = OFF ; /* 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); } /* light or dark */ put_light_dark(); /* convert */ convert_digit(); /* show ( LCD ) */ makeLCDString(); lcd.setCursor(0,0); lcd.print( lineU ); if ( lflag == ON ) { lflag = OFF ; lcd.setCursor(0,1); lcd.print(" "); } } /* software serial handling */ if ( sw_ser.available() ) { xch = sw_ser.read(); /* one shot handling */ if ( xch == 'T' ) { oneShotX(); } } } void update_trigger() { /* set flag */ tflag = ON ; } void show_help() { rs_puts("? help") ; crlf(); rs_puts("T one shot"); 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 convert_digit() { byte ii ; byte j ; byte k ; int tmp ; char xx[3]; /* clear buffer */ for ( ii = 0 ; ii < 17 ; ii++ ) { *(tbuf+ii) = ' ' ; } *(tbuf+16) = '\0' ; /* top */ for ( j = 0 ; j < 4 ; j++ ) { /* integer -> digit */ tmp = *(temp+j) ; for ( ii = 0 ; ii < 3 ; ii++ ) { k = 2 - ii ; *(xx+k) = tmp % 10 ; tmp /= 10 ; } /* zero surpress */ k = 4 * j ; for ( ii = 0 ; ii < 3 ; ii++ ) { *(tbuf+1+ii+k) = *(xx+ii) + '0' ; } if ( *(xx+0) == 0 ) { *(tbuf+1+k) = ' ' ; if ( *(xx+1) == 0 ) { *(tbuf+2+k) = ' ' ; } } } } void makeLCDString() { /* TOP */ *(lineU+1) = *(tbuf+1); *(lineU+2) = *(tbuf+2); *(lineU+3) = *(tbuf+3); /* MIDDLE */ *(lineU+6) = *(tbuf+5); *(lineU+7) = *(tbuf+6); *(lineU+8) = *(tbuf+7); /* BOTTOM */ *(lineU+11) = *(tbuf+ 9); *(lineU+12) = *(tbuf+10); *(lineU+13) = *(tbuf+11); /* day or night */ *(lineU+15) = '_' ; if ( dflag ) { *(lineU+15) = '*' ; } } void oneShot() { rs_puts( tbuf ); crlf(); } void oneShotX() { sw_ser.println( tbuf ); } void put_light_dark() { /* get state */ adv = analogRead( LAST ); *(temp+3) = adv ; /* default (night) */ dflag = OFF ; /* day */ if ( adv > YLIMIT ) { dflag = 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 ; } } }  温湿度計測装置のスケッチは、次のように定義。 /* thermfy.ino Pin assignment PORTB PB5 (output) -- PB4 (output) -- PB3 (output) -- PB2 (output) sensor 2(TOP) PB1 (output) sensor 1(MIDDLE) PB0 (output) sensor 0(BOTTOM) PORTC PC5 (output) PC4 (output) PC3 (output) light sensor PC2 (output) PC1 (output) PC0 (output) 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 <TroykaDHT.h> #include <SoftwareSerial.h> #define OFF 0 #define ON OFF+1 /* pin assignment */ #define BOTTOM 0 #define MIDDLE 1 #define TOP 2 #define LAST 3 #define XINTERVAL 1000 #define XLIMIT 35 #define YLIMIT 170 #define RSIZE 16 #define BSIZE 256 /* LCD rs , e , D4 , D5 , D6 , D7 */ LiquidCrystal lcd( 2, 3, 4, 5, 6, 7 ); SoftwareSerial sw_ser(11,12); /* rx, tx */ /* sensor */ #define PIN_DHT_B 8 #define PIN_DHT_M 9 #define PIN_DHT_T 10 DHT dhtB(PIN_DHT_B,DHT11); DHT dhtM(PIN_DHT_M,DHT11); DHT dhtT(PIN_DHT_T,DHT11); /* function prototype */ void update_trigger(); void show_help(); void rs_putchar(char x); void rs_puts(char *ptr); void crlf(); void convert_digit(); void makeLCDString(); void oneShot(); void oneShotX(); void put_light_dark(); /* variables */ boolean tflag ; boolean uflag ; boolean oflag ; boolean dflag ; /* day or night */ char sbuf[16] ; byte sindex ; byte cmd ; int adv ; int temp[7] ; char lineU[17]; char lineL[17]; char tbuf[29]; void setup() { /* initialize serial */ Serial.begin(9600); sindex = 0 ; rs_puts("Hello !"); crlf(); show_help(); sw_ser.begin(9600); sw_ser.listen(); /* clear flags */ tflag = OFF ; uflag = OFF ; oflag = ON ; dflag = OFF ; /* initialize port values */ PORTB = 0x00 ; PORTC = 0x00 ; PORTD = 0x00 ; /* initialize port direction */ DDRB = 0xf7 ; DDRC = 0x00 ; DDRD = 0xfe ; /* 1000ms 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++ ) { *(lineU+ii) = ' ' ; } *(lineU+ 0) = 'T'; *(lineU+ 4) = '|'; *(lineU+ 5) = 'M'; *(lineU+ 9) = '|'; *(lineU+10) = 'B'; *(lineU+14) = '|'; *(lineU+15) = 'N'; *(lineU+16) = '\0'; *(lineL+ 0) = 't'; *(lineL+ 4) = '|'; *(lineL+ 5) = 'm'; *(lineL+ 9) = '|'; *(lineL+10) = 'b'; *(lineL+16) = '\0'; /* enable humidity sensors */ dhtB.begin(); dhtM.begin(); dhtT.begin(); } void loop() { char xch ; float humidity ; float temperature ; /* 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(); } /* one shot */ if ( cmd == 'T' ) { oneShot(); } /* new line */ crlf() ; } /* timer handling */ if ( tflag == ON ) { /* clear flag */ tflag = OFF ; /* get thermometer and humidity */ for ( int ii = TOP ; ii > -1 ; ii-- ) { /* get data */ if ( ii == TOP ) { dhtT.read(); if ( dhtM.getState() == DHT_OK ) { humidity = dhtT.getHumidity(); temperature = dhtT.getTemperatureC(); } } if ( ii == MIDDLE ) { dhtM.read(); if ( dhtM.getState() == DHT_OK ) { humidity = dhtM.getHumidity(); temperature = dhtM.getTemperatureC(); } } if ( ii == BOTTOM ) { dhtB.read(); if ( dhtB.getState() == DHT_OK ) { humidity = dhtB.getHumidity(); temperature = dhtB.getTemperatureC(); } } /* convert */ *(temp+ii ) = (int)temperature ; *(temp+ii+4) = (int)humidity ; } /* light or dark */ put_light_dark(); /* convert */ convert_digit(); /* show ( LCD ) */ makeLCDString(); lcd.setCursor(0,0); lcd.print( lineU ); lcd.setCursor(0,1); lcd.print( lineL ); } /* software serial handling */ if ( sw_ser.available() ) { xch = sw_ser.read(); /* one shot handling */ if ( xch == 'T' ) { oneShotX(); } } } void update_trigger() { /* set flag */ tflag = ON ; } void show_help() { rs_puts("? help") ; crlf(); rs_puts("T one shot") ; 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 convert_digit() { byte ii ; byte j ; byte k ; int tmp ; char xx[3]; /* clear buffer */ for ( ii = 0 ; ii < 29 ; ii++ ) { *(tbuf+ii) = ' ' ; } *(tbuf+28) = '\0' ; /* thermometer */ for ( j = 0 ; j < 4 ; j++ ) { /* integer -> digit */ tmp = *(temp+j) ; for ( ii = 0 ; ii < 3 ; ii++ ) { k = 2 - ii ; *(xx+k) = tmp % 10 ; tmp /= 10 ; } /* zero surpress */ k = 4 * j ; for ( ii = 0 ; ii < 3 ; ii++ ) { *(tbuf+1+ii+k) = *(xx+ii) + '0' ; } if ( *(xx+0) == 0 ) { *(tbuf+1+k) = ' ' ; if ( *(xx+1) == 0 ) { *(tbuf+2+k) = ' ' ; } } } /* humidity */ for ( j = 4 ; j < 7 ; j++ ) { /* integer -> digit */ tmp = *(temp+j) ; for ( ii = 0 ; ii < 3 ; ii++ ) { k = 2 - ii ; *(xx+k) = tmp % 10 ; tmp /= 10 ; } /* zero surpress */ k = 4 * j ; for ( ii = 0 ; ii < 3 ; ii++ ) { *(tbuf+1+ii+k) = *(xx+ii) + '0' ; } if ( *(xx+0) == 0 ) { *(tbuf+1+k) = ' ' ; if ( *(xx+1) == 0 ) { *(tbuf+2+k) = ' ' ; } } } } void makeLCDString() { /* TOP */ *(lineU+1) = *(tbuf+1); *(lineU+2) = *(tbuf+2); *(lineU+3) = *(tbuf+3); /* MIDDLE */ *(lineU+6) = *(tbuf+5); *(lineU+7) = *(tbuf+6); *(lineU+8) = *(tbuf+7); /* BOTTOM */ *(lineU+11) = *(tbuf+ 9); *(lineU+12) = *(tbuf+10); *(lineU+13) = *(tbuf+11); /* day or night */ *(lineU+15) = '_' ; if ( dflag ) { *(lineU+15) = '*' ; } /* TOP */ *(lineL+1) = *(tbuf+17); *(lineL+2) = *(tbuf+18); *(lineL+3) = *(tbuf+19); /* MIDDLE */ *(lineL+6) = *(tbuf+21); *(lineL+7) = *(tbuf+22); *(lineL+8) = *(tbuf+23); /* BOTTOM */ *(lineL+11) = *(tbuf+25); *(lineL+12) = *(tbuf+26); *(lineL+13) = *(tbuf+27); } void oneShot() { rs_puts( tbuf ); crlf(); } void oneShotX() { sw_ser.println( tbuf ); } void put_light_dark() { /* get state */ adv = analogRead( LAST ); *(temp+3) = adv ; /* default (night) */ dflag = OFF ; /* day */ if ( adv > YLIMIT ) { dflag = 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 ; } } }  温度計測装置と温湿度計測装置の中にあるArduinoスケッチでは  EEPROMとReal Time Clockへの対応を除いたので、プログラムの  容量は、全体の30%から50%となっています。  また、消費電力も大幅に低下したので、電池の交換回数が隔日から  3日に1度にまで減りました。  DTBでの電力消費が激しいので、鉛蓄電池を電源に使います。  選択した鉛蓄電池は、以下。  電池は、ビニルハウス入口の外に置いて、内部の土壌を  汚染しないようにします。(赤マーク位置に設置)  母屋におくコンピュータの画面は、以下。  左側にDTBから収集した温度、湿度の数値を表示し  右側に、温度、湿度の上限と下限を入れて警報を  出すための閾値を設定します。  中央から下には、温度と湿度の変化をグラフで  リアルタイムに表示。  通信を除き、情報はファイルから入力して  グラフ描画するPythonコードは、以下。 import sys import tkinter as tk #********************* # generate window #********************* root = tk.Tk() root.title('Green House NAMARA Firm') root.geometry('620x460') #+++++++++++ # load #+++++++++++ fxtop = [] fxmid = [] fxbot = [] with open('xdat.txt','r') as fd : for e in fd.read().splitlines(): tmp = [] xx = e.split() for ee in xx: tmp.append( int(ee) ) tmpx = [] tmpy = [] tmpz = [] tmpx.append(tmp[0]) tmpx.append(tmp[3]) tmpy.append(tmp[1]) tmpy.append(tmp[3]) tmpz.append(tmp[2]) tmpz.append(tmp[3]) fxtop.append( tmpx ) fxmid.append( tmpy ) fxbot.append( tmpz ) fytop = [] fymid = [] fybot = [] with open('ydat.txt','r') as fd : for e in fd.read().splitlines(): tmp = [] xx = e.split() for ee in xx: tmp.append( int(ee) ) tmpx = [] tmpy = [] tmpz = [] tmpx.append(tmp[0]) tmpx.append(tmp[3]) tmpy.append(tmp[1]) tmpy.append(tmp[3]) tmpz.append(tmp[2]) tmpz.append(tmp[3]) fytop.append( tmpx ) fymid.append( tmpy ) fybot.append( tmpz ) fztop = [] fzmid = [] fzbot = [] with open('zdat.txt','r') as fd : for e in fd.read().splitlines(): tmp = [] xx = e.split() for ee in xx: tmp.append( int(ee) ) tmpx = [] tmpy = [] tmpz = [] tmpx.append(tmp[0]) tmpx.append(tmp[3]) tmpy.append(tmp[1]) tmpy.append(tmp[3]) tmpz.append(tmp[2]) tmpz.append(tmp[3]) fztop.append( tmpx ) fzmid.append( tmpy ) fzbot.append( tmpz ) #********************* # frame #********************* frmL = tk.Frame(root) frmL.grid(row=0,column=0) frmR = tk.Frame(root) frmR.grid(row=0,column=3) frmCH0T = tk.Frame(root) frmCH0T.grid(row=7,column=1) frmCH0M = tk.Frame(root) frmCH0M.grid(row=8,column=1) frmCH0B = tk.Frame(root) frmCH0B.grid(row=9,column=1) frmCH1T = tk.Frame(root) frmCH1T.grid(row=7,column=2) frmCH1M = tk.Frame(root) frmCH1M.grid(row=8,column=2) frmCH1B = tk.Frame(root) frmCH1B.grid(row=9,column=2) frmCH2T = tk.Frame(root) frmCH2T.grid(row=7,column=3) frmCH2M = tk.Frame(root) frmCH2M.grid(row=8,column=3) frmCH2B = tk.Frame(root) frmCH2B.grid(row=9,column=3) #********************* # unit #********************* lblUnit1 = tk.Label(frmL,text = 'Celsius') lblUnit1.grid(row = 0, column = 1) lblUnit2 = tk.Label(frmL,text = 'Celsius') lblUnit2.grid(row = 0, column = 2) lblUnit3 = tk.Label(frmL,text = '%') lblUnit3.grid(row = 0, column = 3) lblUnit4 = tk.Label(frmR,text = 'Celsius') lblUnit4.grid(row = 0, column = 1) lblUnit5 = tk.Label(frmR,text = '%') lblUnit5.grid(row = 0, column = 2) lblTop = tk.Label(frmL,text = 'TOP') lblTop.grid(row = 1, column = 0) lblMid = tk.Label(frmL,text = 'MIDDLE') lblMid.grid(row = 2, column = 0) lblBot = tk.Label(frmL,text = 'BOTTOM') lblBot.grid(row = 3, column = 0) #********************* # channel 0 parameters #********************* ch0TopVal = 28 ch0MidVal = 29 ch0BotVal = 29 lblCh0Top = tk.Label(frmL,text = str(ch0TopVal) ) lblCh0Top.grid(row = 1, column = 1) lblCh0Mid = tk.Label(frmL,text = str(ch0MidVal)) lblCh0Mid.grid(row = 2, column = 1) lblCh0Bot = tk.Label(frmL,text = str(ch0BotVal)) lblCh0Bot.grid(row = 3, column = 1) #********************* # channel 1 parameters #********************* ch1TopVal = 28 ch1MidVal = 27 ch1BotVal = 30 lblCh1Top = tk.Label(frmL,text = str(ch1TopVal) ) lblCh1Top.grid(row = 1, column = 2) lblCh1Mid = tk.Label(frmL,text = str(ch1MidVal)) lblCh1Mid.grid(row = 2, column = 2) lblCh1Bot = tk.Label(frmL,text = str(ch1BotVal)) lblCh1Bot.grid(row = 3, column = 2) #********************* # channel 2 parameters #********************* ch2TopVal = 72 ch2MidVal = 80 ch2BotVal = 79 lblCh2Top = tk.Label(frmL,text = str(ch2TopVal) ) lblCh2Top.grid(row = 1, column = 3) lblCh2Mid = tk.Label(frmL,text = str(ch2MidVal)) lblCh2Mid.grid(row = 2, column = 3) lblCh2Bot = tk.Label(frmL,text = str(ch2BotVal)) lblCh2Bot.grid(row = 3, column = 3) #********************* # item labels #********************* lblChTL = tk.Label(root,text= 'thermometer') lblChTL.grid(row=6,column=1) lblChML = tk.Label(root,text= 'thermometer') lblChML.grid(row=6,column=2) lblChBL = tk.Label(root,text= 'humidity') lblChBL.grid(row=6,column=3) lblCh0Lable = tk.Label(root,text = 'TOP' ) lblCh0Lable.grid(row = 7, column = 0) lblCh1Lable = tk.Label(root,text = 'MIDDLE' ) lblCh1Lable.grid(row = 8, column = 0) lblCh2Lable = tk.Label(root,text = 'BOTTOM' ) lblCh2Lable.grid(row = 9, column = 0) #********************* # limit parameters #********************* tUpper = 40 tLower = 2 hUpper = 95 hLower = 20 lblTUpper = tk.Label(frmR,text = 'Upper' ) lblTUpper.grid(row = 1, column = 0) lblTLower = tk.Label(frmR,text = 'Lower' ) lblTLower.grid(row = 2, column = 0) entTUpper = tk.Entry(frmR,width=4) entTUpper.insert(tk.END,str(tUpper)) entTUpper.grid(row = 1, column = 1) entTLower = tk.Entry(frmR,width=4) entTLower.insert(tk.END,str(tLower)) entTLower.grid(row = 2, column = 1) entHUpper = tk.Entry(frmR,width=4) entHUpper.insert(tk.END,str(hUpper)) entHUpper.grid(row = 1, column = 2) entHLower = tk.Entry(frmR,width=4) entHLower.insert(tk.END,str(hLower)) entHLower.grid(row = 2, column = 2) #********************* # functions #********************* def setTemp() : utemp = entTUpper.get() ltemp = entTLower.get() #print( str(utemp) + ' ' + str(ltemp) ) tUpper = utemp tLower = ltemp entTUpper.delete(0,tk.END) entTLower.delete(0,tk.END) entTUpper.insert(tk.END,str(tUpper)) entTLower.insert(tk.END,str(tLower)) def setHumi(): uhumi = entHUpper.get() lhumi = entHLower.get() #print( str(uhumi) + ' ' + str(lhumi) ) hUpper = uhumi hLower = lhumi entHUpper.delete(0,tk.END) entHLower.delete(0,tk.END) entHUpper.insert(tk.END,str(hUpper)) entHLower.insert(tk.END,str(hLower)) def myExit(): exit() #********************* # Canvas object #********************* wx = 140 wh = 100 dcol = 'lightgray' canCH0T = tk.Canvas(frmCH0T,bg = dcol, width = wx , height = wh ) canCH0T.grid(row=0,column=0) canCH0M = tk.Canvas(frmCH0M,bg = dcol, width = wx , height = wh ) canCH0M.grid(row=0,column=0) canCH0B = tk.Canvas(frmCH0B,bg = dcol, width = wx , height = wh ) canCH0B.grid(row=0,column=0) canCH1T = tk.Canvas(frmCH1T,bg = dcol, width = wx , height = wh ) canCH1T.grid(row=0,column=0) canCH1M = tk.Canvas(frmCH1M,bg = dcol, width = wx , height = wh ) canCH1M.grid(row=0,column=0) canCH1B = tk.Canvas(frmCH1B,bg = dcol, width = wx , height = wh ) canCH1B.grid(row=0,column=0) canCH2T = tk.Canvas(frmCH2T,bg = dcol, width = wx , height = wh ) canCH2T.grid(row=0,column=0) canCH2M = tk.Canvas(frmCH2M,bg = dcol, width = wx , height = wh ) canCH2M.grid(row=0,column=0) canCH2B = tk.Canvas(frmCH2B,bg = dcol, width = wx , height = wh ) canCH2B.grid(row=0,column=0) def canCH012draw(which,dx,dy,dz): # set color ccol = 'green' # top last = len( dx ) - 1 for e in range(0,last) : # generate x axis values xs = e xd = xs + 1 # get value from list tmpx0 = dx[e] tmpx1 = dx[e+1] tmpy0 = dy[e] tmpy1 = dy[e+1] tmpz0 = dz[e] tmpz1 = dz[e+1] # generate y axis values yx = [] tmp = 100 - tmpx0[0] if which < 2 : tmp = tmp - tmpx0[0] yx.append( tmp ) yx.append( 100 - 2 * tmpx0[1] ) tmp = 100 - tmpx1[0] if which < 2 : tmp = tmp - tmpx1[0] yx.append( tmp ) yx.append( 100 - 2 * tmpx1[1] ) yy = [] tmp = 100 - tmpy0[0] if which < 2 : tmp = tmp - tmpy0[0] yy.append( tmp ) yy.append( 100 - 2 * tmpy0[1] ) tmp = 100 - tmpy1[0] if which < 2 : tmp = tmp - tmpy1[0] yy.append( tmp ) yy.append( 100 - 2 * tmpy1[1] ) yz = [] tmp = 100 - tmpz0[0] if which < 2 : tmp = tmp - tmpz0[0] yz.append( tmp ) yz.append( 100 - 2 * tmpz0[1] ) tmp = 100 - tmpz1[0] if which < 2 : tmp = tmp - tmpz1[0] yz.append( tmp ) yz.append( 100 - 2 * tmpz1[1] ) # draw (top) pcol = 'blue' if which == 0 : canCH0T.create_line(xs,yx[0],xd,yx[2],fill=pcol,width=2) canCH0T.create_line(xs,yx[1],xd,yx[3],fill=ccol,width=2) if which == 1 : canCH1T.create_line(xs,yx[0],xd,yx[2],fill=pcol,width=2) canCH1T.create_line(xs,yx[1],xd,yx[3],fill=ccol,width=2) if which == 2: canCH2T.create_line(xs,yx[0],xd,yx[2],fill=pcol,width=2) canCH2T.create_line(xs,yx[1],xd,yx[3],fill=ccol,width=2) # draw (middle) pcol = 'red' if which == 0 : canCH0M.create_line(xs,yy[0],xd,yy[2],fill=pcol,width=2) canCH0M.create_line(xs,yy[1],xd,yy[3],fill=ccol,width=2) if which == 1 : canCH1M.create_line(xs,yx[0],xd,yx[2],fill=pcol,width=2) canCH1M.create_line(xs,yx[1],xd,yx[3],fill=ccol,width=2) if which == 2 : canCH2M.create_line(xs,yx[0],xd,yx[2],fill=pcol,width=2) canCH2M.create_line(xs,yx[1],xd,yx[3],fill=ccol,width=2) # draw (bottom) pcol = 'orange' if which == 0 : canCH0B.create_line(xs,yz[0],xd,yz[2],fill=pcol,width=2) canCH0B.create_line(xs,yz[1],xd,yz[3],fill=ccol,width=2) if which == 1 : canCH1B.create_line(xs,yz[0],xd,yz[2],fill=pcol,width=2) canCH1B.create_line(xs,yz[1],xd,yz[3],fill=ccol,width=2) if which == 2 : canCH2B.create_line(xs,yz[0],xd,yz[2],fill=pcol,width=2) canCH2B.create_line(xs,yz[1],xd,yz[3],fill=ccol,width=2) canCH012draw(0,fxtop,fxmid,fxbot) canCH012draw(1,fytop,fymid,fybot) canCH012draw(2,fztop,fzmid,fzbot) #********************* # Button object #********************* btnExit = tk.Button(frmL, text = 'Exit' , command = myExit ) btnExit.grid(row = 0, column = 0) btnTempGet = tk.Button(frmR, text = 'Set' , command = setTemp ) btnTempGet.grid(row = 3, column = 1) btnHumiGet = tk.Button(frmR, text = 'Set' , command = setHumi ) btnHumiGet.grid(row = 3, column = 2) root.mainloop() (under construction)

目次

inserted by FC2 system