arduino nanoで内気外気をタイマーリレーで切り替え制御 -2

## 前回時の問題
前回プログラムは公開していなかったので公開。
スモールランプ点灯時にLEDの電圧が変化するため、現在内気循環なのか、外気導入かの判断をコンパレータで判断しようと苦戦していた。スモールランプ点灯時だけ内気循環時LED電圧ONとOFFの差が小さいためであった。そのため夜は現在内気なのか外気なのかの判断が結局できず、常に同じ時間間隔で切り替わるモードになるときが多かった。
これは、LED電圧はarduinoの読み取りで行っているが、この読み取り数値は割りと誤差も大きくいつでも同じではない。コンパレータでもなかなか２Vの差を検出するまでテストもできなかった。

## 改善点
電源ONの直後で内気か外気かを判断し、内気の場合は外気に切替えあとは切替えは交互にする。
このタイマー切り替えは下記画像の通り、内気循環の時間と外気の時間を別々に可変抵抗で制御できるが、このそれぞれの時間の割り当ては一度エンジンを始動し外気にして、後は交互にすればよいだけ。エンジン始動時にスモールランプOFFであれば都度の判断は必要ない。
もともと自分は最初にスモールランプやライト点灯してからエンジン始動するくせはないのでこれで十分に実用的になった。
![timer2_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200517/eb748318-f072-6109-48b6-f21eb8d9e2e6.jpeg)

###ソースコード


```  timer3

/*
  Customized from TouchTimer sample code
  For control two timer
  Created by real_free account
*/
#include <MsTimer2.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd( 12, 11, 9, 8, 7, 6 );

#define LED2_PIN  2  // D2 pin (NANO Board)

int iloopCnt = 0;

const int POT1 = A4;
const int POT2 = A2;
const int POT3 = A3;

const float fPotJudgeVal = 30.0;    // 10Vより高い場合は外気でanalogReadは不定となり200以上になるはず
const float fPotInvalidVol = 600.0;  // 1000より高い場合は不正

// ホンダインスパイアエアコン内気循環のLED電圧は外気の時12V
// 12V の時リレーにてA0をアースする A0 valueが低いときは外気
float recycleValue = 0.0;        // value read from the A0 pot
int sensorValue1 = 800;        // value read from the pot1
int sensorValue2 = 1200;        // value read from the pot2

const int iMaxInnerRate = 70; // 内気循環の最大倍率、ほぼ秒数
const int iMaxOuterRate = 50; // 外気時の最大倍率、ほぼ秒数
const int iMaxAcceratorRate = 6;

int iInterval = 0; // 現在、内気外気の判断ができない場合は同じタイマー値とせずインターバルを行う
int iInvalidf = 0;

int s_indexStart = 0; // 最初だけ現在の値をチェックして後はインターバルとする

long lnextPotTime = 4000; 
const int iTouchTime = 1200;
static char sBuffer[48];
int iLn2Mode = 0;  // 0: potval, 1: recycle

// 割り込み時に処理される関数
void pulsewave() {
  long potval2;
  long potval3;

  digitalWrite(LED2_PIN, HIGH);
  Serial.print("---- pulsewave in ----- \n");
  MsTimer2::stop();
  noInterrupts();           // disable all interrupts
 
  Serial.print("recycleValue= ");
  Serial.print(recycleValue);
  Serial.print("\n");
  iInvalidf = 0;
  if (recycleValue > fPotInvalidVol || recycleValue < 0.0)
  {
    Serial.print("recycleValue is invalid\n");
    iInvalidf = 1;
    // 電圧取得の値が不正
    if (iInterval == 1)
    {
      recycleValue = 10.0;
    }
    else
    {
      recycleValue = fPotJudgeVal + 30.5;
    }
  
  }
  // 画面をクリア
  lcd.clear();
  // 表示位置を設定(1行目1文字目)
  lcd.setCursor(0,0);
  if (iInterval == 0)  //recycleValue > fPotJudgeVal)
  {
    Serial.print("Inner OFF OuterAir  \n");
    if (iInvalidf)
    {
      lcd.print("OFF Invrd r=");
      lcd.print(recycleValue);
    }
    else {
      lcd.print("Inner OFF OutAir ");
    }

    potval2 = sensorValue1;
    Serial.print("sensorValue1=");
    Serial.print(sensorValue1);
    Serial.print("  \n");
    if (potval2 < 100) {
      potval2 = 100; 
    }
    if (potval2 > 1000)
    {
      potval2 = potval2 * iMaxAcceratorRate;
    }
    potval2 = potval2 * iMaxOuterRate;
    lcdPrintf((char* )"potval: ", potval2, 1);
    lnextPotTime = potval2;
    iInterval = 1;
  }
  else
  {
    Serial.print("Inner ON\n");
    if (iInvalidf)
    {
      lcd.print("ON Invrd r=");
      lcd.print(recycleValue);
    }
    else {
      lcd.print("Inner ON RecyAir ");
    }

    potval3 = sensorValue2;
    Serial.print("sensorValue2=");
    Serial.print(sensorValue2);
    Serial.print("\n");
    if (potval3 < 100) {
      potval3 = 100; 
    }
    if (potval2 > 1000)
    {
      potval3 = potval3 * iMaxAcceratorRate;
    }
    potval3 = potval3 * iMaxInnerRate;
    lcdPrintf("potval: ", potval3, 1);
    lnextPotTime = potval3;
    iInterval = 0;
  }
  MsTimer2::set(iTouchTime, touchOff); // 関数 touchOff()を呼び出し
  MsTimer2::start();               // タイマー割り込み開始
  
  interrupts();           // enable all interrupts
  iloopCnt= 1;

  //
}
void touchOff()
{
  MsTimer2::set(lnextPotTime, pulsewave); // potval2 sec 毎の割り込み、関数flash()を呼び出し
  MsTimer2::start();               // タイマー割り込み開始
  //Serial.print("lnextPotTime=");
  //Serial.print(lnextPotTime);
  //Serial.print("\n");
  if (iloopCnt% 3 == 0)
  {
     lcdPrintf("recycleValue=", recycleValue, 0);
  }
  digitalWrite(LED2_PIN, LOW);
}

// the setup routine runs once when you press reset:
void setup() {
  long potval2;
  long potval3;
  
  Serial.begin(115200);

  sBuffer[0] = '\0';
  lcd.begin(16, 2);
  lcd.clear();
  lcd.setCursor(0, 0);
 

  
  analogReference(DEFAULT);
  // read the analog in value:
  recycleValue = analogRead(POT1);
  Serial.print("recycleValue=");
  Serial.print(recycleValue);
  Serial.print("\n");
  //float f_inVoltage = (recycleValue*5)/1024;
  //Serial.print("f_inVoltage= ");
  //Serial.print(f_inVoltage);
  //Serial.print("\n");
  MsTimer2::set(potval2, pulsewave); // potval2 sec 毎の割り込み、関数flash()を呼び出し
  MsTimer2::start();               // タイマー割り込み開始
  Serial.print("potval2=");
  Serial.print(potval2);
  Serial.print("\n");
  lcdPrintf("potval2=", potval2, 0);

  pinMode(LED2_PIN, OUTPUT);
  if (recycleValue > fPotJudgeVal)
  {
    digitalWrite(LED2_PIN, HIGH);
    Serial.print("Inner OFF OutAir  \n");
    lcdScrollDisp("InnOFF in setup");
    iInterval = 1;
    MsTimer2::set(iTouchTime, touchOff); // 関数 touchOff()を呼び出し
    MsTimer2::start();               // タイマー割り込み開始
  }
}

 void lcdScrollDisp(const char * pStr)
{
  lcd.print(pStr);
  return;
  // スクロール設定
  lcd.autoscroll();
  // 16文字が流れ切るまでなので、2倍分ループ
  for (int iIdx = 0; iIdx < 32; iIdx++) {
    // 表示文字数以内であるか判定
    if (iIdx < strlen(pStr)) {
      // 変数iの位置の1文字表示
      lcd.print(pStr[iIdx]);
    } else {
      // 空文字表示
      lcd.print(" ");
    }
    // 0.5秒待つ
    delay(500);
  }
  // スクロール停止
  lcd.noAutoscroll();
}
// ibufMode: 1の場合はバッファに退避して、それを定期的に表示する
//           前回同じ文字列を表示しようとするときは、そのまま表示、
void lcdPrintf(char* pName, long lval, int ibufMode)
{
  char buf[48];
  lcd.setCursor(0, 1);
  sprintf(buf, "%s%ld", pName, lval);
  if (ibufMode == 1)
  {
    if (strncmp(buf, "potval", 6) == 0)
    {
      while (strlen(buf) < 17)
      {
        strcat(buf, "  ");
      }
      if (strcmp(sBuffer, buf) != 0)
      {
        strcpy(sBuffer, buf);
      }
      iLn2Mode = 0;
    }
    if (iLn2Mode == 1)
    {
        iLn2Mode = 0;
    }
    else {
        iLn2Mode = 1;
        strcpy(buf, sBuffer);
    }
  }
  lcd.print(buf);
}
void lcdPrintf2(char* pName, long lval, long lval2)
{
  char buf[56];
  lcd.setCursor(0, 1);
  sprintf(buf, "%s %ld, %ld", pName, lval, lval2);
  lcd.print(buf);
}

// the loop routine runs over and over again forever:
void loop() {

  //while (1)
  //{
  noInterrupts();           // disable all interrupts
  recycleValue = analogRead(POT1);
  float convert_v = (recycleValue*5)/1024;
  sensorValue1 = analogRead(POT2);
  sensorValue2 = analogRead(POT3);
  Serial.print("LOOP:recycle Vol=");
  Serial.print(convert_v);
  Serial.print("\n");
  Serial.print("LOOP:sensorValue1=");
  Serial.print(sensorValue1);
  Serial.print("      ");
  Serial.print("LOOP:sensorValue2=");
  Serial.print(sensorValue2);
  Serial.print("\n");
  if (s_indexStart < 2) {
    if (recycleValue > fPotJudgeVal)
    {
      iInterval = 1;
    }
    s_indexStart++;
  }
  interrupts();           // enable all interrupts
  //lcdPrintf2("lvl: ", sensorValue1, sensorValue2);
  delay(1500);
  iloopCnt++;
  if (iloopCnt % 3 == 0)
  {
    lcdPrintf("recycleValue=", recycleValue, 1);
  }
}
```