前回は、ArduinoとCAN-BUS Shield を組み合わせて車両からCANデータを取得し、USB経由でPCのシリアルモニターに表示するプログラムを作成した(こちら)。
しかしながら、毎度パソコンを車両に持ち込んで、USBで接続し、シリアルモニターのデータをコピーペーストしてファイルに保存する、なんてことをやるのは面倒。そこで、作成したArduino+CAN-BUS Shieldに実装されているmicroSDカードスロットルを利用して、microSDカードに取得したCANデータを保存するプログラムを作成した。
なお、Arduinoのプログラム(=スケッチ)で指定するピンがUnoとMegaでは違うが、ピン番号を書き換えればArduino Unoでも使える。
用意するもの
Arduino
今回はArduinoMegaで実装したが、Arduino Unoでも動くはず。
CAN-BUS Shield V2.0
Arduino UnoでもArduino Megaでも、特に問題なく使えると製造元に書いてあったため、これを利用した。
microSDカード(SDHC)
CAN-BUS Shieldに実microSDカードスロットルが実装されているので、それに合うmicroSDカードが必要。SDXC規格のものは使えないので、SDHCを選択する必要がある。カメラや動画データを保存するわけではないため、16GBで十分。相性などはあまりないとは思うが、今回使用したSDカードを紹介しておく。
CAN-BUS Shield V2.0へSDカードをセットする
CAN-BUS Shieldに、以下の手順でSDカードをセットする。
ArduinoでのSDカード読み書きプログラム
使用するライブラリの定義
SdFat.hを使用する。SD.hだと、書き込み速度が遅いし、ファイル名が8.3filename形式である必要があるためだ。もともとテストプログラムはSD.hを利用していたが、SdFat.hに変更している。
#include <SPI.h>
#include <SdFat.h>
#include "string.h"
#include "mcp2515_can.h"
ピンの設定
ピンは、以下のように設定している。SS_defaultPin Arduino unoは10, megaは53を設定する必要がある。
// ピンの設定
// CAN通信用のCSピン(digital 9)
const int SPI_CAN_CS_PIN = 9;
// SDカード通信用のCSピン(digital 4)
const int SPI_SD_CS_PIN = 4;
// SS_defaultPin Arduino unoは10, megaは53
const int PSI_SS_defaultPin = 53;
// 動作確認用のLED
const int LED_PIN = 13;
CAN受信IDのフィルタ設定
周期の早いCANデータが大量に出力されている場合、目的のIDのデータを取りこぼしてしまう恐れがある。数フレームであれば実用上問題ないのだが、断続的に続くと、計測精度が低下する。そこで、CAN-BUS Shieldに搭載されているmcp2515では、不要なデータがArduinoアプリ側に流れてこないようにフィルターする機能がある。フィルター機能についてはこちらの記事を参照。
SDカードへの保存(上書き回避)
グローバル変数で予めSdFatのインスタンスSDを定義しておき、setup()でSDとのやり取りを開始する。.begin()コマンドでSD通信を初期化。初期化が完了したら、ファイル名の存在を確認。ファイルが存在するならファイル名を1つインクリメントしたものを新規ファイルとして書き込んでいく処理とした。
// SD通信開始の初期化
if (!SD.begin(SPI_SD_CS_PIN)) {
loggingFlag = false;
SERIAL_PORT_MONITOR.println("SD card initialized Error !!") ;
}else{
// 初期化完了
SERIAL_PORT_MONITOR.println("SD card initialized OK !!") ;
logFileName = base_logFileName + String(fileNum);
SERIAL_PORT_MONITOR.println(logFileName + LOG_FILE_EXT);
SERIAL_PORT_MONITOR.println(SD.exists(logFileName + LOG_FILE_EXT));
while(SD.exists(logFileName + LOG_FILE_EXT)){
fileNum++;
if(fileNum==250){
break;
}
logFileName = base_logFileName + String(fileNum);
SERIAL_PORT_MONITOR.println("log file" + logFileName);
}
if(fileNum>=250){
SERIAL_PORT_MONITOR.println("Files in SD card are too many...") ;
loggingFlag = false;
}else{
SERIAL_PORT_MONITOR.println("SD log file Name : " + logFileName);
loggingFlag = true;
}
}
シリアルモニターに出力していた情報をCANから取得するのは、以前作成したプログラムの通り。今回はシリアルモニターに出力していた情報をそのままSDカードに書き込む。
リセットボタンが押されたとき
Arduino UNO/Megaともに、動作中にリセットを行えるボタンが用意されている(下図赤◯)。これを押すと、プログラムが最初から実行されて、先程記載したSDカードの初期化~ログの上書き回避処理が走り、SDカード内部に別名でログデータが一つ作られる動きになる。
これで、気が向いた時にボタンを押すと新しいログファイルを作成することができるようになった。
CANの取得とSDカードへの保存のプログラム
ここまでの解説したプログラムをまとめると以下のようになる。
/*
* File: CanPowerCheck.ino
* Author: 岡本一車
* https://kurumashikou.com/
*/
#include <SPI.h>
#include <SdFat.h>
#include "string.h"
#include "mcp2515_can.h"
// 計測モード用のフィルタ設定
#define MASK0 0x7FF
// S660 WheelSpeed CAN ID 0x1D0
//#define Filter0 0x1D0
// HA36S WheelSpeed CAN ID 0x1B8
#define Filter0 0x1B8
#define Filter1 0x000
#define MASK1 0x7FF
#define Filter2 0x000
#define Filter3 0x000
#define Filter4 0x000
#define Filter5 0x000
#define LOG_FILE_EXT ".CSV"
// ピンの設定
// CAN通信用のCSピン(digital 9)
const int SPI_CAN_CS_PIN = 9;
// SDカード通信用のCSピン(digital 4)
const int SPI_SD_CS_PIN = 4;
// SS_defaultPin Arduino unoは10, megaは53
const int PSI_SS_defaultPin = 53;
// 動作確認用のLED
const int LED_PIN = 13;
// グローバル変数 ///////////////////////////////////////////
// SDfatのインスタンス
SdFat SD;
// タイムスタンプ用変数
unsigned long timestamp = 0;
unsigned long timestamp_old = 0;
// 受信メッセージ格納用
unsigned long can_ID = 0;
unsigned char len = 0;
unsigned char buf[8];
// 送信メッセージ格納用
unsigned char sendMsg[8] = {0, 0, 0, 0, 0, 0, 0, 1};
// CANデータ書き込み値を保持する
String logDataStr;
// CANトランシーバにCSピン設定をセットし、CAN通信インスタンスを生成
mcp2515_can CAN(SPI_CAN_CS_PIN);
// 保存するログファイル
File logDataFile;
// ログファイル名
String base_logFileName = "DATALOG_";
String logFileName = "";
// ログファイル通し番号
unsigned char fileNum = 0;
// ログデータのステータス
//false:ログ取得中ではない / true:ログ中
bool loggingFlag = false;
// 初期設定 ////////////////////////////////////////////////
void setup() {
// シリアルモニタの設定
SERIAL_PORT_MONITOR.begin(115200);
// シリアルポートの準備が整うまで待つ
while(!Serial);
// 動作確認用LEDのポート設定
pinMode(LED_PIN, OUTPUT);
// SDファイル初期化処理 //////////////////////////////////////////
// SSピンのdefaultをOUTPUTに設定する
pinMode(PSI_SS_defaultPin, OUTPUT) ;
// SD通信開始の初期化
if (!SD.begin(SPI_SD_CS_PIN)) {
loggingFlag = false;
SERIAL_PORT_MONITOR.println("SD card initialized Error !!") ;
}else{
// 初期化完了
SERIAL_PORT_MONITOR.println("SD card initialized OK !!") ;
logFileName = base_logFileName + String(fileNum);
SERIAL_PORT_MONITOR.println(logFileName + LOG_FILE_EXT);
SERIAL_PORT_MONITOR.println(SD.exists(logFileName + LOG_FILE_EXT));
while(SD.exists(logFileName + LOG_FILE_EXT)){
fileNum++;
if(fileNum==250){
break;
}
logFileName = base_logFileName + String(fileNum);
SERIAL_PORT_MONITOR.println("log file" + logFileName);
}
if(fileNum>=250){
SERIAL_PORT_MONITOR.println("Files in SD card are too many...") ;
loggingFlag = false;
}else{
SERIAL_PORT_MONITOR.println("SD log file Name : " + logFileName);
loggingFlag = true;
}
}
// CANの初期設定 ///////////////////////////////////////////////
if (CAN.begin(CAN_500KBPS) != CAN_OK) {
SERIAL_PORT_MONITOR.println("CAN initialized error ..............");
while(1);
}else{
SERIAL_PORT_MONITOR.println("CAN initialized OK !!");
}
/*
* set mask
*/
// there are 2 mask in mcp2515, you need to set both of them
CAN.init_Mask(0, 0, MASK0);
CAN.init_Mask(1, 0, MASK1);
CAN.init_Filt(0, 0, Filter0);
CAN.init_Filt(1, 0, Filter1);
CAN.init_Filt(2, 0, Filter2);
CAN.init_Filt(3, 0, Filter3);
CAN.init_Filt(4, 0, Filter4);
CAN.init_Filt(5, 0, Filter5);
// CANフィルタ設定の確認
SERIAL_PORT_MONITOR.print("mask0= ");
SERIAL_PORT_MONITOR.print(MASK0);
SERIAL_PORT_MONITOR.print(" / mask1= ");
SERIAL_PORT_MONITOR.println(MASK1);
SERIAL_PORT_MONITOR.print("Filter0= ");
SERIAL_PORT_MONITOR.print(Filter0);
SERIAL_PORT_MONITOR.print(" / Filter1= ");
SERIAL_PORT_MONITOR.print(Filter1);
SERIAL_PORT_MONITOR.print(" / Filter2= ");
SERIAL_PORT_MONITOR.print(Filter2);
SERIAL_PORT_MONITOR.print(" / Filter3= ");
SERIAL_PORT_MONITOR.print(Filter3);
SERIAL_PORT_MONITOR.print(" / Filter4= ");
SERIAL_PORT_MONITOR.print(Filter4);
SERIAL_PORT_MONITOR.print(" / Filter5= ");
SERIAL_PORT_MONITOR.println(Filter5);
// シリアルポートにヘッダを書き込み
SERIAL_PORT_MONITOR.println("TimeStamp CAN_ID Len Data");
}
// メイン処理 ////////////////////////////////////////////////
void loop() {
digitalWrite(LED_PIN, loggingFlag);
// CANメッセージ受信したときの動作
if (CAN_MSGAVAIL == CAN.checkReceive()) {
// 受信したデータをシリアルモニタに送信 ////////////////////////////////////
if(timestamp_old == 0){
timestamp_old = millis();
}
// CANデータを取得
CAN.readMsgBufID(&can_ID, &len, buf);
// タイムスタンプの取得
timestamp = millis() - timestamp_old;
logDataStr = "";
logDataStr += String(timestamp) + ",";
logDataStr += String(can_ID) + ",";
logDataStr += String(len) + ",";
for (int i = 0; i < len-1; i++) {
logDataStr += (String(buf[i]) + String(","));
}
logDataStr += String(buf[len-1]);
logDataStr += "\r\n";
loggingFlag = writeSD_card(logDataStr);
SERIAL_PORT_MONITOR.print(logDataStr);
}
}
// SDカードへの書き込み
bool writeSD_card(String logDataStr){
// ファイルの書込みオープン(動作確認用)1
logDataFile = SD.open(logFileName + LOG_FILE_EXT , FILE_WRITE);
if (logDataFile) {
// 文字列を書込む
logDataFile.print(logDataStr);
// ファイルのクローズ
logDataFile.close() ;
return true;
} else {
// ファイルのオープンエラー
SERIAL_PORT_MONITOR.println("SD file opening ERROR ......") ;
SERIAL_PORT_MONITOR.println("------------------------") ;
return false;
}
}
(これはアルトワークスHA36S用。S660の場合は、上記プログラムの12行~14行目を以下の様に変更する必要がある)
#define MASK0 0x7FF
// S660 WheelSpeed CAN ID 0x1D0
#define Filter0 0x1D0
// HA36S WheelSpeed CAN ID 0x1B8
// #define Filter0 0x1B8
まとめ
Arduino+CAN-BUS Shieldに実装されているmicroSDカードスロットルを利用して、microSDカードに取得したCANデータを保存するプログラムを作成した。Arduinoのプログラム(=スケッチ)で指定するピンがUnoとMegaでは違うが、そこさえ書き換えればArduino Unoでも使える。
今回作成したCANデータ取得ユニットを使って、S660の馬力を計測してみた記事はこちら
アルトワークスのCAN解析と馬力計測の記事はこちら
コメント