/* ; Project: Open Vehicle Monitor System ; Date: 19th Nov 2022 ; ; (C) 2022 Carsten Schmiemann ; ; Permission is hereby granted, free of charge, to any person obtaining a copy ; of this software and associated documentation files (the "Software"), to deal ; in the Software without restriction, including without limitation the rights ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ; copies of the Software, and to permit persons to whom the Software is ; furnished to do so, subject to the following conditions: ; ; The above copyright notice and this permission notice shall be included in ; all copies or substantial portions of the Software. ; ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN ; THE SOFTWARE. */ #include "ovms_log.h" #include #include #include #include "pcp.h" #include "ovms_metrics.h" #include "ovms_events.h" #include "ovms_config.h" #include "ovms_command.h" #include "metrics_standard.h" #include "ovms_notify.h" #include "ovms_peripherals.h" #include "ovms_netmanager.h" #include "vehicle_renaultzoe_ph2_can.h" void OvmsVehicleRenaultZoePh2CAN::CanInit() { OvmsCommand* cmd; OvmsCommand* obd; obd = cmd_xrt->RegisterCommand("can", "CAN tools"); cmd = obd->RegisterCommand("request", "Send ISO-TP request, output response"); cmd->RegisterCommand("device", "Send ISO-TP request to a ECU", shell_can_request, " ", 3, 3); cmd->RegisterCommand("broadcast", "Send ISO-TP request as broadcast", shell_can_request, "", 1, 1); } void OvmsVehicleRenaultZoePh2CAN::shell_obd_request(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv) { uint16_t txid = 0, rxid = 0; string request; string response; // parse args: string device = cmd->GetName(); if (device == "device") { if (argc < 3) { writer->puts("ERROR: too few args, need: txid rxid request"); return; } txid = strtol(argv[0], NULL, 16); rxid = strtol(argv[1], NULL, 16); request = hexdecode(argv[2]); } else { if (argc < 1) { writer->puts("ERROR: too few args, need: request"); return; } request = hexdecode(argv[0]); // "broadcast" txid = 0x7df; rxid = 0; } // validate request: if (request.size() == 0) { writer->puts("ERROR: no request"); return; } else { uint8_t type = request.at(0); if ((POLL_TYPE_HAS_16BIT_PID(type) && request.size() < 3) || (POLL_TYPE_HAS_8BIT_PID(type) && request.size() < 2)) { writer->printf("ERROR: request too short for type %02X\n", type); return; } } // execute request: int err = OvmsVehicleRenaultZoePh2CAN->CanRequest(txid, rxid, request, response); if (err == -1) { writer->puts("ERROR: timeout waiting for response"); return; } else if (err) { writer->printf("ERROR: request failed with response error code %02X\n", err); return; } // output response as hex dump: writer->puts("Response:"); char *buf = NULL; size_t rlen = response.size(), offset = 0; do { rlen = FormatHexDump(&buf, response.data() + offset, rlen, 16); offset += 16; writer->puts(buf ? buf : "-"); } while (rlen); if (buf) free(buf); } int OvmsVehicleRenaultZoePh2CAN::CanRequest(uint16_t txid, uint16_t rxid, string request, string& response, int timeout_ms /*=3000*/) { OvmsMutexLock lock(&zoe_can1_request); // prepare single poll: OvmsVehicle::poll_pid_t poll[] = { { txid, rxid, 0, 0, { 1, 1, 1 }, 0, ISOTP_STD }, POLL_LIST_END }; assert(request.size() > 0); poll[0].type = request[0]; if (POLL_TYPE_HAS_16BIT_PID(poll[0].type)) { assert(request.size() >= 3); poll[0].args.pid = request[1] << 8 | request[2]; poll[0].args.datalen = LIMIT_MAX(request.size()-3, sizeof(poll[0].args.data)); memcpy(poll[0].args.data, request.data()+3, poll[0].args.datalen); } else if (POLL_TYPE_HAS_8BIT_PID(poll[0].type)) { assert(request.size() >= 2); poll[0].args.pid = request.at(1); poll[0].args.datalen = LIMIT_MAX(request.size()-2, sizeof(poll[0].args.data)); memcpy(poll[0].args.data, request.data()+2, poll[0].args.datalen); } else { poll[0].args.pid = 0; poll[0].args.datalen = LIMIT_MAX(request.size()-1, sizeof(poll[0].args.data)); memcpy(poll[0].args.data, request.data()+1, poll[0].args.datalen); } // stop default polling: PollSetPidList(m_can1, NULL); vTaskDelay(pdMS_TO_TICKS(100)); // clear rx semaphore, start single poll: zoe_can1_rxwait.Take(0); PollSetPidList(m_can1, poll); // wait for response: bool rxok = zoe_can1_rxwait.Take(pdMS_TO_TICKS(timeout_ms)); if (rxok == pdTRUE) response = zoe_can1_rxbuf; // restore default polling: zoe_can1_rxwait.Give(); PollSetPidList(m_can1, renault_zoe_polls); return (rxok == pdFALSE) ? -1 : (int)zoe_can1_rxerr; } OvmsVehicle::vehicle_command_t OvmsVehicleRenaultZoePh2CAN::CommandPreHeat(bool climatecontrolon) { //ToDo: Sniff TCU packets for preheat/cool, OVMS is connected on TCU port return NotImplemented; } OvmsVehicle::vehicle_command_t OvmsVehicleRenaultZoePh2CAN::CommandWakeup() { ESP_LOGI(TAG, "Send Wakeup Command"); vTaskDelay(500 / portTICK_PERIOD_MS); uint32_t txid = 0x747, rxid = 0x767; //IVI uint8_t protocol = ISOTP_STD; int timeout_ms = 100; std::string request; std::string response; request = hexdecode("2F90090301"); int err = PollSingleRequest(m_can1, txid, rxid, request, response, timeout_ms, protocol); request = hexdecode("2F900900"); err = PollSingleRequest(m_can1, txid, rxid, request, response, timeout_ms, protocol); if (err == POLLSINGLE_TXFAILURE) { ESP_LOGE(TAG, "ERROR: transmission failure (CAN bus error)"); return Fail; } else if (err < 0) { ESP_LOGD(TAG, "ERROR: timeout waiting for poller/response"); return Fail; } else if (err) { ESP_LOGD(TAG, "ERROR: request failed with response error code %02X\n", err); return Fail; } return Success; } OvmsVehicle::vehicle_command_t OvmsVehicleRenaultZoePh2CAN::CommandLock(const char* pin) { //ToDo: Sniff HFM/BCM packets return NotImplemented; } OvmsVehicle::vehicle_command_t OvmsVehicleRenaultZoePh2CAN::CommandUnlock(const char* pin) { //ToDo: Sniff HFM/BCM packets return NotImplemented; }