CAN制御機器の製作(7) ホスト・ノード(温度読み出し)

前回掲載した、温度測定ノードから周期的に送られてくる温度値を受け取るホスト・ノードを作ります。

●ホスト・ノードの改造

CAN制御機器の製作(5) ホスト・ノード(リレー・ノード用)」で製作したホストノードは、CAN送信のみの一方通行でしたが、これにCAN受信機能を付けて双方向にします。

リレーの操作関係はそのまま残すため、引き続きリレー・ノードの操作にも使えます。

●ホスト・ノードのプログラム (can_host_RT.ino)

実際のプログラムで変更箇所を中心に説明します。

//
// CAN ホスト・ノード
//   13/11/19 www.wsnak.com
//   13/11/20 温度ノードからの温度受信処理追加
//
//   シリアルコマンド制御
//     リレー操作コマンド リレーON "r1"~"r5"、リレーOFF "n1"~"n5"
//

#include <wCan2515.h>

wCan2515 Can(2, 4, 3, 5, CAN_BRP_16MHz_125KBPS);  // CANインスタンス
// D2:SCK, D3:SO, D4:SI, D5:SS (SI/SOクロス)

byte CanBuf[14];    // CAN送受信バッファ

int LedPin = 13;    // オンボードLEDのポート番号

// リレー・ノード CANメッセージID
#define cmRELAY1_OFF 0x090   // "000 1001 0000"
#define cmRELAY1_ON  0x091   // "000 1001 0001"
#define cmRELAY2_OFF 0x0A0   // "000 1010 0000"
#define cmRELAY2_ON  0x0A1   // "000 1010 0001"
#define cmRELAY3_OFF 0x0B0   // "000 1011 0000"
#define cmRELAY3_ON  0x0B1   // "000 1011 0001"
#define cmRELAY4_OFF 0x0C0   // "000 1100 0000"
#define cmRELAY4_ON  0x0C1   // "000 1100 0001"
#define cmRELAY5_OFF 0x0D0   // "000 1101 0000"
#define cmRELAY5_ON  0x0D1   // "000 1101 0001"
#define cmRELAY6_OFF 0x0E0   // "000 1110 0000"
#define cmRELAY6_ON  0x0E1   // "000 1110 0001"

// 温度測定ノード CANメッセージID
#define cmMSG_TMP1_VAL (2<<7 + 1)       // 温度測定ノード1
#define cmMSG_TMP2_VAL (3<<7 + 1)       // 温度測定ノード2
#define cmMSG_TMP3_VAL (4<<7 + 1)       // 温度測定ノード3
#define cmMSG_TMP4_VAL (5<<7 + 1)       // 温度測定ノード4

char strBuf[16];
char cmdStr[16];
byte cmdPtr;

// 動的にメッセージを作る関数(範囲オーバしないよう注意)
int makeMsg(int node, byte rynum, byte on_off) {
  return (node<<7) + (rynum<<4) + on_off;
}

// 初期化
void setup(void) {
  byte dat[8];      // データ用バッファ
  int msg = 0x001;  // メッセージID(SID値)
  byte dlc = 0;     // データ長(0バイト)

  Serial.begin(19200);            // シリアル初期化
  cmdPtr = 0;
  pinMode(LedPin, OUTPUT);        // D13のLED用出力ポート

  Can.setFilterMode(0, FILTER_MOD_ALL_HIT);   // RxBuf0 フィルタ使用せず
  Can.setFilterMode(1, FILTER_MOD_ALL_HIT);   // RxBuf1 フィルタ使用せず

  Can.setOpMode(CAM_MODE_NORMAL);
}

// メインループ
void loop(void) {
  char cdat;
  byte size, datfrm;
  int msg;
  byte buf[8];

  // シリアル受信チェック
  if(Serial.available() > 0) {
    cdat = Serial.read();
    cmdProc(cdat);
  }

  // CAN送信完了チェック
  if(Can.checkTxComp(0)) {
    digitalWrite(LedPin, LOW);      // LED OFF(送信完了)
  }

  // CAN受信チェック
  if(Can.rxCheck()) {
    // 受信しているとき
    msg = Can.getRxBuf(0, buf, &size, &datfrm);   // データとメッセージIDを取り出す。
    canCmdProc(msg, buf, size);
  }
}

//
// CANコマンド処理(受信した温度の表示)
//
void canCmdProc(int msg, byte *dat, byte size) {
  byte tpnum;
  int tmp;

    switch(msg) {
      case cmMSG_TMP1_VAL:    // 温度測定ノード1
        tpnum = 1;
        break;
      case cmMSG_TMP2_VAL:    // 温度測定ノード2
        tpnum = 2;
        break;
      case cmMSG_TMP3_VAL:    // 温度測定ノード3
        tpnum = 3;
        break;
      case cmMSG_TMP4_VAL:    // 温度測定ノード4
        tpnum = 4;
        break;
      default:
        return;
    }

    tmp = (uint16_t)dat[0] << 8;    // 温度値(10倍値) 上位
    tmp |= dat[1];                // 温度値(10倍値) 下位

    if(tmp < 0) {
      // 負数
      tmp = tmp * (-1);
      sprintf(strBuf, "T%d= -%d.%d", tpnum, tmp/10, tmp%10);
    } else {
      // 正数
      sprintf(strBuf, "T%d= %d.%d", tpnum, tmp/10, tmp%10);
    }
    Serial.println(strBuf);   // シリアル送信
}

//
// シリアルコマンド処理
//
void cmdProc(char dat) {
  byte rn;            // リレー番号
  byte buf[1];        // CANデータバッファ(dummy)
  int msg;

  if(dat == '\r') {   // CR
    cmdStr[cmdPtr] = 0;         // コマンド文字列終了 null挿入
    cmdPtr = 0;

    if(strlen(cmdStr) == 2) {
      // -------------- コマンド処理 ----------------
      rn = cmdStr[1] - '0';     // リレー番号取り出し(1-5)
      if(cmdStr[0] == 'r') {
        // リレーONコマンド
        // メッセージID作成
        msg = makeMsg(1, rn, 1);      // リレーノード識別番号, リレー番号, ON
      } else {
        // リレーOFFコマンド
        // メッセージID作成
        msg = makeMsg(1, rn, 0);      // リレーノード識別番号, リレー番号, OFF
      }

// デバッグ用 メッセージID表示
sprintf(strBuf, "MSG = %03x", msg);
Serial.println(strBuf);

      // CAN送信バッファへ送信メッセージ、データを設定
      Can.setTxBuf(0, msg, buf, 0, true);
      Can.txReq(0);                   // 送信要求発行
      digitalWrite(LedPin, HIGH);     // LED ON(送信中)
      // ------------------------------------------------
    } else {
      // コマンド該当なし
      Serial.print(cmdStr);
      Serial.println(" ?");
    }
  } else if(dat != '\n') {
    cmdStr[cmdPtr++] = dat;     // コマンド文字列蓄積
  }
}

追加したCAN受信関係の処理を説明します。

メインループの83行目にCAN受信をチェックする処理が入っています。受信した場合、メッセージ内容を取り出し、処理関数”canCmdProc()”へ渡してそちらでコマンド処理を行います。本体で大きく変わったのはこの部分のみです。

CANコマンド処理として93行目に”canCmdProc()”関数を追加しました。ここでメッセージ内容を調べてコマンドに応じた処理を行います。

今回はまだ、受信するコマンドは温度値データだけなので、97行目で、メッセージIDより送信元の温度測定ノードの番号をしらべて、表示の際にノード番号を表示します。

114、115行目でメッセージのデータフィールドの2バイトより16ビットの値を合成します。この時点では、符号付きの摂氏温度の10倍値となっています。

117行目で正数か負数かを調べて、正数の場合はそのまま、負数の場合は温度値に-1を掛けて正数にもどしてから120か、123行目で表示用のテキストを作成します。温度の10倍値は10で割って整数値、10で割った余りを小数点以下一位の値として別々に表示させています。このとき、温度測定ノードの番号も一緒に付加しておきます。

125行目で成形した温度値のテキストをシリアル出力します。


CANについては下記書籍で説明していますのでよろしければ、ご覧ください。
書籍「動かして学ぶCAN通信」 → 書籍紹介ページ

ArduinoのCAN関係の記事は下記書籍でも扱っています(WSN292など使用)。CANなどの専用ライブラリに付いても解説しています。
書籍「Arduino実験キットで楽ちんマイコン開発」 → 書籍紹介ページ

図やテキストの無断転写はお断りします。

コメントは受け付けていません。