Fragt mithilfe des u-blox c027 über den CAN-Bus den Status der Batterieladung eines Think City Elektroautos ab und informiert per SMS den Halter. Bitte vor der ersten Verwendung die Handynummer anpassen. ---- Uses the u-blox c027 connected over can bus to a Think City electric car to inform a mobile number via SMS about the battery charging. Please change the mobile number to your mobile number before first use. Comments and messages are written in german.
Dependencies: C027 C027_Support mbed
main.cpp
- Committer:
- sleepyhead
- Date:
- 2014-10-18
- Revision:
- 0:4791fa520995
File content as of revision 0:4791fa520995:
/* Dieses Programm überwacht die CAN Bus Nachrichten des Think City (Elektroauto). Es sendet beim aktivieren des u-blox C027 ein SMS mit dem aktuellen Status. Ein weiteres SMS wird gesendet, wenn die Ladung abgebrochen oder beendet wurde. Während des Ladevorgang kann der Status mit einer SMS abgefragt werden, dabei spielt der Inhalt der SMS keine Rolle, sie muss nur von der Nummer in der Variable strMobilenumber kommen. */ #include "mbed.h" #include "GPS.h" #include "MDM.h" #include "C027.h" C027 c027; CAN can1(CANRD, CANTD); void recieve1(); // SIM PIN, NULL wenn keiner gesetzt wurde #define SIMPIN NULL // APN für GPRS, wird momentan nicht verwendet #define APN "gprs.swisscom.ch" // Benutzername für APN #define USERNAME NULL // Passwort für APN #define PASSWORD NULL // Verhindert die Ausgabe von Debugnachrichten über USB, falls das Modem ebenfalls darüber arbeitet #define DOTRACE ((USBRX!=MDMRXD)&&(USBTX!=MDMTXD)) #define TRACE (!DOTRACE)?:printf // An die Nummer strMobilenumber werden die SMS gesendet und auch nur von dieser SMS akzeptiert char strMobilenumber[32] = "+41765377278"; unsigned char idxSendSmsNow = 1; #define MAX_SMS_LEN 160 // Endzeit time_t timeStop; /* intCarStatus: 0 = Entladend 1 = Ladend 2 = Laden beendet 3 = Laden abgebrochen 4 = undefiniert float und int: 999 = undefiniert char: 4 = undefiniert */ unsigned char intCarStatus = 4; float fltStartlevel = 999; // CAN Bus Daten float fltSOC = 999; float fltBatteryTemp = 999; unsigned int intChargeLimitA = 999; float fltBatteryV = 999; float fltBatteryA = 999; float fltBatteryMaxChargeA = 999; float fltMaxChargeV = 999; unsigned int intFailedCells = 999; float fltBatteryTemp1 = 999; float fltBatteryTemp2 = 999; unsigned int intReleasedBatteries = 999; float fltMinDischargeV = 999; float fltMaxDischargeA = 999; float fltChargerPwm = 999; float fltMaxGeneratorV = 999; unsigned int intHighEstErrCat = 999; // 0 = no faults, 1 = Reserved, 2 = Warning, 3 = Delayed switch off, 4 = immediate switch off float fltPcuV = 999; float fltSpeed = 999; float fltPcuTemp = 999; unsigned int intMainsV = 999; float fltMainsA = 999; unsigned int intBmiState = 999; // 0 = idle state, 1 = discharge state (contactor closed), 15 = fault state unsigned int intBatteryType = 999; float fltBmiTempError = 999; float fltZebraTempError = 999; char strShifter[24] = "00 00 00 00 00 00 00 00"; /* char str301[24] = "00 00 00 00 00 00 00 00"; char str302[24] = "00 00 00 00 00 00 00 00"; char str303[24] = "00 00 00 00 00 00 00 00"; char str304[24] = "00 00 00 00 00 00 00 00"; char str305[24] = "00 00 00 00 00 00 00 00"; char str311[24] = "00 00 00 00 00 00 00 00"; char str263[24] = "00 00 00 00 00 00 00 00"; */ // Status unsigned char idxReducedNumberOfBatteries = 4; unsigned char idxEoc = 4; unsigned char idxSocGreater102 = 4; unsigned char idxChargeEn = 4; unsigned char idxOcvMeasurement = 4; unsigned char idxAc = 4; unsigned char idxChargeEnabled = 4; unsigned char idxFastChargeEnabled = 4; unsigned char idxDischargeEnabled = 4; unsigned char idxIsoTest = 4; unsigned char idxAcHeaterRelay = 4; unsigned char idxAcHeaterSwitch = 4; unsigned char idxRegenBrakeEnabled = 4; unsigned char idxDcDcEnabled = 4; unsigned char idxFanActive = 4; // Fehler unsigned char idxEpoEmerg = 4; // emergency power off happened unsigned char idxCrash = 4; unsigned char idxGeneralError = 4; unsigned char idxIntIsoError = 4; unsigned char idxExtIsoError = 4; unsigned char idxThermalIsoError = 4; unsigned char idxIsoError = 4; unsigned char idxTooManyFailedCells = 4; // Informationen unsigned char idxChargeWaitTempPCU = 4; unsigned char idxReachEocPlease = 4; unsigned char idxWaitTempDischarge = 4; unsigned char idxChargeWaitTempBattery = 4; // Warnungen unsigned char idxNoChargeCurrent = 4; unsigned char idxChargeOvervoltage = 4; unsigned char idxChargeOvercurrent = 4; unsigned char readBit(char strByte, char intRow) // Gibt ein Bit aus einem Byte aus, von rechts gezählt, intRow = 0 - 7 { return strByte >> intRow & 1; } int main(void) { int intRet; #ifdef TARGET_LPC1768 char buf[2048] = ""; #else char buf[512] = ""; #endif // Startet die serielle Verbindung über USB, falls das Modem nicht über USB angeschlossen ist if (DOTRACE) { Serial pc(USBTX,USBRX); pc.baud(115200); } wait_ms(1000); TRACE("GSMCAN startet...\r\n"); // Schaltet das Modem und GPS ein c027.mdmPower(true); c027.gpsPower(true); wait(2); // Erstellt das GPS Objekt #if GPSADR // GPSI2C class GPSI2C gps(GPSSDA,GPSSCL,GPSADR); #elif GPSBAUD // GPSSerial class GPSSerial gps(GPSTXD,GPSRXD,GPSBAUD); #else #warning "Bitte GPS Pins definieren" #endif // Erstellt das Modem Objekt MDMSerial mdm(MDMTXD,MDMRXD,MDMBAUD #if DEVICE_SERIAL_FC ,MDMRTS,MDMCTS #endif ); // Initialisert das Modem TRACE("Modem wird initialisiert...\r\n"); MDMParser::DevStatus devStatus; MDMParser::NetStatus netStatus; bool mdmOk = mdm.init(SIMPIN, &devStatus); if (mdmOk) { if (DOTRACE) mdm.dumpDevStatus(&devStatus); // Wartet bis das Netz verbunden ist TRACE("Warte auf GSM Netz...\r\n"); while (!mdm.checkNetStatus(&netStatus)) wait_ms(1000); if (DOTRACE) mdm.dumpNetStatus(&netStatus); } else { // Wenn das Modem nicht gestartet werden kann, versuchen wir eine Neustart TRACE("Modem Initialisierung fehlgeschlagen, Neustartversuch...\r\n"); c027.mdmReset(); wait(5); mdmOk = mdm.init(SIMPIN, &devStatus); if (DOTRACE) mdm.dumpDevStatus(&devStatus); if (mdmOk) { while (!mdm.checkNetStatus(&netStatus)) wait_ms(1000); if (DOTRACE) mdm.dumpNetStatus(&netStatus); } } TRACE("GSM Modem bereit, starte CAN\r\n"); DigitalOut can_standby(CANS); can1.frequency(500000); can1.mode(CAN::Normal); can1.attach(&recieve1); can_standby = 0; char link[128] = ""; unsigned int i = 0xFFFFFFFF; const int wait = 100; bool abort = false; while (!abort) { if (i == 10000/wait) { while ((intRet = gps.getMessage(buf, sizeof(buf))) > 0) { int len = LENGTH(intRet); if ((PROTOCOL(intRet) == GPSParser::NMEA) && (len > 6) && !strncmp("$GPGLL", buf, 6)) { double la = 0, lo = 0; char ch; if (gps.getNmeaAngle(1,buf,len,la) && gps.getNmeaAngle(3,buf,len,lo) && gps.getNmeaItem(6,buf,len,ch) && ch == 'A') { TRACE("GPS Location: %.5f %.5f\r\n", la, lo); sprintf(link, "https://maps.google.com/?q=%.5f,%.5f", la, lo); } } } } // Status bestimmen if (fltSOC != 999 && idxAc != 4 && idxEoc != 4 && (i++ > 10000/wait)) { i = 0; if (idxAc == 1 && idxEoc == 0 && intCarStatus != 1) { intCarStatus = 1; fltStartlevel = fltSOC; set_time(1); TRACE("Ladend; Anfangsstand: %.1f%%\r\n", fltStartlevel); } else if (idxEoc == 1 && intCarStatus <= 1) { intCarStatus = 2; timeStop = time(NULL)/60; TRACE("Laden beendet; Anfangsstand: %.1f%%; Zeit: %d Minuten\r\n", fltStartlevel, timeStop); if (fltStartlevel < 90) { idxSendSmsNow = 1; } } else if (idxAc == 0 && intCarStatus == 1) { intCarStatus = 3; timeStop = time(NULL)/60; TRACE("Laden abgebrochen; Anfangsstand: %.1f%%; Zeit: %d Minuten\r\n", fltStartlevel, timeStop); if (timeStop > 1) { idxSendSmsNow = 1; } } else if (idxAc == 0 && intCarStatus == 2) { intCarStatus = 0; fltStartlevel = fltSOC; TRACE("Entladend; Anfangsstand: %.1f%%\r\n", fltStartlevel); } if (intCarStatus == 4 && idxAc == 0) { intCarStatus = 0; fltStartlevel = fltSOC; TRACE("Entladend; Anfangsstand: %.1f%%\r\n", fltStartlevel); } else if (intCarStatus == 4 && idxEoc == 1) { intCarStatus = 2; fltStartlevel = fltSOC; TRACE("Laden beendet; Anfangsstand: %.1f%%\r\n", fltStartlevel); } else if (intCarStatus == 4 && idxAc == 1 && idxEoc == 0) { intCarStatus = 1; fltStartlevel = fltSOC; set_time(1); TRACE("Ladend; Anfangsstand: %.1f%%\r\n", fltStartlevel); } switch (intCarStatus) { case 0: TRACE("Entladend; Anfangsstand: %.1f%%; Stand: %.1f%%; PCU: %.1fV\r\n", fltStartlevel, fltSOC, fltPcuV); break; case 1: TRACE("Ladend; Anfangsstand: %.1f%%; Stand: %.1f%%; PCU: %.1fV\r\n", fltStartlevel, fltSOC, fltPcuV); break; case 2: TRACE("Laden beendet; Anfangsstand: %.1f%%; Zeit: %d Minuten; PCU: %.1fV\r\n", fltStartlevel, timeStop, fltPcuV); break; case 3: TRACE("Laden abgebrochen; Anfangsstand: %.1f%%; Schlussstand: %.1f%%; Zeit: %d Minuten; PCU: %.1fV\r\n", fltStartlevel, fltSOC, timeStop, fltPcuV); break; case 4: TRACE("undefiniert;\r\n"); break; } if (mdmOk) { // Status des GSM Modems ausgeben if (mdm.checkNetStatus(&netStatus)) { if (DOTRACE) mdm.dumpNetStatus(&netStatus); } // Ungelesene SMS prüfen int ix[8]; int n = mdm.smsList("REC UNREAD", ix, 8); if (8 < n) n = 8; while (0 < n--) { char num[32]; if (mdm.smsRead(ix[n], num, buf, sizeof(buf))) { TRACE("SMS von \"%s\" mit dem Text \"%s\"\r\n", num, buf); TRACE("Loesche SMS %d\r\n", ix[n]); mdm.smsDelete(ix[n]); // Sende den aktuellen Status, wenn die SMS von der strMobilenumber gekommen ist if (strcmp(num, strMobilenumber)==0) { idxSendSmsNow = 1; } } } } if (mdmOk && idxSendSmsNow == 1 && intCarStatus != 4) { idxSendSmsNow = 0; char strReply[160]; switch (intCarStatus) { case 0: snprintf(strReply, MAX_SMS_LEN, "Entladend; Anfangsstand: %.1f%%; Stand: %.1f%%; PCU: %.1fV; %s", fltStartlevel, fltSOC, fltPcuV, link); mdm.smsSend(strMobilenumber, strReply); break; case 1: snprintf(strReply, MAX_SMS_LEN, "Ladend; Anfangsstand: %.1f%%; Stand: %.1f%%; PCU: %.1fV; %s", fltStartlevel, fltSOC, fltPcuV, link); mdm.smsSend(strMobilenumber, strReply); break; case 2: snprintf(strReply, MAX_SMS_LEN, "Laden beendet; Anfangsstand: %.1f%%; Zeit: %d Minuten; PCU: %.1fV; %s", fltStartlevel, timeStop, fltPcuV, link); mdm.smsSend(strMobilenumber, strReply); break; case 3: snprintf(strReply, MAX_SMS_LEN, "Laden abgebrochen; Anfangsstand: %.1f%%; Schlussstand: %.1f%%; Zeit: %d Minuten; PCU: %.1fV; %s", fltStartlevel, fltSOC, timeStop, fltPcuV, link); mdm.smsSend(strMobilenumber, strReply); intCarStatus = 0; fltStartlevel = fltSOC; } TRACE("Sende SMS-Antwort \"%s\" an \"%s\"\r\n", strReply, strMobilenumber); delete[] strReply; } // Gebe den aktuellen Status aller CAN Bus Nachrichten aus //TRACE("0x301: str301=%s\r\n", str301); TRACE("0x301: fltBatteryA=%.1f, fltBatteryV=%.1f, fltSOC=%.1f, fltBatteryTemp=%.1f\r\n", fltBatteryA, fltBatteryV, fltSOC, fltBatteryTemp); //TRACE("0x302: str302=%s\r\n", str302); TRACE("0x302: idxGeneralError=%d, idxIsoError=%d, fltMinDischargeV=%.1f, fltMaxDischargeA=%.1f\r\n", idxGeneralError, idxIsoError, fltMinDischargeV, fltMaxDischargeA); //TRACE("0x303: str303=%s\r\n", str303); TRACE("0x303: fltBatteryMaxChargeA=%.1f, fltMaxChargeV=%.1f, idxChargeEnabled=%d, idxRegenBrakeEnabled=%d, idxDischargeEnabled=%d, idxFastChargeEnabled=%d, idxDcDcEnabled=%d, idxAc=%d, intReleasedBatteries=%d, idxReducedNumberOfBatteries=%d, idxEpoEmerg=%d, idxCrash=%d, idxFanActive=%d, idxSocGreater102=%d, idxIsoTest=%d, idxChargeWaitTempPCU=%d\r\n", fltBatteryMaxChargeA, fltMaxChargeV, idxChargeEnabled, idxRegenBrakeEnabled, idxDischargeEnabled, idxFastChargeEnabled, idxDcDcEnabled, idxAc, intReleasedBatteries, idxReducedNumberOfBatteries, idxEpoEmerg, idxCrash, idxFanActive, idxSocGreater102, idxIsoTest, idxChargeWaitTempPCU); //TRACE("0x304: str304=%s\r\n", str304); TRACE("0x304: fltMaxGeneratorV=%.1f, intHighEstErrCat=%d, idxEoc=%d, idxReachEocPlease=%d, idxChargeWaitTempBattery=%d, idxTooManyFailedCells=%d, idxAcHeaterRelay=%d, idxAcHeaterSwitch=%d, fltBatteryTemp1=%.1f, fltBatteryTemp2=%.1f\r\n", fltMaxGeneratorV, intHighEstErrCat, idxEoc, idxReachEocPlease, idxChargeWaitTempBattery, idxTooManyFailedCells, idxAcHeaterRelay, idxAcHeaterSwitch, fltBatteryTemp1, fltBatteryTemp2); //TRACE("0x305: str305=%s\r\n", str305); TRACE("0x305: fltChargerPwm=%.1f, idxIntIsoError=%d, idxExtIsoError=%d, idxChargeEn=%d, idxOcvMeasurement=%d, idxNoChargeCurrent=%d, idxChargeOvervoltage=%d, idxChargeOvercurrent=%d, intFailedCells=%d, idxWaitTempDischarge=%d, idxThermalIsoError=%d, intBmiState=%d, intBatteryType=%d, fltBmiTempError=%.1f, fltZebraTempError=%.1f\r\n", fltChargerPwm, idxIntIsoError, idxExtIsoError, idxChargeEn, idxOcvMeasurement, idxNoChargeCurrent, idxChargeOvervoltage, idxChargeOvercurrent, intFailedCells, idxWaitTempDischarge, idxThermalIsoError, intBmiState, intBatteryType, fltBmiTempError, fltZebraTempError); //TRACE("0x311: str311=%s\r\n", str311); TRACE("0x311: intChargeLimitA=%d\r\n", intChargeLimitA); //TRACE("0x263: str263=%s\r\n", str263); TRACE("0x263: fltPcuV=%.1f, fltSpeed=%.1f, fltPcuTemp=%.1f, intMainsV=%d, fltMainsA=%.1f\r\n", fltPcuV, fltSpeed, fltPcuTemp, intMainsV, fltMainsA); TRACE("0x264: strShifter=%s\r\n", strShifter); } wait_ms(wait); } mdm.powerOff(); gps.powerOff(); TRACE("Schalte Modem und GPS aus...\r\n"); #ifdef C027_USEONBOARD c027.mdmPower(false); c027.gpsPower(false); #endif return 0; } // Hier werden die CAN Nachrichten verarbeitet und in die Variablen gespeichert void recieve1() { CANMessage canMsg; if (can1.read(canMsg)) { switch (canMsg.id) { case 0x301: fltBatteryA = ((float)(((int) canMsg.data[0] << 8) + canMsg.data[1]) / 10); fltBatteryV = ((float)(((int) canMsg.data[2] << 8) + canMsg.data[3]) / 10); fltSOC = 100.0f - ((float)(((int) canMsg.data[4] << 8) + canMsg.data[5]) / 10); fltBatteryTemp = ((float)(((int) canMsg.data[6] << 8) + canMsg.data[7]) / 10); //snprintf(str301, 24, "%X %X %X %X %X %X %X %X" , canMsg.data[0], canMsg.data[1], canMsg.data[2], canMsg.data[3], canMsg.data[4], canMsg.data[5], canMsg.data[6], canMsg.data[7]); break; case 0x302: idxGeneralError = readBit(canMsg.data[0], 0); idxIsoError = readBit(canMsg.data[2], 0); fltMinDischargeV = ((float)(((int) canMsg.data[4] << 8) + canMsg.data[5]) / 10); fltMaxDischargeA = ((float)(((int) canMsg.data[6] << 8) + canMsg.data[7]) / 10); //snprintf(str302, 24, "%X %X %X %X %X %X %X %X" , canMsg.data[0], canMsg.data[1], canMsg.data[2], canMsg.data[3], canMsg.data[4], canMsg.data[5], canMsg.data[6], canMsg.data[7]); break; case 0x303: fltBatteryMaxChargeA = ((float)(((int) canMsg.data[0] << 8) + canMsg.data[1]) / 10); fltMaxChargeV = ((float)(((int) canMsg.data[2] << 8) + canMsg.data[3]) / 10); idxChargeEnabled = readBit(canMsg.data[4], 0); idxRegenBrakeEnabled = readBit(canMsg.data[4], 1); idxDischargeEnabled = readBit(canMsg.data[4], 2); idxFastChargeEnabled = readBit(canMsg.data[4], 3); idxDcDcEnabled = readBit(canMsg.data[4], 4); idxAc = readBit(canMsg.data[4], 5); intReleasedBatteries = canMsg.data[5]; idxReducedNumberOfBatteries = readBit(canMsg.data[6], 0); idxEpoEmerg = readBit(canMsg.data[6], 3); idxCrash = readBit(canMsg.data[6], 4); idxFanActive = readBit(canMsg.data[6], 5); idxSocGreater102 = readBit(canMsg.data[6], 6); idxIsoTest = readBit(canMsg.data[6], 7); idxChargeWaitTempPCU = readBit(canMsg.data[7], 0); //snprintf(str303, 24, "%X %X %X %X %X %X %X %X" , canMsg.data[0], canMsg.data[1], canMsg.data[2], canMsg.data[3], canMsg.data[4], canMsg.data[5], canMsg.data[6], canMsg.data[7]); break; case 0x304: fltMaxGeneratorV = ((float)(((int) canMsg.data[0] << 8) + canMsg.data[1]) / 10); intHighEstErrCat = ((unsigned char) canMsg.data[2]); idxEoc = readBit(canMsg.data[3], 0); idxReachEocPlease = readBit(canMsg.data[3], 1); idxChargeWaitTempBattery = readBit(canMsg.data[3], 2); idxTooManyFailedCells = readBit(canMsg.data[3], 3); idxAcHeaterRelay = readBit(canMsg.data[3], 4); idxAcHeaterSwitch = readBit(canMsg.data[3], 5); fltBatteryTemp1 = ((float)(((int) canMsg.data[4] << 8) + canMsg.data[5]) / 10); fltBatteryTemp2 = ((float)(((int) canMsg.data[6] << 8) + canMsg.data[7]) / 10); //snprintf(str304, 24, "%X %X %X %X %X %X %X %X" , canMsg.data[0], canMsg.data[1], canMsg.data[2], canMsg.data[3], canMsg.data[4], canMsg.data[5], canMsg.data[6], canMsg.data[7]); break; case 0x305: fltChargerPwm = ((float)(((int) canMsg.data[0] << 8) + canMsg.data[1]) / 10); intBmiState = canMsg.data[2] & 15; idxIntIsoError = readBit(canMsg.data[2], 4); idxExtIsoError = readBit(canMsg.data[2], 5); idxChargeEn = readBit(canMsg.data[3], 0); idxOcvMeasurement = readBit(canMsg.data[3], 1); idxNoChargeCurrent = readBit(canMsg.data[3], 2); idxChargeOvervoltage = readBit(canMsg.data[3], 3); idxChargeOvercurrent = readBit(canMsg.data[3], 4); intFailedCells = ((int) canMsg.data[4]<<8) + canMsg.data[5]; idxWaitTempDischarge = readBit(canMsg.data[6], 6); idxThermalIsoError = readBit(canMsg.data[6], 5); intBatteryType = (canMsg.data[3] & 224) * 0.03125; fltBmiTempError = (canMsg.data[6] & 6) / 2; fltZebraTempError = (canMsg.data[6] & 24) * 0.125; //snprintf(str305, 24, "%X %X %X %X %X %X %X %X" , canMsg.data[0], canMsg.data[1], canMsg.data[2], canMsg.data[3], canMsg.data[4], canMsg.data[5], canMsg.data[6], canMsg.data[7]); break; case 0x311: intChargeLimitA = ((unsigned char) canMsg.data[1]) * 0.2 ; //snprintf(str311, 24, "%X %X %X %X %X %X %X %X" , canMsg.data[0], canMsg.data[1], canMsg.data[2], canMsg.data[3], canMsg.data[4], canMsg.data[5], canMsg.data[6], canMsg.data[7]); break; case 0x263: fltPcuV = ((float)((int) canMsg.data[3]) / 10); fltSpeed = ((float)((int) canMsg.data[5]) / 2); fltPcuTemp = ((float)((int) canMsg.data[2]) / 2); intMainsV = ((unsigned char) canMsg.data[1]); fltMainsA = ((float)((int) canMsg.data[0]) * 2 / 10); //snprintf(str263, 24, "%X %X %X %X %X %X %X %X" , canMsg.data[0], canMsg.data[1], canMsg.data[2], canMsg.data[3], canMsg.data[4], canMsg.data[5], canMsg.data[6], canMsg.data[7]); break; case 0x264: snprintf(strShifter, 24, "%X %X %X %X %X %X %X %X" , canMsg.data[0], canMsg.data[1], canMsg.data[2], canMsg.data[3], canMsg.data[4], canMsg.data[5], canMsg.data[6], canMsg.data[7]); // Mit folgendem Block können alle unbekannten Nachrichten ausgegeben werden, bremst allerdings das Programm merklich ab /* default: TRACE("ID: 0x%x", canMsg.id); TRACE(" Len: %d", canMsg.len); TRACE(" Type: %d", canMsg.type); TRACE(" Format: %d", canMsg.format); TRACE(" Data:"); for (int count = 0; count < canMsg.len; count++) { TRACE(" %x", canMsg.data[count]); } TRACE("\r\n"); */ } } }