勝手な電子工作・・

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

テレワークは換気に注意ーArduinoでCO2ロガー(その2):赤外線吸収型

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

テレワークで部屋に籠ることが多い場合は換気が必要です。実際に2酸化炭素が1000PPMを超えると眠くなったり飽きたり・・・。前々回作った測定装置でそうなるのを体感しました。

梅雨明けした暑い夏は、部屋にエアコンかけて閉じっぱなしにすると益々危険そう。今回は赤外線吸収型(NDIR=nondispersive infrared )のCO2センサーを使って室内の炭酸ガス濃度ロガーを作ってみました。

2酸化炭素が4260nmの赤外線を吸収するという原理を使う古典的な方法のセンサーですが、長年にわたって改良されてきたもので小型化や高性能化が色々行われています。上の写真は今回使うMH-Z19Bという安価なセンサーモジュールです。

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

前々回の記事に金属酸化物を使ったCCS811半導体センサーで作ったロガーを紹介しました。なぜ、新たにNDIRセンサーでロガーを作ってみるかと言えば、半導体センサー(動作の殆どは内蔵ソフトウェアの機能)は比較的新しいセンサーで鋭敏なのですが、長時間記録するロガーとするには結構手間のかかるキャリブレーション(Baseline registerの設定)が度々必要です。

ArduinoのCCS811標準ライブラリーでは、なんと2019年1月にその設定機能が追加されたばかりです。現在それを別プログラムで行っていますが、校正がしばしば必要なのは、扱いが結構手間です。2台作った測定値の間にはこれまで大きな狂いはないですが、正しい数値かどうかを別原理のロガーと比較してみたいという興味もあります。

NDIRセンサーは多くのメーカーから工夫された安定製品がでていますが、どれも比較的高価です。調べた限りは、長期間の使用では当然キャリブレーションを必要としますが、方法も色々あります。MH-Z19Bではシンプルな方法3つが提供されていますし、なによりも安価(海外ネットでは15ドル程ですから、CCS811の倍ぐらい)なので、これを2個入手して挑戦しました。

測定値のとりかたには次の3通りあります。

①シリアル(UART)通信でコマンドを送り、値をデジタルで直接得る

②PWMのDuty幅(アナログ値)を介して得る

③アナログ出力値を読み取る

まず一番簡単な③をやってみたら旨く測れるのですが、なんと!周期的に特定ノイズが載ってきます。2個とも同じ現象があり、配線の問題ではなくこのセンサーモジュール自体の性格のようです。これを無視するようにプログラムすればできそうですが、この際は③を使わないことにしました。ついでに②もけっこう面倒そうなので、一番確実な①でやってみることにしました。

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

試しに値をとりプロットすると、おお!長時間忠実にアウトプットされるではないですか!

上は測定開始1010秒(約17分)後から2610秒(約44分)後の間のグラフです。エアコンの風があたっている間は測定値がギザギザになることがわかりました。空気取り入れ口にフィルターがあるからそういう現象になるかも。風のあたらない場所で測るとスムーズです。しかし、風が当たる場所でもプログラムでスムージングすれば問題ない感じです。

このセンサーは約1秒強の間隔でデータが得られますが、ロガーとしては安全のために2秒間隔で測定し毎分1度記録するとします。たとえば測定値を指数平滑法を使って、測定値x0.25+前回保存値x0.75でスムージングするとした場合、仮に500ppmの急激な変化があっても、記録する1分(つまり測定29回)以内で追従できます(計算してみました)ので問題ないわけです。

さて、UART接続したArduinoスケッチは次のように短い超簡単なものです。

/* CO2 plotter example with NDIR MH-Z19b via serial interface  */
#include "MHZ19.h"
#include "SoftwareSerial.h"
#define Rx 8                      // Master Rx (MISO) = MHZ19 Tx
#define Tx 9                      // Master Tx (MOSI) = MHZ19 Rx
#define swSerB 9600               // MH-Z19 spec. serial baud rate
MHZ19 NDIR;                       // MH-Z19 symbol to be NDIR
SoftwareSerial swSer(Rx, Tx);     // (Uno example) create device to MH-Z19 serial
void setup(){ // ***** Arduino setup *****
  Serial.begin(9600);             // start hardware serial to PC
  swSer.begin(swSerB);            // start software serial to MH-Z19
  delay(100);
  NDIR.begin(swSer);              // begin NDIR CO2 sensor MHZ19
  NDIR.autoCalibration();         // set auto caliration TRUE
}
void loop(){ // ***** Arduino loop *****
  int CO2 = NDIR.getCO2();        // get CO2 PPM
  int lL = 400;  int hL = 1000;   // set scale lines
  Serial.print(lL);  Serial.print(",");  Serial.print(CO2);
  Serial.print(",");  Serial.println(hL);
  delay(2000);                    // measure every 2 seconds
}
// *** end of sketch ***

 このスケッチをためして気づいた重要なことは、次の2つです。

①これにArduino標準ライブラリーを使うとマイコン容量をかなり消耗する。ロガーには他のデバイスも複数使用するため、そのやりかたではUNOではとても困難になる。

②MH-Z19というセンサーモジュールは4ピンと5ピンの列があるが、列間距離の仕様が異常。このままブレッドボードなどに刺すとモジュールを壊す恐れがある。

①についてはコンパイル結果が次のとおりで、グローバル変数で実に34%消耗!

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

 ②については、サイズの仕様が次のとおりです。

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

29.54mmという間隔には、ホントにまいってしまいます!端数にわざわざ0.54mmをつけたりして。30.48mmとか、27.94mmとかなら適切で納得できますが。・・・きっと過去に仕様をきめたときの誰かの間違いが長年ずっとひきずっているのでしょうかね。

とにかくこのままブレッドボードなどに刺してはだめ(データシートにはPCBに無理な力をかけないようにと注意書きがあります、ということはそういうことで壊す例が多い?)。とにかく気を付けましょう。

さて、標準ライブラリーが大きすぎるなら、UNOをあきらめて別のモデルを使う方法もありますが、UNOにこだわるなら手で書けばよいわけです。このセンサーとのやりとりも、ソフトウェアSerialも、複雑なものではありませんから。次のようにRYO(Roll-Your-Own)プログラムに書き換えれば問題なく動作をしますし、容量もあまり食いません。

/* CO2 (PPM) testing plotter with MH-Z19b NDIR sensor,
 * w/RYO serial-com. & RYO handling V00 Aug.2000 (c) Akira Tominaga */
#define sRx 8                     // Master Rx (MISO) = MHZ19 Tx
#define sTx 9                     // Master Tx (MOSI) = MHZ19 Rx
// for serial sending
#define sTuS 104-3                // time unit (9600bps) for sending
#define sTuhS 51                  // time unit half for sending                  
char sByte;                       // character work area to be sent
// for serial receiving
#define tmChk 16                  // timing checker for test (D16=A2)
#define sTuR 104-10               // time unit (9600bps) for receive
#define sTuhR 51-21               // time unit / 2 for receive
byte rByte;                       // character receiving byte
// for MH-Z19b, preset & auto-calib since July 2015, hence no setup required doday.
//  Ref:Datasheet v1.0 (by Zhengzhou Winsen Electronics Technology Co.,Ltd)
byte rCO2cmd[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
//               flags, req, rCO2,  0,    0,    0,    0,    0,  chksum
byte hV;                          // response byte2 high value
byte lV;                          // response byte3 low value
uint16_t CO2;                     // CO2=hV*256+lV

void setup(){  // ***** Arduino setup *****
  Serial.begin(9600);             // start hardware serial to PC
  pinMode(sRx, INPUT_PULLUP);     // soft Rx to be pulled-up
  pinMode(sTx, OUTPUT);           // soft Tx as output
  digitalWrite(sTx, HIGH);        // set it high
  pinMode(tmChk, OUTPUT);         // timing checker
  digitalWrite(tmChk, LOW);       // set it LOW
}

void loop(){  // ***** Arduino loop *****
  // send "Read CO2" command
  for (uint8_t j=0; j<9; j++){
    sByte=rCO2cmd[j];
    sSend();
  }
  // receive CO2 value
    sRcv();                     // byte0 is ignored (0x86 response) 
    sRcv();                     // byte1 is ignored (sensor number)
    sRcv();                     // byte2 is CO2 value high
    hV=rByte;                   // set it to highValue
    sRcv();                     // byte3 is CO2 value low
    lV=rByte;                   // set it to lowValue
    for (uint8_t j=0; j<5; j++){ // read and ignore residual 5 bytes
      sRcv();
    }
    CO2=hV*256+lV;              // calculate CO2 PPM value
  // plot scales and CO2 via hardware serial to PC
  int lowL = 400;  int highL = 1000; // set scales
  Serial.print(lowL);   Serial.print(",");  Serial.print(CO2);
  Serial.print(",");  Serial.println(highL);
  delay(2000);                 // measure every 2 seconds
}

/**************************************
      User defined functions
* *********************************** */
// *** sSend *** send a charater in sByte
void sSend(void) {
#define LSb B00000001             // Least Significant bit
  digitalWrite(sTx, LOW);
  delayMicroseconds(sTuS);

  for (int i = 0; i < 8; i++) {   // do the following for 8bits
    if ((sByte & LSb) == LSb) {   // if one then
      digitalWrite(sTx, HIGH);    // set sTx high
    } else {                      // else
      digitalWrite(sTx, LOW);     // set sTx low
    }
    delayMicroseconds(sTuS);      // take required time
    sByte = sByte >> 1;           // shift a bit to right
  }

  digitalWrite(sTx,  HIGH);       // set high as a stoP bit
  delayMicroseconds(sTuS + sTuhS); // consume time-unit x 1.5
}

// *** sRcv *** receive a charater in rByte
void sRcv(void) {
#define MSb B10000000             // Most-Significant-bit
#define nMSb B01111111            // not Most-Significant-bit
  while(digitalRead(sRx)==HIGH){} // wait for start bit
  delayMicroseconds(sTuR + sTuhR); // skip start-bit + sampling-timing
  rByte = 0x00;   // ** for debug
  for (uint8_t i = 0; i < 8; i++) { // do the following for 8bits
    digitalWrite(tmChk, HIGH);    // sampling-time-checker on
    digitalWrite(tmChk, LOW);     // sampling-time-checker off
    if (digitalRead(sRx) == HIGH) { // if sRx high, then
      rByte = rByte | MSb;        // set MSb on
    } else {                      // else
      rByte = rByte & nMSb;       // set MSb zero
    }
    if (i < 7) rByte = rByte >> 1; // shift rByte excluding last i
    delayMicroseconds(sTuR);
  }
  while (digitalRead(sRx) == LOW) {}; // wait until HIGH (stop bit)
}
/* End of program */

標準ライブラリーではSetupでABC設定(Automatic Baseline calibration)コマンドを送りますが、こちらでは出しません。このデバイスの最近の仕様は出荷時にABC設定済で設定不要となっており、設定の必要性がありません。このスケッチで標準ライブラリーと同じく長時間安定した値が測定されます。

これならArduino Mega2560とかESPなどを持ち出さなくとも、Arduino UNO(ATmega328P)単体で余裕をもってできそうです。なお、タイミングが重要なシリアル通信などをどうやって簡単にRYOで作ったり観察したりするかの方法は、少し長くなりそうなので回を改めてご紹介したいと思います。

 ロガーの回路は次のようにすれば、5.0V供給だけですむので前回の半導体センサーよりだいぶ単純です。毎度汚い手書きですみませんが。じつはこれを考えてからプロッターでのテストをしてみて、ソフトのRYO化が必要なことに気づいたのですが。

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

次はブレッドボードで試作したロガーです。

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

今回のセンサーはまだキャリブレーションをしていませんが、数値は前回作ったロガー(下の写真の下側)とあまり大きくは違いません。キャリブレーションはこのセンサーに接続したスライドスイッチをオンに(接地)するだけでできるようにしました。CO2が400ppm以下の環境を必要とするので、緑の多いところで光合成の盛んな午後に行うのが妥当かと思います。(あるいはCO2ゼロで赤外線吸収のない窒素ガスかアルゴンガスの中でやればよいのですが、そういうのはなかなかね)。

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

 

今回のロガーのプログラムでは、比較的複雑なSD入出力、SPI通信、I2C通信に標準ライブラリーを使い、単純なRTC(リアルタイムクロック)、TM1637表示、このMH-Z19センサー処理、およびソフトウェアシリアル通信をRYOにすることで、十分小さく収まりました。そういうわけでプログラムの記述は少し長いですがArduinoスケッチは次のとおりです。

(2020.8.11 下のスケッチで2行を直接直しました。①回路図に合わせてピン8と9を逆にしました。②Setupの最後の待ち時間を1000ミリ秒に直しました。前の100ミリ秒の場合にはMH-Z19センサーがReadyになってないことがまれにありますので。最近作った基板とともに使い心地の比較等を含めて、今後別記事で最新プログラムとともに改めて紹介するつもりです。)

/*
   CO2 Logger with MH-Z19b NDIR sensor,
   with RYO coding for RTC, TM1637, MHZ19, and Soft-Serial
        Initial version V.00 Aug.1, 2020 (c) Akira Tominaga
*/
#include "Wire.h"             // I2C for RTC-DS3231 
#include "SPI.h"              // SPI for SD drive interface
#include "SD.h"               // Micro SD drive

// *** for micro SD writer ***
File aqLog;                   // aqLog as SD file symbol
#define Cs  10                // SD Chip-select pin is D10
//  MOSI=11, MISO=12, CLK=13
#define SDsw 4                // button to close SD file

// *** for TM1637 display ***
#define lDIO 6                // DIO for TM1637 LED
#define lCLK 7                // CLK for TM1637 LED
#define lBrt 0x02             // duty-r 1/16x 02:4,03:10,05:12,07:14
#define lTu 50                // time unit in micro-second
byte lChr;                    // single byte sent to LED
byte Data[] = { 0x00, 0x00, 0x00, 0x00 }; // LED initial value 0000

// *** UART for CO2 sensor ***
#define sRx 9                 // Master Rx (MISO) = MHZ19 Tx
#define sTx 8                 // Master Tx (MOSI) = MHZ19 Rx
// (for sending)
#define sTuS 104-3            // time unit (9600bps) for sending
#define sTuhS 51              // time unit half for sending                  
char sByte;                   // character work area to be sent
// (for receiving)
#define tmChk 16              // timing checker for test (D16=A2)
#define sTuR 104-10           // time unit (9600bps) for receive
#define sTuhR 51-21           // time unit / 2 for receive
byte rByte;                   // character receiving byte

// *** for NDIR CO2 sensor MH-Z19b
byte rCO2cmd[] = {0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79};
//               flags, req, rCO2,  0,    0,    0,    0,    0,  chksum
// Auto-calib & preset since Jul.2015, hence no setup required as of doday.
// Ref:datasheet v1.0 (by Zhengzhou Winsen Electronics Technology Co.,Ltd)
byte hV;                      // response byte2 high value
byte lV;                      // response byte3 low value
uint16_t CO2=600;             // CO2 ppm smoothed value, init. 600
char strCO2[5];               // edit. area for SD recds and display

// *** for Real Time Clock DS3231
byte  vR[8];                  // values in RTC registers
int vI[8] = { 0, 0, 0, 0, 0, 0, 0, 0};  // RTC data, init 0000
#define RTC_addr 0x68
#define mdI 0                 // Initial mode
#define mds 1                 // ss
#define mdm 2                 // mm
#define mdh 3                 // hh
#define mdW 4                 // WW=Day of Week
#define mdD 5                 // DD
#define mdM 6                 // MM
#define mdY 7                 // YY
#define sS 0
#define mM 1
#define hH 2
char  strMDhm[11];            // edit area for calendar and clock
uint8_t mmSv;                 // minute-llvalue save area

void setup()  // ***** Arduino setup *****
{
  Serial.begin(9600);         // start hardware serial to PC
  // *** for RYO-SW-UART
  pinMode(sRx, INPUT_PULLUP); // soft Rx to be pulled-up
  pinMode(sTx, OUTPUT);       // soft Tx as output
  digitalWrite(sTx, HIGH);    // set it high
  pinMode(tmChk, OUTPUT);     // timing checker
  digitalWrite(tmChk, LOW);   // set it LOW
  // *** for RYO-TM1637 interface
  pinMode(lCLK, OUTPUT);      // lCLK as output
  pinMode(lDIO, OUTPUT);      // lDIO as output
  digitalWrite(lCLK, HIGH);   // lCLK high
  digitalWrite(lDIO, HIGH);   // then lDIO high to avoid Start sig
  lDispData();                // display 0000
  // *** for SD
  pinMode(SDsw, INPUT_PULLUP); // SD file closing button
  Wire.begin();
  if (!SD.begin(Cs)) {
    Data[0]="E";              // show error
    lDispData;                // display E000
    while (1) {}
  }
  // set SD file name MMDDhhmm
  getTime();
  String s = strMDhm;
  s.concat(".txt");           // complete file name
  aqLog = SD.open(s, FILE_WRITE);
  aqLog.println("MMDDhhmm,CO2"); // write column hdr
  mmSv = vI[mdm];             // save current minute value
  delay(1000);                // enough time for stability
}

void loop()  // ***** Arduino loop *****
{
  getTime();
  // *** send "Read CO2" command ***
  for (uint8_t j = 0; j < 9; j++) {
    sByte = rCO2cmd[j];
    sSend();
  }
  // *** get CO2 value ***
  sRcv();                     // byte0 is 0x86
  sRcv();                     // byte1 is Sensor number
  sRcv();                     // byte2 is CO2 value high
  hV = rByte;                 // set it to hV
  sRcv();                     // byte3 is CO2 value low
  lV = rByte;                 // set it to lV
  // for (uint8_t j = 0; j < 5; j++) { // read residual 5 bytes
    // sRcv();                         // *** can be ignored
  // }                                 // hence, test use only
  uint16_t mCO2 = hV * 256 + lV; // get CO2 PPM value
  CO2=(float)(0.250*mCO2+0.750*CO2); // smoothed with exp.factor 0.25
  // plot bottom, ceiling and CO2 to PC via HW-serial
  int lowL = 400;
  int highL = 1000;
  Serial.print(lowL);
  Serial.print(",");
  Serial.print(CO2);
  Serial.print(",");
  Serial.println(highL);
  edDisp();                    // edit CO2 Data and display
  
  // *** Write SD data every minute ***
  if (vI[mdm] != mmSv) {       // if minute changed
    mmSv = vI[mdm];            // save new minute
    String s = strMDhm;        // set date-time
    s.concat(",") ;            // set comma
    s.concat(strCO2);          // set data
    aqLog.println(s);          // write a record
    //Serial.println(s);       // this is for debug only
  }
 
  // *** 2s delay, w/ SD-close-button checks every 100mS ***
  for (int i = 0; i < 20; i++) {
    if (digitalRead(SDsw) == LOW) { // if SD sw to close
      aqLog.close();
      // clear display and stop
      for (int j=0; j<4; j++){
        Data[j]=0x10;
      }
      lDispData();
      while (1) {}
    }
    delay(100);
  }
}

/**************************************
      User defined functions
* *********************************** */
// *** edDisp() *** Edit and display values
void edDisp(void) {
  sprintf(strCO2, "%04d", CO2);
  // display CO2 value via Data
  for (int i = 0; i < 4; i++) {
    Data[i] = strCO2[i] & 0x0F;
  }
  lDispData();
}

// *** lDispData() *** display data to TM1637 4digits
void lDispData(void) {
#define showDat 0x40          // show this is data
#define showAd0 0xC0          // LED register addr is zero
#define showDcB 0x88+lBrt     // show dCtl + brightness
  lStart();                   // start signal
  lChr = showDat;             // identifier for data
  lSend();                    // send it
  lStop();                    // stop signal
  lStart();                   // and restart
  lChr = showAd0;             // identifier for address
  lSend();                    // send it
  for (int j = 0; j < 4; j++) { // for Data[0] to Data[3]
    byte edChr = Data[j];     // set a byte to edChr for edit
    switch (edChr) {
      case 0x00: lChr = 0x3F; break; // 0
      case 0x01: lChr = 0x06; break; // 1
      case 0x02: lChr = 0x5B; break; // 2
      case 0x03: lChr = 0x4F; break; // 3
      case 0x04: lChr = 0x66; break; // 4
      case 0x05: lChr = 0x6D; break; // 5
      case 0x06: lChr = 0x7D; break; // 6
      case 0x07: lChr = 0x07; break; // 7
      case 0x08: lChr = 0x7F; break; // 8
      case 0x09: lChr = 0x6F; break; // 9
      case 0x10: lChr = 0x00; break; // blank
      default:   lChr = 0x79;        // E for error
    }
    lSend();                  // send each byte continuously
  }                           // end of for bytes
  lStop();                    // stop signal
  lStart();                   // restart
  lChr = showDcB;             // identifier for display brightness
  lSend();                    // send it
  lStop();                    // stop signal
}

// *** lSend() *** send a charater in lChr to TM1637
void lSend(void) {
#define LSb B00000001           // Least Significant bit
  for (int i = 0; i < 8; i++) { // do the following for 8bits
    if ((lChr & LSb) == LSb) {  // if one then
      digitalWrite(lDIO, HIGH); // set lDIO high
    } else {                    // else
      digitalWrite(lDIO, LOW);  // set lDIO low
    }
    lCLKon();                   // clock on to show lDIO
    lChr = lChr >> 1;           // shift bits to right
  }
  digitalWrite(lDIO, LOW);      // pull down during Ack
  lCLKon();                     // clock on to simulate reading
}

// *** lStart() *** send Start signal to TM1637
void lStart(void) {
  digitalWrite(lDIO, LOW);
  delayMicroseconds(lTu);
  digitalWrite(lCLK, LOW);
  delayMicroseconds(lTu);
}

// *** lStop() *** send stoP signal to TM1637
void lStop(void) {
  digitalWrite(lCLK, HIGH);
  delayMicroseconds(lTu);
  digitalWrite(lDIO, HIGH);
  delayMicroseconds(lTu);
}

// *** lCLKon() ** TM1637 clock on to show data to TM1637
void lCLKon(void) {
  digitalWrite(lCLK, HIGH);
  delayMicroseconds(lTu);
  digitalWrite(lCLK, LOW);
  delayMicroseconds(lTu);
}

// *** getTime() *** get RTC MMDD & hhmm into strMDhm
void getTime(void) {
  rRTC();      // read RTC device
  cR2I();      // convert RTC-format to Integers
  sprintf(strMDhm, "%02d%02d%02d%02d", vI[mdM], vI[mdD], vI[mdh], vI[mdm]);
}

// *** rRTC() *** read Real-Time-Clock
void rRTC(void) {
  Wire.beginTransmission(RTC_addr);
  Wire.write(0x00);
  Wire.endTransmission();
  Wire.requestFrom(RTC_addr, 7);
  while (Wire.available() < 7) {}  // wait for data ready
  for (int i = 1; i < 8; i++) {
    vR[i] = Wire.read();
  }
  Wire.endTransmission();
}

// *** cR2I() *** convert RTC-format to Integers
void cR2I(void) {
  vI[mds] = ((vR[mds] & B01110000) / 16) * 10 + (vR[mds] & B00001111);
  vI[mdm] = ((vR[mdm] & B01110000) / 16) * 10 + (vR[mdm] & B00001111);
  vI[mdh] = ((vR[mdh] & B00100000) / 32) * 20 + ((vR[mdh] & B00010000) / 16) * 10 + (vR[mdh] & B00001111);
  vI[mdW] = vR[mdW];
  vI[mdD] = ((vR[mdD] & B01110000) / 16) * 10 + (vR[mdD] & B00001111);
  vI[mdM] = ((vR[mdM] & B00010000) / 16) * 10 + (vR[mdM] & B00001111);
  vI[mdY] = ((vR[mdY] & B11110000) / 16) * 10 + (vR[mdY] & B00001111);
}

// *** sSend() *** send a charater in sByte via SW UART
void sSend(void) {
#define LSb B00000001         // Least Significant bit
  digitalWrite(sTx, LOW);
  delayMicroseconds(sTuS);

  for (int i = 0; i < 8; i++) { // do the following for 8bits
    if ((sByte & LSb) == LSb) { // if one then
      digitalWrite(sTx, HIGH); // set sTx high
    } else {                  // else
      digitalWrite(sTx, LOW); // set sTx low
    }
    delayMicroseconds(sTuS);  // take required time
    sByte = sByte >> 1;       // shift a bit to right
  }

  digitalWrite(sTx,  HIGH);   // set high as a stoP bit
  delayMicroseconds(sTuS + sTuhS); // consume time-unit x 1.5
}

// *** sRcv() *** receive a charater in rByte, via SW UART
void sRcv(void) {
#define MSb B10000000          // Most-Significant-bit
#define nMSb B01111111         // not Most-Significant-bit
  while (digitalRead(sRx) == HIGH) {} // wait for start bit
  delayMicroseconds(sTuR + sTuhR); // skip start-bit + sampling-timing
  rByte = 0x00;   // ** for debug
  for (uint8_t i = 0; i < 8; i++) { // do the following for 8bits
    digitalWrite(tmChk, HIGH); // sampling-time-checker on
    digitalWrite(tmChk, LOW);  // sampling-time-checker off
    if (digitalRead(sRx) == HIGH) { // if sRx high, then
      rByte = rByte | MSb;     // set MSb on
    } else {                   // else
      rByte = rByte & nMSb;    // set MSb zero
    }
    if (i < 7) rByte = rByte >> 1; // shift rByte excluding last i
    delayMicroseconds(sTuR);
  }
  while (digitalRead(sRx) == LOW) {}; // wait until HIGH (stop bit)
}

/* End of program */

 このスケッチのコンパイルサイズは次のとおり、UNOの容量の50%台にしっかり収まっています。

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

測定値はいままでのところ長時間使っても安定しており、数値には換気がしっかり反映されています。とりあえず、めでたしめでたし^^

 

では今回はこの辺で。

 

追加記事はこちらにあります:

a-tomi.hatenablog.com

 

©2010 Akira Tominaga, All rights reserved.