Atsushi Hattori
/
20210627_Logger
hattori&ide
Loggermain.cpp
- Committer:
- hattori_atsushi
- Date:
- 18 months ago
- Revision:
- 0:f77369cabd75
File content as of revision 0:f77369cabd75:
/*更新情報 * 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(¤t_tmp[0]); // バッテリー電流 PanelMonitor.getCurrent(¤t_tmp[1]); // パネル電流 MotorMonitor.getCurrent(¤t_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; // エラー } } }