勝手な電子工作・・

勝手なオリジナル電子工作に関する記事を書きます

24時間の動きを表示するといいかな・・温湿度気圧計

f:id:a-tomi:20210425152822j:plain

特別な工夫はないのですが(^^;、小さな画面にグラフ表示して過去1日の変化を示す気象計です。今14時ちょうどですが、昨日の今に比べてずいぶん気圧が下がったのに気づきます。これだと今晩雨が降るかもしれませんね。

昨日から2階の和室に1日置いたのですが、窓や入口の開閉に応じて温度や湿度が少し上下します。

こういう気象計が売られているのは見かけませんが、この表示方法がほかの工作にも応用できそうなので記事にするとします。前に書いたCO2濃度計(右のカテゴリーのCO2測定から参照してください)やガンマ線表示計(同じくガイガーカウンターから)などで、この表示ならかなり便利そう。それらは時間ができたらやってみるとして。

この表示を試したのは単純なきっかけからです。マイコンの容量と速度が増し、Teensy4.0などもArduino-IDEに対応して手軽に使える有難い時代になりました、これまでのESPもですが。Raspberry Pi Picoは少し試した限りではArduino-IDEとの相性が今一つですが。

ともあれ、そういうマイコンの表示はGraphicsディスプレイにしたいものです。そこで見易いOLEDを5種類ほど試してみましたが、簡単な用途向けではサイズがまだまだ小さくて拡大鏡が必要ですね・・^^; 下の写真はTeensy4.0にSSD1306OLEDをつないでみたもの。

f:id:a-tomi:20210425153947j:plain

表示装置付きのM5-stackを使うのも便利ですが、テストでなく組み込むには無理があるし。。。

もう少し大きい表示装置が何かないかなーと手持ちの部品を漁り、このTFTカラー液晶シールドに気づきました。mcufriendとシルク印刷された2.4インチの表示器で、かつてArduino-Unoで使っていました。なぜかUnoのピンをほぼ全部塞ぐだけでなくUNOの容量が次第に不足気味となりもはや用済み、部品箱で長らく眠っていたのでした^^

f:id:a-tomi:20210425154217j:plain

裏は次です。

f:id:a-tomi:20210425154243j:plain

480x320ピクセルのカラー2.4インチ。それならいいかも!・・・。

ただし、これはUNOに合わせたシールドなのでピンが扱いにくい。そこで思いついて、まずはArduino-Megaに挿してみました。内蔵のマイクロSDを使うには少し問題があり、該当ピンがUNOのSPI接続にとられるため、その4本のピンをカットしてMEGAのSPI各ピンに配線でつなぐのがよいかと思われます。とりあえずはこれを無視して放置し、表示だけをテストすることにしました。このシールドをMEGAに次のように取り付けます。

f:id:a-tomi:20210425154633j:plain

そしてセンサーとRTC(リアルタイムクロック)と配線した部分は次です。

f:id:a-tomi:20210425162235j:plain

 

このようにするとArduinoからの3.3V出力もふさがってしまうので、裏面から配線で取り出しています。 

f:id:a-tomi:20210425162321j:plain

そしてプログラムを作りますが、まずはArduinoの普通のTFT-LCDライブラリーではうんともすんとも^^; いったいどんな接続だったっけな(・・? 

この製品に書いてあるmcufriendをググってみるとMCUFRIEND_kbv libraryライブラリーにたどり着きます。Arduinoのモデルを選ばないオールマイティとか。それを使ったらなんと即、問題なく動きました。

f:id:a-tomi:20210425163311j:plain

1日放置したのが上の写真ですが、大きさも鮮明さも良い感じです。測ると電流を200mAほど消耗しているのにはびっくりですが。プログラムは色使いや位置等を少し調整して冒頭に掲げた画面を出しているのが次のスケッチです。作るのと改訂に半日以上使ってしまいましたが。

/****************************************************************
  Weather station with TFT-LCD 320x240 & BME280 -  V.02
    Initial version V.01 on Apr.18,2021   (c) Akira Tominaga
  Function:
    Display temperature-C, relative humidity-% & pressure-hPa.
    24-hour-plot wraps round every 24 hours.
  Pin connections:
    TFTLCD shield(MCUFRIEND_kbv) attached to Arduino-MEGA.
    Arduino-Mega 3.3V,G,SDA&SCL to sensor&RTC's VCC,G,SDA&SCL.
  Revisions:
    V01: Changed design of grid and position-marker.  4/19/2021
    V02: Minor changes to characters and some colors. 4/23/2021
  Remarks:
   Librarie's licensing rules & (c)s are attached in the bottom.
  **************************************************************/
#include "UTFTGLUE.h"     // MCUFRIEND_kbv library
#include "Wire.h"         // I2C for RTC-DS3231 and BME280
#include "Adafruit_Sensor.h"
#include "Adafruit_BME280.h"
// *** for LCD ***
UTFTGLUE lcd(0, A2, A1, A3, A4, A0);
#define wW 320            // TFT width 
#define hH 240            // TFT height
#define bX 43             // x @ begin-graph
#define eX wW-36          // x @ end-graph(284 in this case)
int lX = (eX - 1) - (bX + 1) + 1; // x-axis len (240 this case)
#define bY 18             // y @ begin graph
#define eY hH-15          // y @ end graph
// *** for colors ***
#define White (192,192,192)
#define Black (0,0,0)
#define Red (255,48,48)
#define Green (0,255,0)
#define Blue (0,100,255)
#define Yellow (255,255,0)
#define Purple (255,0,255)
#define rGray (127,127,127)
#define dGray (32,32,64)
// *** for application use ***
char Temp[] = "xx.xC";    // 5+1 bytes for temperature
char rH[] = "xx.x%rH";    // 7+1 bytes for relative humidity
char hPa[] = "xxxx.xhPa"; // 9+1 bytes for pressure
#define iniX bX+1         // graph initial x-position
#define lastX eX-1        // graph last x-position
uint16_t curX = iniX;     // set curX at initial position
uint16_t posY;            // position Y
// *** for measurement ***
Adafruit_BME280 bme;      // name as bme
// to keep measurement for 24hours in lX positions;
uint16_t intervalM = round(24 * 60.0 / lX); // interval minutes
uint8_t moV;              // current month
uint8_t daV;              // current day
uint8_t hrV;              // current hour
uint8_t mnV;              // current minute
uint8_t mnVsv = 99;       // former minute save area
char MDhm[] = "MM/DD hh:mm"; // editing area for clock

void setup() { // ***** Arduino setup() *****
  Serial.begin(9600);
  Wire.begin();
  lcd.InitLCD();
  lcd.clrScr();
  //////////////////////////////////////
  // *** Draw frame ***               //
  //////////////////////////////////////
  lcd.setColor Black;     // make a blackboard
  lcd.fillRect(0, 0, wW - 1, hH - 1);
  lcd.setColor Yellow;
  lcd.drawRect(0, 0, wW - 1, bY - 1); // top bar
  // *** fill texts in top bar ***
  lcd.setFont(SmallFont);
  lcd.setBackColor Black; // text's back-color
  dspVal();               // initial text in top bar
  //////////////////////////////////////
  //*** draw axes, grid and scales ***//
  //////////////////////////////////////
  // *** vertical lines and scales***
  lcd.setColor rGray;
  lcd.drawLine(eX, bY, eX, eY); // right edge (graph limit+1)
  for (uint8_t tU = 0; tU < 25; tU += 2) {
    uint16_t xL = bX + 1 + round(lX * (tU / 24.0));
    lcd.setColor rGray;
    lcd.drawLine(xL, bY, xL, eY);
    lcd.setColor White;    //
    char hhC[3];
    sprintf(hhC, "%2d", tU);
    lcd.print(hhC, xL - 8, hH - 12);
  }
  lcd.print("Hour", 8, hH - 12);
  // *** vertical scales and horizontal lines ***
#define tsMax 50          // temperature max scale
#define tsMin 0           // temperature minimum scale
#define tsStep (tsMax-tsMin)/10
#define hsMax 100         // rel. humidity max scale
#define hsMin 0           // relative hum. min scale
#define hsStep (hsMax-hsMin)/10
#define psMax 1040        // pressure max scale 1040
#define psMin 940         // pressure min scae 940
#define psStep (psMax-psMin)/10
  char tsC[4];            // chars for temperature
  char hsC[4];            // chars for rel.humidity
  char psC[5];            // chars for pressure
  int tS = tsMin;         // init. temperature scale
  int hS = hsMin;         // init. humidity scale
  int pS = psMin;         // init. pressure scale
  // draw horizontal lines and scales in a loop
  for (int g = 0; g < 10; g++) {
    int iy = round(eY - (eY - bY) * g / 10.0);
    lcd.setColor rGray;
    lcd.drawLine(bX + 1, iy, eX, iy); // horizontal line
    sprintf(tsC, "%2d", tS); // temp val. text
    sprintf(hsC, "%2d", hS); // rHum val. text
    sprintf(psC, "%4d", pS); // pres val. text
    lcd.setColor Green;   // for temperature
    lcd.print(tsC, 5, iy - 7);
    lcd.setColor Blue;    //  for humidity
    lcd.print(hsC, 26, iy - 7);
    lcd.setColor Red;     // for pressure
    lcd.print(psC, eX + 5, iy - 7);
    tS = tS + tsStep;     // increase pos. for temp.
    hS = hS + hsStep;     // increase pos. for humi.
    pS = pS + psStep;     // increase pos. for pres.
  }
  //////////////////////////////////////
  //*** prepare BME280 sensor      ***//
  //////////////////////////////////////  
  if (!bme.begin()) {     // check validity of BME280
    Serial.println(F("Invalid BME280."));
    Serial.print("SensorID=0x");
    Serial.println(bme.sensorID(), HEX);
    Serial.println(F(" ID 0x56-0x58 for BMP280."));
    Serial.println(F(" ID 0x61 for BME680."));
    while (true) {}       // if error, stop here
  }
  delay(1000);            // wait sensor stabilized
}
void loop() { // ***** Arduino loop() *****
  // *** display clock and check timing to measure   
#define aMomnt 1000       // mS to wait for a moment
waitTiming:
  geTime();               // get time and calendar
  if (mnV == mnVsv) {     // if no change in minute
    delay(aMomnt);        // then wait for a moment
    goto  waitTiming;     // repeat checking
  }
  // *** display clock in top bar each second
  sprintf(MDhm, "%02d/%02d %02d:%02d", moV, daV, hrV, mnV);
  lcd.setColor White;    // for Clock
  lcd.print(MDhm, 15, 4); // display date and clock
  // *** check graph-drawing timing or not
  if ((mnV % 6) > 0) {    // if not multiple of 6
    delay(aMomnt);        // then wait for a momentr
    goto  waitTiming;     // repeat checking
  }
  // *** now measurement start
  mnVsv = mnV;            //save time (min) processed this time
  uint16_t vMin = hrV * 60 + mnV; // cur. time in minute
  uint16_t xPos = round(lX * (float)(vMin / (60.0 * 24))); 
  curX = bX + 1 + xPos;   // get current hilizontal position
  // *** get measured data and edit them into top bar
  uint16_t Tempx10 = round(10.0 * bme.readTemperature());
  uint8_t temp = Tempx10 / 10;  // get integer part
  uint8_t tempD = Tempx10 % 10; // remainder after dot
  sprintf(Temp, "%2d", temp);   // edit integer 2chars
  Temp[2] = '.';
  sprintf(Temp + 3, "%01d", tempD); // edit after dot
  uint16_t Humix10 = round(10.0 * bme.readHumidity());
  uint8_t humi = Humix10 / 10;  // get integer
  uint8_t humiD = Humix10 % 10; // remainder after dot
  sprintf(rH, "%2d", humi);     // edit integer 2chars
  rH[2] = '.';
  sprintf(rH + 3, "%01d", humiD); // edit after dot
  uint16_t Presx10 = round(bme.readPressure() / 10.0);
  uint16_t pres = Presx10 / 10; // get integer part
  uint16_t presD = Presx10 % 10; // remainder after dot
  sprintf(hPa, "%4d", pres);    // edit integer 4chars
  hPa[4] = '.';
  sprintf(hPa + 5, "%01d", presD); // edit after dot
  dspVal();               // show the values in top bar
  // *** draw position marker line ***
  // erase the past marked line
  lcd.setColor Black;     // with blackboard color
  lcd.drawLine(curX, bY, curX, eY - 1); // erase
  // re-mark grid positions
  lcd.setColor rGray;     // with grid color
  for (int g = 0; g < 10; g++) {
    int iy = round(eY - (eY - bY) * g / 10.0);
    lcd.drawPixel(curX, iy); // re-draw grids
  }
  // ** if on the hour line, then recover it
  if ((curX - (bX + 1)) % (round(lX / 12.0)) == 0) { // if 2hours x N
    lcd.drawLine(curX, bY, curX, eY); // recover the hour line
  }
  // *** draw measurement graphs ***
  lcd.setColor Green;     // temperature
  posY = (eY - 1) - round((Tempx10 / 10.0) * (eY - bY) / 50.0);
  lcd.drawPixel(curX, posY);
  lcd.setColor Blue;      // relative humidity
  posY = (eY - 1) - round((Humix10 / 10.0) * (eY - bY) / 100.0);
  lcd.drawPixel(curX, posY);
  lcd.setColor Red;       // pressure
  posY = (eY - 1) - round(((Presx10 / 10.0) - 940) * (eY - bY) / 100.0);
  lcd.drawPixel(curX, posY);
  // increase x-position for next measurement
  curX++;                 // get next position
  if (curX > lastX) {
    curX = iniX;
  }
  /// *** draw vertical marker line ***
  lcd.setColor Yellow;    // with Yellow color
  lcd.drawLine(curX, bY, curX, eY - 1);
} // *** end of Arduino loop() ***
/**************************************************
    User defined functions
 **************************************************/
// ***** dspVal() *** display values measured *****
void dspVal(void) {
  lcd.setColor Green;     // for temperature C
  lcd.print(Temp, 120, 4);
  lcd.setColor Blue;      // for relative humidity %
  lcd.print(rH, 171, 4);
  lcd.setColor Red;       // for pressure hPa
  lcd.print(hPa, 238, 4);
}
// *** geTime() *** get month,day,hr & min from DS3231
void geTime(void) {
#define aRTC 0x68       // RTC I2C addr
#define mdm 2           // minute position in RTC data
#define mdh 3           // hour position in RTC data
#define mdD 5           // day position in RTC data
#define mdM 6           // month position in RTC data
  byte  vR[8];          // values in RTC format
  Wire.beginTransmission(aRTC);
  Wire.write(0x00);
  Wire.endTransmission();
  Wire.requestFrom(aRTC, 7);
  while (Wire.available() < 7) {}  // wait for data
  for (int i = 1; i < 8; i++) {
    vR[i] = Wire.read();
  }
  Wire.endTransmission();
  moV = ((vR[mdM] & B00010000) / 16) * 10 + (vR[mdM] & B00001111);
  daV = ((vR[mdD] & B01110000) / 16) * 10 + (vR[mdD] & B00001111);
  hrV = ((vR[mdh] & B00100000) / 32) * 20 + ((vR[mdh] & B00010000) / 16) * 10 + (vR[mdh] & B00001111);
  mnV = ((vR[mdm] & B01110000) / 16) * 10 + (vR[mdm] & B00001111);
}
// Library providers' copyrights and lisences are as follows:
/************* UTFTGLUE library *********************************
   TFT-LCD 320x240 pixels.
    MCUFRIEND_kbv (C)2015 Rinky-Dink Electronics, All rights
    reserved.   web: http://www.RinkyDinkElectronics.com/
****************************************************************/
/**** for Adafruit-GFX-Library used by UTFTGLUE *****************
  Software License Agreement (BSD License)  (c) 2012 Adafruit
  Industries, All rights reserved. Redistributions in binary form
  must reproduce copyright notice. See BSD lisence conditions.
***************************************************************/
/***********ふFor Adafruit_BME280 ******************************
  Designed specifically to work with the Adafruit BME280
   http://www.adafruit.com/products/2650
  Use I2C or SPI interface. I2C adrs is 0x76 or 0x77. Adafruit
  invests time and resources providing this open source code,
  please support Adafruit and open hardware by purchasing
  products from Adafruit!
  By Limor Fried & Kevin Townsend for Adafruit Industries.
  BSD license: all above must be included in any redistribution.
  See LICENSE file for details in the library.
*****************************************************************/
// end of program

ピンをよく確認して、これを他のマイコンで動かすのにチャレンジしてみたいと思います。電流が多いので電源の与え方に注意しないといけないかも。。。

と考えたのですが、現在は似た製品が海外ネットで色々出回っていますね。タッチパネルでなければ価格も1000円未満で手ごろそうです。省エネなども少しは改善されたことでしょう。いっそ、そちらを入手して試してみる方がいいかな・・早速今注文してしまいました^^

 

2021.4.26追記 ================================

今回使用したTFT-LCDのピンの記述を調べた結果、次のものと完全に同じです。

f:id:a-tomi:20210426112141j:plain

同型の一般的な製品ではMCUにILI9341が使われているようですが、MCUFRIENDのこのTFT-LCDシールドではILI9320が使われています。それがMCUFRIENDのライブラリーを必要としている理由かなと推察。

ついでに、動作中の接続状態を調べたらSD用の4本(上の表で下4つ)以外はすべて使用しており、Arduinoとのデータのやりとりは8ビットパラレルです。表示速度は速いですが、マイコンの限られたピンを15本消耗するのは厳しい^^;。節約してもせいぜい-4本(電源を外部供給、RSTをHIGHにしCSをLOWにして固定)、それでも11本は表示用にとられます。そのうちに、目的であるTeensyで試そうと思ってはいます。。。

そういうわけで、今回注文したものはSPI接続だけででき、画面サイズがもう少し大きな3.5インチにしました。例によって到着までに数週はかかるでしょうが、ボチボチやるのでちょうどいいかもしれません。

================================== 追記end

 

以上、簡単に書かせていただきましたが、何らかのお役に立てば幸いです。

 

©2021 Akira Tominaga, All rights reserved.