2014年11月15日土曜日

Arduinoでエアコン制御 5-2 (受信データ評価)

処理の内容を書いておかないと絶対忘れる。(特にビット演算まわり)
鳥並みに揮発し易い。いい加減電脳化させてほしい。

スケッチ

       //---一般Const---
const byte pin_irin = 2;  //割込み0(INT0)=PD2=Digital 2pin
const byte pin_led = 13;  //送信確認用LED

const int cycl = 430;  //1bitの長さ[us]

       //---通信データ---
const byte snd_dt_idct[3] = {8, 4, 32};  //bit個数 reader(1),reader(0),repeat(0)
const byte snd_dt_cstm[5] = {B00100011, B11001011, B00100110, B00000001, B00000000};
const byte rcv_sw[] = {B00100000, B00011000};  //受信データ比較用

       //---volatile変数---
volatile byte itpt_in = 0;  //割込み確認フラグ
volatile unsigned long time = 0;
volatile unsigned long time_o = 0;
volatile byte time_s = 0;  //signature(符号)用

       //---一般変数---
byte unit = 0;          //単位時間 32(*cycl)以下程度
byte rcv_dt[18] = {0};  //片側144bit
byte cnt_rcv = 0;       //受信配列データ用カウンタ


//----------[1].セットアップ 2

void setup() {
       //---割込み設定---
  attachInterrupt(0, Ir_in, CHANGE);

       //---  ---
  pinMode(pin_irin, INPUT);
  pinMode(pin_led, OUTPUT);
                                   Serial.begin(115200);  //デバッグ用
}


//----------[2].

void loop() {
  Cnv_rcv();
  
  if (! Rcv_chk((byte*)rcv_sw, sizeof(rcv_sw))) {

    Ir_snd();

  }
}

//----------[3].各機能

       //---受信 + 変換---
void Cnv_rcv() {
  if (itpt_in == 1) {
    if (time - time_o > (snd_dt_idct[0] + 2) * cycl) {      //Reader code長さ(+α)より大きかったらクリア
      cnt_rcv = 0; memset((byte*)rcv_dt, 0, sizeof(rcv_dt));
    } else {
      unit = (cycl / 2 + time - time_o) / cycl;    //cylで丸め 215~644:1、~1074:2、~1504:3
      if (! time_s && unit < 4) {                   //timeがインアクティブ(1ステップ前はアクティブ)、unitが3まで
        rcv_dt[cnt_rcv / 8] |= (unit / 3) << (cnt_rcv & B00000111);
        cnt_rcv++;                                 //unitは1 or 3、カウンタの8の剰余分シフトして加算
      }
    }
    itpt_in = 0;
  }
}

       //---評価---
int Rcv_chk(byte dt[], byte s) {
  volatile byte flg = 5+s;
  byte i;
  byte j;
  
  if (cnt_rcv == 144) {
        //for (int m = 0; m < 18; m++) { Serial.print(rcv_dt[m], BIN); Serial.print(","); }
        //Serial.println("");                    //デバッグ用
    for (byte i = 0; i < 5; i++) { flg -= rcv_dt[i] == snd_dt_cstm[i]; }
    for (byte j = 0; j < s; j++) { flg -= rcv_dt[j + 5] == dt[j]; }
    cnt_rcv = 0; memset((byte*)rcv_dt, 0, sizeof(rcv_dt));
        //Serial.print(5+s);Serial.print(" - ");Serial.println(flg);      //デバッグ用
  }
  return flg;
}

       //---データ送信---
void Ir_snd() {
  Serial.print("Sending data !");
  for (byte i; i < 5; i++) {
    digitalWrite(pin_led, HIGH);delay(300);
    digitalWrite(pin_led, LOW);delay(300);
  }
}

//----------[4].割込み

       //---受信---
void Ir_in() {
  time_o = time;
  time = micros();
  time_s = (PIND >> pin_irin) & 1;  //待機時 : 0  アクティブ時 : 1
  itpt_in = 1;
}

---受信 + 変換---
信号間隔を0.43[ms]で割った単位時間を"unit"とした変数に代入している。
PINDレジスタ2ビット目――PD2 pin (割込み0(INT0)でもあり、Arduino Digital2 pinでもある。)を"time_s"に代入する事でHIGHかLOWを取得している。
エラーさえなければtime_sは0と1を交互に繰り返す。
更にtime_sが0(LOW)の時、unitが取るであろうと期待される値は1、3、4、32。
(その1ステップ前のunitは1か8)
条件分岐によりそれらの内の3まででしか処理を続行しない。
(その1ステップ前のunitは1であるはず)
"unit / 3"により0もしくは1になるように圧縮し、それを順次ビットの上に積んでいる。
受信データ数は"cnt_rcv"とした変数でカウントしている。
ビットの上に積んでいく為にcnt_rcvの8での剰余を求め、それ分だけ左にビットシフトさせたものを加算している。
(剰余は素直に%としてもちゃんと最適化してくれるかもしれないが…まあどちらがわかり易いかというところか。加算では繰上りの処理分無駄になるのではなかろうかと思い論理和でビットをセットしている。)

---評価---
Customer codeから評価用データまでを比較に用いている。
(Reader code長さは評価していない。チェックサムも"Mk_chksm"(生成する関数)で評価できようが、そこまで厳密にする必要が無い為とりあえず同様に無視した)
どこでエラーが出たとしても"Rcv_chk"が0より大きな値を返す。
(true、falseと比べると逆ではあるが、エラー回数を戻り値にしている)
とりあえずCustomer codeは5[byte]固定。
評価用データ"rcv_sw"だけ関数に渡す事にし、12[byte]用意しなくとも前方一致できる。

---データ送信---
仮でLチカさせているだけ。
上の評価用関数"Rcv_chk"による条件分岐で呼び出している。
呼び出されると一連の処理を終えるまで他に処理を渡さない。つまりほとんどの場合は受信データの前半のみ利用される事になる。
もしその前半部分で受信中にエラーを起こしていた場合はこの関数は呼び出されず、後半部分から"Cnv_rcv()"以降の処理が行われる。
よってエラーにより反応させたくない信号で反応する場合もありうるといえる。
言い換えれば誤り検出を行っていないという事であり、"反応する側"に寄せているとも言える。

逆にエラーがあった場合は反応しないようにしても良いが、少なくとも前後半でのエラーの有無を保持する変数がいるだろうから面倒に思いやめた。
チェックサムまで含め出すとまた煩雑になるし、用途的にそこまでシビアにならなくとも――命がかかっているわけでも無いのでとりあえず簡易的な評価でよかろう。

その他
「装飾子の付いた配列を関数の引数に渡す」事が最も理解できていない。
とりあえずすんなり通ったので型キャストしてみた――つまりその程度のレベルでしかない。

試作機の考察

送信機能はとりあえずでも出来ている為、単純に合体させてしまえばArduinoではほぼ完成と言ってもよいのではなかろうか。(未検証で言うのは早計か)
マイコン単体 + 電池での動作での問題は電源だろう。
 ・BODリセットの有無
 ・昇圧回路の有無
 ・安定化電源回路の有無
あるいはそこに発振回路を加えるか否かも考察対象になる。

結局、予備知識…地の知識が無い為、回路を1ユニット増やすだけでもあれこれ資料を見、しばらく検証する時間が必要になる。
まずはなるべく部品点数の少ない構成から始めるべきだろう。

とりあえずは ATtiny85 + 4.8[MHz]クリスタル + ACアダプタto可変三端子レギュレータによる電源 あたりか。
tiny85が難しかった場合は一旦mega328で試す事になるかもしれない。

0 件のコメント :

コメントを投稿