hattori&ide

Dependencies:   mbed

Revision:
0:f77369cabd75
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Loggermain.cpp	Sun Dec 18 08:16:01 2022 +0000
@@ -0,0 +1,979 @@
+/*更新情報
+ * 2016/01/20
+ * LCD表示 現在時刻から経過時間へ変更
+ * LCD表示 単位記号の位置変更
+ *
+ * 2016/01/29
+ * postアドレス変更 TEST→myLogger
+ *
+ * 2016/01/30
+ * サーバー送信データ変更,末尾にデータ取得時間追加
+ *
+ * 2016/02/08
+ * 配列の要素数をconst定数化
+ * LapUSBSaveDataのcsvデータがおかしかったので修正
+ * ラップをラップタイムが3分30秒以内の場合は加算しないように修正
+ * CalculateSet関数を分割
+ *
+ * 2016/02/22
+ * 初期化ルーチンを関数に分割
+ * GPS対応
+ * データバックアップ,引き継ぎ機能追加,ファイル名を日付に変更
+ *
+ * 2016/02/24
+ * エラー処理追加, メンテナンス
+ *
+ * 2016/04~
+ * タイマー処理測定→http送信による遅延を無害化
+ * 加速度センサデータ保存、モーター回転数から速度算出
+ *
+ * 2016/04/24
+ * ラップスイッチ ピン変化割り込み化
+ * 配列過剰書き込み 対策済み
+
+ * 2016/05/11
+ * ファイル名変更 日付/各データ(ファイル構造化)
+ * GPSセンテンス保存 走行ラインGoogle Earth等で閲覧可
+ *
+ * 2016/06/01
+ * スタート前時,積算しないように設定(関数の引数で変更可)
+ *
+ * 2016/06/10
+ * INA226設定レジスタ変更
+ * wait変更
+ *
+ * 2016/06/15
+ * MITSUBA CANデータ対応 回転数データ取得
+ *
+ * 2016/07/03
+ * INA226 異常データ対策
+ * 絶縁監視(漏れ電流)エラー出力機能追加
+ *
+ * 2016/07/11
+ * DriverEmergency機能追加
+ *
+ * 2016/07/17
+ * Web送信データ変更 平均→積算
+ * LAP送信データ追加 コントロールライン通過時刻
+ *
+ * 2016/07/25
+ * Webデータ送信 桁数一部変更
+ * CalculateSet関数修正
+ *
+ * 2016/08/02
+ * ReadyStartLCD()にGPSがfixしたらGPS OKと表示するようにした
+ *
+ * 2017/08/02
+ * INA226 測定平均回数 16 → 128
+ * 2017/08/05 12:00:00自動スタート
+ *
+ * 2021/07/10
+ * バッテリー容量を3.6 * 3.35 * 30 * 14から3.6 * 3.45 * 26 * 16 に変更
+ *
+ */
+
+#include "mbed.h"
+#include "INA226.hpp"
+#include "TextOLED.h"
+#include "MSCFileSystem.h"
+#include "EthernetNetIf.h"
+#include "TCPSocket.h"
+#include "TinyHTTP.h"
+#include "NTPClient.h"
+#include "RTC.h"
+#include "MBed_Adafruit_GPS.h"
+#include "MITSUBA_CAN.h"
+//#include "MMA8652.h"
+
+// INA226用I2Cアドレス I2Cアドレスはmbedの場合1ビット左シフトしたアドレスになる
+const int batteryMonitorAddress = 0x80;         // 0b10000000 (GG)
+const int panelMonitorAddress = 0x82;           // 0b10000010 (G1)
+const int motorMonitorAddress = 0x9E;           // 0b10011110 (CC)
+const unsigned short configuration = 0x4897;    // 平均化処理やタイミング
+
+/*------------------------------設定変更厳禁------------------------------*/
+const double voltageCalibration =  10 / 1.1;          // 分圧比(10倍に補正)
+const unsigned short calibration = 0x1400;      // シャント電圧補正係数
+const unsigned short calibrationPanel = 0x2800; // シャント電圧補正係数(パネル)
+const double currentCalibration[] = {1.5, 0.5, 1.5};
+// [0]: バッテリ [1]: パネル [2]: モータ
+/*------------------------------設定変更厳禁------------------------------*/
+
+const double batteryCapacity = 3.6 * 3.45 * 26 * 16;       // 公称電圧*電流容量*直列数*並列数←2021鈴鹿仕様に変更
+
+const int offsetTime = 60 * 60 * 9;     // 9時間(標準時との時差) = 日本時間
+const int storageTime = 8;              // 8秒ごとにサーバーへ送信 10秒はサイズ的に厳しい
+const int storageOffset = 5;            // 5秒間分余分に配列を用意しておく
+const int lapTime_limit = 60 * 4;       // LAPタイム 4分00秒以内はキャンセル
+const double kmph = 1.852;              // 1ノット = 1.852 km/h
+const double pulseConvert = 0.1303;     // 車速パルス -> 速度変換係数
+const double rpmConvert = 0.104399;       // rpm -> km/h 変換係数
+
+/*各配列要素数*/
+const int RealtimeData_i = 3;
+const int RealtimeData_j = 5;
+const int LapoutData_i = 3;
+const int LapoutData_j = 3;
+const int timeStr_i = 6;
+const int preLapdata_i = 3;
+const int preLapdata_j = 2;
+
+// バッファーサイズ
+const int RequestStr_maxsize = 1210;   // 1600とかでは送信できない メモリ破壊?
+const int LapRequestStr_maxsize = 110;
+const int SerialSendStr_maxsize = 200;
+const int timeStr_maxsize = 32;
+const int fileName_maxsize = 32;
+
+Serial debug(USBTX, USBRX);
+Serial Xbee(p13, p14);
+Serial gpsSerial(p28, p27);
+Adafruit_GPS myGPS(&gpsSerial);
+
+CAN mitsuba_can(p30, p29);
+MITSUBA canlog(mitsuba_can, 250000);
+//MMA8652 acc(p9, p10);  // sda, scl
+
+DigitalOut sublogger_sec(p24);
+DigitalOut sublogger_min(p25);
+// 引数は(rs,e,d4,d5,d6,d7) 接続しないピンはGNDに落としておくと安定する
+TextOLED lcd(p15, p16, p17, p18, p19, p20, TextOLED::LCD20x4);
+
+I2C i2c(p9, p10);  // sda, scl
+INA226 BatteryMonitor(i2c, batteryMonitorAddress, 10000);
+INA226 PanelMonitor(i2c, panelMonitorAddress, 10000);
+INA226 MotorMonitor(i2c, motorMonitorAddress, 10000);
+
+MSCFileSystem msc("usb");  // USBメモリには/usb/...でアクセス
+
+EthernetNetIf eth;
+NTPClient ntp;
+
+DigitalOut isoError(p6);         // 絶縁監視エラー出力
+
+InterruptIn lapSw(p7);          // lapスイッチ  ON: 0, OFF:1
+InterruptIn motorPulse(p12);    // モーターパルス
+
+struct LogData {
+  char timeStr[timeStr_i][timeStr_maxsize];
+  /* [0]:タイムスタンプ    20160801121356
+     [1]:現在時刻          12:13:56
+     [2]:経過時間          00:13:56
+     [3]:カウント用        00:01:30
+     [4]:LCD用ラップタイム 05:15
+     [5]:通過時刻          12:05:00*/
+  unsigned int lap;                                    // ラップ数
+  unsigned int lapTime;                                // ラップタイム
+  int totalTime;                              // 経過時間
+  double RealtimeData[RealtimeData_i][RealtimeData_j]; // 1秒毎の測定データ
+  double LapoutData[LapoutData_i][LapoutData_j];       // ラップ平均等
+  double batteryVoltage;              // バッテリー電圧
+  double remainBatteryParcent;        // バッテリー残量[%]
+  double remainBatteryWh;             // バッテリー残量[Wh]
+  double motorSpeed;                  // モーター速度[km/h] CAN
+  double motorSpeed_pulse;            // モーター速度[km/h] パルス
+  double motorAngle;
+  double gpsSpeed;                    // GPS速度[km/h]
+  // float accData[3];                // 3軸加速度 0:x, 1:y, 2:z[何Gか]
+  double latitude;                    // 緯度
+  double longitude;                   // 経度
+};
+
+// グローバル変数
+bool secTimer = false;
+bool lapSave = false;
+bool lapSave_forsub=false;
+bool emergency = false;
+time_t startUnixTime = 0;
+struct LogData logdata = {0};
+struct LogData Postdata[storageTime + storageOffset] = {0}; // storageTimeかつかつはまずい?
+unsigned int postcount = 0;                                 // logdataを貯める時間をカウント
+unsigned int lapTimeValue = 0;                              // スイッチ判定用のラップタイム
+unsigned int pulseCount = 0;                                // 車速パルスカウント
+time_t prelapUnixTime = 0;                                  // 前のラップタイムのUNIXタイム
+double preLapdata[preLapdata_i][preLapdata_j] = {0};        // 前のラップデータ
+
+// プロトタイプ宣言
+void SetTimer(void);
+void MainFuncTimer(void);
+void SwFuncInterrupt(void);
+void MotorPulseCount(void);
+void print(const char *str, int row, int col);
+int SetupINA226(void);
+int SetupEthernet(void);
+int SetupUSB(struct LogData *data, char name[][fileName_maxsize]);
+int ReadBackup(struct LogData *data, double predata[][preLapdata_j], time_t *preTime);
+int FetchGPSdata(const char name[][fileName_maxsize]);
+void CalculateSet(struct LogData *data, bool run);
+unsigned int CalculateTimeSet(struct LogData *data, time_t preTime);
+void LapCalculateSet(struct LogData *data, double predata[][preLapdata_j], time_t *preTime);
+int httpPost(const struct LogData postdata[], int *httpRes);
+void LaphttpPost(const struct LogData *data, int *httpRes);
+void EmergencyhttpPost(const struct LogData *data);
+void LCD(const struct LogData *data);
+void ReadyStartLCD(const struct LogData *data);
+int USBSaveData(const struct LogData postdata[], const char name[][fileName_maxsize]);
+int LapUSBSaveData(const struct LogData *data, const char name[][fileName_maxsize]);
+int SaveBackup(const struct LogData *data, const double predata[][preLapdata_j], time_t preTime);
+void XbeeSerial(const struct LogData *data);
+void InsulateMonitor(const struct LogData *data);
+
+int main(){
+  int httpResponce = 0;                       // httpリクエスト送信時のレスポンス
+  int LaphttpResponce = 0;
+  char fileName[3][fileName_maxsize] = {0};   // USBファイル名  [0]:LOG, [1]:LAP, [2]:GPS
+
+  debug.baud(9600);
+  Xbee.baud(57600);  // 安心と信頼()の57600bps
+  myGPS.begin(9600);
+  myGPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
+  myGPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
+  myGPS.sendCommand(PGCMD_ANTENNA);
+
+  lcd.cls();
+
+sublogger_sec=0;
+sublogger_min=0;
+
+  // エラー時は自動的にリセット
+
+  if(SetupINA226()) {
+    NVIC_SystemReset();
+    return -1;
+  }
+
+  if(SetupEthernet()) {
+    NVIC_SystemReset();
+    return -1;
+  }
+
+  // Host server(IpAddr(), 123, "ntp.jst.mfeed.ad.jp");  // NTP時刻取得 調子悪い?
+  Host server(IpAddr(), 123, "ntp.nict.jp");
+  ntp.setTime(server);       // RTCにセット
+
+  if(SetupUSB(&logdata, fileName)) {
+    NVIC_SystemReset();
+    return -1;
+  }
+
+  wait(1.0);
+  lcd.cls();
+
+  RTC::attach(&SetTimer, RTC::Second);       // SetTimer()を1秒毎に実行する
+
+  // スタート前ループ
+  // 初回起動時 backup.csvが存在していないときのみ実行
+  if(ReadBackup(&logdata, preLapdata, &prelapUnixTime)) {
+    while(1) {
+      if(!lapSw || time(NULL) == 1564801200) {  // 2019/8/3/12:00:00 自動スタート←2年前になってたかも
+        startUnixTime = time(NULL) + offsetTime+600;    // スタート時刻取得
+        break;
+      }
+      if(secTimer) {
+        CalculateTimeSet(&logdata, prelapUnixTime);
+        CalculateSet(&logdata, false);
+        ReadyStartLCD(&logdata);
+        InsulateMonitor(&logdata);
+        secTimer = false;
+      }
+    }
+  }
+  /*
+     else {
+     // NTP時刻が異常な時(スタートから5時間以上) 自動的にリセット
+     if(labs(startUnixTime - (time(NULL) + offsetTime)) > 5 * 3600) {
+      print("NTP Time isoError\n", 0, 0);
+      RTC::detach(RTC::Second);
+      NVIC_SystemReset();
+      return -1;
+     }
+     }
+   */
+  lapSw.fall(&SwFuncInterrupt);          // ピン立ち下がり割り込みに設定
+  motorPulse.fall(&MotorPulseCount);
+
+  RTC::detach(RTC::Second);
+  lcd.cls();
+  wait(0.5);       // lapSwが0になるのを待つ
+
+  RTC::attach(&MainFuncTimer, RTC::Second);  // MainFuncTimerを1秒ごとに実行
+
+// メインループ
+  while(1) {
+    FetchGPSdata(fileName);  // GPSセンテンス取得・解析
+    if(emergency) {
+      EmergencyhttpPost(&logdata);
+      emergency = false;
+    }
+    if(lapSave) {
+      LaphttpPost(&logdata, &LaphttpResponce);                 // LAPデータ送信
+      LapUSBSaveData(&logdata, fileName);
+      lapSave = false;
+    }
+    if(secTimer) {                                                // fopen関連は割り込みで実行できない
+      SaveBackup(&logdata, preLapdata, prelapUnixTime);           // バックアップ
+      secTimer = false;
+    }
+    if(postcount >= storageTime) {                 // storageTime個のデータが集まったら
+      postcount = 0;
+      USBSaveData(Postdata, fileName);             // USBにデータ保存
+      httpPost(Postdata, &httpResponce);
+      // debug.printf("\n%d\n", httpResponce);
+    }
+  }
+}
+
+/*--------------------------割り込み実行の関数 ここから-------------------------------*/
+// RTCタイマーで1秒ごとに実行(スタート前)
+void SetTimer(){
+  secTimer = true;
+}
+
+// RTCタイマーで1秒ごとに実行(走行時)
+void MainFuncTimer(){
+  secTimer = true;
+  canlog.GetCanData(FRAME0);  // FRAME0のデータを取得
+  lapTimeValue = CalculateTimeSet(&logdata, prelapUnixTime);  // 時間に関するデータを計算
+  CalculateSet(&logdata, true);                               // データ取得・計算し,格納
+  LCD(&logdata);                                              // LCDにデータ表示
+  Postdata[postcount++] = logdata;                            // 蓄積
+  if(postcount >= storageTime + storageOffset) {              // 配列上限超えると強制リセット
+    postcount = 0;
+  }
+  InsulateMonitor(&logdata);
+}
+
+// スイッチ割り込み
+// lapTime_limit未満は緊急スイッチとして動作→非表示に(2019.8.1)
+void SwFuncInterrupt(){
+  if(lapTimeValue >= lapTime_limit) {
+    LapCalculateSet(&logdata, preLapdata, &prelapUnixTime);  // LAPデータ計算
+    XbeeSerial(&logdata);                                    // LAPデータをxbeeで送信
+    lapSave = true;                                          // Web 送信,USB保存のフラグ
+    lapSave_forsub=true;
+  }
+  else{
+    //lcd.cls();
+    //print("Driver Emergency", 2, 1);
+    wait(1.0);
+    lcd.cls();
+    emergency = true;      // 緊急スイッチ,web送信フラグ
+  }
+}
+
+void MotorPulseCount(){
+  pulseCount++;    // ストリーム等でデバックすると遅延で正確にパルス数を測定できない
+}
+
+/*--------------------------割り込み実行の関数 ここまで-------------------------------*/
+
+/*-------------------------------ユーザー定義関数-------------------------------------*/
+
+/** 全てのストリーム系に文字列を出力
+ * @param const char *str 送信文字列
+ * @param int row 列
+ * @param int col 行
+ */
+void print(const char *str, int row, int col){
+  debug.printf("%s", str);
+  Xbee.printf("%s", str);
+  lcd.locate(row, col);
+  lcd.printf("%s", str);
+}
+
+/** INA226 レジスタ書き込み等
+ * @return 0:成功     -1:失敗
+ */
+int SetupINA226(){
+  unsigned short val_1 = 0;
+  unsigned short val_2 = 0;
+  unsigned short val_3 = 0;
+
+  // INA226の存在を確認
+  if(!BatteryMonitor.isExist()) {
+    print("INA226B not found!\n", 0, 0);
+  }
+  if(!PanelMonitor.isExist()) {
+    print("INA226P not found!\n", 0, 1);
+  }
+  if(!MotorMonitor.isExist()) {
+    print("INA226M not found!\n", 0, 2);
+    return -1;
+  }
+
+  // INA226のレジスタの値を正しく読めるか確認
+  if(BatteryMonitor.rawRead(0x00, &val_1) != 0) {
+    print("INA226B read Error!\n", 0, 0);
+    return -1;
+  }
+  if(PanelMonitor.rawRead(0x00, &val_2) != 0) {
+    print("INA226P read Error!\n", 0, 1);
+    return -1;
+  }
+  if(MotorMonitor.rawRead(0x00, &val_3) != 0) {
+    print("INA226M read Error!\n", 0, 2);
+    return -1;
+  }
+
+  // 各INA226にレジスタ値を書き込む
+  BatteryMonitor.setConfiguration(configuration);
+  PanelMonitor.setConfiguration(configuration);
+  MotorMonitor.setConfiguration(configuration);
+  BatteryMonitor.setCurrentCalibration(calibration);
+  PanelMonitor.setCurrentCalibration(calibrationPanel);
+  MotorMonitor.setCurrentCalibration(calibration);
+
+  print("INA226 OK!\n", 0, 0);
+  return 0;
+}
+
+/** ネット関連 初期化
+ * @return 0: 成功 -1: 失敗
+ */
+int SetupEthernet(){
+  EthernetErr ethErr = eth.setup();
+  if(ethErr) {
+    debug.printf("Error %d in setup.\n", ethErr);
+    Xbee.printf("Error %d in setup.\n", ethErr);
+    lcd.locate(0, 1);
+    lcd.printf("Setup Error %d", ethErr);
+    return -1;
+  }
+
+  print("Ethernet OK!\n", 0, 1);
+  return 0;
+}
+
+/** USBメモリ書き込み初期化 ファイル名設定
+ * @param struct LogData LogData 構造体
+ * @param char name ファイル名が書き込まれる配列
+ *
+ * @return 0: 成功 -1: 失敗
+ */
+int SetupUSB(struct LogData *data, char name[][fileName_maxsize]){
+  time_t createdTime = time(NULL) + offsetTime;
+  // 日付がファイル名
+  strftime(name[0], fileName_maxsize, "/usb/%y%m%d_log.csv", localtime(&createdTime));
+  strftime(name[1], fileName_maxsize, "/usb/%y%m%d_LAPdata.csv", localtime(&createdTime));
+  strftime(name[2], fileName_maxsize, "/usb/%y%m%d_GPSNMEA.log", localtime(&createdTime));
+
+  FILE *fp = fopen(name[0], "a");
+  if(fp == NULL) {
+    print("USB can\'t be opened!\n", 0, 2);
+    return -1;
+  }
+  else{
+    strftime(data->timeStr[0], timeStr_maxsize, "%Y%m%d%H%M%S", localtime(&createdTime));
+    fprintf(fp, "%s\n", data->timeStr[0]);    // 試しに現在時刻を書き込み
+  }
+  fclose(fp);
+
+  print("USB OK!\n", 0, 2);
+  return 0;
+}
+
+/** バックアップデータを読み込み,LogData構造体に格納
+ * @param struct LogData バックアップからLogData 構造体に書き込む
+ * @param double predata バックアップから前のラップタイムデータを書き込む
+ * @param time_t *preTime バックアップから前のラップタイムを書き込む
+ *
+ * @return 0:成功     1:バックアップデータが存在していない
+ */
+int ReadBackup(struct LogData *data, double predata[][preLapdata_j], time_t *preTime){
+  int i;
+  FILE *fp_back = fopen("/usb/backup.csv", "r");
+  if(fp_back == NULL) {
+    debug.printf("Backup File No Exist\n");
+    return 1;  // 初回起動時 backup.csvはSaveBackup関数が実行された時に作成される
+  }
+  else{
+    fscanf(fp_back, "%d,%d,%d,%d", &(data->lap), &(data->lapTime), &startUnixTime, preTime);
+    for(i = 0; i < RealtimeData_i; i++) {
+      fscanf(fp_back, "%lf,%lf,", &(data->RealtimeData[i][2]), &(data->RealtimeData[i][3]));  // 電流和,電力和
+    }
+    for(i = 0; i < preLapdata_i; i++) {
+      fscanf(fp_back, "%lf,%lf,", &predata[i][0], &predata[i][1]);
+    }
+  }
+  fclose(fp_back);
+  return 0;
+}
+
+/** シリアルGPSデータを解析,保存
+ * @param const char name ファイル名が保存されている配列
+ *
+ * @return 0:成功    -1:失敗
+ */
+int FetchGPSdata(const char name[][fileName_maxsize]){
+  // char c;
+  while(1) {
+    myGPS.read();                            // ただ読むだけでなくて,GPSセンテンスを文字列化している
+    // if (c) debug.printf("%c", c);
+    if (myGPS.newNMEAreceived()) {           // センテンスが一文取得できたかどうか
+      if (!myGPS.parse(myGPS.lastNMEA())) {  // 解析が完了したかどうか していない場合はもう一度ループ(breakスキップ)
+        continue;
+      }
+      break;                                 // 解析終了した場合はループを抜ける
+    }
+  }
+  // 直前のGPSセンテンスをUSBに保存
+  FILE *fp = fopen(name[2], "a");
+  if(fp == NULL) {
+    debug.printf("USB Error\n");
+    return -1;
+  }
+  else{
+    fprintf(fp, "%s", myGPS.lastNMEA());
+  }
+  fclose(fp);
+  return 0;
+}
+
+/** 1秒毎に電圧・電流測定,計算後のデータを構造体に格納
+ * @param struct LogData
+ * @param  bool run : true 積算する false 積算しない
+ */
+void CalculateSet(struct LogData *data, bool run){
+  int i;
+  bool retry = false;
+  int count = 0;
+
+  double voltage_tmp = 0;      // 仮に電圧,電流データを保存しておく
+  double current_tmp[RealtimeData_i] = {0};
+
+  // 異常な値の場合は,再度データを取り直す
+  do {
+    retry = false;
+    BatteryMonitor.getVoltage(&voltage_tmp);     // 電圧
+    BatteryMonitor.getCurrent(&current_tmp[0]); // バッテリー電流
+    PanelMonitor.getCurrent(&current_tmp[1]);   // パネル電流
+    MotorMonitor.getCurrent(&current_tmp[2]);   // モーター電流
+
+    if(voltage_tmp > 200) retry = true;         // 電圧が200V以上は異常
+    for(i = 0; i < RealtimeData_i; i++) {
+      if(current_tmp[i] > 200) retry = true;    // 電流が200A以上は異常
+    }
+    count++;
+  } while(retry && count < 5);  // 5回とっても異常な場合はさすがにループを抜ける
+
+  // 0割の有無を確認した後,配列に格納する
+  if(data->totalTime != 0) {
+    data->batteryVoltage = voltage_tmp * voltageCalibration;             // 分圧電圧を補正
+    for(i = 0; i < RealtimeData_i; i++) {
+      data->RealtimeData[i][0] = current_tmp[i] * currentCalibration[i];  // 電流補正
+      data->RealtimeData[i][1] = data->batteryVoltage * data->RealtimeData[i][0];   // 電力
+      if(run) data->RealtimeData[i][2] += data->RealtimeData[i][0];                 // 電流の和
+      if(run) data->RealtimeData[i][3] += data->RealtimeData[i][1];                 // 電力の和
+      data->RealtimeData[i][4] = data->RealtimeData[i][3] / 3600;                   // 積算電力
+    }
+    data->remainBatteryWh = batteryCapacity - data->RealtimeData[0][4];     // バッテリー残量[Wh]
+    data->remainBatteryParcent = (data->remainBatteryWh / batteryCapacity) * 100;    // バッテリー残量[%]
+    data->motorSpeed = canlog.rpmMotor * rpmConvert;  // CAN通信で回転数取得 km/h変換
+    data->motorAngle = canlog.angle;  // 進角
+    debug.printf("angle:%.2f\n", canlog.angle);
+    data->motorSpeed_pulse = pulseCount * pulseConvert;     // 1秒間でカウントしたパルスを速度に変換
+    pulseCount = 0;
+    data->gpsSpeed = myGPS.speed * kmph;  // GPS速度
+    data->latitude = myGPS.latitude;     // GPS緯度
+    data->longitude = myGPS.longitude;   // GPS経度
+    //acc.ReadXYZ(data->accData);          // 3軸加速度
+  }
+}
+
+/** 時刻関連のデータを計算,格納
+ * @param struct LogData
+ * @param time_t preTime  前のラップタイム
+ *
+ * @return unsigned int ラップタイム
+ */
+unsigned int CalculateTimeSet(struct LogData *data, time_t preTime){
+  time_t nowTime;              // 現在時刻
+  time_t laptime_temp;         // ラップタイム算出用
+  time_t totalTime_temp;       // 経過時間用
+
+  nowTime = time(NULL) + offsetTime;                // RTCからUNIXタイムを取得し,日本時間に修正 現在時刻
+  totalTime_temp = nowTime - startUnixTime;         // 経過時間 (現在時刻とスタート時刻の差)
+  data->totalTime = (int) totalTime_temp;  // 経過時間(秒)
+  if(!data->lap) {
+    if(nowTime < startUnixTime){
+      laptime_temp = 0;
+    }
+    else{
+      laptime_temp = nowTime - startUnixTime;
+    }         // 現在時刻とスタート時刻の差 = 初回のラップタイム
+  }
+  else{
+    laptime_temp = nowTime - preTime;               // 現在時刻と前のラップタイムの時刻の差 = ラップタイム
+  }
+  if(laptime_temp<0){
+    laptime_temp = 0;
+  }
+  // strftimeでUNIXタイムを適切にフォーマット
+  strftime(data->timeStr[0], timeStr_maxsize, "%Y%m%d%H%M%S", localtime(&nowTime));   // PHP,USB用
+  strftime(data->timeStr[1], timeStr_maxsize, "%T", localtime(&nowTime));             // LCDの現在時刻用
+  strftime(data->timeStr[2], timeStr_maxsize, "%T", localtime(&totalTime_temp));      // LCDの経過時間用
+  strftime(data->timeStr[3], timeStr_maxsize, "%M\'%S", localtime(&laptime_temp));    // LCDのラップ経過時刻用
+
+
+if(lapSave_forsub==true){
+    sublogger_min=1;
+    lapSave_forsub=false;
+    }
+else{    sublogger_min=0;}
+sublogger_sec=1;
+sublogger_sec=0;
+
+
+
+  return (unsigned int) laptime_temp;
+}
+
+/** ラップ平均・積算値を計算し構造体に格納
+ * @param struct LogData
+ * @param double predata  前のラップタイムのデータ
+ * @param time_t *preTime 前のラップタイムを書き込む
+ */
+void LapCalculateSet(struct LogData *data, double predata[][preLapdata_j], time_t *preTime){
+  int i, j;
+  time_t laptime_temp;
+
+  // 0周目から1周目になるときの動作
+  if(!data->lap) {
+    data->lapTime = (int)(time(NULL) + offsetTime - startUnixTime);
+    if(time(NULL) + offsetTime - startUnixTime<0){
+      laptime_temp = 0;
+    }
+    else{
+      laptime_temp = time(NULL) + offsetTime - startUnixTime;
+    }            // 現在時刻とスタート時刻の差 = 初回のラップタイム
+    strftime(data->timeStr[4], timeStr_maxsize, "%M\'%S", localtime(&laptime_temp));     // LCD用にフォーマット
+  }
+  // それ以外の動作
+  else{
+    data->lapTime = (unsigned int)(time(NULL) + offsetTime - *preTime);
+    laptime_temp = time(NULL) + offsetTime - *preTime;
+    strftime(data->timeStr[4], timeStr_maxsize, "%M\'%S", localtime(&laptime_temp));
+  }
+  data->lap = data->lap + 1;             // ラップ加算
+  *preTime = time(NULL) + offsetTime;    // 現在時刻を前のラップタイムのUNIXタイムとする
+  strftime(data->timeStr[5], timeStr_maxsize, "%T", localtime(preTime));  // コントロールライン通過時刻
+
+// 0割の有無を確認した後,配列に格納 前のデータとの差で一周分を算出
+  if(data->lapTime) {
+    for(i = 0; i < LapoutData_i; i++) {
+      data->LapoutData[i][0] = (data->RealtimeData[i][2] - predata[i][0]) / data->lapTime;   // ラップ平均電流
+      data->LapoutData[i][1] = (data->RealtimeData[i][3] - predata[i][1]) / data->lapTime;   // ラップ平均電力
+      data->LapoutData[i][2] = (data->RealtimeData[i][3] - predata[i][1]) / 3600;            // ラップ電力量
+    }
+    for(i = 0; i < preLapdata_i; i++) {
+      for(j = 0; j < preLapdata_j; j++) {
+        predata[i][j] = data->RealtimeData[i][j + 2];                 // 前のデータを記憶しておく
+      }
+    }
+  }
+}
+
+/*この関数では構造体を引数に,httpリクエスト文を作成しサーバーにポストします
+ * リクエスト文(POSTデータ) key[]=value1&key[]=value2&...
+ * keyがp[]でvalueがカンマで結合した1秒間データ(8秒分なので8個作ることになる)
+ * keyをkey[]形式にするとphp側で添字配列として受け取ることができる
+   ---骨格---
+   構造体のデータをchar型に変換してsnprintfでフォーマットしていく(追記方式)
+   そうして,httpRequest関数に渡す
+ */
+/** データをネット送信
+ * @param const struct LogData
+ * @param *httpRes   HTTPレスポンスコード
+ */
+int httpPost(const struct LogData postdata[], int *httpRes){
+  IpAddr ip(133, 68, 205, 140);
+  Host host;
+  host.setName("solar-car.club.nitech.ac.jp");
+  host.setIp(ip);
+
+  int i, j, k;
+  char RequestStr[RequestStr_maxsize] = {0};
+
+  // リクエスト文を作成していく
+  for(k = 0; k < storageTime; k++) {
+    snprintf(RequestStr, RequestStr_maxsize, "%sp[]=%s,%d,%d,%d,%.2f",              // pがkey
+             RequestStr,
+             postdata[k].timeStr[1],
+             postdata[k].lap,
+             postdata[k].lapTime,
+             postdata[k].totalTime,
+             postdata[k].batteryVoltage);
+    for(j = 0; j < 2; j++) {
+      for(i = 0; i < RealtimeData_i; i++) {
+        snprintf(RequestStr, RequestStr_maxsize, "%s,%.1f", RequestStr, postdata[k].RealtimeData[i][j]);     // 電流,電力
+      }
+    }
+    for(i = 0; i < RealtimeData_i; i++) {
+      snprintf(RequestStr, RequestStr_maxsize, "%s,%.1f", RequestStr, postdata[k].RealtimeData[i][4]);       // 積算電力
+    }
+    snprintf(RequestStr, RequestStr_maxsize, "%s,%.2f,%.2f,%.1f,%.4f,%.4f,%s",
+             RequestStr,
+             postdata[k].remainBatteryParcent,
+             postdata[k].remainBatteryWh,
+             postdata[k].motorSpeed,
+             postdata[k].latitude,
+             postdata[k].longitude,
+             postdata[k].timeStr[0]);
+    if(k < (storageTime - 1)) {
+      snprintf(RequestStr, RequestStr_maxsize, "%s&", RequestStr);        // 最後のデータの末尾には&は不要
+    }
+  }
+  *httpRes = httpRequest(METHOD_POST, 8000, &host,"/myLogger/Get/GetRealtimeData.php", "Content-Type: application/x-www-form-urlencoded\r\n", RequestStr);
+  // debug.printf("%s", RequestStr);
+
+  return 0;
+}
+
+void LaphttpPost(const struct LogData *data, int *httpRes){
+  IpAddr ip(133, 68, 205, 140);
+  Host host;
+  host.setName("solar-car.club.nitech.ac.jp");
+  host.setIp(ip);
+
+  char LapRequestStr[LapRequestStr_maxsize] = {0};
+  int i, j;
+
+  snprintf(LapRequestStr, LapRequestStr_maxsize, "q=%s,%d,%d", data->timeStr[5], data->lap, data->lapTime);
+  for(j = 0; j < LapoutData_j; j++) {
+    for(i = 0; i < LapoutData_i; i++) {
+      snprintf(LapRequestStr, LapRequestStr_maxsize, "%s,%.2f", LapRequestStr, data->LapoutData[i][j]);                   // 列に関して出力
+    }
+  }
+  snprintf(LapRequestStr, LapRequestStr_maxsize, "%s,%s", LapRequestStr, data->timeStr[0]);
+  *httpRes = httpRequest(METHOD_POST, HTTP_TIMEOUT, &host, "/myLogger/Get/GetLapoutData.php", "Content-Type: application/x-www-form-urlencoded\r\n", LapRequestStr);
+  // debug.printf("%s", LapRequestStr);
+}
+
+void EmergencyhttpPost(const struct LogData *data){
+  IpAddr ip(133, 68, 205, 140);
+  Host host;
+  host.setName("solar-car.club.nitech.ac.jp");
+  host.setIp(ip);
+
+  char sendStr[32] = {0};
+
+  snprintf(sendStr, 32, "p=%s,%s", data->timeStr[1], data->timeStr[0]);
+  httpRequest(METHOD_POST, HTTP_TIMEOUT, &host, "/myLogger/Get/GetEmergency.php", "Content-Type: application/x-www-form-urlencoded\r\n", sendStr);
+}
+
+/*LCDへのデータ表示を行う locate関数は0行0列からはじまり,引数は(列,行)*/
+void LCD(const struct LogData *data){
+  lcd.locate(0, 0);
+  lcd.printf("SP");
+  lcd.locate(3, 0);
+  lcd.printf("%3.0f", data->motorSpeed);
+  lcd.locate(7, 0);
+  lcd.printf("km/h");
+  lcd.locate(12, 0);
+  lcd.printf("%s", data->timeStr[1]);
+
+  lcd.locate(0, 1);
+  lcd.printf("B");
+  lcd.locate(3, 1);
+  lcd.printf("%5.1f", data->remainBatteryParcent);
+  lcd.locate(9, 1);
+  lcd.printf("%%");
+  lcd.locate(15, 1);
+  lcd.printf("%s", data->timeStr[3]);
+
+  lcd.locate(0, 2);
+  lcd.printf("V");
+  lcd.locate(3, 2);
+  lcd.printf("%5.1f", data->batteryVoltage);
+  lcd.locate(9, 2);
+  lcd.printf("V");
+  lcd.locate(12, 2);
+  lcd.printf("LAP");
+  lcd.locate(15, 2);
+  lcd.printf("%s", data->timeStr[4]);
+
+  lcd.locate(0, 3);
+  lcd.printf("M");
+  lcd.locate(1, 3);
+  lcd.printf("%7.1f", data->RealtimeData[2][1]);
+  lcd.locate(9, 3);
+  lcd.printf("W");
+  lcd.locate(12, 3);
+  lcd.printf("%3.0f", data->LapoutData[2][2]);
+  lcd.locate(15, 3);
+  lcd.printf("Wh");
+  lcd.locate(18, 3);
+  lcd.printf("%d", data->lap);
+}
+
+void ReadyStartLCD(const struct LogData *data){
+  lcd.locate(2, 0);
+  lcd.printf(">>Push to Start<<");
+
+  lcd.locate(0, 1);
+  lcd.printf("B");
+  lcd.locate(2, 1);
+  lcd.printf("%5.1f", data->remainBatteryParcent);
+  lcd.locate(8, 1);
+  lcd.printf("%%");
+  lcd.locate(11, 1);
+  lcd.printf("Now Time");
+
+  lcd.locate(0, 2);
+  lcd.printf("V");
+  lcd.locate(2, 2);
+  lcd.printf("%5.1f", data->batteryVoltage);
+  lcd.locate(8, 2);
+  lcd.printf("V");
+  lcd.locate(11, 2);
+  lcd.printf("%s", data->timeStr[1]);
+
+  lcd.locate(0, 3);
+  lcd.printf("P");
+  lcd.locate(1, 3);
+  lcd.printf("%6.1f", data->RealtimeData[0][1]);
+  lcd.locate(8, 3);
+  lcd.printf("W");
+  if(myGPS.fix) {
+    lcd.locate(11, 3);
+    lcd.printf("GPS OK");
+  }
+}
+
+/*構造体のデータをUSBメモリへデータを保存(csvファイル)*/
+int USBSaveData(const struct LogData postdata[], const char name[][fileName_maxsize]){
+  int i, j, k;
+  FILE *fp = fopen(name[0], "a");
+
+  if(fp == NULL) {
+    debug.printf("USB can\'t open\n");
+    return -1;
+  }
+  else{
+    for(k = 0; k < storageTime; k++) {
+      fprintf(fp, "%s,%d,%d,%d,%.2f,", postdata[k].timeStr[1], postdata[k].lap, postdata[k].lapTime, postdata[k].totalTime, postdata[k].batteryVoltage);
+      for(j = 0; j < RealtimeData_j; j++) {
+        for(i = 0; i < RealtimeData_i; i++) {
+          fprintf(fp, "%.3f,", postdata[k].RealtimeData[i][j]);                         // 列に関して出力(全データ)
+        }
+      }
+      fprintf(fp, "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f\n", postdata[k].remainBatteryParcent, postdata[k].remainBatteryWh, postdata[k].motorSpeed, postdata[k].motorSpeed_pulse, postdata[k].gpsSpeed, postdata[k].motorAngle);
+/*
+      for(i = 0; i < 3; i++) {
+        if(i == 2) {
+          fprintf(fp, "%.5f\n", postdata[k].accData[i]);
+        }
+        else{
+          fprintf(fp, "%.5f,", postdata[k].accData[i]);
+        }
+      }
+ */
+    }
+  }
+  fclose(fp);
+  return 0;
+}
+
+int LapUSBSaveData(const struct LogData *data, const char name[][fileName_maxsize]){
+  int i, j;
+
+  FILE *fp = fopen(name[1], "a");
+  if(fp == NULL) {
+    debug.printf("USB can\'t open\n");
+    return -1;
+  }
+  else{
+    fprintf(fp, "\n%s,%d,%d,%d", data->timeStr[5], data->lap, data->lapTime, data->totalTime);
+    for(j = 0; j < LapoutData_j; j++) {
+      for(i = 0; i < LapoutData_i; i++) {
+        fprintf(fp, ",%7.2f", data->LapoutData[i][j]);                         // 列に関して(ラップ平均等の全データ)
+      }
+    }
+  }
+  fclose(fp);
+
+  return 0;
+}
+
+/** 蓄積されているデータをcsvファイルにバックアップ
+ * @param const strct LogData
+ * @param const double predata
+ * @param time_t preTime
+ */
+int SaveBackup(const struct LogData *data, const double predata[][preLapdata_j], time_t preTime){
+  int i, j;
+
+  FILE *fp_back = fopen("/usb/backup.csv", "w");
+  if(fp_back == NULL) {
+    debug.printf("USB can\'t open!\n");
+    return -1;
+  }
+  else{
+    fprintf(fp_back, "%d,%d,%d,%d\r\n", data->lap, data->totalTime, (unsigned int)startUnixTime, (unsigned int)preTime);
+    for(i = 0; i < RealtimeData_i; i++) {
+      for(j = 0; j < 2; j++) {
+        fprintf(fp_back, "%f,", data->RealtimeData[i][j+2]);  // 電流和,電力和
+      }
+      fprintf(fp_back, "\r\n");
+    }
+    for(i = 0; i < preLapdata_i; i++) {
+      for(j = 0; j < preLapdata_j; j++) {
+        fprintf(fp_back, "%f,", predata[i][j]);
+      }
+      fprintf(fp_back, "\r\n");
+    }
+  }
+  fclose(fp_back);
+
+  return 0;
+}
+
+/*基本はhttp通信を使いますが,何かあったときにシリアル通信できるようにしておきます
+ *送信データは基本的にラップ数,ラップタイム,LapoutData
+ */
+void XbeeSerial(const struct LogData *data){
+  int i, j;
+  char SerialSendStr[SerialSendStr_maxsize] = {0};
+
+  snprintf(SerialSendStr, SerialSendStr_maxsize, "\nLAP: %d, %s, BV:%6.2f[V],%6.2f[%%],%7.2f[Wh],\n",
+           data->lap,
+           data->timeStr[4],
+           data->batteryVoltage,
+           data->remainBatteryParcent,
+           data->remainBatteryWh);
+  snprintf(SerialSendStr, SerialSendStr_maxsize, "%s            Battery, Panel, Motor\nAllAve[W]: ", SerialSendStr);
+  for(i = 0; i < RealtimeData_i; i++) {
+    snprintf(SerialSendStr, SerialSendStr_maxsize, "%s%7.2f,", SerialSendStr, data->RealtimeData[i][4]);  // 平均電力
+  }
+  snprintf(SerialSendStr, SerialSendStr_maxsize, "%s\nlapAve[W]: ", SerialSendStr);   // 改行
+  for(j = 1; j < LapoutData_j; j++) {
+    for(i = 0; i < LapoutData_i; i++) {
+      snprintf(SerialSendStr, SerialSendStr_maxsize, "%s%7.2f,", SerialSendStr, data->LapoutData[i][j]);   // ラップ平均,積算
+    }
+    if(j == 1) snprintf(SerialSendStr, SerialSendStr_maxsize, "%s\nlapWh[Wh]: ", SerialSendStr);  // 改行
+  }
+  Xbee.printf("%s\n", SerialSendStr);   // Xbeeでシリアル送信
+}
+
+// 絶縁監視機能(漏れ電流測定)
+// 電流の値が0.5A以下の場合は例外
+// バッテリ+パネル電流ーモータ電流が1A以上になった場合、エラー出力
+void InsulateMonitor(const struct LogData *data){
+  bool stopDecison = false;
+
+  for(int i = 0; i < RealtimeData_i; i++) {
+    if(data->RealtimeData[i][0] < 0.5) {
+      stopDecison = true;
+      break;
+    }
+  }
+  if(!stopDecison) {
+    if((data->RealtimeData[0][0] + data->RealtimeData[1][0] - data->RealtimeData[2][0]) < 1) {
+      isoError = 0;    // 正常
+    }
+    else{
+      // isoError = 1;    // エラー
+    }
+  }
+}
+