Compare commits

...

132 commits

Author SHA1 Message Date
Carsten Schmiemann d5c089feb0 Set ota server 2023-01-24 20:53:18 +01:00
Carsten Schmiemann 1b7124c2c7 Sync upstream 2023-01-24 20:50:05 +01:00
Carsten Schmiemann 9eb77d6aef Write down caputured packets for preheat, no function atm 2022-11-23 01:31:19 +01:00
Carsten Schmiemann 5c2f3497db Add console writer for commands 2022-11-23 01:06:17 +01:00
Carsten Schmiemann 69b4d25e62 Enable ph2 can commands 2022-11-23 01:02:31 +01:00
Carsten Schmiemann f0aa4187c7 Re arrange can bus interfaces, disable logging because crash 2022-11-20 23:46:21 +01:00
Carsten Schmiemann c1a2b4dbf8 Try menu structure for future command 2022-11-20 23:42:02 +01:00
Carsten Schmiemann 34ad8c41b8 Vehicle tag conflict 2022-11-20 19:21:00 +01:00
Carsten Schmiemann 5d5d8b7870 Remov console logging because of error flooding (defective serial-usb?) 2022-11-20 19:17:39 +01:00
Carsten Schmiemann b4486dd06e Include zoe modules default build 2022-11-20 19:10:55 +01:00
Carsten Schmiemann a3bb202c6a Add can msg text log, charge type, gen type 2022-11-20 19:05:11 +01:00
Carsten Schmiemann 705a4d1d3a Revert SOC modification multiplier 2022-11-20 18:52:27 +01:00
Carsten Schmiemann 6c8ca654bc Sync module files 2022-11-20 18:48:10 +01:00
Carsten Schmiemann 0db69f901c Re-arrang header, rename can1 functions 2022-11-20 18:43:47 +01:00
Carsten Schmiemann 3d87aa9a79 Revert custom metrics back to original 2022-11-20 16:52:49 +01:00
Carsten Schmiemann 9a451be72a Fix metrics from original project 2022-11-19 01:47:12 +01:00
Carsten Schmiemann 9c874adfaa Update jenkins config 2022-11-19 01:43:01 +01:00
Carsten Schmiemann fe485dc049 Add command for manual ISO TP Std poll 2022-11-19 01:40:39 +01:00
Carsten Schmiemann 5b036b9ba6 Fix build errors 2022-11-19 01:20:40 +01:00
Carsten Schmiemann 08269a30c3 Add ph2_commands file, update headers 2022-11-19 01:14:10 +01:00
Carsten Schmiemann 42e5f1c6ba Add ph2_commands file, update headers 2022-11-19 01:09:59 +01:00
Carsten Schmiemann 5bf9c60a47 Add V1-CAN to CAN2 interface 2022-11-19 00:05:06 +01:00
Carsten Schmiemann 89d64b2211 Modify sleep behavior for direct CAN access with free frames 2022-11-19 00:01:40 +01:00
Carsten Schmiemann 848429860c Initial can direct access version derrived from obd poll version 2022-11-18 23:44:35 +01:00
Carsten Schmiemann 901371a989 Original project changes: Add commonly used power consumption units to Metrics 2022-11-18 23:26:11 +01:00
Carsten Schmiemann c761ec774c Original project changes: Metrics - Improve accuracy/consistency km <--> mi and other length conversions 2022-11-18 23:24:11 +01:00
Carsten Schmiemann 3121178a97 Original project changes: mdns: task stack size configuration (MDNS_TASK_STACK_SIZE) 2022-11-18 23:21:41 +01:00
Carsten Schmiemann 88661c680d Re-Do customizations 2022-11-06 02:56:04 +01:00
Carsten Schmiemann 949affec1f Sync to original project 2022-11-06 02:40:10 +01:00
Carsten Schmiemann 8bbf09ffc5 Original project changes: Add Method to change voltage cell arrangement, preserving existing data 2022-11-01 15:03:25 +01:00
Carsten Schmiemann cf9242d798 Original project changes: BMS - Add Function to resize BMS Temperatures 2022-11-01 15:02:18 +01:00
Carsten Schmiemann 54ab5fa818 Original project changes: Vehicle - Use Enum for Bat/Temp status 2022-11-01 14:59:17 +01:00
Carsten Schmiemann ba1a0b10a3 Original project changes: Vehicle - Method to change voltage cell arrangement, preserving existing data 2022-11-01 14:58:14 +01:00
Carsten Schmiemann ef2f091267 Original project changes: Vehicle - Use Enum for Bat/Temp status 2022-11-01 14:56:56 +01:00
Carsten Schmiemann 631d3bbb2c Original project changes: Task stack size adjustments addressing issue 2022-11-01 14:54:22 +01:00
Carsten Schmiemann 1bbb754ff7 Original project changes: Config restore: shutdown SSH RSAKeyGen as necessary, fixes issue 2022-11-01 14:50:52 +01:00
Carsten Schmiemann 54edebfb5b Original project changes: Duktape: mark DuktapeConsoleCommand TODO, silence warning 2022-11-01 14:48:48 +01:00
Carsten Schmiemann d7944c8894 Original project changes: Zoe Ph2: include in standard build, fix tag warning 2022-11-01 14:47:28 +01:00
Carsten Schmiemann 337b736771 Original project changes: Bit/Byte Utils - Fixes and improvements 2022-11-01 14:29:18 +01:00
Carsten Schmiemann f6f72e8c16 Original project changes: Vehicle - Add buffer handling tools to ovms utils 2022-11-01 14:28:10 +01:00
Carsten Schmiemann 58402124dd Original project changes: Server V2 & V3: fix race condition on stopping 2022-11-01 14:24:57 +01:00
Carsten Schmiemann 5d3e4f4efa Original project changes: Boot: minor calculation optimization on 12V check 2022-11-01 14:22:57 +01:00
Carsten Schmiemann fa09dc8c71 Original project changes: fix negative integer DBC numbers having wrong values 2022-11-01 14:21:27 +01:00
Carsten Schmiemann 57e0666b47 Original project changes: fix small typos/re-formatting in documentation 2022-11-01 14:16:51 +01:00
Carsten Schmiemann a95e6e4fa2 Original project changes: Vehicle - Fix RestartCellTemperatures to resize the correct vector 2022-11-01 14:11:31 +01:00
Carsten Schmiemann dab42140b8 Rename lawicel typo fix 2022-10-03 12:40:17 +02:00
Carsten Schmiemann 2f9e12f0f0 Original project changes: Fix issue with use of char * after std::string deallocated in multiple places 2022-10-03 12:38:52 +02:00
Carsten Schmiemann a2a079c812 Original project changes: Cellular: reduce UART queue overflows by distributing status poll 2022-10-03 12:33:00 +02:00
Carsten Schmiemann e6f2a6b992 Original project changes: rename lawricel format to lawicel (openvehicles#728) 2022-10-03 12:29:40 +02:00
Carsten Schmiemann b5e18a4c0f Original project changes: fix cs11 (canswitch) tag, and reference this format in the documentation 2022-10-03 12:27:55 +02:00
Carsten Schmiemann de5abbbace Original project changes: Improvements of the TCP client CAN logger fix by using a semaphore instead of a wait loop. 2022-10-03 12:25:13 +02:00
Carsten Schmiemann 70575cd193 Original project changes: Fix the TCP client CAN logger so that it waits for the state of the connection to be known before returning. 2022-10-03 12:23:51 +02:00
Carsten Schmiemann ba5aa7d84c Disable debug on bat_soc 2022-10-03 12:20:10 +02:00
Carsten Schmiemann 5955b23617 Debug SOC calculation to match CLUSTER 2022-09-08 12:23:18 +02:00
Carsten Schmiemann 3433335cbe Original project changes: Support PANDA can logging format 2022-09-08 12:09:25 +02:00
Carsten Schmiemann 4a298c2d21 Original project changes: Add can log udpserver feature 2022-09-08 12:07:27 +02:00
Carsten Schmiemann 96ffaf8f40 Bump to 3.3.003 2022-09-08 12:03:19 +02:00
Carsten Schmiemann faad212a43 Original project changes: Use StatusPoller() for all cellular polls, to better support CGREG and CEREG 2022-09-08 11:54:11 +02:00
Carsten Schmiemann 1fa378c5f9 Original project changes: Support multiple network registration types, including GSM, GPRS, and…
… EPS (E-UTRAN)
2022-09-08 11:51:11 +02:00
Carsten Schmiemann 7ea81e8fa4 Original project changes: Enhance "cellular cmd" to show output from modem 2022-09-08 11:45:47 +02:00
Carsten Schmiemann a0ec7148db Kristian: Modify battery SOC reading to match CLUSTER 2022-08-19 20:02:16 +02:00
Carsten Schmiemann b17eca6d14 Clean up code 2022-07-05 22:22:05 +02:00
Carsten Schmiemann 6faf2aee97 Original project changes: Docs/Userguide: add battery cell monitor UI explanation 2022-07-05 22:16:52 +02:00
Carsten Schmiemann 4063e7167c Original project changes: Support detection of SIM card not inserted in cellular modem system 2022-07-05 22:11:31 +02:00
Carsten Schmiemann 259d13b3f1 Remove CLUSTER trip counter completely 2022-06-29 02:07:12 +02:00
Carsten Schmiemann 15e8d58744 Removed User SOC (EVC) because its unreliable 2022-06-29 02:03:57 +02:00
Carsten Schmiemann 798acb1839 Improve efficiency of MQTT streaming behavior 2022-06-29 01:54:20 +02:00
Carsten Schmiemann b36c0c4621 Remove omvs-ca since integrated into fw 2022-05-27 23:49:27 +02:00
Carsten Schmiemann 6d07e8fe64 Update index.rst 2022-05-27 23:27:01 +02:00
Carsten Schmiemann 8f285cc917 Clean up code for two implementations 2022-05-27 23:25:17 +02:00
Carsten Schmiemann 894a063083 Original project changes: Location: fix concurrency between park position update & check 2022-05-27 23:07:28 +02:00
Carsten Schmiemann dec6c220d5 Add ms_env_on to MetricStreaming server v3 2022-05-24 00:08:25 +02:00
Carsten Schmiemann c83a176527 Fix logging of MetricModified 2022-05-23 23:49:48 +02:00
Carsten Schmiemann 2b6285307e Another attempt transmit metrics on streaming 2022-05-23 23:11:27 +02:00
Carsten Schmiemann d9d0e00435 Remove debug message from MetricModified 2022-05-23 22:43:25 +02:00
Carsten Schmiemann a782c48079 Set important metrics while streaming for server v3 2022-05-23 22:42:51 +02:00
Carsten Schmiemann 81fb803667 Debug MetricModified 2022-05-23 22:18:59 +02:00
Carsten Schmiemann e5c13e0bb6 Fix server v3 MetricModified 2022-05-23 22:12:03 +02:00
Carsten Schmiemann c3cd9ba23e Test MQTT metric modified tx timer 2022-05-23 22:06:24 +02:00
Carsten Schmiemann ad786045ae Revert debug server v3 2022-05-23 22:05:55 +02:00
Carsten Schmiemann eeb4e06e04 Debug server v3 2022-05-23 21:15:18 +02:00
Carsten Schmiemann 9e7939b514 Debug MQTT 2022-05-21 23:09:22 +02:00
Carsten Schmiemann 9ceead8a77 Fix typo in comment 2022-05-21 13:12:59 +02:00
Carsten Schmiemann c1f026933c Fix TPMS temperature readings 2022-05-21 12:58:49 +02:00
Carsten Schmiemann e1d8a850b1 Add car not locked notify 2022-05-18 00:43:20 +02:00
Carsten Schmiemann cd469ff48a Debug tpms temp raw values 2022-05-18 00:23:09 +02:00
Carsten Schmiemann cd537f1907 Use ignition relay state from BCM 2022-05-18 00:18:00 +02:00
Carsten Schmiemann 34fe261ec9 Another try to read TPMS temps properly 2022-05-11 17:46:41 +02:00
Carsten Schmiemann 3ea43525b2 Debug tpms temps 2022-05-11 13:41:16 +02:00
Carsten Schmiemann 59d452452e Remove light sensor reading, correct values but not chaging 2022-05-09 16:26:06 +02:00
Carsten Schmiemann e0d0968211 TPMS temperature wrong calc 2022-05-09 16:24:37 +02:00
Carsten Schmiemann c7758be02b TPMS fixed, remove debug log 2022-05-09 01:02:43 +02:00
Carsten Schmiemann afc7fb8b2b Fix TPMS pressures, test temperature 2022-05-09 00:54:03 +02:00
Carsten Schmiemann 6e6df5fa01 Remove working but actually unneeded PIDs 2022-05-09 00:40:02 +02:00
Carsten Schmiemann 105cf60e3c Fix VIN string 2022-05-08 23:52:03 +02:00
Carsten Schmiemann 011cc31d50 Write VIN into metric 2022-05-08 23:41:21 +02:00
Carsten Schmiemann c6858eaf16 Fix tpms pressure readings 2022-05-08 23:29:14 +02:00
Carsten Schmiemann 43df545b41 further debug 2022-05-08 23:24:00 +02:00
Carsten Schmiemann 1edbfcb331 Resync 2022-05-08 23:15:44 +02:00
Carsten Schmiemann ee2435006e Try VIN first nibble 2022-05-08 23:13:47 +02:00
Carsten Schmiemann e1c8c563c4 Remove VIN readout 2022-05-08 23:02:23 +02:00
Carsten Schmiemann 88130b05bc VIN full response 2022-05-08 22:57:56 +02:00
Carsten Schmiemann b30cd3498f Debug VIN string 2022-05-08 22:51:13 +02:00
Carsten Schmiemann 40952e9730 Debug VIN 2022-05-08 22:47:44 +02:00
Carsten Schmiemann d0b1911f3b Fix compile errors 2022-05-08 22:10:17 +02:00
Carsten Schmiemann ac6471ad99 FIx VIN readout crash 2022-05-08 21:52:52 +02:00
Carsten Schmiemann 7614b6809b Add VIN readout 2022-05-08 20:56:59 +02:00
Carsten Schmiemann dcf134ba2e Add rain detection 2022-05-08 20:43:17 +02:00
Carsten Schmiemann df8ffc1bc5 Add interior lights reading 2022-05-08 20:36:05 +02:00
Carsten Schmiemann bb58a0cb9f Add another source for ignition 2022-05-08 20:31:06 +02:00
Carsten Schmiemann 76e9d97385 Add lights 2022-05-08 20:25:54 +02:00
Carsten Schmiemann 32f6b2560d Change tpms pids, debugging 2022-05-08 20:05:02 +02:00
Carsten Schmiemann ea0fd89c56 Add more BCM readouts 2022-05-08 19:21:15 +02:00
Carsten Schmiemann e0107bee1c Remove Car Trip Counter from Web, because of unreliablility 2022-05-07 12:35:50 +02:00
Carsten Schmiemann 36f7e1aff2 Debug TPMS temperatures because of implausibility 2022-05-07 12:31:38 +02:00
Carsten Schmiemann c4ed87552c Stop polling after full charge 2022-05-07 12:22:59 +02:00
Carsten Schmiemann 150338320b Fix charge efficiency over 100% on balancing 2022-05-07 01:26:48 +02:00
Carsten Schmiemann c0896e209a Faster bms poll while driving 2022-05-07 01:21:24 +02:00
Carsten Schmiemann 424cc5c24f Remove charge current too low msg, because balancing 2022-05-07 01:18:41 +02:00
Carsten Schmiemann 29f880300c Re-enable aux and ptc power readings 2022-05-07 01:04:06 +02:00
Carsten Schmiemann 0bf1180a48 Persist variables 2022-05-07 01:02:15 +02:00
Carsten Schmiemann 0321a36088 Remove ambient air pressure 2022-05-07 00:51:29 +02:00
Carsten Schmiemann 910f480a9b Remove CLUSTER trip counter 2022-05-07 00:50:07 +02:00
Carsten Schmiemann 4d79bbdf7b Fix own ovms server 2022-05-03 01:00:04 +02:00
Carsten Schmiemann 781be6126d Add motor rpm 2022-05-03 00:44:12 +02:00
Carsten Schmiemann 84293ed725 Original project changes: GPS: add normalized signal quality level, add web UI live status info 2022-05-03 00:18:10 +02:00
Carsten Schmiemann efb2404917 Original project changes: ServerV2: fix dropped notifications from network switching 2022-05-03 00:08:32 +02:00
Carsten Schmiemann aac0445c26 Fix index 2022-05-03 00:02:43 +02:00
Carsten Schmiemann cf7dbe93dc Update feature list 2022-04-23 20:25:02 +00:00
Carsten Schmiemann 69a64bc1f1 Chg time calc fix 2022-04-23 17:51:46 +02:00
Carsten Schmiemann de9389dab3 First stable working build 2022-04-23 17:39:08 +02:00
Carsten Schmiemann 6ca15dca10 Fix readme 2022-04-23 17:37:51 +02:00
131 changed files with 7896 additions and 1322 deletions

@ -1 +1 @@
Subproject commit 1ff5e24b1b722a225e904b0d1b505d8a7c6b10be
Subproject commit 25d79ca572cf6b92ead9fa45a9cb9579cb0bd092

View file

@ -213,7 +213,7 @@ void can_tx(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const
uint32_t uv = strtoul(argv[0], &ep, 16);
if (*ep != '\0' || uv > idmax)
{
writer->printf("Error: Invalid CAN ID \"%s\" (0x%lx max)\n", argv[0], idmax);
writer->printf("Error: Invalid CAN ID \"%s\" (0x%x max)\n", argv[0], idmax);
return;
}
frame.MsgID = uv;
@ -298,7 +298,7 @@ void can_testtx(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, c
uint32_t uv = strtoul(argv[0], &ep, 16);
if (*ep != '\0' || uv > idmax)
{
writer->printf("Error: Invalid CAN ID \"%s\" (0x%lx max)\n", argv[0], idmax);
writer->printf("Error: Invalid CAN ID \"%s\" (0x%x max)\n", argv[0], idmax);
return;
}
frame.MsgID = uv;
@ -570,7 +570,8 @@ static const char* const CAN_log_type_names[] = {
"Status",
"Comment",
"Info",
"Event"
"Event",
"Metric"
};
const char* GetCanLogTypeName(CAN_log_type_t type)

View file

@ -251,6 +251,7 @@ typedef enum
CAN_LogInfo_Comment, // general comment
CAN_LogInfo_Config, // logger setup info (type, file, filters, vehicle)
CAN_LogInfo_Event, // system event (i.e. vehicle started)
CAN_LogInfo_Metric, // system or plugin metric (i.e. v.p.altitude)
} CAN_log_type_t;
// Log message:

View file

@ -25,7 +25,7 @@
*/
#include "ovms_log.h"
static const char *TAG = "canformat-crtd";
static const char *TAG = "canformat-cs11";
#include <errno.h>
#include "pcp.h"

View file

@ -123,10 +123,11 @@ std::string canformat_crtd::get(CAN_log_message_t* message)
case CAN_LogInfo_Comment:
case CAN_LogInfo_Config:
case CAN_LogInfo_Event:
case CAN_LogInfo_Metric:
snprintf(buf,sizeof(buf),"%ld.%06ld %c%s %s %s",
message->timestamp.tv_sec, message->timestamp.tv_usec,
busnumber,
(message->type == CAN_LogInfo_Event) ? "CEV" : "CXX",
(message->type == CAN_LogInfo_Event) ? "CEV" : (message->type == CAN_LogInfo_Metric) ? "CMT" : "CXX",
GetCanLogTypeName(message->type),
message->text);
break;
@ -151,7 +152,7 @@ std::string canformat_crtd::getheader(struct timeval *time)
time = &t;
}
snprintf(buf,sizeof(buf),"%ld.%06ld CXX OVMS CRTD\n%ld.%06ld CVR 3.0\n",
snprintf(buf,sizeof(buf),"%ld.%06ld CXX OVMS CRTD\n%ld.%06ld CVR 3.1\n",
time->tv_sec, time->tv_usec,
time->tv_sec, time->tv_usec);

View file

@ -25,44 +25,44 @@
*/
#include "ovms_log.h"
static const char *TAG = "canformat-lawricel";
static const char *TAG = "canformat-lawicel";
#include <errno.h>
#include "pcp.h"
#include "canformat_lawricel.h"
#include "canformat_lawicel.h"
////////////////////////////////////////////////////////////////////////
// Initialisation and Registration
////////////////////////////////////////////////////////////////////////
class OvmsCanFormatLawricelInit
class OvmsCanFormatLawicelInit
{
public: OvmsCanFormatLawricelInit();
} MyOvmsCanFormatLawricelTInit __attribute__ ((init_priority (4505)));
public: OvmsCanFormatLawicelInit();
} MyOvmsCanFormatLawicelTInit __attribute__ ((init_priority (4505)));
OvmsCanFormatLawricelInit::OvmsCanFormatLawricelInit()
OvmsCanFormatLawicelInit::OvmsCanFormatLawicelInit()
{
ESP_LOGI(TAG, "Registering CAN Format: LAWRICEL (4505)");
ESP_LOGI(TAG, "Registering CAN Format: LAWICEL (4505)");
MyCanFormatFactory.RegisterCanFormat<canformat_lawricel>("lawricel");
MyCanFormatFactory.RegisterCanFormat<canformat_lawicel>("lawicel");
}
////////////////////////////////////////////////////////////////////////
// Base GVRET implementation (utility)
////////////////////////////////////////////////////////////////////////
canformat_lawricel::canformat_lawricel(const char* type)
canformat_lawicel::canformat_lawicel(const char* type)
: canformat(type)
{
}
canformat_lawricel::~canformat_lawricel()
canformat_lawicel::~canformat_lawicel()
{
}
std::string canformat_lawricel::get(CAN_log_message_t* message)
std::string canformat_lawicel::get(CAN_log_message_t* message)
{
char buf[CANFORMAT_LAWRICEL_MAXLEN];
char buf[CANFORMAT_LAWICEL_MAXLEN];
if ((message->type != CAN_LogFrame_RX)&&
(message->type != CAN_LogFrame_TX))
@ -87,12 +87,12 @@ std::string canformat_lawricel::get(CAN_log_message_t* message)
return std::string(buf);
}
std::string canformat_lawricel::getheader(struct timeval *time)
std::string canformat_lawicel::getheader(struct timeval *time)
{
return std::string("");
}
size_t canformat_lawricel::put(CAN_log_message_t* message, uint8_t *buffer, size_t len, bool* hasmore, canlogconnection* clc)
size_t canformat_lawicel::put(CAN_log_message_t* message, uint8_t *buffer, size_t len, bool* hasmore, canlogconnection* clc)
{
if (m_buf.FreeSpace()==0) SetServeDiscarding(true); // Buffer full, so discard from now on
if (IsServeDiscarding()) return len; // Quick return if discarding

View file

@ -24,18 +24,18 @@
; THE SOFTWARE.
*/
#ifndef __CANFORMAT_LAWRICEL_H__
#define __CANFORMAT_LAWRICEL_H__
#ifndef __CANFORMAT_LAWICEL_H__
#define __CANFORMAT_LAWICEL_H__
#include "canformat.h"
#define CANFORMAT_LAWRICEL_MAXLEN 48
#define CANFORMAT_LAWICEL_MAXLEN 48
class canformat_lawricel : public canformat
class canformat_lawicel : public canformat
{
public:
canformat_lawricel(const char* type);
virtual ~canformat_lawricel();
canformat_lawicel(const char* type);
virtual ~canformat_lawicel();
public:
virtual std::string get(CAN_log_message_t* message);
@ -43,4 +43,4 @@ class canformat_lawricel : public canformat
virtual size_t put(CAN_log_message_t* message, uint8_t *buffer, size_t len, bool* hasmore, canlogconnection* clc=NULL);
};
#endif // __CANFORMAT_LAWRICEL_H__
#endif // __CANFORMAT_LAWICEL_H__

View file

@ -0,0 +1,88 @@
/*
; Project: Open Vehicle Monitor System
; Module: CAN dump framework
; Date: 18th January 2018
;
; (C) 2022 Mark Webb-Johnson
;
; 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"
static const char *TAG = "canformat-panda";
#include <errno.h>
#include "pcp.h"
#include "canlog.h"
#include "canformat_panda.h"
#include "ovms_utils.h"
class OvmsCanFormatPandaInit
{
public: OvmsCanFormatPandaInit();
} MyOvmsCanFormatPandaInit __attribute__ ((init_priority (4505)));
OvmsCanFormatPandaInit::OvmsCanFormatPandaInit()
{
ESP_LOGI(TAG, "Registering CAN Format: PANDA (4505)");
MyCanFormatFactory.RegisterCanFormat<canformat_panda>("panda");
}
canformat_panda::canformat_panda(const char *type)
: canformat(type)
{
}
canformat_panda::~canformat_panda()
{
}
std::string canformat_panda::get(CAN_log_message_t* message)
{
struct
{
uint32_t w1;
uint32_t w2;
uint64_t data;
} packet;
switch (message->type)
{
case CAN_LogFrame_RX:
case CAN_LogFrame_TX:
packet.w1 = (uint32_t)message->frame.MsgID <<21;
packet.w2 = (message->frame.FIR.B.DLC & 0x0f) | (message->origin->m_busnumber << 4);
memcpy(&packet.data, message->frame.data.u8, 8);
return std::string((char*)&packet,sizeof(packet));
default:
return std::string("");
}
}
std::string canformat_panda::getheader(struct timeval *time)
{
return std::string("");
}
size_t canformat_panda::put(CAN_log_message_t* message, uint8_t *buffer, size_t len, bool* hasmore, canlogconnection* clc)
{
return len; // Just ignore incoming data
}

View file

@ -0,0 +1,44 @@
/*
; Project: Open Vehicle Monitor System
; Module: CAN dump CRTD format
; Date: 18th January 2018
;
; (C) 2022 Mark Webb-Johnson
;
; 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.
*/
#ifndef __CANFORMAT_PANDA_H__
#define __CANFORMAT_PANDA_H__
#include "canformat.h"
class canformat_panda : public canformat
{
public:
canformat_panda(const char* type);
virtual ~canformat_panda();
public:
virtual std::string get(CAN_log_message_t* message);
virtual std::string getheader(struct timeval *time);
virtual size_t put(CAN_log_message_t* message, uint8_t *buffer, size_t len, bool* hasmore, canlogconnection* clc=NULL);
};
#endif // __CANFORMAT_PANDA_H__

View file

@ -42,6 +42,8 @@ static const char *TAG = "canlog";
#include "ovms_peripherals.h"
#include "metrics_standard.h"
static const char *CAN_PARAM = "can";
////////////////////////////////////////////////////////////////////////
// Command Processing
////////////////////////////////////////////////////////////////////////
@ -352,8 +354,12 @@ canlog::canlog(const char* type, std::string format, canformat::canformat_serve_
using std::placeholders::_1;
using std::placeholders::_2;
MyEvents.RegisterEvent(IDTAG, "*", std::bind(&canlog::EventListener, this, _1, _2));
MyEvents.RegisterEvent(IDTAG,"config.mounted", std::bind(&canlog::UpdatedConfig, this, _1, _2));
MyEvents.RegisterEvent(IDTAG,"config.changed", std::bind(&canlog::UpdatedConfig, this, _1, _2));
MyMetrics.RegisterListener(IDTAG, "*", std::bind(&canlog::MetricListener, this, _1));
int queuesize = MyConfig.GetParamValueInt("can", "log.queuesize",100);
int queuesize = MyConfig.GetParamValueInt(CAN_PARAM, "log.queuesize",100);
LoadConfig();
m_queue = xQueueCreate(queuesize, sizeof(CAN_log_message_t));
xTaskCreatePinnedToCore(RxTask, "OVMS CanLog", 4096, (void*)this, 10, &m_task, CORE(1));
}
@ -361,6 +367,7 @@ canlog::canlog(const char* type, std::string format, canformat::canformat_serve_
canlog::~canlog()
{
MyEvents.DeregisterEvent(IDTAG);
MyMetrics.DeregisterListener(IDTAG);
if (m_task)
{
@ -383,6 +390,7 @@ canlog::~canlog()
case CAN_LogInfo_Comment:
case CAN_LogInfo_Config:
case CAN_LogInfo_Event:
case CAN_LogInfo_Metric:
free(msg.text);
break;
default:
@ -418,6 +426,7 @@ void canlog::RxTask(void *context)
case CAN_LogInfo_Comment:
case CAN_LogInfo_Config:
case CAN_LogInfo_Event:
case CAN_LogInfo_Metric:
me->OutputMsg(msg);
free(msg.text);
break;
@ -429,13 +438,179 @@ void canlog::RxTask(void *context)
}
}
/**
* Parse a comma-separated list of filters, and assign them to a member of the class.
* We have 3 kind of comparisons, and an unlimited list of filters.
*
* A filter can be:
* - A "startsWith" comparison - when ending with '*',
* - An "endsWith" comparison - when starting with '*',
* - Invalid (and skipped) if empty, or with a '*' in any other position than beginning or end,
* - A "string equal" comparison for all other cases
*/
static void LoadFilters(conn_filters_arr_t &member, const std::string &value)
{
// Empty all previously defined filters, for all operators
for (int i=0; i<COUNT_OF_OPERATORS; i++)
{
member[i].clear();
}
if (!value.empty())
{
std::stringstream stream (value);
std::string item;
unsigned char comparison_operator;
// Comma-separated list
while (getline (stream, item, ','))
{
trim(item); // Removing leading and trailing spaces
if (item.empty())
{
ESP_LOGW(TAG, "LoadFilters: skipping empty value in the filter list");
continue;
}
// Check if there is a wildcard ('*') in any other place than first or last position
size_t wildcard_position = item.find('*', 1);
if ((wildcard_position != std::string::npos) && (wildcard_position != item.size()-1))
{
ESP_LOGW(TAG, "LoadFilters: skipping incorrect value (%s) in the filter list (wildcard in wrong position)", item.c_str());
continue;
}
// Depending on the presence and position of the wildcard, push the filter
// in the proper vector (without the wildcard)
if (item.front() == '*')
{
comparison_operator = OPERATOR_ENDSWITH;
item.erase(0, 1);
}
else if (item.back() == '*')
{
comparison_operator = OPERATOR_STARTSWITH;
item.pop_back();
}
else
{
comparison_operator = OPERATOR_EQUALS;
}
member[comparison_operator].push_back(item);
}
}
// for (int i=0; i<COUNT_OF_OPERATORS; i++)
// {
// ESP_LOGI(TAG, "LoadFilters: filters for operator %d:", i);
// for (std::vector<std::string>::iterator it=member[i].begin(); it!=member[i].end(); ++it)
// {
// ESP_LOGI(TAG, "LoadFilters: filter value '%s'", it->c_str());
// }
// if (member[i].begin() == member[i].end())
// {
// ESP_LOGI(TAG, "LoadFilters: (empty filter list)");
// }
// }
}
/**
* Load, or reload, the configuration of events and metrics filters.
*
* The configuration item is a string containing a comma-separated list of filters.
*/
void canlog::LoadConfig()
{
std::size_t str_hash;
std::string list_of_events_filters = MyConfig.GetParamValue(CAN_PARAM, "log.events_filters", "x*,vehicle*");
str_hash = std::hash<std::string>{}(list_of_events_filters);
if (str_hash != m_events_filters_hash)
{
m_events_filters_hash = str_hash;
LoadFilters(m_events_filters, list_of_events_filters);
MyCan.LogInfo(NULL, CAN_LogInfo_Config, ("Events filters: " + list_of_events_filters).c_str());
}
std::string list_of_metrics_filters = MyConfig.GetParamValue(CAN_PARAM, "log.metrics_filters");
str_hash = std::hash<std::string>{}(list_of_metrics_filters);
if (str_hash != m_metrics_filters_hash)
{
m_metrics_filters_hash = str_hash;
LoadFilters(m_metrics_filters, list_of_metrics_filters);
MyCan.LogInfo(NULL, CAN_LogInfo_Config, ("Metrics filters: " + list_of_metrics_filters).c_str());
}
}
/**
* Load, or reload, the configuration if a config event occurred.
*/
void canlog::UpdatedConfig(std::string event, void* data)
{
if (event == "config.changed")
{
// Only reload if our parameter has changed
OvmsConfigParam*p = (OvmsConfigParam*)data;
if (p->GetName() != CAN_PARAM)
{
return;
}
}
LoadConfig();
}
/**
* Check if a value matches in a list of filters.
*
* Match can be a:
* - startsWith match,
* - endsWith match,
* - equality match.
*/
static bool CheckFilter(conn_filters_arr_t &member, const std::string &value)
{
for (int i=0; i<COUNT_OF_OPERATORS; i++)
{
for (std::vector<std::string>::iterator it=member[i].begin(); it!=member[i].end(); ++it)
{
if ((i == OPERATOR_STARTSWITH) && (startsWith(value, *it)))
{
return true;
}
else if ((i == OPERATOR_ENDSWITH) && (endsWith(value, *it)))
{
return true;
}
else if ((i == OPERATOR_EQUALS) && (value == *it))
{
return true;
}
}
}
return false;
}
void canlog::EventListener(std::string event, void* data)
{
// Log vehicle custom (x…) & framework events:
if (startsWith(event, 'x') || startsWith(event, "vehicle"))
if (CheckFilter(m_events_filters, event))
LogInfo(NULL, CAN_LogInfo_Event, event.c_str());
}
void canlog::MetricListener(OvmsMetric* metric)
{
std::string name = metric->m_name;
// Log metrics (in JSON for later parsing):
if (CheckFilter(m_metrics_filters, name))
{
std::string metric_text = "{ ";
metric_text += "\"name\": \"" + json_encode(name) + "\", ";
metric_text += "\"value\": " + metric->AsJSON() + ", ";
metric_text += "\"unit\": \"" + json_encode(std::string(OvmsMetricUnitLabel(metric->GetUnits()))) + "\" }";
LogInfo(NULL, CAN_LogInfo_Metric, metric_text.c_str());
}
}
const char* canlog::GetType()
{
return m_type;
@ -596,7 +771,11 @@ void canlog::LogInfo(canbus* bus, CAN_log_type_t type, const char* text)
msg.origin = bus;
msg.text = strdup(text);
m_msgcount++;
if (xQueueSend(m_queue, &msg, 0) != pdTRUE) m_dropcount++;
if (xQueueSend(m_queue, &msg, 0) != pdTRUE)
{
m_dropcount++;
free(msg.text);
}
}
else
{

View file

@ -95,6 +95,11 @@ class canlogconnection: public InternalRamAllocated
uint32_t m_filtercount;
};
#define OPERATOR_STARTSWITH 0
#define OPERATOR_ENDSWITH 1
#define OPERATOR_EQUALS 2
#define COUNT_OF_OPERATORS 3
typedef std::array<std::vector<std::string>, COUNT_OF_OPERATORS> conn_filters_arr_t;
class canlog : public InternalRamAllocated
{
@ -105,6 +110,7 @@ class canlog : public InternalRamAllocated
public:
static void RxTask(void* context);
void EventListener(std::string event, void* data);
void MetricListener(OvmsMetric* metric);
public:
const char* GetType();
@ -152,6 +158,16 @@ class canlog : public InternalRamAllocated
uint32_t m_msgcount;
uint32_t m_dropcount;
uint32_t m_filtercount;
protected:
virtual void UpdatedConfig(std::string event, void* data);
virtual void LoadConfig();
protected:
conn_filters_arr_t m_events_filters;
size_t m_events_filters_hash = 0;
conn_filters_arr_t m_metrics_filters;
size_t m_metrics_filters_hash = 0;
};
#endif // __CANLOG_H__

View file

@ -109,6 +109,7 @@ void canlog_monitor_conn::OutputMsg(CAN_log_message_t& msg, std::string &result)
case CAN_LogInfo_Comment:
case CAN_LogInfo_Config:
case CAN_LogInfo_Event:
case CAN_LogInfo_Metric:
ESP_LOGD(TAG,"%s",result.c_str());
break;
default:

View file

@ -34,7 +34,6 @@ static const char *TAG = "canlog-tcpclient";
#include "canformat.h"
#include "canlog_tcpclient.h"
#include "ovms_config.h"
#include "ovms_peripherals.h"
canlog_tcpclient* MyCanLogTcpClient = NULL;
@ -83,9 +82,9 @@ OvmsCanLogTcpClientInit::OvmsCanLogTcpClientInit()
{
// We have a place to put our command tree..
OvmsCommand* start = cmd_can_log_start->RegisterCommand("tcpclient", "CAN logging as TCP client");
OvmsCommand* discard = start->RegisterCommand("discard","CAN logging as TCP client (discard mode)");
OvmsCommand* simulate = start->RegisterCommand("simulate","CAN logging as TCP client (simulate mode)");
OvmsCommand* transmit = start->RegisterCommand("transmit","CAN logging as TCP client (transmit mode)");
OvmsCommand* discard = start->RegisterCommand("discard", "CAN logging as TCP client (discard mode)");
OvmsCommand* simulate = start->RegisterCommand("simulate", "CAN logging as TCP client (simulate mode)");
OvmsCommand* transmit = start->RegisterCommand("transmit", "CAN logging as TCP client (transmit mode)");
MyCanFormatFactory.RegisterCommandSet(discard, "Start CAN logging as TCP client (discard mode)",
can_log_tcpclient_start,
"<host:port> [filter1] ... [filterN]\n"
@ -164,23 +163,25 @@ bool canlog_tcpclient::Open()
opts.error_string = &err;
if (mg_connect_opt(mgr, m_path.c_str(), tcMongooseHandler, opts) != NULL)
{
return true;
// Wait 10s max for connection establishment...
m_connecting.Take(pdMS_TO_TICKS(10*1000));
return m_isopen;
}
else
{
ESP_LOGE(TAG,"Could not connect to %s",m_path.c_str());
ESP_LOGE(TAG, "Could not connect to %s", m_path.c_str());
return false;
}
}
else
{
ESP_LOGI(TAG,"Delay TCP client (as network manager not up)");
ESP_LOGI(TAG, "Delay TCP client (as network manager not up)");
return true;
}
}
else
{
ESP_LOGE(TAG,"Network manager is not available");
ESP_LOGE(TAG, "Network manager is not available");
return false;
}
}
@ -221,9 +222,9 @@ void canlog_tcpclient::MongooseHandler(struct mg_connection *nc, int ev, void *p
int *success = (int*)p;
ESP_LOGV(TAG, "MongooseHandler(MG_EV_CONNECT=%d)",*success);
if (*success == 0)
{ // Successful connection
{ // Connection successful
OvmsRecMutexLock lock(&m_cmmutex);
ESP_LOGI(TAG, "Connection successful to %s",m_path.c_str());
ESP_LOGI(TAG, "Connection successful to %s", m_path.c_str());
canlogconnection* clc = new canlogconnection(this, m_format, m_mode);
clc->m_nc = nc;
clc->m_peer = m_path;
@ -236,11 +237,11 @@ void canlog_tcpclient::MongooseHandler(struct mg_connection *nc, int ev, void *p
}
}
else
{
// Connection failed
ESP_LOGE(TAG, "Connection failed to %s",m_path.c_str());
{ // Connection failed
ESP_LOGE(TAG, "Connection failed to %s", m_path.c_str());
m_isopen = false;
}
m_connecting.Give();
}
break;
case MG_EV_CLOSE:
@ -248,7 +249,7 @@ void canlog_tcpclient::MongooseHandler(struct mg_connection *nc, int ev, void *p
if (m_isopen)
{
OvmsRecMutexLock lock(&m_cmmutex);
ESP_LOGE(TAG,"Disconnected from %s",m_path.c_str());
ESP_LOGE(TAG, "Disconnected from %s", m_path.c_str());
m_isopen = false;
auto k = m_connmap.find(nc);
if (k != m_connmap.end())

View file

@ -31,6 +31,7 @@
#include "canlog.h"
#include "ovms_netmanager.h"
#include "ovms_mutex.h"
#include "ovms_semaphore.h"
class canlog_tcpclient : public canlog
{
@ -48,6 +49,7 @@ class canlog_tcpclient : public canlog
public:
std::string m_path;
OvmsSemaphore m_connecting;
};
#endif // __CANLOG_TCP_CLIENT_H__

View file

@ -32,7 +32,6 @@
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
#include "canlog.h"
#include "canlog_tcpserver.h"
#include "ovms_netmanager.h"
#include "ovms_mutex.h"

View file

@ -0,0 +1,330 @@
/*
; Project: Open Vehicle Monitor System
; Module: CAN logging framework
; Date: 18th January 2018
;
; (C) 2018 Michael Balzer
;
; 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 <sdkconfig.h>
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
#include "ovms_log.h"
static const char *TAG = "canlog-udpserver";
#include "can.h"
#include "canformat.h"
#include "canlog_udpserver.h"
#include "ovms_config.h"
#include "ovms_events.h"
#include "ovms_peripherals.h"
#define UDP_TIMEOUT 30
canlog_udpserver* MyCanLogUdpServer = NULL;
udpcanlogconnection::udpcanlogconnection(canlog* logger, std::string format, canformat::canformat_serve_mode_t mode)
: canlogconnection(logger, format, mode)
{
m_timeout = monotonictime + UDP_TIMEOUT;
}
udpcanlogconnection::~udpcanlogconnection()
{
}
void udpcanlogconnection::OutputMsg(CAN_log_message_t& msg, std::string &result)
{
m_msgcount++;
if ((m_filters != NULL) && (! m_filters->IsFiltered(&msg.frame)))
{
m_filtercount++;
return;
}
if (result.length()>0)
{
sendto(m_sock, (const char*)result.c_str(), result.length(), 0, &m_sa, sizeof(m_sa));
}
}
void udpcanlogconnection::Tickle()
{
m_timeout = monotonictime + UDP_TIMEOUT;
}
void can_log_udpserver_start(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
std::string format(cmd->GetName());
std::string mode(cmd->GetParent()->GetName());
canlog_udpserver* logger = new canlog_udpserver(argv[0],format,GetFormatModeType(mode));
if (logger->Open())
{
if (argc>1)
{ MyCan.AddLogger(logger, argc-1, &argv[1]); }
else
{ MyCan.AddLogger(logger); }
writer->printf("CAN logging as UDP server: %s\n", logger->GetInfo().c_str());
}
else
{
writer->printf("Error: Could not start CAN logging as UDP server: %s\n",logger->GetInfo().c_str());
delete logger;
}
}
class OvmsCanLogUdpServerInit
{
public:
OvmsCanLogUdpServerInit();
void NetManInit(std::string event, void* data);
void NetManStop(std::string event, void* data);
} MyOvmsCanLogUdpServerInit __attribute__ ((init_priority (4560)));
OvmsCanLogUdpServerInit::OvmsCanLogUdpServerInit()
{
ESP_LOGI(TAG, "Initialising CAN logging as UDP server (4560)");
OvmsCommand* cmd_can = MyCommandApp.FindCommand("can");
if (cmd_can)
{
OvmsCommand* cmd_can_log = cmd_can->FindCommand("log");
if (cmd_can_log)
{
OvmsCommand* cmd_can_log_start = cmd_can_log->FindCommand("start");
if (cmd_can_log_start)
{
// We have a place to put our command tree..
OvmsCommand* start = cmd_can_log_start->RegisterCommand("udpserver", "CAN logging as UDP server");
OvmsCommand* discard = start->RegisterCommand("discard","CAN logging as UDP server (discard mode)");
OvmsCommand* simulate = start->RegisterCommand("simulate","CAN logging as UDP server (simulate mode)");
OvmsCommand* transmit = start->RegisterCommand("transmit","CAN logging as UDP server (transmit mode)");
MyCanFormatFactory.RegisterCommandSet(discard, "Start CAN logging as UDP server (discard mode)",
can_log_udpserver_start,
"<port> [filter1] ... [filterN]\n"
"Filter: <bus> | <id>[-<id>] | <bus>:<id>[-<id>]\n"
"Example: 2:2a0-37f",
1, 9);
MyCanFormatFactory.RegisterCommandSet(simulate, "Start CAN logging as UDP server (simulate mode)",
can_log_udpserver_start,
"<port> [filter1] ... [filterN]\n"
"Filter: <bus> | <id>[-<id>] | <bus>:<id>[-<id>]\n"
"Example: 2:2a0-37f",
1, 9);
MyCanFormatFactory.RegisterCommandSet(transmit, "Start CAN logging as UDP server (transmit mode)",
can_log_udpserver_start,
"<port> [filter1] ... [filterN]\n"
"Filter: <bus> | <id>[-<id>] | <bus>:<id>[-<id>]\n"
"Example: 2:2a0-37f",
1, 9);
}
}
}
using std::placeholders::_1;
using std::placeholders::_2;
MyEvents.RegisterEvent(TAG, "network.mgr.init", std::bind(&OvmsCanLogUdpServerInit::NetManInit, this, _1, _2));
MyEvents.RegisterEvent(TAG, "network.mgr.stop", std::bind(&OvmsCanLogUdpServerInit::NetManStop, this, _1, _2));
}
void OvmsCanLogUdpServerInit::NetManInit(std::string event, void* data)
{
if (MyCanLogUdpServer) MyCanLogUdpServer->Open();
}
void OvmsCanLogUdpServerInit::NetManStop(std::string event, void* data)
{
if (MyCanLogUdpServer) MyCanLogUdpServer->Close();
}
static void tsMongooseHandler(struct mg_connection *nc, int ev, void *p)
{
if (MyCanLogUdpServer)
MyCanLogUdpServer->MongooseHandler(nc, ev, p);
else if (ev == MG_EV_ACCEPT)
{
ESP_LOGI(TAG, "Log service connection rejected (logger not running)");
nc->flags |= MG_F_CLOSE_IMMEDIATELY;
}
}
canlog_udpserver::canlog_udpserver(std::string path, std::string format, canformat::canformat_serve_mode_t mode)
: canlog("udpserver", format, mode)
{
MyCanLogUdpServer = this;
m_path = path;
if (m_path.find(':') == std::string::npos)
{
m_path.insert(0, "udp://");
}
m_isopen = false;
m_mgconn = NULL;
#undef bind // Kludgy, but works
using std::placeholders::_1;
using std::placeholders::_2;
MyEvents.RegisterEvent(TAG,"ticker.10", std::bind(&canlog_udpserver::Ticker, this, _1, _2));
}
canlog_udpserver::~canlog_udpserver()
{
Close();
MyCanLogUdpServer = NULL;
MyEvents.DeregisterEvent(TAG);
}
bool canlog_udpserver::Open()
{
if (m_isopen) return true;
ESP_LOGI(TAG, "Launching UDP server at %s",m_path.c_str());
struct mg_mgr* mgr = MyNetManager.GetMongooseMgr();
if (mgr != NULL)
{
if (MyNetManager.m_network_any)
{
m_mgconn = mg_bind(mgr, m_path.c_str(), tsMongooseHandler);
if (m_mgconn != NULL)
{
ESP_LOGI(TAG,"Listening with nc %p",m_mgconn);
m_isopen = true;
return true;
}
else
{
ESP_LOGE(TAG,"Could not listen on %s",m_path.c_str());
return false;
}
}
else
{
ESP_LOGI(TAG,"Delay UDP server (as network manager not up)");
return true;
}
}
else
{
ESP_LOGE(TAG,"Network manager is not available");
return false;
}
}
void canlog_udpserver::Close()
{
if (m_isopen)
{
if (m_connmap.size() > 0)
{
OvmsRecMutexLock lock(&m_cmmutex);
for (conn_map_t::iterator it=m_connmap.begin(); it!=m_connmap.end(); ++it)
{
it->first->flags |= MG_F_CLOSE_IMMEDIATELY;
delete it->second;
}
m_connmap.clear();
}
ESP_LOGI(TAG, "Closed UDP server log: %s", GetStats().c_str());
m_mgconn->flags |= MG_F_CLOSE_IMMEDIATELY;
m_mgconn = NULL;
m_isopen = false;
}
}
std::string canlog_udpserver::GetInfo()
{
std::string result = canlog::GetInfo();
result.append(" Path:");
result.append(m_path);
return result;
}
void canlog_udpserver::Ticker(std::string event, void* data)
{
OvmsRecMutexLock lock(&m_cmmutex);
for (conn_map_t::iterator it=m_connmap.begin(); it!=m_connmap.end(); ++it)
{
udpcanlogconnection* clc = (udpcanlogconnection*)it->second;
if (clc->m_timeout < monotonictime)
{
// This client has timed out
ESP_LOGD(TAG,"Timed out connection from %s",clc->m_peer.c_str());
m_connmap.erase(it);
delete clc;
}
}
}
void canlog_udpserver::MongooseHandler(struct mg_connection *nc, int ev, void *p)
{
char addr[32];
switch (ev)
{
case MG_EV_RECV:
{
OvmsRecMutexLock lock(&m_cmmutex);
mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP|MG_SOCK_STRINGIFY_PORT);
size_t used = nc->recv_mbuf.len;
// Try to find an existing matching connection
udpcanlogconnection* clc = NULL;
for (conn_map_t::iterator it=m_connmap.begin(); it!=m_connmap.end(); ++it)
{
clc = (udpcanlogconnection*)it->second;
if (memcmp(&clc->m_sa, &nc->sa, sizeof(&nc->sa.sin)) == 0)
{
// We have found an existing one, so just handle and tickle it
ESP_LOGD(TAG,"Tickle connection from %s",addr);
used = clc->m_formatter->Serve((uint8_t*)nc->recv_mbuf.buf, used, clc);
if (used>0) mbuf_remove(&nc->recv_mbuf, used);
clc->Tickle();
return;
}
}
// No matching connection, so make a new one
ESP_LOGD(TAG,"New connection from %s",addr);
clc = new udpcanlogconnection(this, m_format, m_mode);
clc->m_nc = m_mgconn;
clc->m_sock = m_mgconn->sock;
memcpy(&clc->m_sa,&nc->sa.sin,sizeof(nc->sa.sin));
clc->m_peer = std::string(addr);
m_connmap[&clc->m_fakenc] = clc;
std::string result = clc->m_formatter->getheader();
if (result.length()>0)
{
mg_send(nc, (const char*)result.c_str(), result.length());
}
used = clc->m_formatter->Serve((uint8_t*)nc->recv_mbuf.buf, used, clc);
if (used > 0) mbuf_remove(&nc->recv_mbuf, used);
break;
}
default:
break;
}
}
#endif // #ifdef CONFIG_OVMS_SC_GPL_MONGOOSE

View file

@ -0,0 +1,80 @@
/*
; Project: Open Vehicle Monitor System
; Module: CAN logging framework
; Date: 18th January 2018
;
; (C) 2018 Michael Balzer
; (C) 2019 Mark Webb-Johnson
;
; 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.
*/
#ifndef __CANLOG_UDP_SERVER_H__
#define __CANLOG_UDP_SERVER_H__
#include <sdkconfig.h>
#ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
#include "canlog.h"
#include "ovms_netmanager.h"
#include "ovms_mutex.h"
class udpcanlogconnection: public canlogconnection
{
public:
udpcanlogconnection(canlog* logger, std::string format, canformat::canformat_serve_mode_t mode);
virtual ~udpcanlogconnection();
public:
virtual void OutputMsg(CAN_log_message_t& msg, std::string &result);
public:
void Tickle();
public:
sock_t m_sock; // Our main listening UDP socket
struct sockaddr m_sa; // Our remote client address
mg_connection m_fakenc; // A fake nc, just as an index to us
uint32_t m_timeout; // Our timeout
};
class canlog_udpserver : public canlog
{
public:
canlog_udpserver(std::string path, std::string format, canformat::canformat_serve_mode_t mode);
virtual ~canlog_udpserver();
public:
virtual bool Open();
virtual void Close();
virtual std::string GetInfo();
public:
void MongooseHandler(struct mg_connection *nc, int ev, void *p);
void Ticker(std::string event, void* data);
public:
struct mg_connection *m_mgconn;
public:
std::string m_path;
};
#endif // #ifdef CONFIG_OVMS_SC_GPL_MONGOOSE
#endif // __CANLOG_UDP_SERVER_H__

View file

@ -89,7 +89,7 @@ OvmsCanLogVFSInit::OvmsCanLogVFSInit()
canlog_vfs_conn::canlog_vfs_conn(canlog* logger, std::string format, canformat::canformat_serve_mode_t mode)
: canlogconnection(logger, format, mode)
: canlogconnection(logger, format, mode), m_file_size(0)
{
m_file = NULL;
}
@ -114,7 +114,10 @@ void canlog_vfs_conn::OutputMsg(CAN_log_message_t& msg, std::string &result)
}
if (result.length()>0)
{
fwrite(result.c_str(),result.length(),1,m_file);
m_file_size += result.length();
}
}
@ -181,7 +184,10 @@ bool canlog_vfs::Open()
std::string header = m_formatter->getheader();
if (header.length()>0)
{
fwrite(header.c_str(),header.length(),1,clc->m_file);
clc->m_file_size += header.length();
}
m_connmap[NULL] = clc;
m_isopen = true;
@ -207,6 +213,47 @@ void canlog_vfs::Close()
}
}
size_t canlog_vfs::GetFileSize()
{
size_t result = 0;
if (m_isopen)
{
for (conn_map_t::iterator it=m_connmap.begin(); it!=m_connmap.end(); ++it)
{
canlog_vfs_conn * clc = static_cast<canlog_vfs_conn *>(it->second);
result += clc->m_file_size;
}
}
return result;
}
std::string canlog_vfs_conn::GetStats()
{
char bufsize[15];
format_file_size(bufsize, sizeof(bufsize), m_file_size);
std::string result = "Size:";
result.append(bufsize);
result.append(" ");
result.append(canlogconnection::GetStats());
return result;
}
std::string canlog_vfs::GetStats()
{
char bufsize[15];
size_t size = GetFileSize();
format_file_size(bufsize, sizeof(bufsize), size);
std::string result = "Size:";
result.append(bufsize);
result.append(" ");
result.append(canlog::GetStats());
return result;
}
std::string canlog_vfs::GetInfo()
{
std::string result = canlog::GetInfo();

View file

@ -39,9 +39,11 @@ class canlog_vfs_conn: public canlogconnection
public:
virtual void OutputMsg(CAN_log_message_t& msg, std::string &result);
virtual std::string GetStats();
public:
FILE* m_file;
size_t m_file_size;
};
@ -55,9 +57,11 @@ class canlog_vfs : public canlog
virtual bool Open();
virtual void Close();
virtual std::string GetInfo();
virtual size_t GetFileSize();
public:
virtual void MountListener(std::string event, void* data);
virtual std::string GetStats();
public:
std::string m_path;

View file

@ -158,6 +158,7 @@ OvmsSSH::OvmsSSH()
using std::placeholders::_2;
MyEvents.RegisterEvent(tag,"network.mgr.init", std::bind(&OvmsSSH::NetManInit, this, _1, _2));
MyEvents.RegisterEvent(tag,"network.mgr.stop", std::bind(&OvmsSSH::NetManStop, this, _1, _2));
MyEvents.RegisterEvent(tag,"config.restore", std::bind(&OvmsSSH::ConfigRestore, this, _1, _2));
MyConfig.RegisterParam("ssh.server", "SSH server private key store", true, false);
MyConfig.RegisterParam("ssh.info", "SSH server information", false, true);
MyConfig.RegisterParam("ssh.keys", "SSH public key store", true, true);
@ -232,6 +233,14 @@ void OvmsSSH::NetManStop(std::string event, void* data)
}
}
void OvmsSSH::ConfigRestore(std::string event, void* data)
{
if (RSAKeyGenerator::KillInstance())
{
ESP_LOGW(tag, "RSAKeyGenerator killed by request");
}
}
int OvmsSSH::Authenticate(uint8_t type, WS_UserAuthData* data, void* ctx)
{
ConsoleSSH* cons = (ConsoleSSH*)ctx;
@ -1058,11 +1067,26 @@ int SendCallback(WOLFSSH* ssh, void* data, uint32_t size, void* ctx)
// Class RSAKeyGenerator
//-----------------------------------------------------------------------------
RSAKeyGenerator* RSAKeyGenerator::s_instance = NULL;
RSAKeyGenerator::RSAKeyGenerator() : TaskBase("RSAKeyGen", 7*1024, 0)
{
s_instance = this;
Instantiate();
}
RSAKeyGenerator::~RSAKeyGenerator()
{
s_instance = NULL;
}
bool RSAKeyGenerator::KillInstance()
{
if (!s_instance) return false;
s_instance->Kill();
return true;
}
void RSAKeyGenerator::Service()
{
// Generate a random 2048-bit SSH host key for the SSH server in DER format.

View file

@ -49,6 +49,7 @@ class OvmsSSH
void EventHandler(struct mg_connection *nc, int ev, void *p);
void NetManInit(std::string event, void* data);
void NetManStop(std::string event, void* data);
void ConfigRestore(std::string event, void* data);
static int Authenticate(uint8_t type, WS_UserAuthData* data, void* ctx);
WOLFSSH_CTX* ctx() { return m_ctx; }
@ -74,7 +75,7 @@ class ConsoleSSH : public OvmsConsole
void Sent();
void Exit();
int puts(const char* s);
int printf(const char* fmt, ...);
int printf(const char* fmt, ...) __attribute__ ((format (printf, 2, 3)));
ssize_t write(const void *buf, size_t nbyte);
int RecvCallback(char* buf, uint32_t size);
bool IsDraining() { return m_drain > 0; }
@ -118,14 +119,18 @@ class ConsoleSSH : public OvmsConsole
class RSAKeyGenerator : public TaskBase
{
private:
static RSAKeyGenerator* s_instance;
public:
RSAKeyGenerator();
virtual ~RSAKeyGenerator() {}
virtual ~RSAKeyGenerator();
static bool KillInstance();
private:
void Service();
private:
byte m_der[1200]; // Big enough for 2048-bit private key
byte m_der[1200]; // Big enough for 2048-bit private key
};
#endif //#ifndef __CONSOLE_SSH_H__

View file

@ -64,7 +64,7 @@ class ConsoleTelnet : public OvmsConsole
void Receive();
void Exit();
int puts(const char* s);
int printf(const char* fmt, ...);
int printf(const char* fmt, ...) __attribute__ ((format (printf, 2, 3)));
ssize_t write(const void *buf, size_t nbyte);
protected:

View file

@ -19,6 +19,10 @@ COMPONENT_SRCDIRS:=src yacclex
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
COMPONENT_OBJS = src/dbc_app.o src/dbc_number.o src/dbc.o yacclex/dbc_tokeniser.o yacclex/dbc_parser.o
# silence warning about unused yy_flex_strncpy()
CFLAGS += -Wno-unused-function
CXXFLAGS += -Wno-unused-function
COMPONENT_EXTRA_CLEAN := $(COMPONENT_PATH)/yacclex/dbc_tokeniser.cpp \
$(COMPONENT_PATH)/yacclex/dbc_tokeniser.c \
$(COMPONENT_PATH)/yacclex/dbc_tokeniser.hpp \

View file

@ -830,8 +830,10 @@ dbcNumber dbcSignal::Decode(CAN_frame_t* msg)
if (m_value_type == DBC_VALUETYPE_UNSIGNED)
result.Cast((uint32_t)val, DBC_NUMBER_INTEGER_UNSIGNED);
else
result.Cast((uint32_t)val, DBC_NUMBER_INTEGER_SIGNED);
else {
int32_t signed_val = sign_extend<uint32_t, int32_t>((uint32_t)val, m_signal_size-1);
result.Cast(static_cast<uint32_t>(signed_val), DBC_NUMBER_INTEGER_SIGNED);
}
// Apply factor and offset
if (!(m_factor == 1))

View file

@ -191,12 +191,28 @@ void GsmNMEA::IncomingLine(const std::string line)
*StdMetrics.ms_v_pos_gpsmode = (std::string) mode;
*StdMetrics.ms_v_pos_satcount = (int) satcnt;
*StdMetrics.ms_v_pos_gpshdop = (float) hdop;
// Derive signal quality from lock status, satellite count and HDOP:
// quality ~ satcnt / hdop
*StdMetrics.ms_v_pos_gpssq = (int) LIMIT_MAX(gpslock * LIMIT_MIN(satcnt-1,0) / LIMIT_MIN(hdop,0.1) * 10, 100);
// Quality raises by satellite count and drops by HDOP. HDOP 1.0 = perfect.
// The calculation is designed to get 50% as the threshold for a "good" signal.
// GPS needs at least 4 satellites in view to get a position, but that will need
// HDOP << 1 to be considered reliable. 6 satellites are on the edge to "good"
// (with HDOP=1). Samples:
// 4 satellites / HDOP 1.0 → SQ 30%
// 4 satellites / HDOP 0.6 → SQ 50%
// 6 satellites / HDOP 1.0 → SQ 50%
// 9 satellites / HDOP 1.0 → SQ 80%
// 10 satellites / HDOP 0.9 → SQ 100%
// 10 satellites / HDOP 1.8 → SQ 50%
if (gpslock)
{
*StdMetrics.ms_v_pos_latitude = (float) lat;
*StdMetrics.ms_v_pos_longitude = (float) lon;
*StdMetrics.ms_v_pos_altitude = (float) alt;
*StdMetrics.ms_v_pos_gpstime = (int) time(NULL);
}
// upodate gpslock last, so listeners will see updated lat/lon values:
@ -281,6 +297,12 @@ void GsmNMEA::Shutdown(bool hard)
{
ESP_LOGI(TAG, "Shutdown (direct)");
*StdMetrics.ms_v_pos_gpsmode = (std::string) "";
*StdMetrics.ms_v_pos_gpsspeed = (float) 0;
*StdMetrics.ms_v_pos_satcount = (int) 0;
*StdMetrics.ms_v_pos_gpshdop = (float) 500;
*StdMetrics.ms_v_pos_gpssq = (int) 0;
if (StdMetrics.ms_v_pos_gpslock->AsBool())
{
*StdMetrics.ms_v_pos_gpslock = (bool) false;

View file

@ -79,12 +79,16 @@ const char* ModemNetRegName(modem::network_registration_t netreg)
{
switch (netreg)
{
case modem::NotRegistered: return "NotRegistered";
case modem::Searching: return "Searching";
case modem::DeniedRegistration: return "DeniedRegistration";
case modem::RegisteredHome: return "RegisteredHome";
case modem::RegisteredRoaming: return "RegisteredRoaming";
default: return "Unknown";
case modem::NotRegistered: return "NotRegistered";
case modem::DeniedRegistration: return "DeniedRegistration";
case modem::Searching: return "Searching";
case modem::Registered: return "Registered";
case modem::RegisteredEmergencyServices: return "RegisteredEmergencyServices";
case modem::RegisteredRoamingSMS: return "RegisteredRoamingSMS";
case modem::RegisteredRoaming: return "RegisteredRoaming";
case modem::RegisteredHomeSMS: return "RegisteredHomeSMS";
case modem::RegisteredHome: return "RegisteredHome";
default: return "Unknown";
};
}
@ -115,7 +119,7 @@ void modem::Task()
.use_ref_tick = 0,
};
uart_param_config(m_uartnum, &uart_config);
uart_set_pin(m_uartnum, m_txpin, m_rxpin, 0, 0);
uart_set_pin(m_uartnum, m_txpin, m_rxpin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
uart_driver_install(m_uartnum,
CONFIG_OVMS_HW_CELLULAR_MODEM_UART_SIZE,
CONFIG_OVMS_HW_CELLULAR_MODEM_UART_SIZE,
@ -241,11 +245,20 @@ void modem::Task()
}
}
}
if (m_state1 == PoweredOff && MyBoot.IsShuttingDown())
{
m_task = 0;
}
}
// Shutdown:
uart_driver_delete(m_uartnum);
ESP_LOGD(TAG, "UART shutdown");
m_queue = 0;
uart_wait_tx_done(m_uartnum, portMAX_DELAY);
uart_flush(m_uartnum);
uart_driver_delete(m_uartnum);
if (MyBoot.IsShuttingDown()) MyBoot.ShutdownReady(TAG);
vTaskDelete(NULL);
}
@ -272,6 +285,7 @@ modem::modem(const char* name, uart_port_t uartnum, int baud, int rxpin, int txp
m_line_unfinished = -1;
m_line_buffer.clear();
m_netreg = Unknown;
for (size_t k=0; k<CELLULAR_NETREG_COUNT; k++) { m_netreg_d[k] = Unknown; }
m_provider = "";
m_sq = 99; // Unknown
m_powermode = Off;
@ -285,6 +299,8 @@ modem::modem(const char* name, uart_port_t uartnum, int baud, int rxpin, int txp
m_mux = NULL;
m_ppp = NULL;
m_driver = NULL;
m_cmd_running = false;
m_cmd_output.clear();
ClearNetMetrics();
StartTask();
@ -402,6 +418,9 @@ void modem::SupportSummary(OvmsWriter* writer, bool debug /*=FALSE*/)
m_provider.c_str(),
UnitConvert(sq, dbm, m_sq),
StandardMetrics.ms_m_net_mdm_mode->AsString().c_str());
// writer->printf(" GSM Registration: %s\n",ModemNetRegName(m_netreg_d[NRT_GSM]));
// writer->printf(" GPRS Registration: %s\n",ModemNetRegName(m_netreg_d[NRT_GPRS]));
// writer->printf(" EPS Registration: %s\n",ModemNetRegName(m_netreg_d[NRT_EPS]));
}
writer->printf(" State: %s\n", ModemState1Name(m_state1));
@ -652,7 +671,6 @@ void modem::State1Enter(modem_state1_t newstate)
case PoweredOff:
ClearNetMetrics();
MyEvents.SignalEvent("system.modem.poweredoff", NULL);
if (MyBoot.IsShuttingDown()) MyBoot.ShutdownReady(TAG);
StopMux();
if (m_driver)
{
@ -832,7 +850,7 @@ modem::modem_state1_t modem::State1Ticker1()
if (m_mux != NULL)
{
if ((m_state1_ticker>5)&&((m_state1_ticker % 30) == 0))
{ muxtx(m_mux_channel_POLL, "AT+CREG?;+CCLK?;+CSQ;+COPS?\r\n"); }
{ m_driver->StatusPoller(); }
if (m_mux->IsMuxUp())
return NetWait;
}
@ -850,10 +868,10 @@ modem::modem_state1_t modem::State1Ticker1()
if ((!MyConfig.GetParamValueBool("modem", "enable.net", true))||(p.empty()))
return NetHold; // Just hold, without starting PPP
}
else if ((m_state1_ticker > 3)&&((m_netreg==RegisteredHome)||(m_netreg==RegisteredRoaming)))
else if ((m_state1_ticker > 3)&&((m_netreg >= Registered)))
return NetStart; // We have GSM, so start the network
if ((m_mux != NULL)&&(m_state1_ticker>3)&&((m_state1_ticker % 10) == 0))
{ muxtx(m_mux_channel_POLL, "AT+CREG?;+CCLK?;+CSQ;+COPS?\r\n"); }
{ m_driver->StatusPoller(); }
break;
case NetStart:
@ -889,14 +907,14 @@ modem::modem_state1_t modem::State1Ticker1()
case NetHold:
if ((m_mux != NULL)&&(m_state1_ticker>5)&&((m_state1_ticker % 30) == 0))
{ muxtx(m_mux_channel_POLL, "AT+CREG?;+CCLK?;+CSQ;+COPS?\r\n"); }
{ m_driver->StatusPoller(); }
break;
case NetSleep:
if (m_powermode == On) return NetWait;
if (m_powermode != Sleep) return PoweringOn;
if ((m_mux != NULL)&&(m_state1_ticker>5)&&((m_state1_ticker % 30) == 0))
{ muxtx(m_mux_channel_POLL, "AT+CREG?;+CCLK?;+CSQ;+COPS?\r\n"); }
{ m_driver->StatusPoller(); }
break;
case NetMode:
@ -905,7 +923,7 @@ modem::modem_state1_t modem::State1Ticker1()
// Need to shutdown ppp, and get back to NetSleep mode
return NetSleep;
}
if ((m_netreg!=RegisteredHome)&&(m_netreg!=RegisteredRoaming))
if (m_netreg < Registered)
{
// We've lost the network connection
ESP_LOGW(TAG, "Lost network connection (NetworkRegistration in NetMode)");
@ -988,6 +1006,12 @@ void modem::StandardLineHandler(int channel, OvmsBuffer* buf, std::string line)
if (line.length() == 0)
return;
if ((m_cmd_running)&&(channel == m_mux_channel_CMD))
{
m_cmd_output.append(line);
m_cmd_output.append("\r\n");
}
// expecting continuation of previous line?
if (m_line_unfinished == channel)
{
@ -1002,13 +1026,16 @@ void modem::StandardLineHandler(int channel, OvmsBuffer* buf, std::string line)
line = m_line_buffer;
}
const char *cp = line.c_str();
if ((line.length()>2)&&(cp[0]!='$')&&(cp[1])!='G')
if (line.compare(0, 2, "$G") == 0)
{
// Log incoming data other than GPS NMEA
ESP_LOGD(TAG, "mux-rx-line #%d: %s", channel, line.c_str());
// GPS NMEA URC:
if (m_nmea) m_nmea->IncomingLine(line);
return;
}
// Log incoming data other than GPS NMEA
ESP_LOGD(TAG, "mux-rx-line #%d: %s", channel, line.c_str());
if ((line.compare(0, 8, "CONNECT ") == 0)&&(m_state1 == NetStart)&&(m_state1_userdata == 1))
{
ESP_LOGI(TAG, "PPP Connection is ready to start");
@ -1047,26 +1074,57 @@ void modem::StandardLineHandler(int channel, OvmsBuffer* buf, std::string line)
creg = atoi(line.substr(7,1).c_str());
switch (creg)
{
case 0:
case 4:
nreg = NotRegistered;
break;
case 1:
nreg = RegisteredHome;
break;
case 2:
nreg = Searching;
break;
case 3:
nreg = DeniedRegistration;
break;
case 5:
nreg = RegisteredRoaming;
break;
default:
break;
case 0: case 4: nreg = NotRegistered; break;
case 1: nreg = RegisteredHome; break;
case 2: nreg = Searching; break;
case 3: nreg = DeniedRegistration; break;
case 5: nreg = RegisteredRoaming; break;
default: break;
}
SetNetworkRegistration(nreg);
SetNetworkRegistration(NRT_GSM, nreg);
}
else if (line.compare(0, 8, "+CGREG: ") == 0)
{
size_t qp = line.find(',');
int creg;
network_registration_t nreg = Unknown;
if (qp != string::npos)
creg = atoi(line.substr(qp+1,1).c_str());
else
creg = atoi(line.substr(7,1).c_str());
switch (creg)
{
case 0: case 4: nreg = NotRegistered; break;
case 1: nreg = RegisteredHome; break;
case 2: nreg = Searching; break;
case 3: nreg = DeniedRegistration; break;
case 5: nreg = RegisteredRoaming; break;
default: break;
}
SetNetworkRegistration(NRT_GPRS, nreg);
}
else if (line.compare(0, 8, "+CEREG: ") == 0)
{
size_t qp = line.find(',');
int creg;
network_registration_t nreg = Unknown;
if (qp != string::npos)
creg = atoi(line.substr(qp+1,1).c_str());
else
creg = atoi(line.substr(7,1).c_str());
switch (creg)
{
case 0: case 4: nreg = NotRegistered; break;
case 1: nreg = RegisteredHome; break;
case 2: nreg = Searching; break;
case 3: nreg = DeniedRegistration; break;
case 5: nreg = RegisteredRoaming; break;
case 6: nreg = RegisteredHomeSMS; break;
case 7: nreg = RegisteredRoamingSMS; break;
case 8: nreg = RegisteredEmergencyServices; break;
default: break;
}
SetNetworkRegistration(NRT_EPS, nreg);
}
else if (line.compare(0, 7, "+CPSI: ") == 0)
{
@ -1128,15 +1186,22 @@ void modem::StandardLineHandler(int channel, OvmsBuffer* buf, std::string line)
}
else if (line.compare(0, 30, "+CME ERROR: incorrect password") == 0)
{
std::string pincode = MyConfig.GetParamValue("modem", "pincode");
ESP_LOGE(TAG,"Wrong PIN code entered!");
MyEvents.SignalEvent("system.modem.wrongpingcode", NULL);
MyNotify.NotifyStringf("alert", "modem.wrongpincode", "Wrong pin code (%s) entered!", MyConfig.GetParamValue("modem", "pincode"));
MyNotify.NotifyStringf("alert", "modem.wrongpincode", "Wrong pin code (%s) entered!", pincode.c_str());
MyConfig.SetParamValueBool("modem","wrongpincode",true);
}
else if (line.compare(0, 28, "+CME ERROR: SIM not inserted") == 0)
{
ESP_LOGE(TAG,"SIM not inserted!");
MyEvents.SignalEvent("system.modem.simnotinserted", NULL);
StandardMetrics.ms_m_net_mdm_iccid->SetValue(line.substr(12));
}
// MMI/USSD response (URC):
// sent on all free channels, so we only process m_mux_channel_CMD
else if (channel == m_mux_channel_CMD && line.compare(0, 7, "+CUSD: ") == 0)
// sent on all free channels or only on POLL, so we only process m_mux_channel_POLL
else if (channel == m_mux_channel_POLL && line.compare(0, 7, "+CUSD: ") == 0)
{
// Format: +CUSD: 0,"…msg…",15
// The message string may contain CR/LF so can come on multiple lines, with unknown length
@ -1280,7 +1345,7 @@ void modem::StartTask()
if (!m_task)
{
ESP_LOGV(TAG, "Starting modem task");
xTaskCreatePinnedToCore(MODEM_task, "OVMS Cellular", CONFIG_OVMS_HW_CELLULAR_MODEM_STACK_SIZE, (void*)this, 20, &m_task, CORE(0));
xTaskCreatePinnedToCore(MODEM_task, "OVMS Cellular", CONFIG_OVMS_HW_CELLULAR_MODEM_STACK_SIZE, (void*)this, 20, (void**)&m_task, CORE(0));
}
}
@ -1298,27 +1363,42 @@ void modem::StopTask()
}
}
void modem::StartNMEA()
bool modem::StartNMEA(bool force /*=false*/)
{
if ( (m_nmea == NULL) &&
(MyConfig.GetParamValueBool("modem", "enable.gps", false)) )
(force || MyConfig.GetParamValueBool("modem", "enable.gps", false)) )
{
ESP_LOGV(TAG, "Starting NMEA");
m_nmea = new GsmNMEA(m_mux, m_mux_channel_NMEA, m_mux_channel_CMD);
m_nmea->Startup();
m_driver->StartupNMEA();
if (!m_mux || !m_driver)
{
ESP_LOGE(TAG, "StartNMEA failed: MUX or driver not available");
}
else
{
ESP_LOGV(TAG, "Starting NMEA");
m_nmea = new GsmNMEA(m_mux, m_mux_channel_NMEA, m_mux_channel_CMD);
m_nmea->Startup();
m_driver->StartupNMEA();
}
}
return (m_nmea != NULL);
}
void modem::StopNMEA()
{
if (m_nmea != NULL)
{
ESP_LOGV(TAG, "Stopping NMEA");
m_driver->ShutdownNMEA();
m_nmea->Shutdown();
delete m_nmea;
m_nmea = NULL;
if (!m_mux || !m_driver)
{
ESP_LOGE(TAG, "StopNMEA failed: MUX or driver not available");
}
else
{
ESP_LOGV(TAG, "Stopping NMEA");
m_driver->ShutdownNMEA();
m_nmea->Shutdown();
delete m_nmea;
m_nmea = NULL;
}
}
}
@ -1393,7 +1473,8 @@ void modem::Ticker(std::string event, void* data)
ev.event.type = TICKER1;
xQueueSend(m_queue,&ev,0);
QueueHandle_t queue = m_queue;
if (queue) xQueueSend(queue,&ev,0);
}
void modem::EventListener(std::string event, void* data)
@ -1421,17 +1502,7 @@ void modem::IncomingMuxData(GsmMuxChannel* channel)
}
else if (channel->m_channel == m_mux_channel_NMEA)
{
if (m_nmea != NULL)
{
while (channel->m_buffer.HasLine() >= 0)
{
m_nmea->IncomingLine(channel->m_buffer.ReadLine());
}
}
else
{
channel->m_buffer.EmptyAll();
}
StandardIncomingHandler(channel->m_channel, &channel->m_buffer);
}
else if (channel->m_channel == m_mux_channel_DATA)
{
@ -1470,7 +1541,8 @@ void modem::SendSetState1(modem_state1_t newstate)
ev.event.type = SETSTATE;
ev.event.data.newstate = newstate;
xQueueSend(m_queue,&ev,0);
QueueHandle_t queue = m_queue;
if (queue) xQueueSend(queue,&ev,0);
}
bool modem::IsStarted()
@ -1478,14 +1550,25 @@ bool modem::IsStarted()
return (m_task != NULL);
}
void modem::SetNetworkRegistration(network_registration_t netreg)
void modem::SetNetworkRegistration(network_regtype_t regtype, network_registration_t netreg)
{
if (netreg != m_netreg)
if (netreg != m_netreg_d[regtype])
{
m_netreg = netreg;
const char *v = ModemNetRegName(m_netreg);
ESP_LOGI(TAG, "Network Registration status: %s", v);
StdMetrics.ms_m_net_mdm_netreg->SetValue(v);
m_netreg_d[regtype] = netreg;
// Need to re-calculate m_netreg as best of these
network_registration_t highest = Unknown;
for (size_t k=0; k<CELLULAR_NETREG_COUNT; k++)
{
if (highest < m_netreg_d[k]) highest = m_netreg_d[k];
}
if (highest != m_netreg)
{
m_netreg = highest;
const char *v = ModemNetRegName(m_netreg);
ESP_LOGI(TAG, "Network Registration status: %s", v);
StdMetrics.ms_m_net_mdm_netreg->SetValue(v);
}
}
}
@ -1525,6 +1608,7 @@ void modem::SetSignalQuality(int newsq)
void modem::ClearNetMetrics()
{
m_netreg = Unknown;
for (size_t k=0; k<CELLULAR_NETREG_COUNT; k++) { m_netreg_d[k] = Unknown; }
StdMetrics.ms_m_net_mdm_netreg->Clear();
m_provider = "";
@ -1587,6 +1671,10 @@ void cellular_muxtx(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int arg
void cellular_cmd(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
std::string msg;
MyModem->m_cmd_output.clear();
MyModem->m_cmd_running = true;
for (int k=0; k<argc; k++)
{
if (k>0)
@ -1596,14 +1684,31 @@ void cellular_cmd(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc,
msg.append(argv[k]);
}
msg.append("\r\n");
bool sent = MyModem->txcmd(msg.c_str(),msg.length());
if (!MyModem->txcmd(msg.c_str(),msg.length()))
{
if (verbosity >= COMMAND_RESULT_MINIMAL)
{
writer->puts("ERROR: MODEM command channel not available!");
}
return;
}
// Wait for output to stabilise
size_t cmdsize = UINT_MAX;
size_t iter = 0;
while ((MyModem->m_cmd_output.size() != cmdsize) && (iter < 5))
{
iter++;
cmdsize = MyModem->m_cmd_output.size();
vTaskDelay(pdMS_TO_TICKS(500));
}
MyModem->m_cmd_running = false;
if (verbosity >= COMMAND_RESULT_MINIMAL)
{
if (sent)
writer->puts("MODEM command has been sent.");
else
writer->puts("ERROR: MODEM command channel not available!");
writer->write(MyModem->m_cmd_output.c_str(), MyModem->m_cmd_output.size());
}
MyModem->m_cmd_output.clear();
}
void cellular_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
@ -1636,6 +1741,53 @@ void modem_setstate(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int arg
writer->printf("Error: Unrecognised state %s\n",statename);
}
void modem_gps_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
writer->printf("GPS status: autostart %s, currently %s.\n",
MyConfig.GetParamValueBool("modem", "enable.gps", false) ? "enabled" : "disabled",
(MyModem && MyModem->m_nmea) ? "running" : "not running");
}
void modem_gps_start(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyModem || !MyModem->m_mux || !MyModem->m_mux->IsMuxUp())
{
writer->puts("ERROR: Modem not ready");
return;
}
if (MyModem->m_nmea)
{
writer->puts("GPS already running.");
return;
}
if (MyModem->StartNMEA(true))
{
writer->puts("GPS started (may take a minute to find satellites).");
}
else
{
writer->puts("ERROR: GPS startup failed.");
}
}
void modem_gps_stop(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
if (!MyModem || !MyModem->m_mux || !MyModem->m_mux->IsMuxUp())
{
writer->puts("ERROR: Modem not ready");
return;
}
if (!MyModem->m_nmea)
{
writer->puts("GPS already stopped.");
return;
}
MyModem->StopNMEA();
writer->puts("GPS stopped.");
}
void cellular_drivers(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
writer->puts("Type Name");
@ -1691,6 +1843,11 @@ CellularModemInit::CellularModemInit()
cmd_setstate->RegisterCommand(ModemState1Name((modem::modem_state1_t)x),"Force CELLULAR MODEM state change",modem_setstate);
}
OvmsCommand* cmd_gps = cmd_cellular->RegisterCommand("gps", "GPS/GNSS state control", modem_gps_status);
cmd_gps->RegisterCommand("status", "GPS/GNSS status", modem_gps_status);
cmd_gps->RegisterCommand("start", "Start GPS/GNSS", modem_gps_start);
cmd_gps->RegisterCommand("stop", "Stop GPS/GNSS", modem_gps_stop);
MyConfig.RegisterParam("modem", "Modem Configuration", true, true);
// Our instances:
// 'driver': Driver to use (default: auto)

View file

@ -47,6 +47,8 @@
using namespace std;
#define CELLULAR_NETREG_COUNT 3
class modemdriver; // Forward declaration
class modem : public pcp, public InternalRamAllocated
@ -56,7 +58,7 @@ class modem : public pcp, public InternalRamAllocated
~modem();
protected:
TaskHandle_t m_task;
volatile TaskHandle_t m_task;
volatile QueueHandle_t m_queue;
int m_baud;
int m_rxpin;
@ -93,13 +95,23 @@ class modem : public pcp, public InternalRamAllocated
} event_type_t;
typedef enum
{
NotRegistered = 0,
Searching = 2,
DeniedRegistration = 3,
RegisteredHome = 1,
RegisteredRoaming = 5,
Unknown = 99
Unknown = 0,
NotRegistered = 1,
DeniedRegistration = 2,
Searching = 3,
Registered = 4,
RegisteredEmergencyServices = 5,
RegisteredRoamingSMS = 6,
RegisteredRoaming = 7,
RegisteredHomeSMS = 8,
RegisteredHome = 9
} network_registration_t;
typedef enum
{
NRT_GSM = 0,
NRT_GPRS = 1,
NRT_EPS = 2
} network_regtype_t;
typedef struct
{
event_type_t type;
@ -126,6 +138,7 @@ class modem : public pcp, public InternalRamAllocated
std::string m_line_buffer;
network_registration_t m_netreg;
network_registration_t m_netreg_d[CELLULAR_NETREG_COUNT];
std::string m_provider;
int m_sq;
bool m_pincode_required;
@ -150,6 +163,9 @@ class modem : public pcp, public InternalRamAllocated
GsmPPPOS* m_ppp;
GsmNMEA* m_nmea;
bool m_cmd_running;
std::string m_cmd_output;
public:
// Modem power control and initialisation
virtual void SetPowerMode(PowerMode powermode);
@ -185,7 +201,7 @@ class modem : public pcp, public InternalRamAllocated
// High level API functions
void StartTask();
void StopTask();
void StartNMEA();
bool StartNMEA(bool force=false);
void StopNMEA();
void StartMux();
void StopMux();
@ -198,7 +214,7 @@ class modem : public pcp, public InternalRamAllocated
void IncomingMuxData(GsmMuxChannel* channel);
void SendSetState1(modem_state1_t newstate);
bool IsStarted();
void SetNetworkRegistration(network_registration_t netreg);
void SetNetworkRegistration(network_regtype_t regtype, network_registration_t netreg);
void SetProvider(std::string provider);
void SetSignalQuality(int newsq);
void ClearNetMetrics();
@ -241,6 +257,7 @@ class modemdriver : public InternalRamAllocated
protected:
unsigned int m_powercyclefactor;
modem* m_modem;
int m_statuspoller_step;
};
template<typename Type> modemdriver* CreateCellularModemDriver()

View file

@ -61,6 +61,7 @@ modemdriver::modemdriver()
{
m_modem = MyPeripherals->m_cellular_modem;
m_powercyclefactor = 0;
m_statuspoller_step = 0;
}
modemdriver::~modemdriver()
@ -81,9 +82,9 @@ void modemdriver::Restart()
{
ESP_LOGI(TAG, "Restart");
if (MyConfig.GetParamValueBool("auto", "modem", false))
m_modem->SetState1((m_modem->GetState1() != modem::PoweredOff) ? modem::PowerOffOn : modem::PoweringOn);
m_modem->SendSetState1((m_modem->GetState1() != modem::PoweredOff) ? modem::PowerOffOn : modem::PoweringOn);
else
m_modem->SetState1(modem::PoweringOff);
m_modem->SendSetState1(modem::PoweringOff);
}
void modemdriver::PowerOff()
@ -100,6 +101,7 @@ void modemdriver::PowerCycle()
m_powercyclefactor = m_powercyclefactor % 3;
ESP_LOGI(TAG, "Power Cycle %dms", psd);
uart_wait_tx_done(m_modem->m_uartnum, portMAX_DELAY);
uart_flush(m_modem->m_uartnum); // Flush the ring buffer, to try to address MUX start issues
#ifdef CONFIG_OVMS_COMP_MAX7317
MyPeripherals->m_max7317->Output(MODEM_EGPIO_PWR, 0); // Modem EN/PWR line low
@ -141,7 +143,12 @@ void modemdriver::ShutdownNMEA()
{
// Switch off GPS:
if (m_modem->m_mux != NULL)
{ m_modem->muxtx(GetMuxChannelCMD(), "AT+CGPS=0\r\n"); }
{
// send single commands, as each can fail:
m_modem->muxtx(GetMuxChannelCMD(), "AT+CGPSNMEA=0\r\n");
vTaskDelay(pdMS_TO_TICKS(100));
m_modem->muxtx(GetMuxChannelCMD(), "AT+CGPS=0\r\n");
}
else
{ ESP_LOGE(TAG, "Attempt to transmit on non running mux"); }
}
@ -154,11 +161,13 @@ void modemdriver::StatusPoller()
bool modemdriver::State1Leave(modem::modem_state1_t oldstate)
{
m_statuspoller_step = 0;
return false;
}
bool modemdriver::State1Enter(modem::modem_state1_t newstate)
{
m_statuspoller_step = 0;
return false;
}
@ -169,5 +178,9 @@ modem::modem_state1_t modemdriver::State1Activity(modem::modem_state1_t curstate
modem::modem_state1_t modemdriver::State1Ticker1(modem::modem_state1_t curstate)
{
if (m_statuspoller_step > 0)
{
StatusPoller();
}
return curstate;
}

View file

@ -136,7 +136,7 @@ bool OvmsHttpClient::Request(std::string url, const char* method)
{
// ESP_LOGI(TAG, "Got response %s",m_buf->ReadLine().c_str());
std::string header = m_buf->ReadLine();
if (header.compare(0,15,"Content-Length:") == 0)
if (strncasecmp(header.c_str(), "Content-Length:", 15) == 0)
{
m_bodysize = atoi(header.substr(15).c_str());
}

View file

@ -290,9 +290,10 @@ void OvmsLocation::Store(std::string& buf)
void OvmsLocation::Render(std::string& buf)
{
char val[32];
snprintf(val, sizeof(val), "%0.6f,%0.6f (%dm)", m_latitude, m_longitude, m_radius);
buf = val;
metric_unit_t user_length = OvmsMetricGetUserUnit(GrpDistanceShort, Meters);
buf = string_format("%0.6f,%0.6f (%d%s)",
m_latitude, m_longitude, UnitConvert(Meters, user_length, m_radius), OvmsMetricUnitLabel(user_length));
bool first = true;
for (ActionList::iterator it = m_actions.begin(); it != m_actions.end(); ++it)
{
@ -369,11 +370,22 @@ void location_list(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc
writer->puts("NOTE: ACC actions are not implemented yet!"); // XXX IMPLEMENT AND REMOVE THIS!
}
int location_set_validate(OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv, bool complete)
{
if (argc == 5)
{
return OvmsMetricUnit_Validate(writer, argc, argv[4], complete, GrpDistanceShort);
}
return -1;
}
void location_set(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
const char *name = argv[0];
float latitude, longitude;
int radius = LOCATION_DEFRADIUS;
int base_value = radius;
metric_unit_t user_length = Meters;
if (strcmp(name, "?") == 0)
{
@ -391,12 +403,37 @@ void location_set(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc,
longitude = MyLocations.m_longitude;
}
if (argc > 3) radius = atoi(argv[3]);
if (argc > 3)
{
radius = atoi(argv[3]);
base_value = radius;
if (argc < 5)
user_length = OvmsMetricGetUserUnit(GrpDistanceShort, Meters);
else
{
user_length = OvmsMetricUnitFromName(argv[4]);
if (user_length == UnitNotFound)
{
writer->printf("Error: Invalid Metric %s\n", argv[4]);
return;
}
user_length = OvmsMetricCheckUnit(Meters, user_length);
if (user_length == UnitNotFound)
{
writer->printf("Error: Metric %s is not a length unit\n", argv[4]);
return;
}
}
char val[32];
snprintf(val,sizeof(val),"%0.6f,%0.6f,%d",latitude,longitude,radius);
MyConfig.SetParamValue(LOCATIONS_PARAM,name,val);
writer->puts("Location defined");
radius = UnitConvert(user_length, Meters, radius);
}
std::string val = string_format("%0.6f,%0.6f,%d",latitude,longitude,radius);
MyConfig.SetParamValue(LOCATIONS_PARAM,name,val.c_str());
if (user_length == Meters)
writer->printf("Location defined with radius of %dm\n", base_value);
else
writer->printf("Location defined with radius of %d%s = %dm\n", base_value, OvmsMetricUnitLabel(user_length), radius);
}
void location_radius(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
@ -409,12 +446,35 @@ void location_radius(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int ar
writer->printf("Error: No location %s defined\n",name);
return;
}
metric_unit_t user_length;
if (argc < 3)
user_length = OvmsMetricGetUserUnit(GrpDistanceShort, Meters);
else
{
user_length = OvmsMetricUnitFromName(argv[2]);
if (user_length == UnitNotFound)
{
writer->printf("Error: Invalid Metric %s\n", argv[2]);
return;
}
user_length = OvmsMetricCheckUnit(Meters, user_length);
if (user_length == UnitNotFound)
{
writer->printf("Error: Metric %s is not a length unit\n", argv[2]);
return;
}
}
std::string buf;
OvmsLocation* loc = *locp;
loc->m_radius = atoi(argv[1]);
int base_value = atoi(argv[1]);
int radius_m = UnitConvert(user_length, Meters, base_value);
loc->m_radius = radius_m;
loc->Store(buf);
writer->puts("Location radius set");
if (user_length == Meters)
writer->printf("Location radius set to %dm\n", base_value);
else
writer->printf("Location radius set to %d%s = %dm\n", base_value, OvmsMetricUnitLabel(user_length), radius_m);
}
void location_rm(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
@ -439,7 +499,10 @@ void location_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int ar
writer->printf("(%sGPS lock", MyLocations.m_gpslock ? "" : "without ");
n = StandardMetrics.ms_v_pos_satcount->AsInt();
if (n > 0)
writer->printf(", %d satellite%s", n, n == 1 ? "" : "s");
{
writer->printf(", %d satellite%s, %s", n, n == 1 ? "" : "s",
MyLocations.m_gpsgood ? "reliable" : "unreliable");
}
writer->puts(")");
if ((MyLocations.m_park_latitude != 0) || (MyLocations.m_park_longitude != 0))
@ -474,6 +537,16 @@ int location_validate(OvmsWriter* writer, OvmsCommand* cmd, int argc, const char
return -1;
}
int location_radius_validate(OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv, bool complete)
{
switch (argc)
{
case 1: return MyLocations.m_locations.Validate(writer, argc, argv[0], complete);
case 3: return OvmsMetricUnit_Validate(writer, argc, argv[2], complete, GrpDistanceShort);
}
return -1;
}
void location_action(int verbosity, OvmsWriter* writer, enum LocationAction act, std::string& params)
{
const char* const* rargv = writer->GetArgv();
@ -618,6 +691,8 @@ OvmsLocations::OvmsLocations()
m_ready = false;
m_gpslock = false;
m_gpssq = 0;
m_gpsgood = false;
m_latitude = 0;
m_longitude = 0;
m_park_latitude = 0;
@ -629,8 +704,8 @@ OvmsLocations::OvmsLocations()
// Register our commands
OvmsCommand* cmd_location = MyCommandApp.RegisterCommand("location","LOCATION framework", location_status, "", 0, 0, false);
cmd_location->RegisterCommand("list","Show all locations",location_list);
cmd_location->RegisterCommand("set","Set the position of a location",location_set, "<name> [<latitude> <longitude> [<radius>]]", 1, 4);
cmd_location->RegisterCommand("radius","Set the radius of a location",location_radius, "<name> <radius>", 2, 2, true, location_validate);
cmd_location->RegisterCommand("set","Set the position of a location",location_set, "<name> [<latitude> <longitude> [<radius> [<unit>]] ]", 1, 5, true, location_set_validate);
cmd_location->RegisterCommand("radius","Set the radius of a location (defaults to user 'height' units)",location_radius, "<name> <radius> [<unit>]", 2, 3, true, location_radius_validate);
cmd_location->RegisterCommand("rm","Remove a defined location",location_rm, "<name>", 1, 1, true, location_validate);
cmd_location->RegisterCommand("status","Show location status",location_status);
OvmsCommand* cmd_action = cmd_location->RegisterCommand("action","Set an action for a location");
@ -671,8 +746,8 @@ OvmsLocations::OvmsLocations()
using std::placeholders::_1;
using std::placeholders::_2;
MyMetrics.RegisterListener(TAG, MS_V_POS_GPSLOCK, std::bind(&OvmsLocations::UpdatedGpsLock, this, _1));
MyMetrics.RegisterListener(TAG, MS_V_POS_LATITUDE, std::bind(&OvmsLocations::UpdatedLatitude, this, _1));
MyMetrics.RegisterListener(TAG, MS_V_POS_LONGITUDE, std::bind(&OvmsLocations::UpdatedLongitude, this, _1));
MyMetrics.RegisterListener(TAG, MS_V_POS_GPSSQ, std::bind(&OvmsLocations::UpdatedGpsSQ, this, _1));
MyMetrics.RegisterListener(TAG, MS_V_POS_GPSTIME, std::bind(&OvmsLocations::UpdatedPosition, this, _1));
MyMetrics.RegisterListener(TAG, MS_V_ENV_ON, std::bind(&OvmsLocations::UpdatedVehicleOn, this, _1));
MyEvents.RegisterEvent(TAG,"config.mounted", std::bind(&OvmsLocations::UpdatedConfig, this, _1, _2));
MyEvents.RegisterEvent(TAG,"config.changed", std::bind(&OvmsLocations::UpdatedConfig, this, _1, _2));
@ -696,12 +771,6 @@ void OvmsLocations::UpdatedGpsLock(OvmsMetric* metric)
m_gpslock = m->AsBool();
if (m_gpslock)
{
if (!m_ready)
{
UpdateParkPosition();
m_ready = true;
}
UpdateLocations();
MyEvents.SignalEvent("gps.lock.acquired", NULL);
}
else
@ -710,22 +779,36 @@ void OvmsLocations::UpdatedGpsLock(OvmsMetric* metric)
}
}
void OvmsLocations::UpdatedLatitude(OvmsMetric* metric)
void OvmsLocations::UpdatedGpsSQ(OvmsMetric* metric)
{
OvmsMetricFloat* m = (OvmsMetricFloat*)metric;
m_latitude = m->AsFloat();
if (m_gpslock)
m_gpssq = StdMetrics.ms_v_pos_gpssq->AsInt();
int level_good = MyConfig.GetParamValueInt("vehicle", "gps.sq.good", 60);
int level_bad = MyConfig.GetParamValueInt("vehicle", "gps.sq.bad", 40);
if (level_bad >= level_good) level_bad = level_good - 1;
if (!m_gpsgood && m_gpssq >= level_good)
{
m_gpsgood = true;
if (!m_ready)
{
UpdateParkPosition();
m_ready = true;
}
UpdateLocations();
CheckTheft();
MyEvents.SignalEvent("gps.sq.good", NULL);
}
else if (m_gpsgood && m_gpssq <= level_bad)
{
m_gpsgood = false;
MyEvents.SignalEvent("gps.sq.bad", NULL);
}
}
void OvmsLocations::UpdatedLongitude(OvmsMetric* metric)
void OvmsLocations::UpdatedPosition(OvmsMetric* metric)
{
OvmsMetricFloat* m = (OvmsMetricFloat*)metric;
m_longitude = m->AsFloat();
if (m_gpslock)
m_latitude = StdMetrics.ms_v_pos_latitude->AsFloat();
m_longitude = StdMetrics.ms_v_pos_longitude->AsFloat();
if (m_gpsgood)
{
UpdateLocations();
CheckTheft();
@ -739,6 +822,8 @@ void OvmsLocations::UpdatedVehicleOn(OvmsMetric* metric)
void OvmsLocations::UpdateParkPosition()
{
OvmsRecMutexLock lock(&m_park_lock);
bool caron = StdMetrics.ms_v_env_on->AsBool();
if (caron)
{
@ -753,13 +838,13 @@ void OvmsLocations::UpdateParkPosition()
{
m_park_latitude = m_latitude;
m_park_longitude = m_longitude;
m_park_invalid = (!m_gpslock || StdMetrics.ms_v_pos_latitude->IsStale() || StdMetrics.ms_v_pos_longitude->IsStale());
m_park_invalid = (!m_gpsgood || StdMetrics.ms_v_pos_latitude->IsStale() || StdMetrics.ms_v_pos_longitude->IsStale());
m_last_alarm = 0;
ESP_LOGI(TAG, "UpdateParkPosition: vehicle is parking @%0.6f,%0.6f gpslock=%d satcount=%d hdop=%.1f invalid=%d",
ESP_LOGI(TAG, "UpdateParkPosition: vehicle is parking @%0.6f,%0.6f gpslock=%d satcount=%d hdop=%.1f sq=%d invalid=%d",
m_park_latitude, m_park_longitude, m_gpslock,
StdMetrics.ms_v_pos_satcount->AsInt(),
StdMetrics.ms_v_pos_gpshdop->AsFloat(),
m_park_invalid);
m_gpssq, m_park_invalid);
}
}
@ -813,7 +898,7 @@ void OvmsLocations::ReloadMap()
}
}
if (m_gpslock) UpdateLocations();
if (m_gpsgood) UpdateLocations();
}
void OvmsLocations::UpdateLocations()
@ -830,6 +915,8 @@ void OvmsLocations::CheckTheft()
{
static int last_dist = 0;
OvmsRecMutexLock lock(&m_park_lock);
if ((m_park_latitude == 0) && (m_park_longitude == 0)) return;
if (StandardMetrics.ms_v_env_on->AsBool()) return;

View file

@ -96,12 +96,15 @@ class OvmsLocations
public:
bool m_ready;
bool m_gpslock;
int m_gpssq;
bool m_gpsgood;
float m_latitude;
float m_longitude;
float m_park_latitude;
float m_park_longitude;
float m_park_distance;
bool m_park_invalid;
OvmsRecMutex m_park_lock;
uint32_t m_last_alarm;
LocationMap m_locations;
@ -113,8 +116,8 @@ class OvmsLocations
public:
void UpdatedGpsLock(OvmsMetric* metric);
void UpdatedLatitude(OvmsMetric* metric);
void UpdatedLongitude(OvmsMetric* metric);
void UpdatedGpsSQ(OvmsMetric* metric);
void UpdatedPosition(OvmsMetric* metric);
void UpdatedVehicleOn(OvmsMetric* metric);
void UpdatedConfig(std::string event, void* data);
};

View file

@ -198,7 +198,7 @@ size_t OvmsNetHttpAsyncClient::IncomingData(void *data, size_t length)
{
// Process the header
ESP_LOGD(TAG, "OvmsNetHttpAsyncClient Headers got %s", header.c_str());
if (header.compare(0,15,"Content-Length:") == 0)
if (strncasecmp(header.c_str(), "Content-Length:", 15) == 0)
{
m_bodysize = atoi(header.substr(15).c_str());
ESP_LOGD(TAG, "OvmsNetHttpAsyncClient content-length is %d", m_bodysize);

View file

@ -38,7 +38,9 @@ static const char *TAG = "ota";
#include <string.h>
#include <esp_system.h>
#include <esp_ota_ops.h>
#if ESP_IDF_VERSION_MAJOR < 4
#include "strverscmp.h"
#endif
#include "ovms_ota.h"
#include "ovms_command.h"
#include "ovms_boot.h"
@ -180,7 +182,7 @@ void ota_flash_vfs(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc
writer->printf("Error: Cannot find file %s\n",argv[0]);
return;
}
writer->printf("Source image is %d bytes in size\n",ds.st_size);
writer->printf("Source image is %ld bytes in size\n",ds.st_size);
FILE* f = fopen(argv[0], "r");
if (f == NULL)
@ -242,7 +244,7 @@ void ota_flash_vfs(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc
return;
}
writer->printf("OTA flash was successful\n Flashed %d bytes from %s\n Next boot will be from '%s'\n",
writer->printf("OTA flash was successful\n Flashed %ld bytes from %s\n Next boot will be from '%s'\n",
ds.st_size,argv[0],target->label);
MyConfig.SetParamValue("ota", "vfs.mru", argv[0]);
}
@ -931,7 +933,11 @@ static void OTAFlashTask(void *pvParameters)
if (fromsd)
{
#ifdef CONFIG_OVMS_COMP_SDCARD
success = MyOTA.AutoFlashSD();
#else
success = false;
#endif //CONFIG_OVMS_COMP_SDCARD
}
else
{

View file

@ -36,7 +36,10 @@ static const char *TAG = "pluginstore";
#include <sys/stat.h>
#include <string>
#include <string.h>
#include "esp_idf_version.h"
#if ESP_IDF_VERSION_MAJOR < 4
#include "strverscmp.h"
#endif
#include "ovms_plugins.h"
#include "ovms_command.h"
#include "ovms_config.h"

View file

@ -180,9 +180,9 @@ DuktapeHTTPRequest::DuktapeHTTPRequest(duk_context *ctx, int obj_idx)
m_headers.append(": ");
m_headers.append(val);
m_headers.append("\r\n");
if (key == "User-Agent")
if (strcasecmp(key.c_str(), "User-Agent") == 0)
have_useragent = true;
else if (key == "Content-Type")
else if (strcasecmp(key.c_str(), "Content-Type") == 0)
have_contenttype = true;
}
duk_pop(ctx); // enum
@ -325,7 +325,7 @@ void DuktapeHTTPRequest::MongooseCallback(struct mg_connection *nc, int ev, void
key.assign(hm->header_names[i].p, hm->header_names[i].len);
val.assign(hm->header_values[i].p, hm->header_values[i].len);
m_response_headers.push_back(std::make_pair(key, val));
if (key == "Location") location = val;
if (strcasecmp(key.c_str(), "Location") == 0) location = val;
}
// follow redirect?

View file

@ -1218,7 +1218,8 @@ void DukOvmsCommandRegisterRun(int verbosity, OvmsWriter* writer, OvmsCommand* c
}
else
{
DuktapeConsoleCommand* dcc = it->second;
// TODO
// DuktapeConsoleCommand* dcc = it->second;
// Perform the callback
}
}

View file

@ -721,11 +721,11 @@ void OvmsServerV2::ProcessCommand(const char* payload)
delete buffer;
}
void OvmsServerV2::Transmit(const std::string& message)
bool OvmsServerV2::Transmit(const std::string& message)
{
OvmsMutexLock mg(&m_mgconn_mutex);
if (!m_mgconn)
return;
return false;
int len = message.length();
char* s = new char[(len*2)+4];
@ -775,6 +775,7 @@ void OvmsServerV2::Transmit(const std::string& message)
delete [] buf;
delete [] s;
return true;
}
void OvmsServerV2::SetStatus(const char* status, bool fault, State newstate)
@ -1181,6 +1182,7 @@ void OvmsServerV2::TransmitMsgGPS(bool always)
StandardMetrics.ms_v_pos_direction->IsModifiedAndClear(MyOvmsServerV2Modifier) |
StandardMetrics.ms_v_pos_altitude->IsModifiedAndClear(MyOvmsServerV2Modifier) |
StandardMetrics.ms_v_pos_gpslock->IsModifiedAndClear(MyOvmsServerV2Modifier) |
StandardMetrics.ms_v_pos_gpssq->IsModifiedAndClear(MyOvmsServerV2Modifier) |
StandardMetrics.ms_v_pos_gpsmode->IsModifiedAndClear(MyOvmsServerV2Modifier) |
StandardMetrics.ms_v_pos_gpshdop->IsModifiedAndClear(MyOvmsServerV2Modifier) |
StandardMetrics.ms_v_pos_satcount->IsModifiedAndClear(MyOvmsServerV2Modifier) |
@ -1197,10 +1199,7 @@ void OvmsServerV2::TransmitMsgGPS(bool always)
if ((!always)&&(!modified)) return;
bool stale =
StandardMetrics.ms_v_pos_latitude->IsStale() ||
StandardMetrics.ms_v_pos_longitude->IsStale() ||
StandardMetrics.ms_v_pos_direction->IsStale() ||
StandardMetrics.ms_v_pos_altitude->IsStale();
StandardMetrics.ms_v_pos_gpstime->IsStale();
char drivemode[10];
sprintf(drivemode, "%x", StandardMetrics.ms_v_env_drivemode->AsInt());
@ -1243,6 +1242,8 @@ void OvmsServerV2::TransmitMsgGPS(bool always)
<< StandardMetrics.ms_v_pos_gpshdop->AsString("0", Native, 1)
<< ","
<< StandardMetrics.ms_v_pos_gpsspeed->AsString("0", units_speed, 1)
<< ","
<< StandardMetrics.ms_v_pos_gpssq->AsInt()
;
Transmit(buffer.str().c_str());
@ -1610,9 +1611,15 @@ void OvmsServerV2::TransmitNotifyInfo()
buffer
<< "MP-0 PI"
<< mp_encode(e->GetValue());
Transmit(buffer.str().c_str());
info->MarkRead(MyOvmsServerV2Reader, e);
if (Transmit(buffer.str().c_str()))
{
info->MarkRead(MyOvmsServerV2Reader, e);
}
else
{
m_pending_notify_info = true;
return;
}
}
}
@ -1634,9 +1641,15 @@ void OvmsServerV2::TransmitNotifyError()
buffer
<< "MP-0 PE"
<< e->GetValue(); // no mp_encode; payload structure "<vehicletype>,<errorcode>,<errordata>"
Transmit(buffer.str().c_str());
alert->MarkRead(MyOvmsServerV2Reader, e);
if (Transmit(buffer.str().c_str()))
{
alert->MarkRead(MyOvmsServerV2Reader, e);
}
else
{
m_pending_notify_error = true;
return;
}
}
}
@ -1658,9 +1671,15 @@ void OvmsServerV2::TransmitNotifyAlert()
buffer
<< "MP-0 PA"
<< mp_encode(e->GetValue());
Transmit(buffer.str().c_str());
alert->MarkRead(MyOvmsServerV2Reader, e);
if (Transmit(buffer.str().c_str()))
{
alert->MarkRead(MyOvmsServerV2Reader, e);
}
else
{
m_pending_notify_alert = true;
return;
}
}
}
@ -1704,7 +1723,13 @@ void OvmsServerV2::TransmitNotifyData()
<< -((int)(now - e->m_created) / 1000)
<< ","
<< msg;
Transmit(buffer.str().c_str());
if (!Transmit(buffer.str().c_str()))
{
m_pending_notify_data = true;
m_pending_notify_data_last = 0;
return;
}
m_pending_notify_data_last = e->m_id;
// be nice to other tasks, the network & the server:
@ -1833,8 +1858,7 @@ bool OvmsServerV2::IncomingNotification(OvmsNotifyType* type, OvmsNotifyEntry* e
buffer
<< "MP-0 PI"
<< mp_encode(entry->GetValue());
Transmit(buffer.str().c_str());
return true; // Mark it as read, as we've managed to send it
return Transmit(buffer.str().c_str()); // Mark it as read if we've managed to send it
}
else if (strcmp(type->m_name,"error")==0)
{
@ -1848,8 +1872,7 @@ bool OvmsServerV2::IncomingNotification(OvmsNotifyType* type, OvmsNotifyEntry* e
buffer
<< "MP-0 PE"
<< entry->GetValue(); // no mp_encode; payload structure "<vehicletype>,<errorcode>,<errordata>"
Transmit(buffer.str().c_str());
return true; // Mark it as read, as we've managed to send it
return Transmit(buffer.str().c_str()); // Mark it as read if we've managed to send it
}
else if (strcmp(type->m_name,"alert")==0)
{
@ -1863,8 +1886,7 @@ bool OvmsServerV2::IncomingNotification(OvmsNotifyType* type, OvmsNotifyEntry* e
buffer
<< "MP-0 PA"
<< mp_encode(entry->GetValue());
Transmit(buffer.str().c_str());
return true; // Mark it as read, as we've managed to send it
return Transmit(buffer.str().c_str()); // Mark it as read if we've managed to send it
}
else if (strcmp(type->m_name,"data")==0)
{
@ -2172,8 +2194,13 @@ void ovmsv2_stop(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc,
if (MyOvmsServerV2 != NULL)
{
writer->puts("Stopping OVMS Server V2 connection (oscv2)");
delete MyOvmsServerV2;
OvmsServerV2 *instance = MyOvmsServerV2;
MyOvmsServerV2 = NULL;
delete instance;
}
else
{
writer->puts("OVMS v2 server has not been started");
}
}

View file

@ -64,7 +64,7 @@ class OvmsServerV2 : public OvmsServer
protected:
void ProcessServerMsg();
void ProcessCommand(const char* payload);
void Transmit(const std::string& message);
bool Transmit(const std::string& message);
protected:
void TransmitMsgStat(bool always = false);

View file

@ -282,14 +282,133 @@ void OvmsServerV3::TransmitModifiedMetrics()
if (!m_mgconn)
return;
OvmsMetric* metric = MyMetrics.m_first;
while (metric != NULL)
if (StandardMetrics.ms_v_bat_soc->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
if (metric->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(metric);
}
metric = metric->m_next;
TransmitMetric(StandardMetrics.ms_v_bat_soc);
}
if (StandardMetrics.ms_v_charge_voltage->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_voltage);
}
if (StandardMetrics.ms_v_charge_current->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_current);
}
if (StandardMetrics.ms_v_charge_state->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_state);
}
if (StandardMetrics.ms_v_charge_substate->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_substate);
}
if (StandardMetrics.ms_v_charge_mode->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_mode);
}
if (StandardMetrics.ms_v_bat_range_ideal->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_bat_range_ideal);
}
if (StandardMetrics.ms_v_bat_range_est->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_bat_range_est);
}
if (StandardMetrics.ms_v_charge_climit->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_climit);
}
if (StandardMetrics.ms_v_charge_kwh->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_kwh);
}
if (StandardMetrics.ms_v_charge_timermode->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_timermode);
}
if (StandardMetrics.ms_v_charge_timerstart->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_timerstart);
}
if (StandardMetrics.ms_v_bat_cac->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_bat_cac);
}
if (StandardMetrics.ms_v_charge_duration_full->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_duration_full);
}
if (StandardMetrics.ms_v_charge_duration_range->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_duration_range);
}
if (StandardMetrics.ms_v_charge_duration_soc->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_duration_soc);
}
if (StandardMetrics.ms_v_charge_inprogress->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_inprogress);
}
if (StandardMetrics.ms_v_charge_limit_range->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_limit_range);
}
if (StandardMetrics.ms_v_charge_limit_soc->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_limit_soc);
}
if (StandardMetrics.ms_v_env_cooling->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_env_cooling);
}
if (StandardMetrics.ms_v_bat_range_full->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_bat_range_full);
}
if (StandardMetrics.ms_v_bat_power->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_bat_power);
}
if (StandardMetrics.ms_v_bat_voltage->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_bat_voltage);
}
if (StandardMetrics.ms_v_bat_soh->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_bat_soh);
}
if (StandardMetrics.ms_v_charge_power->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_power);
}
if (StandardMetrics.ms_v_charge_efficiency->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_charge_efficiency);
}
if (StandardMetrics.ms_v_bat_current->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_bat_current);
}
if (StandardMetrics.ms_v_bat_range_speed->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_bat_range_speed);
}
if (StandardMetrics.ms_v_tpms_pressure->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_tpms_pressure);
}
if (StandardMetrics.ms_v_tpms_temp->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_tpms_temp);
}
if (StandardMetrics.ms_v_tpms_health->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_tpms_health);
}
if (StandardMetrics.ms_v_tpms_alert->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(StandardMetrics.ms_v_tpms_alert);
}
}
@ -748,13 +867,25 @@ void OvmsServerV3::MetricModified(OvmsMetric* metric)
{
if (!StandardMetrics.ms_s_v3_connected->AsBool()) return;
if (m_streaming)
int now = StandardMetrics.ms_m_monotonic->AsInt();
//ESP_LOGD(TAG, "now: %d, m_lasttx_stream: %d, m_streaming: %d", now, m_lasttx_stream, m_streaming);
if (StandardMetrics.ms_v_env_on->AsBool() && m_streaming && now > m_lasttx_stream+m_streaming)
{
OvmsMutexLock mg(&m_mgconn_mutex);
if (!m_mgconn)
return;
metric->ClearModified(MyOvmsServerV3Modifier);
TransmitMetric(metric);
//metric->ClearModified(MyOvmsServerV3Modifier);
while (metric != NULL)
{
if (metric->IsModifiedAndClear(MyOvmsServerV3Modifier))
{
TransmitMetric(metric);
}
metric = metric->m_next;
}
//TransmitMetric(metric);
m_lasttx_stream = now;
}
}
@ -817,13 +948,22 @@ void OvmsServerV3::EventListener(std::string event, void* data)
*/
void OvmsServerV3::ConfigChanged(OvmsConfigParam* param)
{
ESP_LOGD(TAG, "--- ConfigChanged ---");
m_streaming = MyConfig.GetParamValueInt("vehicle", "stream", 0);
ESP_LOGD(TAG, "m_streaming: %d", m_streaming);
m_updatetime_connected = MyConfig.GetParamValueInt("server.v3", "updatetime.connected", 60);
ESP_LOGD(TAG, "m_updatetime_connected: %d", m_updatetime_connected);
m_updatetime_idle = MyConfig.GetParamValueInt("server.v3", "updatetime.idle", 600);
ESP_LOGD(TAG, "m_updatetime_idle: %d", m_updatetime_idle);
m_updatetime_awake = MyConfig.GetParamValueInt("server.v3", "updatetime.awake", m_updatetime_idle);
ESP_LOGD(TAG, "m_updatetime_awake: %d", m_updatetime_awake);
m_updatetime_on = MyConfig.GetParamValueInt("server.v3", "updatetime.on", m_updatetime_idle);
ESP_LOGD(TAG, "m_updatetime_on: %d", m_updatetime_on);
m_updatetime_charging = MyConfig.GetParamValueInt("server.v3", "updatetime.charging", m_updatetime_idle);
ESP_LOGD(TAG, "m_updatetime_charging: %d", m_updatetime_charging);
m_updatetime_sendall = MyConfig.GetParamValueInt("server.v3", "updatetime.sendall", 0);
ESP_LOGD(TAG, "m_updatetime_sendall: %d", m_updatetime_sendall);
ESP_LOGD(TAG, "--- ConfigChanged ---");
}
void OvmsServerV3::NetUp(std::string event, void* data)
@ -929,12 +1069,55 @@ void OvmsServerV3::Ticker1(std::string event, void* data)
}
else if ((m_lasttx==0)||(now>(m_lasttx+next)))
{
ESP_LOGD(TAG, "m_lasttx: %d next: %d, now: %d", m_lasttx, next, now);
ESP_LOGD(TAG, "m_updatetime_connected: %d m_updatetime_on: %d, m_updatetime_charging: %d", m_updatetime_connected, m_updatetime_on, m_updatetime_charging);
ESP_LOGD(TAG, "m_updatetime_awake: %d ", m_updatetime_awake);
TransmitModifiedMetrics();
m_lasttx = m_lasttx_stream = now;
}
else if (m_streaming && caron && m_peers && now > m_lasttx_stream+m_streaming)
{
// TODO: transmit streaming metrics
//Transmit important metrics while streaming
bool modified =
StandardMetrics.ms_v_pos_latitude->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_pos_longitude->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_pos_direction->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_pos_altitude->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_pos_gpslock->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_pos_gpssq->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_pos_gpsmode->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_pos_gpshdop->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_pos_satcount->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_pos_gpsspeed->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_pos_speed->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_env_drivemode->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_bat_power->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_bat_energy_used->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_bat_energy_recd->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_inv_power->IsModifiedAndClear(MyOvmsServerV3Modifier) |
StandardMetrics.ms_v_inv_efficiency->IsModifiedAndClear(MyOvmsServerV3Modifier);
// Quick exit if nothing modified
if ((!modified)) return;
TransmitMetric(StandardMetrics.ms_v_pos_latitude);
TransmitMetric(StandardMetrics.ms_v_pos_longitude);
TransmitMetric(StandardMetrics.ms_v_pos_direction);
TransmitMetric(StandardMetrics.ms_v_pos_altitude);
TransmitMetric(StandardMetrics.ms_v_pos_gpslock);
TransmitMetric(StandardMetrics.ms_v_pos_gpssq);
TransmitMetric(StandardMetrics.ms_v_pos_gpsmode);
TransmitMetric(StandardMetrics.ms_v_pos_gpshdop);
TransmitMetric(StandardMetrics.ms_v_pos_satcount);
TransmitMetric(StandardMetrics.ms_v_pos_gpsspeed);
TransmitMetric(StandardMetrics.ms_v_pos_speed);
TransmitMetric(StandardMetrics.ms_v_env_drivemode);
TransmitMetric(StandardMetrics.ms_v_bat_power);
TransmitMetric(StandardMetrics.ms_v_bat_energy_used);
TransmitMetric(StandardMetrics.ms_v_bat_energy_recd);
TransmitMetric(StandardMetrics.ms_v_inv_power);
TransmitMetric(StandardMetrics.ms_v_inv_efficiency);
m_lasttx_stream = now;
}
}
@ -979,6 +1162,7 @@ void ovmsv3_start(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc,
{
if (MyOvmsServerV3 == NULL)
{
writer->puts("Launching OVMS Server V3 connection (oscv3)");
MyOvmsServerV3 = new OvmsServerV3("oscv3");
}
}
@ -987,8 +1171,14 @@ void ovmsv3_stop(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc,
{
if (MyOvmsServerV3 != NULL)
{
delete MyOvmsServerV3;
writer->puts("Stopping OVMS Server V3 connection (oscv3)");
OvmsServerV3 *instance = MyOvmsServerV3;
MyOvmsServerV3 = NULL;
delete instance;
}
else
{
writer->puts("OVMS v3 server has not been started");
}
}

View file

@ -167,7 +167,7 @@ void OvmsTLS::Reload()
extern const unsigned char ovms_ca[] asm("_binary_ovms_ca_crt_start");
extern const unsigned char ovms_ca_end[] asm("_binary_ovms_ca_crt_end");
m_trustlist["OVMS Bit-Cloud.de CA"] = new OvmsTrustedCert(ovms_ca, ovms_ca_end - ovms_ca);
// Add trusted certs on disk (/store/trustedca)
DIR *dir;
struct dirent *dp;

View file

@ -140,8 +140,10 @@ void tpms_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc,
if (StandardMetrics.ms_v_tpms_pressure->IsDefined())
{
writer->printf("Pressure...[kPa]: ");
for (auto val : StandardMetrics.ms_v_tpms_pressure->AsVector())
metric_unit_t user_pressure = OvmsMetricGetUserUnit(GrpPressure, kPa);
writer->printf("Pressure...[%s]: ", OvmsMetricUnitLabel(user_pressure) );
for (auto val : StandardMetrics.ms_v_tpms_pressure->AsVector(user_pressure))
writer->printf(" %8.1f", val);
writer->puts(StandardMetrics.ms_v_tpms_pressure->IsStale() ? " [stale]" : "");
data_shown = true;
@ -149,8 +151,9 @@ void tpms_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc,
if (StandardMetrics.ms_v_tpms_temp->IsDefined())
{
writer->printf("Temperature.[°C]: ");
for (auto val : StandardMetrics.ms_v_tpms_temp->AsVector())
metric_unit_t user_temp = OvmsMetricGetUserUnit(GrpTemp, Celcius);
writer->printf("Temperature.[%s]: ", OvmsMetricUnitLabel(user_temp));
for (auto val : StandardMetrics.ms_v_tpms_temp->AsVector(user_temp))
writer->printf(" %8.1f", val);
writer->puts(StandardMetrics.ms_v_tpms_temp->IsStale() ? " [stale]" : "");
data_shown = true;

View file

@ -402,6 +402,180 @@ $.fn.loadcmd = function(command, filter, timeout) {
var monitorTimer, last_monotonic = 0;
var ws, ws_inhibit = 0;
var metrics = {};
var units = { metrics: {}, prefs: {} };
mi_to_km = function(mi) { return mi * 1.609347; }
km_to_mi = function(km) { return km * 0.6213700; }
pkm_to_pmi = function(pkm) { return pkm * 1.609347; }
pmi_to_pkm = function(pmi) { return pmi * 0.6213700; }
no_conversion = function (value) { return value;}
x_to_kx = function (value) { return value/1000; }
kx_to_x = function (value) { return value*1000; }
const feet_per_mile = 5280;
var unit_conversions = {
"native": no_conversion,
"km>miles": km_to_mi,
"km>meters": kx_to_x,
"km>feet": function (value) { return km_to_mi(value) * feet_per_mile; },
"miles>km": mi_to_km,
"miles>meters": function (value) { return (mi_to_km(value)*1000); },
"miles>feet": function (value) { return value * feet_per_mile; },
"meters>miles": function (value) { return km_to_mi(value/1000); },
"meters>km": x_to_kx,
"meters>feet": function (value) { return km_to_mi(value/1000) * feet_per_mile; },
"feet>km": function (value) { return mi_to_km(value/feet_per_mile); },
"feet>meters": function (value) { return (mi_to_km(value/feet_per_mile)*1000); },
"feet>miles": function (value) { return value / feet_per_mile; },
"kmphps>miphps": km_to_mi,
"kmphps>mpss": function (value) { return value/3.6; },
"kmphps>ftpss": function (value) { return km_to_mi(value)*feet_per_mile/3600; },
"miphps>kmphps": mi_to_km,
"miphps>mpss": function (value) { return mi_to_km(value)/3.6; },
"miphps>ftpss": function (value) { return value*feet_per_mile/3600; },
"mpss>kmphps": function (value) { return (value*3.6); },
"mpss>miphps": function (value) { return km_to_mi(value)*3.6; },
"mpss>ftpss": function (value) { return km_to_mi(value)*feet_per_mile; },
"ftpss>kmphps": function (value) { return (mi_to_km(value/feet_per_mile)*3.6); },
"ftpss>miphps": function (value) { return value *3600/feet_per_mile; },
"ftpss>mpss": function (value) { return mi_to_km(value/feet_per_mile)*1000; },
"kw>watts": kx_to_x,
"watts>kw": x_to_kx,
"kwh>watthours": kx_to_x,
"watthours>kwh": x_to_kx,
"whpkm>whpmi": pkm_to_pmi,
"whpkm>kwhp100km": function (value) { return value / 10; },
"whpkm>kmpkwh": function (value) { return value ? 1000.0 / value : 0; },
"whpkm>mipkwh": function (value) { return value ? (km_to_mi(1000.0 / value)) : 0; },
"whpmi>whpkm": pmi_to_pkm,
"whpmi>kwhp100km": function (value) { return pmi_to_pkm(value) / 10; },
"whpmi>kmpkwh": function (value) { return value ? (mi_to_km(1000.0 / value)) : 0; },
"whpmi>mipkwh": function (value) { return value ? (1000.0 / value) : 0; },
"kwhp100km>whpmi": function (value) { return pkm_to_pmi(value * 10); },
"kwhp100km>whpkm": function (value) { return value * 10; },
"kwhp100km>kmpkwh": function (value) { return value ? (100.0 / value) : 0; },
"kwhp100km>mipkwh": function (value) { return value ? km_to_mi(100.0 / value) : 0; },
"kmpkwh>whpmi": function (value) { return value ? (1000.0 / km_to_mi(value)) : 0;},
"kmpkwh>whpkm": function (value) { return value ? (1/(1000.0 * value)) : 0;},
"kmpkwh>kwhp100km": function (value) { return value ? (100.0/value) : 0;},
"kmpkwh>mipkwh": km_to_mi,
"mipkwh>whpmi": function (value) { return value ? 1000/value : 0;},
"mipkwh>whpkm": function (value) { return value ? (1000 / mi_to_km(value)) : 0;},
"mipkwh>kwhp100km": function (value) { return value ? (100.0/mi_to_km(value)) : 0;},
"mipkwh>kmpkwh": mi_to_km,
"celcius>fahrenheit": function (value) { return ((value*9)/5) + 32; },
"fahrenheit>celcius": function (value) { return ((value-32)*5)/9; },
"kpa>pa": kx_to_x,
"kpa>bar": function (value) { return value/100; },
"kpa>psi": function (value) { return value * 0.14503773773020923; },
"pa>kpa": x_to_kx,
"pa>bar": function (value) { return value/100000; },
"pa>psi": function (value) { return value * 0.00014503773773020923; },
"psi>kpa": function (value) { return value * 6.894757293168361; },
"psi>pa": function (value) { return value * 6894.757293168361; },
"psi>bar": function (value) { return value * 0.06894757293168361; },
"bar>pa": function (value) { return value*100000; },
"bar>kpa": function (value) { return value*100; },
"bar>psi": function (value) { return value * 14.503773773020923; },
"seconds>minutes": function (value) { return value/60; },
"seconds>hours": function (value) { return value/3600; },
"minutes>seconds": function (value) { return value*60; },
"minutes>hours": function (value) { return value/60; },
"hours>seconds": function (value) { return value*3600; },
"hours>minutes": function (value) { return value*60; },
"kmph>miph": km_to_mi,
"miph>kmph": mi_to_km,
"dbm>sq": function (value) { return Math.round((value <= -51) ? ((value + 113)/2) : 0); },
"sq>dbm": function (value) { return Math.round((value <= 31) ? (-113 + (value*2)) : 0); },
"percent>permille": function (value) { return value*10.0; },
"permille>percent": function (value) { return value*0.10; }
}
convertUnitFunction = function (from, to) {
return unit_conversions[from + ">" + to] || no_conversion;
}
convertUnits = function (from, to, value) {
return convertUnitFunction(from, to)(value);
}
units.convertMetricToUserUnits = function (value, name) {
if (value == undefined)
return value
var unit_entry = this.metrics[name];
if (unit_entry == undefined)
return value
var cnvfn = unit_entry.user_fn;
if (cnvfn == undefined) {
cnvfn = convertUnitFunction(unit_entry.native, unit_entry.code);
this.metrics[name].user_fn = cnvfn;
}
return cnvfn(value);
}
units.userUnitLabelFromMetric = function (name) {
var unit_entry = this.metrics[name];
if (unit_entry == undefined)
return "";
return unit_entry.label;
}
units.unitLabelToUser = function (unitType, defaultLabel) {
var res = this.prefs[unitType];
return (res && res.label) ? res.label : defaultLabel
}
units.unitValueToUser = function (unitType, value) {
var entry = this.prefs[unitType];
if (!entry)
return value;
return convertUnits(value, unitType, entry.unit);
}
// Works for units and metrics collection.
metricsProxyHas = function(target, name) {
return target[name] != undefined
}
var metrics_all = new Proxy(metrics, {
get: function(target, name) {
if (name == Symbol.toStringTag)
return 'metrics_all[]';
if (!(typeof name === "string" || name instanceof String))
return undefined;
var names = name.split('#',2);
var name = names[0];
var value_type = names[1]
if (value_type === "unit")
return units.userUnitLabelFromMetric(name);
var value = target[name];
if (value_type === "label")
value = units.convertMetricToUserUnits(value, name)
return value;
},
has: metricsProxyHas
});
var metrics_user = new Proxy(metrics, {
get:
function(target, name) {
if (name == Symbol.toStringTag)
return 'metrics_user[]';
return units.convertMetricToUserUnits(target[name], name)
},
has: metricsProxyHas
});
var metrics_label = new Proxy(units.metrics, {
get:
function(target, name) {
if (name == Symbol.toStringTag)
return 'metrics_label[]';
var unit_entry = target[name];
if (unit_entry == undefined) {
return "";
}
return unit_entry.label;
},
has: metricsProxyHas
});
var shellhist = [""], shellhpos = 0;
var loghist = [];
const loghist_maxsize = 100;
@ -415,6 +589,7 @@ function initSocketConnection(){
ws.onopen = function(ev) {
console.log("WebSocket OPENED", ev);
$(".receiver").subscribe();
subscribeToTopic("units/#");
};
ws.onerror = function(ev) { console.log("WebSocket ERROR", ev); };
ws.onclose = function(ev) { console.log("WebSocket CLOSED", ev); };
@ -443,6 +618,21 @@ function initSocketConnection(){
$.extend(metrics, msg.metrics);
$(".receiver").trigger("msg:metrics", msg.metrics);
}
else if (msgtype == "units") {
for (var subtype in msg.units) {
if (subtype == "metrics") {
$.extend(units.metrics, msg.units.metrics);
$(".receiver").trigger("msg:units:metrics", msg.units.metrics);
var msgmetrics = {};
for (metricname in msg.units.metrics)
msgmetrics[metricname] = metrics[metricname];
$(".receiver").trigger("msg:metrics", msgmetrics);
} else if (subtype == "prefs") {
$.extend(units.prefs, msg.units.prefs);
$(".receiver").trigger("msg:units:prefs", msg.units.prefs);
}
}
}
else if (msgtype == "notify") {
processNotification(msg.notify);
$(".receiver").trigger("msg:notify", msg.notify);
@ -513,7 +703,14 @@ function processNotification(msg) {
confirmdialog(opts.title, opts.body, ["OK"], opts.timeout);
}
subscribeToTopic = function (topic) {
try {
console.debug("subscribe " + topic);
if (ws) ws.send("subscribe " + topic);
} catch (e) {
console.log(e);
}
}
$.fn.subscribe = function(topics) {
return this.each(function() {
var subscriptions = $(this).data("subscriptions");
@ -526,13 +723,7 @@ $.fn.subscribe = function(topics) {
var tops = topics ? topics.split(' ') : [];
for (var i = 0; i < tops.length; i++) {
if (tops[i] && !subs.includes(tops[i])) {
try {
console.log("subscribe " + tops[i]);
if (ws) ws.send("subscribe " + tops[i]);
subs.push(tops[i]);
} catch (e) {
console.log(e);
}
subscribeToTopic(tops[i]);
}
}
$(this).data("subscriptions", subs.join(' '));
@ -1721,20 +1912,43 @@ $(function(){
// Metrics displays:
$("body").on('msg:metrics', '.receiver', function(e, update) {
$(this).find(".metric").each(function() {
var $el = $(this), metric = $el.data("metric"), prec = $el.data("prec"), scale = $el.data("scale");
var $el = $(this), metric = $el.data("metric"), prec = $el.data("prec"), scale = $el.data("scale"), useUser = $el.data("user");
if (!metric) return;
// filter:
var keys = metric.split(","), val;
var metricName = "";
for (var i=0; i<keys.length; i++) {
if ((val = update[keys[i]]) != null) break;
metricName = keys[i];
if ((val = update[metricName]) != null) {
break;
}
}
if (val == null) return;
// process:
if ($el.hasClass("text")) {
$el.children(".value").text(val);
var elt = $el.children(".value");
if (elt) elt.text(val);
elt = $el.children(".unit");
if (elt) elt.text(val);
} else if ($el.hasClass("number")) {
var vf = val;
if (scale != null) vf = Number(vf) * scale;
if (scale != null)
vf = Number(vf) * scale;
else {
var mun = units.userUnitLabelFromMetric(metricName);
if (mun != "") {
// If there's a .unit.. then convert it.
item = $el.children(".unit");
if (item) {
item.text(mun);
useUser = true;
}
}
if (useUser)
vf = units.convertMetricToUserUnits(vf, metricName);
}
if (prec != null) vf = Number(vf).toFixed(prec);
$el.children(".value").text(vf);
} else if ($el.hasClass("progress")) {

View file

@ -829,6 +829,180 @@ $.fn.loadcmd = function(command, filter, timeout) {
var monitorTimer, last_monotonic = 0;
var ws, ws_inhibit = 0;
var metrics = {};
var units = { metrics: {}, prefs: {} };
mi_to_km = function(mi) { return mi * 1.609347; }
km_to_mi = function(km) { return km * 0.6213700; }
pkm_to_pmi = function(pkm) { return pkm * 1.609347; }
pmi_to_pkm = function(pmi) { return pmi * 0.6213700; }
no_conversion = function (value) { return value;}
x_to_kx = function (value) { return value/1000; }
kx_to_x = function (value) { return value*1000; }
const feet_per_mile = 5280;
var unit_conversions = {
"native": no_conversion,
"km>miles": km_to_mi,
"km>meters": kx_to_x,
"km>feet": function (value) { return km_to_mi(value) * feet_per_mile; },
"miles>km": mi_to_km,
"miles>meters": function (value) { return (mi_to_km(value)*1000); },
"miles>feet": function (value) { return value * feet_per_mile; },
"meters>miles": function (value) { return km_to_mi(value/1000); },
"meters>km": x_to_kx,
"meters>feet": function (value) { return km_to_mi(value/1000) * feet_per_mile; },
"feet>km": function (value) { return mi_to_km(value/feet_per_mile); },
"feet>meters": function (value) { return (mi_to_km(value/feet_per_mile)*1000); },
"feet>miles": function (value) { return value / feet_per_mile; },
"kmphps>miphps": km_to_mi,
"kmphps>mpss": function (value) { return value/3.6; },
"kmphps>ftpss": function (value) { return km_to_mi(value)*feet_per_mile/3600; },
"miphps>kmphps": mi_to_km,
"miphps>mpss": function (value) { return mi_to_km(value)/3.6; },
"miphps>ftpss": function (value) { return value*feet_per_mile/3600; },
"mpss>kmphps": function (value) { return (value*3.6); },
"mpss>miphps": function (value) { return km_to_mi(value)*3.6; },
"mpss>ftpss": function (value) { return km_to_mi(value)*feet_per_mile; },
"ftpss>kmphps": function (value) { return (mi_to_km(value/feet_per_mile)*3.6); },
"ftpss>miphps": function (value) { return value *3600/feet_per_mile; },
"ftpss>mpss": function (value) { return mi_to_km(value/feet_per_mile)*1000; },
"kw>watts": kx_to_x,
"watts>kw": x_to_kx,
"kwh>watthours": kx_to_x,
"watthours>kwh": x_to_kx,
"whpkm>whpmi": pkm_to_pmi,
"whpkm>kwhp100km": function (value) { return value / 10; },
"whpkm>kmpkwh": function (value) { return value ? 1000.0 / value : 0; },
"whpkm>mipkwh": function (value) { return value ? (km_to_mi(1000.0 / value)) : 0; },
"whpmi>whpkm": pmi_to_pkm,
"whpmi>kwhp100km": function (value) { return pmi_to_pkm(value) / 10; },
"whpmi>kmpkwh": function (value) { return value ? (mi_to_km(1000.0 / value)) : 0; },
"whpmi>mipkwh": function (value) { return value ? (1000.0 / value) : 0; },
"kwhp100km>whpmi": function (value) { return pkm_to_pmi(value * 10); },
"kwhp100km>whpkm": function (value) { return value * 10; },
"kwhp100km>kmpkwh": function (value) { return value ? (100.0 / value) : 0; },
"kwhp100km>mipkwh": function (value) { return value ? km_to_mi(100.0 / value) : 0; },
"kmpkwh>whpmi": function (value) { return value ? (1000.0 / km_to_mi(value)) : 0;},
"kmpkwh>whpkm": function (value) { return value ? (1/(1000.0 * value)) : 0;},
"kmpkwh>kwhp100km": function (value) { return value ? (100.0/value) : 0;},
"kmpkwh>mipkwh": km_to_mi,
"mipkwh>whpmi": function (value) { return value ? 1000/value : 0;},
"mipkwh>whpkm": function (value) { return value ? (1000 / mi_to_km(value)) : 0;},
"mipkwh>kwhp100km": function (value) { return value ? (100.0/mi_to_km(value)) : 0;},
"mipkwh>kmpkwh": mi_to_km,
"celcius>fahrenheit": function (value) { return ((value*9)/5) + 32; },
"fahrenheit>celcius": function (value) { return ((value-32)*5)/9; },
"kpa>pa": kx_to_x,
"kpa>bar": function (value) { return value/100; },
"kpa>psi": function (value) { return value * 0.14503773773020923; },
"pa>kpa": x_to_kx,
"pa>bar": function (value) { return value/100000; },
"pa>psi": function (value) { return value * 0.00014503773773020923; },
"psi>kpa": function (value) { return value * 6.894757293168361; },
"psi>pa": function (value) { return value * 6894.757293168361; },
"psi>bar": function (value) { return value * 0.06894757293168361; },
"bar>pa": function (value) { return value*100000; },
"bar>kpa": function (value) { return value*100; },
"bar>psi": function (value) { return value * 14.503773773020923; },
"seconds>minutes": function (value) { return value/60; },
"seconds>hours": function (value) { return value/3600; },
"minutes>seconds": function (value) { return value*60; },
"minutes>hours": function (value) { return value/60; },
"hours>seconds": function (value) { return value*3600; },
"hours>minutes": function (value) { return value*60; },
"kmph>miph": km_to_mi,
"miph>kmph": mi_to_km,
"dbm>sq": function (value) { return Math.round((value <= -51) ? ((value + 113)/2) : 0); },
"sq>dbm": function (value) { return Math.round((value <= 31) ? (-113 + (value*2)) : 0); },
"percent>permille": function (value) { return value*10.0; },
"permille>percent": function (value) { return value*0.10; }
}
convertUnitFunction = function (from, to) {
return unit_conversions[from + ">" + to] || no_conversion;
}
convertUnits = function (from, to, value) {
return convertUnitFunction(from, to)(value);
}
units.convertMetricToUserUnits = function (value, name) {
if (value == undefined)
return value
var unit_entry = this.metrics[name];
if (unit_entry == undefined)
return value
var cnvfn = unit_entry.user_fn;
if (cnvfn == undefined) {
cnvfn = convertUnitFunction(unit_entry.native, unit_entry.code);
this.metrics[name].user_fn = cnvfn;
}
return cnvfn(value);
}
units.userUnitLabelFromMetric = function (name) {
var unit_entry = this.metrics[name];
if (unit_entry == undefined)
return "";
return unit_entry.label;
}
units.unitLabelToUser = function (unitType, defaultLabel) {
var res = this.prefs[unitType];
return (res && res.label) ? res.label : defaultLabel
}
units.unitValueToUser = function (unitType, value) {
var entry = this.prefs[unitType];
if (!entry)
return value;
return convertUnits(value, unitType, entry.unit);
}
// Works for units and metrics collection.
metricsProxyHas = function(target, name) {
return target[name] != undefined
}
var metrics_all = new Proxy(metrics, {
get: function(target, name) {
if (name == Symbol.toStringTag)
return 'metrics_all[]';
if (!(typeof name === "string" || name instanceof String))
return undefined;
var names = name.split('#',2);
var name = names[0];
var value_type = names[1]
if (value_type === "unit")
return units.userUnitLabelFromMetric(name);
var value = target[name];
if (value_type === "label")
value = units.convertMetricToUserUnits(value, name)
return value;
},
has: metricsProxyHas
});
var metrics_user = new Proxy(metrics, {
get:
function(target, name) {
if (name == Symbol.toStringTag)
return 'metrics_user[]';
return units.convertMetricToUserUnits(target[name], name)
},
has: metricsProxyHas
});
var metrics_label = new Proxy(units.metrics, {
get:
function(target, name) {
if (name == Symbol.toStringTag)
return 'metrics_label[]';
var unit_entry = target[name];
if (unit_entry == undefined) {
return "";
}
return unit_entry.label;
},
has: metricsProxyHas
});
var shellhist = [""], shellhpos = 0;
var loghist = [];
const loghist_maxsize = 100;
@ -842,6 +1016,7 @@ function initSocketConnection(){
ws.onopen = function(ev) {
console.log("WebSocket OPENED", ev);
$(".receiver").subscribe();
subscribeToTopic("units/#");
};
ws.onerror = function(ev) { console.log("WebSocket ERROR", ev); };
ws.onclose = function(ev) { console.log("WebSocket CLOSED", ev); };
@ -870,6 +1045,21 @@ function initSocketConnection(){
$.extend(metrics, msg.metrics);
$(".receiver").trigger("msg:metrics", msg.metrics);
}
else if (msgtype == "units") {
for (var subtype in msg.units) {
if (subtype == "metrics") {
$.extend(units.metrics, msg.units.metrics);
$(".receiver").trigger("msg:units:metrics", msg.units.metrics);
var msgmetrics = {};
for (metricname in msg.units.metrics)
msgmetrics[metricname] = metrics[metricname];
$(".receiver").trigger("msg:metrics", msgmetrics);
} else if (subtype == "prefs") {
$.extend(units.prefs, msg.units.prefs);
$(".receiver").trigger("msg:units:prefs", msg.units.prefs);
}
}
}
else if (msgtype == "notify") {
processNotification(msg.notify);
$(".receiver").trigger("msg:notify", msg.notify);
@ -940,7 +1130,14 @@ function processNotification(msg) {
confirmdialog(opts.title, opts.body, ["OK"], opts.timeout);
}
subscribeToTopic = function (topic) {
try {
console.debug("subscribe " + topic);
if (ws) ws.send("subscribe " + topic);
} catch (e) {
console.log(e);
}
}
$.fn.subscribe = function(topics) {
return this.each(function() {
var subscriptions = $(this).data("subscriptions");
@ -953,13 +1150,7 @@ $.fn.subscribe = function(topics) {
var tops = topics ? topics.split(' ') : [];
for (var i = 0; i < tops.length; i++) {
if (tops[i] && !subs.includes(tops[i])) {
try {
console.log("subscribe " + tops[i]);
if (ws) ws.send("subscribe " + tops[i]);
subs.push(tops[i]);
} catch (e) {
console.log(e);
}
subscribeToTopic(tops[i]);
}
}
$(this).data("subscriptions", subs.join(' '));
@ -2148,20 +2339,43 @@ $(function(){
// Metrics displays:
$("body").on('msg:metrics', '.receiver', function(e, update) {
$(this).find(".metric").each(function() {
var $el = $(this), metric = $el.data("metric"), prec = $el.data("prec"), scale = $el.data("scale");
var $el = $(this), metric = $el.data("metric"), prec = $el.data("prec"), scale = $el.data("scale"), useUser = $el.data("user");
if (!metric) return;
// filter:
var keys = metric.split(","), val;
var metricName = "";
for (var i=0; i<keys.length; i++) {
if ((val = update[keys[i]]) != null) break;
metricName = keys[i];
if ((val = update[metricName]) != null) {
break;
}
}
if (val == null) return;
// process:
if ($el.hasClass("text")) {
$el.children(".value").text(val);
var elt = $el.children(".value");
if (elt) elt.text(val);
elt = $el.children(".unit");
if (elt) elt.text(val);
} else if ($el.hasClass("number")) {
var vf = val;
if (scale != null) vf = Number(vf) * scale;
if (scale != null)
vf = Number(vf) * scale;
else {
var mun = units.userUnitLabelFromMetric(metricName);
if (mun != "") {
// If there's a .unit.. then convert it.
item = $el.children(".unit");
if (item) {
item.text(mun);
useUser = true;
}
}
if (useUser)
vf = units.convertMetricToUserUnits(vf, metricName);
}
if (prec != null) vf = Number(vf).toFixed(prec);
$el.children(".value").text(vf);
} else if ($el.hasClass("progress")) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View file

@ -0,0 +1,161 @@
===============
Battery Monitor
===============
.. image:: bms-cell-monitor.png
:width: 90%
:align: center
The BMS cell monitor is a **live view** of the battery pack and cell status.
It shows all individual cell voltages and module temperatures recorded
by the BMS, their average values and standard deviations.
The battery monitoring availability and detail depends on the vehicle
adaptor to provide cell voltage and temperature measurements. The BMS
framework takes care of doing the statistics.
The OVMS BMS normally records over a typical usage period, i.e. a full
driving or charging period. You can also issue a reset manually any time
using the button (or command ``bms reset``).
All statistics (min/max values and deviations) relate to the recording
period, so a reset clears these, as well as the warning and alert status
of all cells.
Of course all data shown is also available as metrics, namely the
``v.b.p.`` (vehicle battery pack) and ``v.b.c.`` (vehicle battery cell)
range of metrics.
-----------
Chart Usage
-----------
Voltage and temperature sensors normally differ in number, as voltages
are measured for each cell bundle (cells connected in parallel), while
temperatures only are measured for a cell module (multiple cell bundles
connected in series). Accordingly the monitor is divided into a voltage
and a temperature chart.
Click on the chart series names below the charts to show/hide the
respective series or group of metrics in that chart.
The charts can be zoomed and pinched using the mouse or dual touch finger
gestures as usual. When zoomed, a zoom reset button is shown in the upper
right corner. To move the zoom window, hold the ``Ctrl`` key while clicking,
or tap and move two fingers. Note: horizontal zoom needs an up to date
firmware.
Hovering or clicking on a cell/module chart column will show the actual
values (numbers) in a small overlay window.
Values may change while viewing the chart, that's normal: the chart is
a live view of the actual current measurements. That allows to see how
the cells respond to sudden high loads.
-----------------------
Cell/Module Info Detail
-----------------------
.. image:: bms-cell-info.png
:width: 90%
:align: center
-----------------
Overall Pack Info
-----------------
The table at the bottom shows the overall pack statistics in numbers.
This includes the overall cell/module averages, the maximum and minimum
voltage/temperature readings recorded, and the current and recorded maximum
standard deviation.
**On the cell gradient & max stddev deviation**: voltages are very volatile,
as current battery cells immediately react to load changes. A high discharge
pulse will lead to an instantaneous substantial drop of the cell voltages.
So, depending on how the vehicle's BMS does the measurements, a full series of
voltages may include such a change in between some cells. That would lead to
wrong standard deviations and possibly false alerts, so the BMS framework
analyses the full voltage series for a gradient, and ignores the series
if the configured threshold is exceeded.
The series is also ignored if the standard deviation exceeds the configured
"max stddev deviation" value. Combine both thresholds to filter unusable
series. The vehicle adaptor provides sensible default thresholds, but you
may need to adjust them depending on the age and health of your battery.
------------------------
Cell Alert Configuration
------------------------
Cell voltage and temperature warnings and alerts are triggered by cell
deviation from the current average. Warnings and alerts are detected by
the BMS framework in the background, so you don't need to keep the monitor
open to get them.
Deviations exceeding configured threshold are shown color coded, yellow
for warnings and red for alerts. Alerts will also trigger text notifications
of type ``alert`` subtype ``batt.bms.alert`` unless disabled.
Click "Alert config" to change the thresholds or control text alert
notifications. Adjust the warning & alert thresholds as needed.
The vehicle adaptor provides reasonable defaults for the warning and alert
thresholds matching the specific type of battery normally built into the
vehicle, but you may need to change the thresholds to adapt to the quality
and age of your battery.
The BMS configuration can also be changed directly via the ``config``
command. The parameters are located under ``vehicle``, issue
``config list vehicle`` and look for parameter names including ``bms``.
In case you don't see any, save some custom values in the UI to create
them.
---------------------
Health Interpretation
---------------------
You generally want your cells to be as close together as possible in terms
of voltages and temperatures. When aging, deviations will rise, as internal
resistances rise.
The weakest cell normally defines the overall discharge and charge limits
of the pack, as the vehicle BMS normally cannot shift load around individual
cells -- current flows through all cells equally (more or less). A weak cell
will reach the safety cutoff limits first, causing the BMS to terminate
discharging or charging or to limit the power levels available.
The higher the pack standard deviation the worse the overall battery
performance. A high standard deviation is a sign for an old battery, or
for a new battery with a very poor cell matching (read: poor build
quality).
A high individual cell voltage deviation is a sign for a defective cell
that should be checked and possibly replaced as soon as possible to avoid
further damage.
A high voltage drop under load implies a high internal resistance and/or
some chemical defect.
A high positive temperature deviation of a cell or module can be a sign for
a chemical defect (e.g. dendrites causing short circuits) or for a bad
connector (e.g. a lose screw). Both not only degrade the overall pack
performance but can lead to battery fires in the worst case, so must be
addressed as soon as possible.
Some temperature & voltage deviation may be normal, caused by the layout
of the cells in your vehicle: cells placed closer to the outside will
have a higher temperature variance than those deep within the pack, and
a cell with higher temperature will have a higher voltage stability.
Also keep in mind, not all deviations may be caused by actual battery
issues. Voltage and temperature sensors can lose their calibration or
become defective as well.

View file

@ -6,9 +6,28 @@ OVMS V3 is based on metrics. Metrics can be single numerical or textual values o
like sets and arrays. The web framework keeps all metrics in a global object, which can be read
simply by e.g. ``metrics["v.b.soc"]``.
In addition to the raw metric values, there are 2 main proxy arrays that give access to the
user-configured versions of the raw metric values. The ``metrics_user`` array
converts the 'metrics' value to user-configured value and the ``metrics_label`` array
provides the corresponding label for that metric.
So for example the user could configure distance values to be in miles, and in this case
``metrics["v.p.odometer"]`` would still contain the value in km (the default) but
``metrics_user["v.p.odometer"]`` would give the value converted to miles and
``metrics_label["v.p.odometer"]`` would return "M".
The user conversion information is contained in another object ``units``. ``units.metrics``
has the user configuration for each metric and ``units.prefs`` has the user configuration
for each group of metrics (distance, temperature, consumption, pressure etc). There also some methods
for general conversions allowing user preferences.
- The method ``units.unitLabelToUser(unitType,name)`` will return the user
defined label for that 'unitType', defaulting to ``name``.
- The method ``units.unitValueToUser(unitType,value)`` will convert ``value``
to the user defined unit (if set) for the group.
Metrics updates (as well as other updates) are sent to all DOM elements having the
``receiver`` class. To hook into these updates, simply add an event listener for
``msg:metrics``.
``msg:metrics:``. The event ``msg:units:metrics`` is called when ``units.metrics`` is change
and ``msg:units:prefs`` when ``units.prefs`` are changed.
Listening to the event is not necessary though if all you need is some metrics
display. This is covered by the ``metric`` widget class family as shown here.
@ -30,6 +49,11 @@ The following example covers…
- Gauges
- Charts
Where a number element of class 'metric' contains both elements of class
'value' and 'unit', these will be automatically displayed in the units selected
in the user preferences. Having a 'data-user' attribute will also cause the
'value' element to be displayed in user units (unless 'data-scale' attribute is present).
Gauges & charts use the HighCharts library, which is included in the web server. The other widgets
are simple standard Bootstrap widgets extended by an automatic metrics value update mechanism.

View file

@ -1086,3 +1086,105 @@ void OvmsWebServer::HandleLogout(PageEntry_t& p, PageContext_t& c)
"<script>loggedin = false; $(\"#menu\").load(\"/menu\"); loaduri(\"#main\", \"get\", \"/home\", {})</script>");
c.done();
}
// Dash Gauge implementations.
//
dash_gauge_t::dash_gauge_t(const char *titlePrefix, metric_unit_t defUnit, metric_group_t group)
{
has_tick = false;
title_prefix = titlePrefix ? titlePrefix : "";
if (group == GrpNone)
group = GetMetricGroup(defUnit);
if (group == GrpOther)
user_unit = defUnit;
else
user_unit = MyUnitConfig.GetUserUnit(group, defUnit);
base_unit = defUnit;
}
float dash_gauge_t::UntConvert( float inValue ) const
{
return UnitConvert(base_unit, user_unit, inValue);
}
float dash_gauge_t::UntConvert( float inValue, float roundValue ) const
{
if (base_unit == user_unit)
return inValue;
return truncf(UnitConvert(base_unit, user_unit, inValue) / roundValue) * roundValue;
}
void dash_gauge_t::SetMinMax( float minValue, float maxValue)
{
min_value = UntConvert(minValue);
float temp_max = UntConvert(maxValue);
if (min_value < temp_max)
max_value = temp_max;
else {
max_value = min_value;
min_value = temp_max;
}
}
void dash_gauge_t::SetMinMax( float minValue, float maxValue, float roundValue)
{
min_value = UntConvert(minValue, roundValue);
float temp_max = UntConvert(maxValue, roundValue);
if (min_value < temp_max)
max_value = temp_max;
else {
max_value = min_value;
min_value = temp_max;
}
}
void dash_gauge_t::SetTick( float tickValue)
{
tick_value = UntConvert(tickValue);
has_tick =true;
}
void dash_gauge_t::SetTick( float tickValue, float roundValue)
{
tick_value = UntConvert(tickValue);
has_tick = true;
}
void dash_gauge_t::DoAddBand( const std::string &colour, float minValue, float maxValue, bool round, float roundValue)
{
dash_plot_band_t new_band;
new_band.colour = colour;
new_band.min_value = round ? UntConvert(minValue,roundValue) : UntConvert(minValue);
float temp_max = round ? UntConvert(maxValue, roundValue) : UntConvert(maxValue);
if (new_band.min_value <= temp_max) {
new_band.max_value = temp_max;
bands.insert(bands.end(), new_band);
} else
{
new_band.max_value = new_band.min_value;
new_band.min_value = temp_max;
bands.insert(bands.begin(), new_band);
}
}
std::ostream &dash_gauge_t::Output(std::ostream &ostream) const
{
ostream <<
"{"
"title: { text: '"<< title_prefix << PageContext::encode_html(OvmsMetricUnitLabel(user_unit)) << "' },";
if (min_value < max_value) {
ostream <<
"min: " << min_value << ", max: " << max_value << ",";
}
if (has_tick)
ostream << "tickInterval: " << tick_value << ",";
ostream << "plotBands: [";
bool first = true;
for (auto iter = bands.begin() ; iter != bands.end(); ++iter) {
if (first)
first = false;
else
ostream << ",";
ostream <<
"{ from: " << iter->min_value << ", to: " << iter->max_value << ", className: '" << iter->colour <<"-band' }";
}
ostream << "]}";
return ostream;
}

View file

@ -155,7 +155,7 @@ struct PageContext : public ExternalRamAllocated
void print(const std::string text);
void print(const extram::string text);
void print(const char* text);
void printf(const char *fmt, ...);
void printf(const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
void done();
void panel_start(const char* type, const char* title);
void panel_end(const char* footer="");
@ -367,6 +367,8 @@ enum WebSocketTxJobType
WSTX_Config, // payload: config (todo)
WSTX_Notify, // payload: notification
WSTX_LogBuffers, // payload: logbuffers
WSTX_UnitMetricUpdate, // payload: -
WSTX_UnitPrefsUpdate, // payload: -
};
struct WebSocketTxJob
@ -412,6 +414,10 @@ class WebSocketHandler : public MgHandler, public OvmsWriter
void Unsubscribe(std::string topic);
bool IsSubscribedTo(std::string topic);
void SubscriptionChanged();
void UnitsCheckSubscribe();
void UnitsCheckVehicleSubscribe();
// OvmsWriter:
public:
void Log(LogBuffers* message);
@ -428,7 +434,10 @@ class WebSocketHandler : public MgHandler, public OvmsWriter
WebSocketTxJob m_job = {};
int m_sent = 0;
int m_ack = 0;
int m_last = 0; // last entry sent up
std::set<std::string> m_subscriptions;
bool m_units_subscribed;
bool m_units_prefs_subscribed;
};
struct WebSocketSlot
@ -469,7 +478,7 @@ class HttpCommandStream : public OvmsShell, public MgHandler
void Initialize(bool print);
virtual bool IsInteractive() { return false; }
int puts(const char* s);
int printf(const char* fmt, ...);
int printf(const char* fmt, ...) __attribute__ ((format (printf, 2, 3)));
ssize_t write(const void *buf, size_t nbyte);
void Log(LogBuffers* message);
};
@ -536,7 +545,7 @@ class OvmsWebServer : public ExternalRamAllocated
static void HandleLogin(PageEntry_t& p, PageContext_t& c);
static void HandleLogout(PageEntry_t& p, PageContext_t& c);
static void OutputReboot(PageEntry_t& p, PageContext_t& c);
static void OutputReconnect(PageEntry_t& p, PageContext_t& c, const char* info=NULL);
static void OutputReconnect(PageEntry_t& p, PageContext_t& c, const char* info=NULL, const char* cmd=NULL);
public:
static void HandleStatus(PageEntry_t& p, PageContext_t& c);
@ -610,6 +619,80 @@ class OvmsWebServer : public ExternalRamAllocated
extern OvmsWebServer MyWebServer;
/** Dashboard Gauge generator.
* Handles unit conversions.
*/
struct dash_gauge_t {
protected:
struct dash_plot_band_t {
std::string colour;
float min_value, max_value;
};
std::string title_prefix;
metric_unit_t user_unit, base_unit;
float min_value, max_value, tick_value;
bool has_tick;
std::vector<dash_plot_band_t> bands;
void DoAddBand( const std::string &colour, float minValue, float maxValue, bool round, float roundValue);
public:
/**
* @param titlePrefix The title/caption prefix (to the unit)
* @param defUnit The unit used as a default for these metrics.
* @param group (Optional) The group of units it belong to.
*/
dash_gauge_t(const char *titlePrefix, metric_unit_t defUnit, metric_group_t group = GrpNone);
/**
* Convert a unit to the user unit.
*/
float UntConvert( float inValue ) const;
/**
* Convert a unit to the user unit with rounding to the nearest value.
*/
float UntConvert( float inValue, float roundValue ) const;
/**
* Set the minimum and maximum values for the gauge (in original units).
*/
void SetMinMax( float minValue, float maxValue);
/**
* Set the minimum and maximum values for the gauge (in original units) including
* rounding to the nearest value.
*/
void SetMinMax( float minValue, float maxValue, float roundValue);
/**
* Set the tickmark intervals (in original units).
*/
void SetTick( float tickValue);
/**
* Set the tickmark intervals (in original units) with rounding to nearest.
*/
void SetTick( float tickValue, float roundValue);
/** Zero the minimum. Used when 0 cannot be originally used as a minimum.
*/
inline void ZeroMin() { if (max_value > 0) min_value = 0; }
/** Add a colour band to the gauge.
*/
inline void AddBand( const std::string &colour, float minValue, float maxValue)
{
DoAddBand(colour, minValue, maxValue, false, 0);
}
/** Add a colour band to the gauge with rounding to the nearest value.
*/
inline void AddBand( const std::string &colour, float minValue, float maxValue, float roundValue)
{
DoAddBand(colour, minValue, maxValue, true, roundValue);
}
/** Output this element to a stream.
*/
std::ostream &Output(std::ostream &ostream) const;
};
inline std::ostream &operator<<(std::ostream &out, const dash_gauge_t& gauge)
{
return gauge.Output(out);
}
/**
* DashboardConfig:

View file

@ -71,7 +71,12 @@ WebSocketHandler::WebSocketHandler(mg_connection* nc, size_t slot, size_t modifi
m_jobqueue_overflow_dropcnt = 0;
m_jobqueue_overflow_dropcntref = 0;
m_job.type = WSTX_None;
m_sent = m_ack = 0;
m_sent = m_ack = m_last = 0;
m_units_subscribed = false;
m_units_prefs_subscribed = false;
MyMetrics.InitialiseSlot(m_slot);
MyUnitConfig.InitialiseSlot(m_slot);
// Register as logging console:
SetMonitoring(true);
@ -116,39 +121,42 @@ void WebSocketHandler::ProcessTxJob()
case WSTX_MetricsAll:
case WSTX_MetricsUpdate:
{
// Note: this loops over the metrics by index, keeping the checked count
// in m_sent. It will not detect new metrics added between polls if they are
// inserted before m_sent, so new metrics may not be sent until first changed.
// Note: this loops over the metrics by index, keeping the last checked position
// in m_last. It will not detect new metrics added between polls if they are
// inserted before m_last, so new metrics may not be sent until first changed.
// The Metrics set normally is static, so this should be no problem.
// find start:
int i;
OvmsMetric* m;
for (i=0, m=MyMetrics.m_first; i < m_sent && m != NULL; m=m->m_next, i++);
for (i=0, m=MyMetrics.m_first; i < m_last && m != NULL; m=m->m_next, i++);
// build msg:
std::string msg;
msg.reserve(2*XFER_CHUNK_SIZE+128);
msg = "{\"metrics\":{";
for (i=0; m && msg.size() < XFER_CHUNK_SIZE; m=m->m_next) {
if (m->IsModifiedAndClear(m_modifier) || m_job.type == WSTX_MetricsAll) {
if (i) msg += ',';
msg += '\"';
msg += m->m_name;
msg += "\":";
msg += m->AsJSON();
i++;
if (m) {
std::string msg;
msg.reserve(2*XFER_CHUNK_SIZE+128);
msg = "{\"metrics\":{";
for (i=0; m && msg.size() < XFER_CHUNK_SIZE; m=m->m_next) {
++m_last;
if (m->IsModifiedAndClear(m_modifier) || m_job.type == WSTX_MetricsAll) {
if (i) msg += ',';
msg += '\"';
msg += m->m_name;
msg += "\":";
msg += m->AsJSON();
i++;
}
}
// send msg:
if (i) {
msg += "}}";
ESP_EARLY_LOGV(TAG, "WebSocket msg: %s", msg.c_str());
mg_send_websocket_frame(m_nc, WEBSOCKET_OP_TEXT, msg.data(), msg.size());
m_sent += i;
}
}
// send msg:
if (i) {
msg += "}}";
ESP_EARLY_LOGV(TAG, "WebSocket msg: %s", msg.c_str());
mg_send_websocket_frame(m_nc, WEBSOCKET_OP_TEXT, msg.data(), msg.size());
m_sent += i;
}
// done?
if (!m && m_ack == m_sent) {
if (m_sent)
@ -158,7 +166,135 @@ void WebSocketHandler::ProcessTxJob()
break;
}
case WSTX_UnitMetricUpdate:
{
// Note: this loops over the metrics by index, keeping the last checked position
// in m_last. It will not detect new metrics added between polls if they are
// inserted before m_last, so new metrics may not be sent until first changed.
// The Metrics set normally is static, so this should be no problem.
ESP_EARLY_LOGD(TAG, "WebSocketHandler[%p/%d]: ProcessTxJob MetricsUnitUpdate, last=%d sent=%d ack=%d", m_nc, m_modifier, m_last, m_sent, m_ack);
// find start:
int i;
OvmsMetric* m;
for (i=0, m=MyMetrics.m_first; i < m_last && m != NULL; m=m->m_next, i++);
ESP_EARLY_LOGD(TAG, "WebSocketHandler[%p/%d]: ProcessTxJob MetricsUnitUpdate, i=%d", m_nc, m_modifier, i);
if (m) { // Bypass this if we are on the 'just sent' leg.
// build msg:
std::string msg;
msg.reserve(2*XFER_CHUNK_SIZE+128);
msg = "{\"units\":{\"metrics\":{";
// Cache the user mappings for each group.
for (i=0; m && msg.size() < XFER_CHUNK_SIZE; m=m->m_next) {
++m_last;
bool send = m->IsUnitSendAndClear(m_modifier);
if (send) {
if (i)
msg += ',';
metric_unit_t units = m->m_units;
metric_unit_t user_units = MyUnitConfig.GetUserUnit(units);
if (user_units == UnitNotFound)
user_units = Native;
std::string unitlabel = OvmsMetricUnitLabel((user_units == Native) ? units : user_units);
const char *metricname = (units == Native) ? "Other" : OvmsMetricUnitName(units);
if (metricname == NULL)
metricname = "";
const char *user_metricname = (user_units == Native) ? metricname : OvmsMetricUnitName(user_units);
if (user_metricname == NULL)
user_metricname = metricname;
std::string entry = string_format("\"%s\":{\"native\":\"%s\",\"code\":\"%s\",\"label\":\"%s\"}",
m->m_name, metricname, user_metricname, json_encode(unitlabel).c_str()
);
msg += entry;
i++;
}
}
// send msg:
if (i) {
msg += "}}}";
ESP_EARLY_LOGD(TAG, "WebSocket msg: %s", msg.c_str());
mg_send_websocket_frame(m_nc, WEBSOCKET_OP_TEXT, msg.data(), msg.size());
m_sent += i;
}
}
// done?
if (!m && m_ack == m_sent) {
if (m_sent)
ESP_EARLY_LOGD(TAG, "WebSocketHandler[%p/%d]: ProcessTxJob MetricsUnitsUpdate done, sent=%d metrics", m_nc, m_modifier, m_sent);
ClearTxJob(m_job);
}
break;
}
case WSTX_UnitPrefsUpdate:
{
// Note: this loops over the metrics by index, keeping the last checked position
// in m_last. It will not detect new metrics added between polls if they are
// inserted before m_last, so new metrics may not be sent until first changed.
// The Metrics set normally is static, so this should be no problem.
ESP_EARLY_LOGD(TAG, "WebSocketHandler[%p/%d]: ProcessTxJob MetricsVehicleUpdate, last=%d sent=%d ack=%d", m_nc, m_modifier, m_last, m_sent, m_ack);
if (m_last < MyUnitConfig.config_groups.size()) {
// Bypass this if we are on the 'just sent' leg.
// build msg:
std::string msg;
msg.reserve(2*XFER_CHUNK_SIZE+128);
msg = "{\"units\":{\"prefs\":{";
// Cache the user mappings for each group.
int i = 0;
for (int groupindex = m_last;
groupindex < MyUnitConfig.config_groups.size() && msg.size() < XFER_CHUNK_SIZE;
++groupindex) {
++m_last;
metric_group_t group = MyUnitConfig.config_groups[groupindex];
bool send = MyUnitConfig.IsModifiedAndClear(group, m_modifier);
if (send) {
metric_unit_t user_units = MyUnitConfig.GetUserUnit(group);
std::string unitLabel;
if (user_units == UnitNotFound)
unitLabel = "null";
else {
unitLabel = '"';
unitLabel += json_encode(std::string(OvmsMetricUnitLabel(user_units)));
unitLabel += '"';
}
const char *groupName = OvmsMetricGroupName(group);
const char *unitName = (user_units == Native) ? "Native" : OvmsMetricUnitName(user_units);
std::string entry = string_format("%s\"%s\":{\"unit\":\"%s\",\"label\":%s}",
i ? "," : "",
groupName, unitName, unitLabel.c_str()
);
msg += entry;
i++;
}
}
// send msg:
if (i) {
msg += "}}}";
ESP_EARLY_LOGD(TAG, "WebSocket msg: %s", msg.c_str());
mg_send_websocket_frame(m_nc, WEBSOCKET_OP_TEXT, msg.data(), msg.size());
m_sent += i;
}
}
// done?
if (m_last >= MyUnitConfig.config_groups.size() && m_ack == m_sent) {
if (m_sent)
ESP_EARLY_LOGD(TAG, "WebSocketHandler[%p/%d]: ProcessTxJob MetricsUnitsUpdate done, sent=%d metrics", m_nc, m_modifier, m_sent);
ClearTxJob(m_job);
}
break;
}
case WSTX_Notify:
{
if (m_sent && m_ack == m_job.notification->GetValueSize()+1) {
@ -300,7 +436,7 @@ bool WebSocketHandler::GetNextTxJob()
if (!m_jobqueue) return false;
if (xQueueReceive(m_jobqueue, &m_job, 0) == pdTRUE) {
// init new job state:
m_sent = m_ack = 0;
m_sent = m_ack = m_last = 0;
return true;
} else {
return false;
@ -610,17 +746,31 @@ void OvmsWebServer::UpdateTicker(TimerHandle_t timer)
break;
}
}
// trigger metrics update:
// trigger metrics update if required.
unsigned long mask_all = MyMetrics.GetUnitSendAll();
for (auto slot: MyWebServer.m_client_slots) {
if (slot.handler)
if (slot.handler) {
slot.handler->AddTxJob({ WSTX_MetricsUpdate, NULL });
if (slot.handler->m_units_subscribed) {
unsigned long bit = 1ul << slot.handler->m_modifier;
bool addJob = (bit & mask_all) != 0;
if (addJob) {
// Trigger Units update:
slot.handler->AddTxJob({ WSTX_UnitMetricUpdate, NULL });
}
}
if (slot.handler->m_units_prefs_subscribed) {
// Triger unit group config update.
if (MyUnitConfig.HasModified(slot.handler->m_modifier))
slot.handler->AddTxJob({ WSTX_UnitPrefsUpdate, NULL });
}
}
}
xSemaphoreGive(MyWebServer.m_client_mutex);
}
/**
* Notifications:
*/
@ -642,18 +792,57 @@ void WebSocketHandler::Subscribe(std::string topic)
}
m_subscriptions.insert(topic);
ESP_LOGD(TAG, "WebSocketHandler[%p]: subscription '%s' added", m_nc, topic.c_str());
SubscriptionChanged();
}
void WebSocketHandler::Unsubscribe(std::string topic)
{
bool changed = false;
for (auto it = m_subscriptions.begin(); it != m_subscriptions.end();) {
if (mg_mqtt_match_topic_expression(mg_mk_str(topic.c_str()), mg_mk_str((*it).c_str()))) {
ESP_LOGD(TAG, "WebSocketHandler[%p]: subscription '%s' removed", m_nc, (*it).c_str());
it = m_subscriptions.erase(it);
changed = true;
} else {
it++;
}
}
if (changed)
SubscriptionChanged();
}
void WebSocketHandler::SubscriptionChanged()
{
UnitsCheckSubscribe();
UnitsCheckVehicleSubscribe();
}
void WebSocketHandler::UnitsCheckSubscribe()
{
bool newSubscribe = IsSubscribedTo("units/metrics");
if (newSubscribe != m_units_subscribed) {
m_units_subscribed = newSubscribe;
if (newSubscribe) {
ESP_LOGD(TAG, "WebSocketHandler[%p/%d]: Subscribed to units/metrics", m_nc, m_modifier);
MyMetrics.SetAllUnitSend(m_modifier);
} else {
ESP_LOGD(TAG, "WebSocketHandler[%p/%d]: Unsubscribed from units/metrics", m_nc, m_modifier);
}
}
}
void WebSocketHandler::UnitsCheckVehicleSubscribe()
{
bool newSubscribe = IsSubscribedTo("units/prefs");
if (newSubscribe != m_units_prefs_subscribed) {
m_units_prefs_subscribed = newSubscribe;
if (newSubscribe) {
ESP_LOGD(TAG, "WebSocketHandler[%p/%d]: Subscribed to units/prefs", m_nc, m_modifier);
MyUnitConfig.InitialiseSlot(m_modifier);
} else {
ESP_LOGD(TAG, "WebSocketHandler[%p/%d]: Unsubscribed from units/prefs", m_nc, m_modifier);
}
}
}
bool WebSocketHandler::IsSubscribedTo(std::string topic)

View file

@ -74,6 +74,12 @@ void OvmsWebServer::HandleStatus(PageEntry_t& p, PageContext_t& c)
c.done();
return;
}
else {
// "network restart", "wifi reconnect"
OutputReconnect(p, c, NULL, cmd.c_str());
c.done();
return;
}
}
PAGE_HOOK("body.pre");
@ -101,6 +107,13 @@ void OvmsWebServer::HandleStatus(PageEntry_t& p, PageContext_t& c)
"<div class=\"metric number\" data-metric=\"m.net.sq\"><span class=\"value\">?</span><span class=\"unit\">dBm</span></div>"
"</td>"
"</tr>"
"<tr>"
"<th>GPS</th>"
"<td>"
"<div class=\"metric number\" data-metric=\"v.p.satcount\"><span class=\"value\">?</span><span class=\"unit\">Satellites</span></div>"
"<div class=\"metric number\" data-metric=\"v.p.gpssq\"><span class=\"value\">?</span><span class=\"unit\">%</span></div>"
"</td>"
"</tr>"
"<tr>"
"<th>Main battery</th>"
"<td>"
@ -137,7 +150,7 @@ void OvmsWebServer::HandleStatus(PageEntry_t& p, PageContext_t& c)
output = ExecuteCommand("stat");
c.printf("<samp class=\"monitor\" id=\"vehicle-status\" data-updcmd=\"stat\" data-events=\"vehicle.charge\">%s</samp>", _html(output));
output = ExecuteCommand("location status");
c.printf("<samp class=\"monitor\" data-updcmd=\"location status\" data-events=\"gps.lock|location\">%s</samp>", _html(output));
c.printf("<samp class=\"monitor\" data-updcmd=\"location status\" data-events=\"gps.lock|gps.sq|location\">%s</samp>", _html(output));
c.panel_end(
"<ul class=\"list-inline\">"
"<li><button type=\"button\" class=\"btn btn-default btn-sm\" data-target=\"#vehicle-cmdres\" data-cmd=\"charge start\">Start charge</button></li>"
@ -202,7 +215,10 @@ void OvmsWebServer::HandleStatus(PageEntry_t& p, PageContext_t& c)
c.panel_start("primary", "Network");
output = ExecuteCommand("network status");
c.printf("<samp class=\"monitor\" data-updcmd=\"network status\" data-events=\"^network\">%s</samp>", _html(output));
c.panel_end();
c.panel_end(
"<ul class=\"list-inline\">"
"<li><button type=\"button\" class=\"btn btn-default btn-sm\" name=\"action\" value=\"network restart\">Restart network</button></li>"
"</ul>");
c.print(
"</div>"
@ -211,7 +227,10 @@ void OvmsWebServer::HandleStatus(PageEntry_t& p, PageContext_t& c)
c.panel_start("primary", "Wifi");
output = ExecuteCommand("wifi status");
c.printf("<samp class=\"monitor\" data-updcmd=\"wifi status\" data-events=\"\\.wifi\\.\">%s</samp>", _html(output));
c.panel_end();
c.panel_end(
"<ul class=\"list-inline\">"
"<li><button type=\"button\" class=\"btn btn-default btn-sm\" name=\"action\" value=\"wifi reconnect\">Reconnect Wifi</button></li>"
"</ul>");
c.print(
"</div>"
@ -222,8 +241,10 @@ void OvmsWebServer::HandleStatus(PageEntry_t& p, PageContext_t& c)
c.printf("<samp class=\"monitor\" data-updcmd=\"cellular status\" data-events=\"\\.modem\\.\">%s</samp>", _html(output));
c.panel_end(
"<ul class=\"list-inline\">"
"<li><button type=\"button\" class=\"btn btn-default btn-sm\" data-target=\"#modem-cmdres\" data-cmd=\"power cellular on\">Start cellular modem</button></li>"
"<li><button type=\"button\" class=\"btn btn-default btn-sm\" data-target=\"#modem-cmdres\" data-cmd=\"power cellular off\">Stop cellular modem</button></li>"
"<li><button type=\"button\" class=\"btn btn-default btn-sm\" data-target=\"#modem-cmdres\" data-cmd=\"power cellular on\">Start modem</button></li>"
"<li><button type=\"button\" class=\"btn btn-default btn-sm\" data-target=\"#modem-cmdres\" data-cmd=\"power cellular off\">Stop modem</button></li>"
"<li><button type=\"button\" class=\"btn btn-default btn-sm\" data-target=\"#modem-cmdres\" data-cmd=\"cellular gps start\">Start GPS</button></li>"
"<li><button type=\"button\" class=\"btn btn-default btn-sm\" data-target=\"#modem-cmdres\" data-cmd=\"cellular gps stop\">Stop GPS</button></li>"
"<li><samp id=\"modem-cmdres\" class=\"samp-inline\"></samp></li>"
"</ul>");
@ -584,9 +605,13 @@ void OvmsWebServer::HandleCfgPassword(PageEntry_t& p, PageContext_t& c)
void OvmsWebServer::HandleCfgVehicle(PageEntry_t& p, PageContext_t& c)
{
std::string error, info;
std::string vehicleid, vehicletype, vehiclename, timezone, timezone_region, units_distance, pin;
std::string vehicleid, vehicletype, vehiclename, timezone, timezone_region, pin;
std::string bat12v_factor, bat12v_ref, bat12v_alert;
std::map<metric_group_t,std::string> units_values;
metric_group_list_t unit_groups;
OvmsMetricGroupConfigList(unit_groups);
if (c.method == "POST") {
// process form submission:
vehicleid = c.getvar("vehicleid");
@ -594,7 +619,13 @@ void OvmsWebServer::HandleCfgVehicle(PageEntry_t& p, PageContext_t& c)
vehiclename = c.getvar("vehiclename");
timezone = c.getvar("timezone");
timezone_region = c.getvar("timezone_region");
units_distance = c.getvar("units_distance");
for ( auto grpiter = unit_groups.begin(); grpiter != unit_groups.end(); ++grpiter) {
std::string name = OvmsMetricGroupName(*grpiter);
std::string cfg = "units_";
cfg += name;
units_values[*grpiter] = c.getvar(cfg);
}
bat12v_factor = c.getvar("bat12v_factor");
bat12v_ref = c.getvar("bat12v_ref");
bat12v_alert = c.getvar("bat12v_alert");
@ -620,7 +651,12 @@ void OvmsWebServer::HandleCfgVehicle(PageEntry_t& p, PageContext_t& c)
MyConfig.SetParamValue("vehicle", "name", vehiclename);
MyConfig.SetParamValue("vehicle", "timezone", timezone);
MyConfig.SetParamValue("vehicle", "timezone_region", timezone_region);
MyConfig.SetParamValue("vehicle", "units.distance", units_distance);
for ( auto grpiter = unit_groups.begin(); grpiter != unit_groups.end(); ++grpiter) {
std::string name = OvmsMetricGroupName(*grpiter);
std::string value = units_values[*grpiter];
OvmsMetricSetUserConfig(*grpiter, value);
}
MyConfig.SetParamValue("system.adc", "factor12v", bat12v_factor);
MyConfig.SetParamValue("vehicle", "12v.ref", bat12v_ref);
MyConfig.SetParamValue("vehicle", "12v.alert", bat12v_alert);
@ -649,7 +685,8 @@ void OvmsWebServer::HandleCfgVehicle(PageEntry_t& p, PageContext_t& c)
vehiclename = MyConfig.GetParamValue("vehicle", "name");
timezone = MyConfig.GetParamValue("vehicle", "timezone");
timezone_region = MyConfig.GetParamValue("vehicle", "timezone_region");
units_distance = MyConfig.GetParamValue("vehicle", "units.distance");
for ( auto grpiter = unit_groups.begin(); grpiter != unit_groups.end(); ++grpiter)
units_values[*grpiter] = OvmsMetricGetUserConfig(*grpiter);
bat12v_factor = MyConfig.GetParamValue("system.adc", "factor12v");
bat12v_ref = MyConfig.GetParamValue("vehicle", "12v.ref");
bat12v_alert = MyConfig.GetParamValue("vehicle", "12v.alert");
@ -696,10 +733,40 @@ void OvmsWebServer::HandleCfgVehicle(PageEntry_t& p, PageContext_t& c)
, _attr(timezone_region)
, _attr(timezone));
c.input_radiobtn_start("Distance units", "units_distance");
c.input_radiobtn_option("units_distance", "Kilometers", "K", units_distance == "K");
c.input_radiobtn_option("units_distance", "Miles", "M", units_distance == "M");
c.input_radiobtn_end();
for ( auto grpiter = unit_groups.begin(); grpiter != unit_groups.end(); ++grpiter) {
std::string name = OvmsMetricGroupName(*grpiter);
metric_unit_set_t group_units;
if (OvmsMetricGroupUnits(*grpiter,group_units)) {
bool use_select = group_units.size() > 3;
std::string cfg = "units_";
cfg += name;
std::string value = units_values[*grpiter];
if (use_select)
c.input_select_start(OvmsMetricGroupLabel(*grpiter), cfg.c_str() );
else
c.input_radiobtn_start(OvmsMetricGroupLabel(*grpiter), cfg.c_str() );
bool checked = value.empty();
if (use_select)
c.input_select_option( "Default", "", checked);
else
c.input_radiobtn_option(cfg.c_str(), "Default", "", checked);
for (auto unititer = group_units.begin(); unititer != group_units.end(); ++unititer) {
const char* unit_name = OvmsMetricUnitName(*unititer);
const char* unit_label = OvmsMetricUnitLabel(*unititer);
checked = value == unit_name;
if (use_select)
c.input_select_option( unit_label, unit_name, checked);
else
c.input_radiobtn_option(cfg.c_str(), unit_label, unit_name, checked);
}
if (use_select)
c.input_select_end();
else
c.input_radiobtn_end();
}
}
c.input_password("PIN", "pin", "", "empty = no change",
"<p>Vehicle PIN code used for unlocking etc.</p>", "autocomplete=\"section-vehiclepin new-password\"");
@ -956,8 +1023,7 @@ void OvmsWebServer::HandleCfgPushover(PageEntry_t& p, PageContext_t& c)
}
if (error == "") {
if (c.getvar("action") == "save")
{
if (c.getvar("action") == "save") {
// save:
param->m_map.clear();
param->m_map = std::move(pmap);
@ -968,9 +1034,8 @@ void OvmsWebServer::HandleCfgPushover(PageEntry_t& p, PageContext_t& c)
OutputHome(p, c);
c.done();
return;
}
else if (c.getvar("action") == "test")
{
} else if (c.getvar("action") == "test")
{
std::string reply;
std::string popup;
c.head(200);
@ -984,10 +1049,10 @@ void OvmsWebServer::HandleCfgPushover(PageEntry_t& p, PageContext_t& c)
atoi(c.getvar("retry").c_str()),
atoi(c.getvar("expire").c_str()),
true /* receive server reply as reply/pushover-type notification */ ))
{
{
c.alert("danger", "<p class=\"lead\">Could not send test message!</p>");
}
}
}
}
else {
// output error, return to form:
@ -1156,7 +1221,7 @@ void OvmsWebServer::HandleCfgPushover(PageEntry_t& p, PageContext_t& c)
"<td><button type=\"button\" class=\"btn btn-danger\" onclick=\"delRow(this)\"><strong>✖</strong></button></td>"
"<td><input type=\"text\" class=\"form-control\" name=\"nfy_%d\" value=\"%s\" placeholder=\"Enter notification type/subtype\""
" autocomplete=\"section-notification-type\"></td>"
"<td width=\"20%\"><select class=\"form-control\" name=\"np_%d\" size=\"1\">"
"<td width=\"20%%\"><select class=\"form-control\" name=\"np_%d\" size=\"1\">"
, max, _attr(name)
, max);
gen_options_priority(kv.second);
@ -2644,7 +2709,7 @@ void OvmsWebServer::HandleCfgFirmware(PageEntry_t& p, PageContext_t& c)
c.print(
"<datalist id=\"server-list\">"
"<option value=\"https://ovms-ota.bit-cloud.de\">"
"<option value=\"https://api.openvehicles.com/firmware/ota\">"
"<option value=\"https://ovms-ota.bit-cloud.de\">"
"<option value=\"https://ovms.dexters-web.de/firmware/ota\">"
"</datalist>"
"<datalist id=\"tag-list\">"
@ -3924,7 +3989,7 @@ void OvmsWebServer::HandleEditor(PageEntry_t& p, PageContext_t& c)
"text-align: center !important;\n"
"}\n"
"}\n"
".log { font-size: 87%; color: gray; }\n"
".log { font-size: 87%%; color: gray; }\n"
".log.log-I { color: green; }\n"
".log.log-W { color: darkorange; }\n"
".log.log-E { color: red; }\n"

View file

@ -846,7 +846,7 @@ std::string OvmsWebServer::CfgInit3(PageEntry_t& p, PageContext_t& c, std::strin
c.input_radio_option("server", "Europe, Germany (ovms-ota.bit-cloud.de)",
"https://ovms-ota.bit-cloud.de" , server == "https://ovms-ota.bit-cloud.de");
c.input_radio_option("server", "Asia-Pacific (openvehicles.com)",
"https://api.openvehicles.com/firmware/ota" , server == "https://api.openvehicles.com/firmware/ota");
"https://ovms-ota.bit-cloud.de" , server == "https://ovms-ota.bit-cloud.de");
c.input_radio_option("server", "Europe (dexters-web.de)",
"https://ovms.dexters-web.de/firmware/ota", server == "https://ovms.dexters-web.de/firmware/ota");
c.input_radio_end();
@ -885,7 +885,7 @@ std::string OvmsWebServer::CfgInit3(PageEntry_t& p, PageContext_t& c, std::strin
std::string OvmsWebServer::CfgInit4(PageEntry_t& p, PageContext_t& c, std::string step)
{
std::string error, info;
std::string vehicletype, units_distance;
std::string vehicletype, units_distance, units_temp, units_pressure;
std::string server, vehicleid, password;
if (c.method == "POST") {
@ -905,6 +905,8 @@ std::string OvmsWebServer::CfgInit4(PageEntry_t& p, PageContext_t& c, std::strin
// process form input:
vehicletype = c.getvar("vehicletype");
units_distance = c.getvar("units_distance");
units_temp = c.getvar("units_temp");
units_pressure = c.getvar("units_pressure");
server = c.getvar("server");
vehicleid = c.getvar("vehicleid");
password = c.getvar("password");
@ -917,7 +919,22 @@ std::string OvmsWebServer::CfgInit4(PageEntry_t& p, PageContext_t& c, std::strin
error += "<li data-input=\"vehicleid\">Vehicle ID may only contain ASCII letters, digits and '-'</li>";
// configure vehicle:
MyConfig.SetParamValue("vehicle", "units.distance", units_distance);
OvmsMetricSetUserConfig(GrpDistance, units_distance);
if (units_distance == "miles") {
OvmsMetricSetUserConfig(GrpDistanceShort, "feet");
OvmsMetricSetUserConfig(GrpSpeed, "miph");
OvmsMetricSetUserConfig(GrpAccel, "miphps");
OvmsMetricSetUserConfig(GrpAccelShort, "ftpss");
OvmsMetricSetUserConfig(GrpConsumption, "mipkwh");
} else {
// Set to their defaults.
OvmsMetricSetUserConfig(GrpDistanceShort, "");
OvmsMetricSetUserConfig(GrpSpeed, "");
OvmsMetricSetUserConfig(GrpAccel, "");
OvmsMetricSetUserConfig(GrpAccelShort, "");
OvmsMetricSetUserConfig(GrpConsumption, "");
}
MyConfig.SetParamValue("auto", "vehicle.type", vehicletype);
// configure server:
@ -945,7 +962,10 @@ std::string OvmsWebServer::CfgInit4(PageEntry_t& p, PageContext_t& c, std::strin
// read configuration:
vehicleid = MyConfig.GetParamValue("vehicle", "id");
vehicletype = MyConfig.GetParamValue("auto", "vehicle.type");
units_distance = MyConfig.GetParamValue("vehicle", "units.distance", "K");
units_distance = OvmsMetricGetUserConfig(GrpDistance);
units_temp = OvmsMetricGetUserConfig(GrpTemp);
units_pressure = OvmsMetricGetUserConfig(GrpPressure);
server = MyConfig.GetParamValue("server.v2", "server");
password = MyConfig.GetParamValue("password","server.v2");
@ -1029,9 +1049,22 @@ std::string OvmsWebServer::CfgInit4(PageEntry_t& p, PageContext_t& c, std::strin
c.input_select_option(k->second.name, k->first, (vehicletype == k->first));
c.input_select_end();
c.input_radiobtn_start("Distance units", "units_distance");
c.input_radiobtn_option("units_distance", "Kilometers", "K", units_distance == "K");
c.input_radiobtn_option("units_distance", "Miles", "M", units_distance == "M");
bool is_metric = units_distance != "miles";
c.input_radiobtn_start("Distance related units", "units_distance");
c.input_radiobtn_option("units_distance", "Metric (km & metres)", "", is_metric);
c.input_radiobtn_option("units_distance", "Imperial (miles & feet)", "miles", !is_metric);
c.input_radiobtn_end();
is_metric = units_temp != "fahrenheit";
c.input_radiobtn_start("Temperature units", "units_temp");
c.input_radiobtn_option("units_temp", "Metric (°C)", "", is_metric);
c.input_radiobtn_option("units_temp", "Imperial (°F)", "fahrenheit", !is_metric);
c.input_radiobtn_end();
is_metric = units_pressure != "psi";
c.input_radiobtn_start("Pressure units", "units_pressure");
c.input_radiobtn_option("units_pressure", "Metric (kPa)", "", is_metric);
c.input_radiobtn_option("units_pressure", "Imperial (PSI)", "psi", !is_metric);
c.input_radiobtn_end();
c.input_radio_start("OVMS data server", "server");

View file

@ -305,7 +305,7 @@ void OvmsWebServer::HandleDashboard(PageEntry_t& p, PageContext_t& c)
"</div>"
"<div class=\"underlay\">"
"<div class=\"voltage-value\"><span class=\"value\">0</span></div>"
"<div class=\"soc-value\"><span class=\"value\">0</span></div>"
"<div class=\"soc-value\"><span class=\"value\">0</span><span class=\"unit\">%</span></div>"
"<div class=\"consumption-value\"><span class=\"value\">0</span></div>"
"<div class=\"power-value\"><span class=\"value\">0</span></div>"
"</div>"
@ -320,29 +320,29 @@ void OvmsWebServer::HandleDashboard(PageEntry_t& p, PageContext_t& c)
"var gaugeset1;"
""
"function get_dashboard_data() {"
"var rmin = metrics[\"v.b.range.est\"]||0, rmax = metrics[\"v.b.range.ideal\"]||0;\n"
"var euse = metrics[\"v.b.energy.used\"]||0, erec = metrics[\"v.b.energy.recd\"]||0;\n"
"var voltage = metrics[\"v.b.voltage\"]||0, soc = metrics[\"v.b.soc\"]||0;\n"
"var consumption = metrics[\"v.b.consumption\"]||0, power = metrics[\"v.b.power\"]||0;\n"
"var rmin = metrics_user[\"v.b.range.est\"]||0, rmax = metrics_user[\"v.b.range.ideal\"]||0;\n"
"var euse = metrics_user[\"v.b.energy.used\"]||0, erec = metrics_user[\"v.b.energy.recd\"]||0;\n"
"var voltage = metrics[\"v.b.voltage\"]||0, soc = metrics_user[\"v.b.soc\"]||0;\n"
"var consumption = metrics_user[\"v.b.consumption\"]||0, power = metrics_user[\"v.b.power\"]||0;\n"
"euse = Math.floor(euse*10)/10; erec = Math.floor(erec*10)/10;"
"if (rmin > rmax) { var x = rmin; rmin = rmax; rmax = x; }"
"var md = {"
"range: { value: \"\" + rmin.toFixed(0) + \"\" + rmax.toFixed(0) },"
"energy: { value: \"\" + euse.toFixed(1) + \"\" + erec.toFixed(1) },"
"range: { value: \"\" + rmin.toFixed(0) + \"\" + rmax.toFixed(0), unit: metrics_label[\"v.b.range.est\"] },"
"energy: { value: \"\" + euse.toFixed(1) + \"\" + erec.toFixed(1), unit: metrics_label[\"v.b.energy.used\"] },"
"voltage: { value: voltage.toFixed(0) },"
"soc: { value: soc.toFixed(0) },"
"consumption: { value: consumption.toFixed(0) },"
"power: { value: power.toFixed(0) },"
"soc: { value: soc.toFixed(0), unit: metrics_label[\"v.b.soc\"] },"
"consumption: { value: consumption.toFixed(0), unit: metrics_label[\"v.b.consumption\"] },"
"power: { value: power.toFixed(0), unit: metrics_label[\"v.b.power\"] },"
"series: ["
"{ data: [metrics[\"v.p.speed\"]] },"
"{ data: [metrics_user[\"v.p.speed\"]] },"
"{ data: [metrics[\"v.b.voltage\"]] },"
"{ data: [metrics[\"v.b.soc\"]] },"
"{ data: [metrics[\"v.b.consumption\"]] },"
"{ data: [metrics[\"v.b.power\"]] },"
"{ data: [metrics[\"v.c.temp\"]] },"
"{ data: [metrics[\"v.b.temp\"]] },"
"{ data: [metrics[\"v.i.temp\"]] },"
"{ data: [metrics[\"v.m.temp\"]] }],"
"{ data: [metrics_user[\"v.b.soc\"]] },"
"{ data: [metrics_user[\"v.b.consumption\"]] },"
"{ data: [metrics_user[\"v.b.power\"]] },"
"{ data: [metrics_user[\"v.c.temp\"]] },"
"{ data: [metrics_user[\"v.b.temp\"]] },"
"{ data: [metrics_user[\"v.i.temp\"]] },"
"{ data: [metrics_user[\"v.m.temp\"]] }],"
"};"
"return md;"
"}"
@ -350,9 +350,12 @@ void OvmsWebServer::HandleDashboard(PageEntry_t& p, PageContext_t& c)
"function update_dashboard() {"
"var md = get_dashboard_data();"
"$('.range-value .value').text(md.range.value);"
"$('.range-value .unit').text(md.range.unit);"
"$('.energy-value .value').text(md.energy.value);"
"$('.energy-value .unit').text(md.energy.unit);"
"$('.voltage-value .value').text(md.voltage.value);"
"$('.soc-value .value').text(md.soc.value);"
"$('.soc-value .unit').text(md.soc.unit);"
"$('.consumption-value .value').text(md.consumption.value);"
"$('.power-value .value').text(md.power.value);"
"gaugeset1.update({ series: md.series });"
@ -534,7 +537,7 @@ void OvmsWebServer::HandleDashboard(PageEntry_t& p, PageContext_t& c)
""
"/* Inject vehicle config: */"
"for (var i = 0; i < chart_config.yAxis.length; i++) {"
"$.extend(chart_config.yAxis[i], vehicle_config.yAxis[i]);"
"$.extend(true, chart_config.yAxis[i], vehicle_config.yAxis[i]);"
"}"
""
"gaugeset1 = Highcharts.chart('gaugeset1', chart_config,"
@ -1010,7 +1013,7 @@ void OvmsWebServer::HandleBmsCellMonitor(PageEntry_t& p, PageContext_t& c)
"});\n"
"}\n"
"},\n"
"zoomType: 'y',\n"
"zoomType: 'xy',\n"
"panning: true,\n"
"panKey: 'ctrl',\n"
"},\n"
@ -1088,20 +1091,20 @@ void OvmsWebServer::HandleBmsCellMonitor(PageEntry_t& p, PageContext_t& c)
"// get_temp_data: build boxplot dataset from metrics\n"
"function get_temp_data() {\n"
"var data = { cells: [], temps: [], devmax: [], tempmean: 0, sdlo: 0, sdhi: 0, sdmaxlo: 0, sdmaxhi: 0 };\n"
"var cnt = metrics[\"v.b.c.temp\"] ? metrics[\"v.b.c.temp\"].length : 0;\n"
"var cnt = metrics_user[\"v.b.c.temp\"] ? metrics_user[\"v.b.c.temp\"].length : 0;\n"
"if (cnt == 0)\n"
"return data;\n"
"var i, act, min, max, devmax, dalert, dlow, dhigh;\n"
"data.tempmean = metrics[\"v.b.p.temp.avg\"] || 0;\n"
"data.sdlo = data.tempmean - (metrics[\"v.b.p.temp.stddev\"] || 0);\n"
"data.sdhi = data.tempmean + (metrics[\"v.b.p.temp.stddev\"] || 0);\n"
"data.sdmaxlo = data.tempmean - (metrics[\"v.b.p.temp.stddev.max\"] || 0);\n"
"data.sdmaxhi = data.tempmean + (metrics[\"v.b.p.temp.stddev.max\"] || 0);\n"
"data.tempmean = metrics_user[\"v.b.p.temp.avg\"] || 0;\n"
"data.sdlo = data.tempmean - (metrics_user[\"v.b.p.temp.stddev\"] || 0);\n"
"data.sdhi = data.tempmean + (metrics_user[\"v.b.p.temp.stddev\"] || 0);\n"
"data.sdmaxlo = data.tempmean - (metrics_user[\"v.b.p.temp.stddev.max\"] || 0);\n"
"data.sdmaxhi = data.tempmean + (metrics_user[\"v.b.p.temp.stddev.max\"] || 0);\n"
"for (i=0; i<cnt; i++) {\n"
"act = metrics[\"v.b.c.temp\"][i];\n"
"min = metrics[\"v.b.c.temp.min\"][i] || act;\n"
"max = metrics[\"v.b.c.temp.max\"][i] || act;\n"
"devmax = metrics[\"v.b.c.temp.dev.max\"][i] || 0;\n"
"act = metrics_user[\"v.b.c.temp\"][i];\n"
"min = metrics_user[\"v.b.c.temp.min\"][i] || act;\n"
"max = metrics_user[\"v.b.c.temp.max\"][i] || act;\n"
"devmax = metrics_user[\"v.b.c.temp.dev.max\"][i] || 0;\n"
"dalert = metrics[\"v.b.c.temp.alert\"][i] || 0;\n"
"if (devmax > 0) {\n"
"dlow = data.tempmean;\n"
@ -1150,7 +1153,7 @@ void OvmsWebServer::HandleBmsCellMonitor(PageEntry_t& p, PageContext_t& c)
"});\n"
"}\n"
"},\n"
"zoomType: 'y',\n"
"zoomType: 'xy',\n"
"panning: true,\n"
"panKey: 'ctrl',\n"
"},\n"

View file

@ -49,22 +49,32 @@
* & &amp;
*/
std::string PageContext::encode_html(const char* text) {
std::string buf;
for (int i=0; i<strlen(text); i++) {
if (text[i] == '\"')
buf += "&quot;";
else if (text[i] == '\'')
buf += "&#x27;";
else if(text[i] == '<')
buf += "&lt;";
else if(text[i] == '>')
buf += "&gt;";
else if(text[i] == '&')
buf += "&amp;";
else
buf += text[i];
int len = strlen(text);
std::string buf;
buf.reserve(len);
for (int i=0; i < len; i++) {
char ch = text[i];
switch(ch) {
case '\"':
buf.append("&quot;");
break;
case '\'':
buf.append("&#x27;");
break;
case '<':
buf.append("&lt;");
break;
case '>':
buf.append("&gt;");
break;
case '&':
buf.append("&amp;");
break;
default:
buf.append(&ch,1);
}
}
return buf;
return buf;
}
std::string PageContext::encode_html(std::string text) {
@ -72,23 +82,31 @@ std::string PageContext::encode_html(std::string text) {
}
extram::string PageContext::encode_html(const extram::string& text) {
extram::string buf;
extram::string buf;
buf.reserve(text.length() + 500);
for (int i=0; i<text.length(); i++) {
if (text[i] == '\"')
buf += "&quot;";
else if (text[i] == '\'')
buf += "&#x27;";
else if(text[i] == '<')
buf += "&lt;";
else if(text[i] == '>')
buf += "&gt;";
else if(text[i] == '&')
buf += "&amp;";
else
buf += text[i];
for (int i=0; i<text.length(); i++) {
char ch = text[i];
switch (ch) {
case '\"':
buf.append("&quot;");
break;
case '\'':
buf.append("&#x27;");
break;
case '<':
buf.append("&lt;");
break;
case '>':
buf.append("&gt;");
break;
case '&':
buf.append("&amp;");
break;
default:
buf.append(&ch,1);
}
}
return buf;
return buf;
}
#define _attr(text) (encode_html(text).c_str())
@ -102,11 +120,12 @@ extram::string PageContext::encode_html(const extram::string& text) {
*
*/
std::string PageContext::make_id(const char* text) {
std::string buf;
std::string buf;
char lc = 0;
for (int i=0; i<strlen(text); i++) {
if (isalnum(text[i]))
buf += (lc = tolower(text[i]));
int len = strlen(text);
for (int i=0; i<len; i++) {
if (isalnum(text[i]))
buf += (lc = tolower(text[i]));
else if (lc && lc != '-')
buf += (lc = '-');
}
@ -114,7 +133,7 @@ std::string PageContext::make_id(const char* text) {
lc = buf.back();
buf.pop_back();
}
return buf;
return buf;
}
std::string PageContext::make_id(std::string text) {
@ -361,7 +380,7 @@ void PageContext::input_slider(const char* label, const char* name, int size, co
"<label class=\"control-label col-sm-3\" for=\"input-%s\">%s:</label>"
"<div class=\"col-sm-9\">"
"<div class=\"form-control slider\" data-default=\"%g\" data-reset=\"false\""
" data-value=\"%g\" data-min=\"%g\" data-max=\"%g\" data-step=\"%g\">"
" data-value=\"%g\" data-min=\"%g\" data-max=\"%g\" data-step=\"%g\" data-checked=\"%s\">"
"<div class=\"slider-control form-inline\">"
"<input class=\"slider-enable\" type=\"%s\" %s> "
"<input class=\"form-control slider-value\" %s type=\"number\" style=\"width:%dpx;\""
@ -380,6 +399,7 @@ void PageContext::input_slider(const char* label, const char* name, int size, co
, _attr(name)
, label
, defval, value, min, max, step
, (enabled != 0) ? "true" : "false"
, (enabled < 0) ? "hidden" : "checkbox" // -1 => no checkbox
, (enabled > 0) ? "checked" : ""
, (enabled == 0) ? "disabled" : ""
@ -714,7 +734,8 @@ void OvmsWebServer::OutputReboot(PageEntry_t& p, PageContext_t& c)
/**
* OutputReconnect: output reconnect script
*/
void OvmsWebServer::OutputReconnect(PageEntry_t& p, PageContext_t& c, const char* info /*=NULL*/)
void OvmsWebServer::OutputReconnect(PageEntry_t& p, PageContext_t& c, const char* info /*=NULL*/,
const char* cmd /*=NULL*/)
{
c.printf(
"<div class=\"alert alert-warning\">"
@ -733,8 +754,16 @@ void OvmsWebServer::OutputReconnect(PageEntry_t& p, PageContext_t& c, const char
"$(\"#dots\").append(\"\");"
"}"
"}, 1000);"
"</script>"
, info ? info : "Reconnecting…");
if (cmd) {
c.printf(
"loadcmd(\"%s\");"
, __attr(cmd));
}
c.print(
"</script>");
}

View file

@ -76,7 +76,7 @@ void pushover_send_message(int verbosity, OvmsWriter* writer, OvmsCommand* cmd,
sound = "pushover";
}
writer->printf("Sending PUSHOVER message \"%s\" with priority %d and sound %s\n",argv[0],priority,sound);
writer->printf("Sending PUSHOVER message \"%s\" with priority %d and sound %s\n",argv[0],priority,sound.c_str());
MyPushoverClient.SendMessage(argv[0], priority, sound);
}

View file

@ -6,4 +6,4 @@ PID SCAN
Vehicle Type: **PID**
This is a research module that allows developers to scan an ECU for PIDs that respond
with a valid reply to an extended OBDII query. It is not made to me functional.
with a valid reply to an extended OBDII query. It is not made to be functional.

View file

@ -199,7 +199,7 @@ void scanStart(int, OvmsWriter* writer, OvmsCommand*, int argc, const char* cons
if (start > end)
{
writer->printf(
"Error: Invalid Start PID %04x is after End PID %04x\n", start, end
"Error: Invalid Start PID %04lx is after End PID %04lx\n", start, end
);
valid = false;
}
@ -209,7 +209,7 @@ void scanStart(int, OvmsWriter* writer, OvmsCommand*, int argc, const char* cons
}
if (POLL_TYPE_HAS_8BIT_PID(polltype) && end > 0xff)
{
writer->printf("Error: Poll type %x PID range is 00..ff\n");
writer->printf("Error: Poll type %lx PID range is 00..ff\n", polltype);
valid = false;
}
if (!valid)
@ -229,7 +229,7 @@ void scanStart(int, OvmsWriter* writer, OvmsCommand*, int argc, const char* cons
if (valid)
{
s_scanner = new OvmsReToolsPidScanner(can, ecu, rxid_low, rxid_high, polltype, start, end, step, timeout);
writer->printf("Scan started: bus %d, ecu %x, rxid %x-%x, polltype %x, PID %x-%x (step %x), timeout %d seconds\n",
writer->printf("Scan started: bus %ld, ecu %lx, rxid %lx-%lx, polltype %lx, PID %lx-%lx (step %lx), timeout %d seconds\n",
bus, ecu, rxid_low, rxid_high, polltype, start, end, step, timeout);
}
}

View file

@ -97,13 +97,13 @@ static void IRAM_ATTR sdcard_isr_handler(void* arg)
sdcard::sdcard(const char* name, bool mode1bit, bool autoformat, int cdpin)
: pcp(name)
{
m_host = SDMMC_HOST_DEFAULT();
m_host = sdmmc_host_t SDMMC_HOST_DEFAULT();
if (mode1bit)
{
m_host.flags = SDMMC_HOST_FLAG_1BIT;
}
m_slot = SDMMC_SLOT_CONFIG_DEFAULT();
m_slot = sdmmc_slot_config_t SDMMC_SLOT_CONFIG_DEFAULT();
// Disable driver-level CD pin, as we do this ourselves
// if (cdpin)
// {

View file

@ -69,17 +69,43 @@ const char* simcom5360::GetName()
void simcom5360::StatusPoller()
{
if (m_modem->m_mux != NULL)
{ m_modem->muxtx(GetMuxChannelPOLL(), "AT+CREG?;+CCLK?;+CSQ;+CPSI?;+COPS?\r\n"); }
{
// The ESP32 UART queue has a capacity of 128 bytes, NMEA and PPP data may be
// coming in concurrently, and we cannot use flow control.
// Reduce the queue stress by distributing the status poll:
switch (++m_statuspoller_step)
{
case 1:
m_modem->muxtx(GetMuxChannelPOLL(), "AT+CREG?;+CCLK?;+CSQ\r\n");
// → ~ 55 bytes, e.g.
// +CREG: 1,5 +CCLK: "22/10/02,09:33:27+08" +CSQ: 24,99
break;
case 2:
m_modem->muxtx(GetMuxChannelPOLL(), "AT+CPSI?\r\n");
// → ~ 60 bytes, e.g.
// +CPSI: GSM,Online,262-02,0x011f,14822,4 EGSM 900,-67,0,40-40
break;
case 3:
m_modem->muxtx(GetMuxChannelPOLL(), "AT+COPS?\r\n");
// → ~ 35 bytes, e.g.
// +COPS: 0,0,"vodafone.de Hologram",0
// done, fallthrough:
default:
m_statuspoller_step = 0;
break;
}
}
}
bool simcom5360::State1Leave(modem::modem_state1_t oldstate)
{
return false;
return modemdriver::State1Leave(oldstate);
}
bool simcom5360::State1Enter(modem::modem_state1_t newstate)
{
return false;
return modemdriver::State1Enter(newstate);
}
modem::modem_state1_t simcom5360::State1Activity(modem::modem_state1_t curstate)
@ -93,7 +119,7 @@ modem::modem_state1_t simcom5360::State1Activity(modem::modem_state1_t curstate)
return modem::None;
}
return curstate;
return modemdriver::State1Activity(curstate);
}
modem::modem_state1_t simcom5360::State1Ticker1(modem::modem_state1_t curstate)
@ -125,11 +151,13 @@ modem::modem_state1_t simcom5360::State1Ticker1(modem::modem_state1_t curstate)
m_modem->tx("AT+CMUXSRVPORT=0,5\r\n");
break;
case 20:
m_modem->tx("AT+CMUX=0\r\n");
// start MUX mode, route URCs to MUX channel 3 (POLL)
// Note: NMEA URCs will still be sent only on channel 1 (NMEA) by the SIMCOM 5360
m_modem->tx("AT+CMUX=0;+CATR=6\r\n");
break;
}
return modem::None;
}
return curstate;
return modemdriver::State1Ticker1(curstate);
}

View file

@ -73,6 +73,7 @@ void simcom7000::PowerCycle()
m_powercyclefactor = m_powercyclefactor % 3;
ESP_LOGI(TAG, "Power Cycle (SIM7000) %dms",psd);
uart_wait_tx_done(m_modem->m_uartnum, portMAX_DELAY);
uart_flush(m_modem->m_uartnum); // Flush the ring buffer, to try to address MUX start issues
#ifdef CONFIG_OVMS_COMP_MAX7317
MyPeripherals->m_max7317->Output(MODEM_EGPIO_PWR, 0); // Modem EN/PWR line low
@ -125,7 +126,8 @@ modem::modem_state1_t simcom7000::State1Ticker1(modem::modem_state1_t curstate)
m_modem->tx("AT+CGMR;+ICCID\r\n");
break;
case 20:
m_modem->tx("AT+CMUX=0\r\n");
// start MUX mode, route URCs to MUX channel 3 (POLL)
m_modem->tx("AT+CMUX=0;+CATR=6\r\n");
break;
}
return modem::None;

View file

@ -80,6 +80,7 @@ void simcom7600::StartupNMEA()
{
m_modem->muxtx(GetMuxChannelCMD(), "AT+CGPS=0\r\n");
vTaskDelay(2000 / portTICK_PERIOD_MS);
// send single commands, as each can fail:
m_modem->muxtx(GetMuxChannelCMD(), "AT+CGPSNMEA=258\r\n");
m_modem->muxtx(GetMuxChannelCMD(), "AT+CGPSINFOCFG=5,258\r\n");
m_modem->muxtx(GetMuxChannelCMD(), "AT+CGPS=1,1\r\n");
@ -88,10 +89,51 @@ void simcom7600::StartupNMEA()
{ ESP_LOGE(TAG, "Attempt to transmit on non running mux"); }
}
void simcom7600::ShutdownNMEA()
{
// Switch off GPS:
if (m_modem->m_mux != NULL)
{
// send single commands, as each can fail:
m_modem->muxtx(GetMuxChannelCMD(), "AT+CGPSNMEA=0\r\n");
m_modem->muxtx(GetMuxChannelCMD(), "AT+CGPSINFOCFG=0\r\n");
vTaskDelay(pdMS_TO_TICKS(100));
m_modem->muxtx(GetMuxChannelCMD(), "AT+CGPS=0\r\n");
}
else
{ ESP_LOGE(TAG, "Attempt to transmit on non running mux"); }
}
void simcom7600::StatusPoller()
{
if (m_modem->m_mux != NULL)
{ m_modem->muxtx(GetMuxChannelPOLL(), "AT+CREG?;+CCLK?;+CSQ;+CPSI?;+COPS?\r\n"); }
{
// The ESP32 UART queue has a capacity of 128 bytes, NMEA and PPP data may be
// coming in concurrently, and we cannot use flow control.
// Reduce the queue stress by distributing the status poll:
switch (++m_statuspoller_step)
{
case 1:
m_modem->muxtx(GetMuxChannelPOLL(), "AT+CREG?;+CGREG?;+CEREG?;+CCLK?;+CSQ\r\n");
// → ~ 80 bytes, e.g.
// +CREG: 1,5 +CGREG: 1,5 +CEREG: 1,5 +CCLK: "22/09/09,12:54:41+08" +CSQ: 13,99
break;
case 2:
m_modem->muxtx(GetMuxChannelPOLL(), "AT+CPSI?\r\n");
// → ~ 85 bytes, e.g.
// +CPSI: LTE,Online,262-02,0xB0F5,13179412,448,EUTRAN-BAND1,100,4,4,-122,-1184,-874,9
break;
case 3:
m_modem->muxtx(GetMuxChannelPOLL(), "AT+COPS?\r\n");
// → ~ 35 bytes, e.g.
// +COPS: 0,0,"vodafone.de Hologram",7
// done, fallthrough:
default:
m_statuspoller_step = 0;
break;
}
}
}
void simcom7600::PowerCycle()
@ -100,6 +142,7 @@ void simcom7600::PowerCycle()
m_powercyclefactor = m_powercyclefactor % 3;
ESP_LOGI(TAG, "Power Cycle (SIM7600) %dms",psd);
uart_wait_tx_done(m_modem->m_uartnum, portMAX_DELAY);
uart_flush(m_modem->m_uartnum); // Flush the ring buffer, to try to address MUX start issues
#ifdef CONFIG_OVMS_COMP_MAX7317
MyPeripherals->m_max7317->Output(MODEM_EGPIO_PWR, 0); // Modem EN/PWR line low
@ -111,12 +154,12 @@ void simcom7600::PowerCycle()
bool simcom7600::State1Leave(modem::modem_state1_t oldstate)
{
return false;
return modemdriver::State1Leave(oldstate);
}
bool simcom7600::State1Enter(modem::modem_state1_t newstate)
{
return false;
return modemdriver::State1Enter(newstate);
}
modem::modem_state1_t simcom7600::State1Activity(modem::modem_state1_t curstate)
@ -130,7 +173,7 @@ modem::modem_state1_t simcom7600::State1Activity(modem::modem_state1_t curstate)
return modem::None;
}
return curstate;
return modemdriver::State1Activity(curstate);
}
modem::modem_state1_t simcom7600::State1Ticker1(modem::modem_state1_t curstate)
@ -144,17 +187,21 @@ modem::modem_state1_t simcom7600::State1Ticker1(modem::modem_state1_t curstate)
switch (m_modem->m_state1_ticker)
{
case 10:
m_modem->tx("AT+CPIN?;+CREG=1;+CTZU=1;+CTZR=1;+CLIP=1;+CMGF=1;+CNMI=1,2,0,0,0;+CSDH=1;+CMEE=2;+CSQ;+AUTOCSQ=1,1;E0;S0=0\r\n");
m_modem->tx("AT+CPIN?;+CREG=1;+CGREG=1;+CEREG=1;+CTZU=1;+CTZR=1;+CLIP=1;+CMGF=1;+CNMI=1,2,0,0,0;+CSDH=1;+CMEE=2;+CSQ;+AUTOCSQ=1,1;E0;S0=0\r\n");
break;
case 12:
m_modem->tx("AT+CGMR;+ICCID\r\n");
break;
case 20:
m_modem->tx("AT+CMUX=0\r\n");
// start MUX mode, route URCs to MUX channel 3 (POLL)
// Note: NMEA URCs will now also be sent on channel 3 by the SIMCOM 7600;
// without +CATR, NMEA URCs are sent identically on all channels;
// there is no option to route these separately to channel 1 (NMEA)
m_modem->tx("AT+CMUX=0;+CATR=6\r\n");
break;
}
return modem::None;
}
return curstate;
return modemdriver::State1Ticker1(curstate);
}

View file

@ -50,6 +50,7 @@ class simcom7600 : public modemdriver
int GetMuxChannelPOLL() { return 3; }
int GetMuxChannelCMD() { return 4; }
void StartupNMEA();
void ShutdownNMEA();
void StatusPoller();
void PowerCycle();

View file

@ -7,6 +7,8 @@
# please read the ESP-IDF documents if you need to do this.
#
ifeq ($(shell expr $(IDF_VERSION_MAJOR) \< 4), 1)
COMPONENT_ADD_INCLUDEDIRS:=src
COMPONENT_SRCDIRS:=src
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
endif

View file

@ -90,6 +90,8 @@ OvmsVehicleFactory::OvmsVehicleFactory()
OvmsCommand* cmd_bms = MyCommandApp.RegisterCommand("bms","BMS framework", bms_status, "", 0, 0, false);
cmd_bms->RegisterCommand("status","Show BMS status",bms_status);
cmd_bms->RegisterCommand("temp","Show BMS temperature status",bms_status);
cmd_bms->RegisterCommand("volt","Show BMS voltage status",bms_status);
cmd_bms->RegisterCommand("reset","Reset BMS statistics",bms_reset);
cmd_bms->RegisterCommand("alerts","Show BMS alerts",bms_alerts);
@ -356,6 +358,8 @@ OvmsVehicle::OvmsVehicle()
m_brakelight_ignftbrk = false;
m_tpms_lastcheck = 0;
m_inv_energyused = 0;
m_inv_energyrecd = 0;
m_rxqueue = xQueueCreate(CONFIG_OVMS_VEHICLE_CAN_RX_QUEUE_SIZE,sizeof(CAN_frame_t));
xTaskCreatePinnedToCore(OvmsVehicleRxTask, "OVMS Vehicle",
@ -977,7 +981,6 @@ bool OvmsVehicle::TPMSWrite(std::vector<uint32_t> &tpms)
*/
OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStat(int verbosity, OvmsWriter* writer)
{
metric_unit_t rangeUnit = (MyConfig.GetParamValue("vehicle", "units.distance") == "M") ? Miles : Kilometers;
bool chargeport_open = StdMetrics.ms_v_door_chargeport->AsBool();
std::string charge_state = StdMetrics.ms_v_charge_state->AsString();
@ -1028,10 +1031,9 @@ OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStat(int verbosity, OvmsWrite
}
// Charge speed:
if (StdMetrics.ms_v_bat_range_speed->AsFloat() != 0)
if (StdMetrics.ms_v_bat_range_speed->IsDefined() && StdMetrics.ms_v_bat_range_speed->AsFloat() != 0)
{
metric_unit_t speedUnit = (rangeUnit == Miles) ? Mph : Kph;
writer->printf("%s\n", StdMetrics.ms_v_bat_range_speed->AsUnitString("-", speedUnit, 1).c_str());
writer->printf("%s\n", StdMetrics.ms_v_bat_range_speed->AsUnitString("-", ToUser, 1).c_str());
}
else if (show_vc)
{
@ -1046,13 +1048,13 @@ OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStat(int verbosity, OvmsWrite
int duration_soc = StdMetrics.ms_v_charge_duration_soc->AsInt();
if (duration_soc > 0)
writer->printf("%s: %d:%02dh\n",
(char*) StdMetrics.ms_v_charge_limit_soc->AsUnitString("SOC", Native, 0).c_str(),
(char*) StdMetrics.ms_v_charge_limit_soc->AsUnitString("SOC", ToUser, 0).c_str(),
duration_soc / 60, duration_soc % 60);
int duration_range = StdMetrics.ms_v_charge_duration_range->AsInt();
if (duration_range > 0)
writer->printf("%s: %d:%02dh\n",
(char*) StdMetrics.ms_v_charge_limit_range->AsUnitString("Range", rangeUnit, 0).c_str(),
(char*) StdMetrics.ms_v_charge_limit_range->AsUnitString("Range", ToUser, 0).c_str(),
duration_range / 60, duration_range % 60);
}
@ -1060,12 +1062,12 @@ OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStat(int verbosity, OvmsWrite
if (StdMetrics.ms_v_charge_kwh_grid->IsDefined())
{
writer->printf("Drawn: %s\n",
StdMetrics.ms_v_charge_kwh_grid->AsUnitString("-", Native, 1).c_str());
StdMetrics.ms_v_charge_kwh_grid->AsUnitString("-", ToUser, 1).c_str());
}
if (StdMetrics.ms_v_charge_kwh->IsDefined())
{
writer->printf("Charged: %s\n",
StdMetrics.ms_v_charge_kwh->AsUnitString("-", Native, 1).c_str());
StdMetrics.ms_v_charge_kwh->AsUnitString("-", ToUser, 1).c_str());
}
}
else
@ -1073,27 +1075,37 @@ OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStat(int verbosity, OvmsWrite
writer->puts("Not charging");
}
writer->printf("SOC: %s\n", (char*) StdMetrics.ms_v_bat_soc->AsUnitString("-", Native, 1).c_str());
writer->printf("SOC: %s\n", (char*) StdMetrics.ms_v_bat_soc->AsUnitString("-", ToUser, 1).c_str());
const char* range_ideal = StdMetrics.ms_v_bat_range_ideal->AsUnitString("-", rangeUnit, 0).c_str();
if (*range_ideal != '-')
writer->printf("Ideal range: %s\n", range_ideal);
if (StdMetrics.ms_v_bat_range_ideal->IsDefined())
{
const std::string& range_ideal = StdMetrics.ms_v_bat_range_ideal->AsUnitString("-", ToUser, 0);
writer->printf("Ideal range: %s\n", range_ideal.c_str());
}
const char* range_est = StdMetrics.ms_v_bat_range_est->AsUnitString("-", rangeUnit, 0).c_str();
if (*range_est != '-')
writer->printf("Est. range: %s\n", range_est);
if (StdMetrics.ms_v_bat_range_est->IsDefined())
{
const std::string& range_est = StdMetrics.ms_v_bat_range_est->AsUnitString("-", ToUser, 0);
writer->printf("Est. range: %s\n", range_est.c_str());
}
const char* odometer = StdMetrics.ms_v_pos_odometer->AsUnitString("-", rangeUnit, 1).c_str();
if (*odometer != '-')
writer->printf("ODO: %s\n", odometer);
if (StdMetrics.ms_v_pos_odometer->IsDefined())
{
const std::string& odometer = StdMetrics.ms_v_pos_odometer->AsUnitString("-", ToUser, 1);
writer->printf("ODO: %s\n", odometer.c_str());
}
const char* cac = StdMetrics.ms_v_bat_cac->AsUnitString("-", Native, 1).c_str();
if (*cac != '-')
writer->printf("CAC: %s\n", cac);
if (StdMetrics.ms_v_bat_cac->IsDefined())
{
const std::string& cac = StdMetrics.ms_v_bat_cac->AsUnitString("-", ToUser, 1);
writer->printf("CAC: %s\n", cac.c_str());
}
const char* soh = StdMetrics.ms_v_bat_soh->AsUnitString("-", Native, 0).c_str();
if (*soh != '-')
writer->printf("SOH: %s\n", soh);
if (StdMetrics.ms_v_bat_soh->IsDefined())
{
const std::string& soh = StdMetrics.ms_v_bat_soh->AsUnitString("-", ToUser, 0);
writer->printf("SOH: %s\n", soh.c_str());
}
return Success;
}
@ -1103,12 +1115,12 @@ OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStat(int verbosity, OvmsWrite
*/
OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStatTrip(int verbosity, OvmsWriter* writer)
{
metric_unit_t rangeUnit = (MyConfig.GetParamValue("vehicle", "units.distance") == "M") ? Miles : Kilometers;
metric_unit_t speedUnit = (rangeUnit == Miles) ? Mph : Kph;
metric_unit_t accelUnit = (rangeUnit == Miles) ? MphPS : KphPS;
metric_unit_t consumUnit = (rangeUnit == Miles) ? WattHoursPM : WattHoursPK;
metric_unit_t rangeUnit = OvmsMetricGetUserUnit(GrpDistance, Kilometers);
metric_unit_t speedUnit = OvmsMetricGetUserUnit(GrpSpeed, Kph);
metric_unit_t accelUnit = OvmsMetricGetUserUnit(GrpAccel, KphPS);
metric_unit_t consumUnit = OvmsMetricGetUserUnit(GrpConsumption, WattHoursPK);
metric_unit_t energyUnit = kWh;
metric_unit_t altitudeUnit = (rangeUnit == Miles) ? Feet : Meters;
metric_unit_t altitudeUnit = OvmsMetricGetUserUnit(GrpDistanceShort, Meters);
const char* rangeUnitLabel = OvmsMetricUnitLabel(rangeUnit);
const char* speedUnitLabel = OvmsMetricUnitLabel(speedUnit);
const char* accelUnitLabel = OvmsMetricUnitLabel(accelUnit);
@ -1116,7 +1128,7 @@ OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStatTrip(int verbosity, OvmsW
const char* energyUnitLabel = OvmsMetricUnitLabel(energyUnit);
const char* altitudeUnitLabel = OvmsMetricUnitLabel(altitudeUnit);
float trip_length = StdMetrics.ms_v_pos_trip->AsFloat(0, rangeUnit);
float trip_length = StdMetrics.ms_v_pos_trip->AsFloat(0);
float speed_avg = (m_drive_speedcnt > 0)
? UnitConvert(Kph, speedUnit, (float)(m_drive_speedsum / m_drive_speedcnt))
@ -1132,7 +1144,7 @@ OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStatTrip(int verbosity, OvmsW
float energy_used = StdMetrics.ms_v_bat_energy_used->AsFloat();
float energy_recd = StdMetrics.ms_v_bat_energy_recd->AsFloat();
float energy_recd_perc = (energy_used > 0) ? energy_recd / energy_used * 100 : 0;
float wh_per_rangeunit = (trip_length > 0) ? (energy_used - energy_recd) * 1000 / trip_length : 0;
float wh_per_km = (trip_length > 0) ? (energy_used - energy_recd) * 1000 / trip_length : 0;
float soc = StdMetrics.ms_v_bat_soc->AsFloat();
float soc_diff = soc - m_drive_startsoc;
@ -1146,7 +1158,7 @@ OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStatTrip(int verbosity, OvmsW
<< "Trip "
<< std::fixed
<< std::setprecision(1)
<< trip_length << rangeUnitLabel
<< UnitConvert(Kilometers, rangeUnit, trip_length) << rangeUnitLabel
<< " Avg "
<< std::setprecision(0)
<< speed_avg << speedUnitLabel
@ -1154,11 +1166,11 @@ OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStatTrip(int verbosity, OvmsW
<< ((alt_diff >= 0) ? "+" : "")
<< alt_diff << altitudeUnitLabel
;
if (wh_per_rangeunit != 0)
if (wh_per_km != 0)
{
buf
<< "\nEnergy "
<< wh_per_rangeunit << consumUnitLabel
<< UnitConvert(WattHoursPK, consumUnit, wh_per_km) << consumUnitLabel
<< ", "
<< energy_recd_perc << "% recd"
;
@ -1490,6 +1502,12 @@ void OvmsVehicle::MetricModified(OvmsMetric* metric)
NotifyChargeState();
}
}
else if (metric == StandardMetrics.ms_v_charge_type)
{
std::string m = metric->AsString();
MyEvents.SignalEvent("vehicle.charge.type", (void*)m.c_str(), m.size()+1);
NotifiedVehicleChargeType(m);
}
else if (metric == StandardMetrics.ms_v_gen_state)
{
std::string state = metric->AsString();
@ -1497,6 +1515,12 @@ void OvmsVehicle::MetricModified(OvmsMetric* metric)
if (m_autonotifications)
NotifyGenState();
}
else if (metric == StandardMetrics.ms_v_gen_type)
{
std::string m = metric->AsString();
MyEvents.SignalEvent("vehicle.gen.type", (void*)m.c_str(), m.size()+1);
NotifiedVehicleGenType(m);
}
else if (metric == StandardMetrics.ms_v_pos_speed)
{
// Collect data for trip speed average:
@ -1988,68 +2012,82 @@ OvmsVehicle::vehicle_command_t OvmsVehicle::ProcessMsgCommand(std::string &resul
*/
void OvmsVehicle::GetDashboardConfig(DashboardConfig& cfg)
{
cfg.gaugeset1 =
"yAxis: [{"
// Speed:
"min: 0, max: 200,"
"plotBands: ["
"{ from: 0, to: 120, className: 'green-band' },"
"{ from: 120, to: 160, className: 'yellow-band' },"
"{ from: 160, to: 200, className: 'red-band' }]"
"},{"
// Voltage:
"min: 310, max: 410,"
"plotBands: ["
"{ from: 310, to: 325, className: 'red-band' },"
"{ from: 325, to: 340, className: 'yellow-band' },"
"{ from: 340, to: 410, className: 'green-band' }]"
"},{"
// SOC:
"min: 0, max: 100,"
"plotBands: ["
"{ from: 0, to: 12.5, className: 'red-band' },"
"{ from: 12.5, to: 25, className: 'yellow-band' },"
"{ from: 25, to: 100, className: 'green-band' }]"
"},{"
// Efficiency:
"min: 0, max: 400,"
"plotBands: ["
"{ from: 0, to: 200, className: 'green-band' },"
"{ from: 200, to: 300, className: 'yellow-band' },"
"{ from: 300, to: 400, className: 'red-band' }]"
"},{"
// Power:
"min: -50, max: 200,"
"plotBands: ["
"{ from: -50, to: 0, className: 'violet-band' },"
"{ from: 0, to: 100, className: 'green-band' },"
"{ from: 100, to: 150, className: 'yellow-band' },"
"{ from: 150, to: 200, className: 'red-band' }]"
"},{"
// Charger temperature:
"min: 20, max: 80, tickInterval: 20,"
"plotBands: ["
"{ from: 20, to: 65, className: 'normal-band border' },"
"{ from: 65, to: 80, className: 'red-band border' }]"
"},{"
// Battery temperature:
"min: -15, max: 65, tickInterval: 25,"
"plotBands: ["
"{ from: -15, to: 0, className: 'red-band border' },"
"{ from: 0, to: 50, className: 'normal-band border' },"
"{ from: 50, to: 65, className: 'red-band border' }]"
"},{"
// Inverter temperature:
"min: 20, max: 80, tickInterval: 20,"
"plotBands: ["
"{ from: 20, to: 70, className: 'normal-band border' },"
"{ from: 70, to: 80, className: 'red-band border' }]"
"},{"
// Motor temperature:
"min: 50, max: 125, tickInterval: 25,"
"plotBands: ["
"{ from: 50, to: 110, className: 'normal-band border' },"
"{ from: 110, to: 125, className: 'red-band border' }]"
"}]";
// Speed:
dash_gauge_t speed_dash(NULL,Kph);
speed_dash.SetMinMax(0, 200, 5);
speed_dash.AddBand("green", 0, 120);
speed_dash.AddBand("yellow", 120, 160);
speed_dash.AddBand("red", 160, 200);
// Voltage:
dash_gauge_t voltage_dash(NULL,Volts);
voltage_dash.SetMinMax(310, 410);
voltage_dash.AddBand("red", 310, 325);
voltage_dash.AddBand("yellow", 325, 340);
voltage_dash.AddBand("green", 340, 410);
// SOC:
dash_gauge_t soc_dash("SOC ",Percentage);
soc_dash.SetMinMax(0, 100);
soc_dash.AddBand("red", 0, 12.5);
soc_dash.AddBand("yellow", 12.5, 25);
soc_dash.AddBand("green", 25, 100);
// Efficiency:
dash_gauge_t eff_dash(NULL,WattHoursPK);
eff_dash.SetMinMax(0, 400);
eff_dash.AddBand("green", 0, 200);
eff_dash.AddBand("yellow", 200, 300);
eff_dash.AddBand("red", 300, 400);
// Power:
dash_gauge_t power_dash(NULL,kW);
power_dash.SetMinMax(-50, 200);
power_dash.AddBand("violet", -50, 0);
power_dash.AddBand("green", 0, 100);
power_dash.AddBand("yellow", 100, 150);
power_dash.AddBand("red", 150, 200);
// Charger temperature:
dash_gauge_t charget_dash("CHG ",Celcius);
charget_dash.SetMinMax(20, 80);
charget_dash.SetTick(20);
charget_dash.AddBand("normal", 20, 65);
charget_dash.AddBand("red", 65, 80);
// Battery temperature:
dash_gauge_t batteryt_dash("BAT ",Celcius);
batteryt_dash.SetMinMax(-15, 65);
batteryt_dash.SetTick(25);
batteryt_dash.AddBand("red", -15, 0);
batteryt_dash.AddBand("normal", 0, 50);
batteryt_dash.AddBand("red", 50, 65);
// Inverter temperature:
dash_gauge_t invertert_dash("PEM ",Celcius);
invertert_dash.SetMinMax(20, 80);
invertert_dash.SetTick(20);
invertert_dash.AddBand("normal", 20, 70);
invertert_dash.AddBand("red", 70, 80);
// Motor temperature:
dash_gauge_t motort_dash("MOT ",Celcius);
motort_dash.SetMinMax(50, 125);
motort_dash.SetTick(25);
motort_dash.AddBand("normal", 50, 110);
motort_dash.AddBand("red", 110, 125);
std::ostringstream str;
str
<< batteryt_dash // Battery temperature
<< voltage_dash // Voltage
<< motort_dash // Motor temperature
<< invertert_dash // Inverter temperature
<< power_dash // Power
<< soc_dash // SOC
<< charget_dash // Charger temperature
<< speed_dash // Speed
<< eff_dash; // Efficiency
cfg.gaugeset1 = str.str();
}
#endif // #ifdef CONFIG_OVMS_COMP_WEBSERVER

View file

@ -155,6 +155,8 @@ struct DashboardConfig;
#define VEHICLE_POLL_TYPE_OBDIIGROUP 0x21 // Custom: Read data by 8 bit PID
#define VEHICLE_POLL_TYPE_OBDII_32 0x32 // Custom: VW routine control extension (8 bit PID)
#define VEHICLE_OBD_BROADCAST_MODULE_TX 0x7df
#define VEHICLE_OBD_BROADCAST_MODULE_RX 0x0
// A note on "PID" and their sizes here:
// By "PID" for the service types we mean the part of the request parameters
// after the service type that is reflected in _every_ valid response to the request.
@ -273,6 +275,14 @@ typedef struct
uint32_t lastused; // Timestamp of last channel access
} vwtp_channel_t;
enum class OvmsStatus : short {
OK = 0,
Warn = 1,
Alert = 2
};
inline bool operator<(OvmsStatus lhs, OvmsStatus rhs) {
return static_cast<short>(lhs) < static_cast<short>(rhs);
}
class OvmsVehicle : public InternalRamAllocated
{
@ -437,7 +447,9 @@ class OvmsVehicle : public InternalRamAllocated
virtual void NotifiedVehicleDrivemode(int drivemode) {}
virtual void NotifiedVehicleChargeMode(const char* m) {}
virtual void NotifiedVehicleChargeState(const char* s) {}
virtual void NotifiedVehicleChargeType(const std::string& state) {}
virtual void NotifiedVehicleGenState(const std::string& state) {}
virtual void NotifiedVehicleGenType(const std::string& state) {}
protected:
virtual void ConfigChanged(OvmsConfigParam* param);
@ -471,6 +483,12 @@ class OvmsVehicle : public InternalRamAllocated
Range = 3,
Performance = 4
} vehicle_mode_t;
enum class vehicle_bms_status_t
{
Both,
Voltage,
Temperature
};
public:
vehicle_mode_t VehicleModeKey(const std::string code);
@ -631,7 +649,7 @@ class OvmsVehicle : public InternalRamAllocated
float* m_bms_vmins; // BMS minimum voltages seen (since reset)
float* m_bms_vmaxs; // BMS maximum voltages seen (since reset)
float* m_bms_vdevmaxs; // BMS maximum voltage deviations seen (since reset)
short* m_bms_valerts; // BMS voltage deviation alerts (since reset)
OvmsStatus* m_bms_valerts; // BMS voltage deviation alerts (since reset)
int m_bms_valerts_new; // BMS new voltage alerts since last notification
int m_bms_vstddev_cnt; // BMS internal stddev counter
float m_bms_vstddev_avg; // BMS internal stddev average
@ -640,7 +658,7 @@ class OvmsVehicle : public InternalRamAllocated
float* m_bms_tmins; // BMS minimum temperatures seen (since reset)
float* m_bms_tmaxs; // BMS maximum temperatures seen (since reset)
float* m_bms_tdevmaxs; // BMS maximum temperature deviations seen (since reset)
short* m_bms_talerts; // BMS temperature deviation alerts (since reset)
OvmsStatus* m_bms_talerts; // BMS temperature deviation alerts (since reset)
int m_bms_talerts_new; // BMS new temperature alerts since last notification
bool m_bms_has_temperatures; // True if BMS has a complete set of temperature values
std::vector<bool> m_bms_bitset_v; // BMS tracking: true if corresponding voltage set
@ -686,8 +704,10 @@ class OvmsVehicle : public InternalRamAllocated
void BmsGetCellDefaultThresholdsVoltage(float* warn, float* alert, float* maxgrad=NULL, float* maxsddev=NULL);
void BmsGetCellDefaultThresholdsTemperature(float* warn, float* alert);
void BmsResetCellStats();
virtual void BmsStatus(int verbosity, OvmsWriter* writer);
virtual void BmsStatus(int verbosity, OvmsWriter* writer, vehicle_bms_status_t statusmode);
virtual bool FormatBmsAlerts(int verbosity, OvmsWriter* writer, bool show_warnings);
bool BmsCheckChangeCellArrangementVoltage(int readings, int readingspermodule = 0);
bool BmsCheckChangeCellArrangementTemperature(int readings, int readingspermodule = 0);
};
template<typename Type> OvmsVehicle* CreateVehicle()

View file

@ -60,7 +60,7 @@ void OvmsVehicle::BmsSetCellArrangementVoltage(int readings, int readingspermodu
if (m_bms_vdevmaxs != NULL) delete m_bms_vdevmaxs;
m_bms_vdevmaxs = new float[readings];
if (m_bms_valerts != NULL) delete m_bms_valerts;
m_bms_valerts = new short[readings];
m_bms_valerts = new OvmsStatus[readings];
m_bms_valerts_new = 0;
m_bms_bitset_v.clear();
@ -72,6 +72,129 @@ void OvmsVehicle::BmsSetCellArrangementVoltage(int readings, int readingspermodu
BmsResetCellVoltages(true);
}
// Internal entry for changing cell arrangements.
struct reading_entry_t
{
int cell_no;
float entry;
};
/**
* Changes the cell arrangement for Voltage, if needs be restoring any readings that were there.
* Really should only do work the first time it is called.. as for any one car, the number of readings
* should be consistent.
* @return true If the cell arrangement was changed.
*/
bool OvmsVehicle::BmsCheckChangeCellArrangementVoltage(int readings, int readingspermodule /*=0*/)
{
bool res = false;
if (readingspermodule <= 0)
{
// Passing in 0 will leave the readings per module unchanged.
readingspermodule = m_bms_readingspermodule_v;
}
if (readings != m_bms_readings_v)
{
res = true;
if (m_bms_bitset_cv == 0)
{
BmsSetCellArrangementVoltage(readings, readingspermodule);
}
else
{
// Store (potentially) sparse readings into a vector.
std::vector<reading_entry_t> cells;
// At most we will need the number of voltages set in the bitset.
cells.reserve(m_bms_bitset_cv);
reading_entry_t reading;
int maxv = readings;
if (m_bms_readings_v < maxv)
{
maxv = m_bms_readings_v;
}
for (int i = 0; i< maxv; ++i)
{
if (m_bms_bitset_v[i])
{
reading.cell_no = i;
reading.entry = m_bms_voltages[i];
cells.insert(cells.end(),reading);
}
}
BmsSetCellArrangementVoltage(readings, readingspermodule);
for ( std::vector<reading_entry_t>::iterator iter = cells.begin(); iter != cells.end(); ++iter)
{
BmsSetCellVoltage(iter->cell_no, iter->entry);
}
}
}
else if (readingspermodule != m_bms_readingspermodule_v)
{
// This only changes the read-out.
m_bms_readingspermodule_v = readingspermodule;
res = true;
}
return res;
}
/**
* Changes the cell arrangement for Temperature, if needs be restoring any readings that were there.
* Really should only do work the first time it is called.. as for any one car, the number of readings
* should be consistent.
* @return true If the cell arrangement was changed.
*/
bool OvmsVehicle::BmsCheckChangeCellArrangementTemperature(int readings, int readingspermodule /*=0*/)
{
bool res = false;
if (readingspermodule <= 0)
{
// Passing in 0 will leave the readings per module unchanged.
readingspermodule = m_bms_readingspermodule_t;
}
if (readings != m_bms_readings_t)
{
res = true;
if (m_bms_bitset_ct == 0)
{
BmsSetCellArrangementTemperature(readings, readingspermodule);
}
else
{
// Store (potentially) sparse readings into a vector.
std::vector<reading_entry_t> cells;
// At most we will need the number of Temperatures set in the bitset.
cells.reserve(m_bms_bitset_ct);
reading_entry_t reading;
int maxv = readings;
if (m_bms_readings_t < maxv)
{
maxv = m_bms_readings_t;
}
for (int i = 0; i< maxv; ++i)
{
if (m_bms_bitset_t[i])
{
reading.cell_no = i;
reading.entry = m_bms_temperatures[i];
cells.insert(cells.end(),reading);
}
}
BmsSetCellArrangementTemperature(readings, readingspermodule);
for ( std::vector<reading_entry_t>::iterator iter = cells.begin(); iter != cells.end(); ++iter)
{
BmsSetCellTemperature(iter->cell_no, iter->entry);
}
}
}
else if (readingspermodule != m_bms_readingspermodule_t)
{
// This only changes the read-out.
m_bms_readingspermodule_t = readingspermodule;
res = true;
}
return res;
}
void OvmsVehicle::BmsSetCellArrangementTemperature(int readings, int readingspermodule)
{
if (m_bms_temperatures != NULL) delete m_bms_temperatures;
@ -83,7 +206,7 @@ void OvmsVehicle::BmsSetCellArrangementTemperature(int readings, int readingsper
if (m_bms_tdevmaxs != NULL) delete m_bms_tdevmaxs;
m_bms_tdevmaxs = new float[readings];
if (m_bms_talerts != NULL) delete m_bms_talerts;
m_bms_talerts = new short[readings];
m_bms_talerts = new OvmsStatus[readings];
m_bms_talerts_new = 0;
m_bms_bitset_t.clear();
@ -153,7 +276,7 @@ void OvmsVehicle::BmsSetCellLimitsTemperature(float min, float max)
void OvmsVehicle::BmsSetCellVoltage(int index, float value)
{
//ESP_LOGV(TAG,"BmsSetCellVoltage(%d,%f) c=%d", index, value, m_bms_bitset_cv);
// ESP_LOGV(TAG,"BmsSetCellVoltage(%d,%f) c=%d", index, value, m_bms_bitset_cv);
if ((index<0)||(index>=m_bms_readings_v)) return;
if ((value<m_bms_limit_vmin)||(value>m_bms_limit_vmax)) return;
m_bms_voltages[index] = value;
@ -245,20 +368,20 @@ void OvmsVehicle::BmsSetCellVoltage(int index, float value)
dev = ROUNDPREC(m_bms_voltages[i] - avg, 5);
if (ABS(dev) > ABS(m_bms_vdevmaxs[i]))
m_bms_vdevmaxs[i] = dev;
if (ABS(dev) >= stddev + thr_alert && m_bms_valerts[i] < 2)
if (ABS(dev) >= stddev + thr_alert && m_bms_valerts[i] <= OvmsStatus::Warn)
{
m_bms_valerts[i] = 2;
m_bms_valerts[i] = OvmsStatus::Alert;
m_bms_valerts_new++; // trigger notification
}
else if (ABS(dev) >= stddev + thr_warn && m_bms_valerts[i] < 1)
m_bms_valerts[i] = 1;
else if (ABS(dev) >= stddev + thr_warn && m_bms_valerts[i] < OvmsStatus::Warn)
m_bms_valerts[i] = OvmsStatus::Warn;
}
// Publish deviation maximums & alerts:
if (stddev > StandardMetrics.ms_v_bat_pack_vstddev_max->AsFloat())
StandardMetrics.ms_v_bat_pack_vstddev_max->SetValue(stddev);
StandardMetrics.ms_v_bat_cell_vdevmax->SetElemValues(0, m_bms_readings_v, m_bms_vdevmaxs);
StandardMetrics.ms_v_bat_cell_valert->SetElemValues(0, m_bms_readings_v, m_bms_valerts);
StandardMetrics.ms_v_bat_cell_valert->SetElemValues(0, m_bms_readings_v, (short *)m_bms_valerts);
}
// complete:
@ -275,7 +398,7 @@ void OvmsVehicle::BmsSetCellVoltage(int index, float value)
void OvmsVehicle::BmsSetCellTemperature(int index, float value)
{
//ESP_LOGV(TAG,"BmsSetCellTemperature(%d,%f) c=%d", index, value, m_bms_bitset_ct);
// ESP_LOGV(TAG,"BmsSetCellTemperature(%d,%f) c=%d", index, value, m_bms_bitset_ct);
if ((index<0)||(index>=m_bms_readings_t)) return;
if ((value<m_bms_limit_tmin)||(value>m_bms_limit_tmax)) return;
m_bms_temperatures[index] = value;
@ -319,13 +442,13 @@ void OvmsVehicle::BmsSetCellTemperature(int index, float value)
dev = ROUNDPREC(m_bms_temperatures[i] - avg, 2);
if (ABS(dev) > ABS(m_bms_tdevmaxs[i]))
m_bms_tdevmaxs[i] = dev;
if (ABS(dev) >= stddev + thr_alert && m_bms_talerts[i] < 2)
if (ABS(dev) >= stddev + thr_alert && m_bms_talerts[i] < OvmsStatus::Alert)
{
m_bms_talerts[i] = 2;
m_bms_talerts[i] = OvmsStatus::Alert;
m_bms_talerts_new++; // trigger notification
}
else if (ABS(dev) >= stddev + thr_warn && m_bms_valerts[i] < 1)
m_bms_talerts[i] = 1;
else if (ABS(dev) >= stddev + thr_warn && m_bms_valerts[i] < OvmsStatus::Warn)
m_bms_talerts[i] = OvmsStatus::Warn;
}
// publish to metrics:
@ -341,7 +464,7 @@ void OvmsVehicle::BmsSetCellTemperature(int index, float value)
StandardMetrics.ms_v_bat_cell_tmin->SetElemValues(0, m_bms_readings_t, m_bms_tmins);
StandardMetrics.ms_v_bat_cell_tmax->SetElemValues(0, m_bms_readings_t, m_bms_tmaxs);
StandardMetrics.ms_v_bat_cell_tdevmax->SetElemValues(0, m_bms_readings_t, m_bms_tdevmaxs);
StandardMetrics.ms_v_bat_cell_talert->SetElemValues(0, m_bms_readings_t, m_bms_talerts);
StandardMetrics.ms_v_bat_cell_talert->SetElemValues(0, m_bms_readings_t, (short *) m_bms_talerts);
// complete:
m_bms_has_temperatures = true;
@ -365,7 +488,7 @@ void OvmsVehicle::BmsRestartCellVoltages()
void OvmsVehicle::BmsRestartCellTemperatures()
{
m_bms_bitset_t.clear();
m_bms_bitset_v.resize(m_bms_readings_t);
m_bms_bitset_t.resize(m_bms_readings_t);
m_bms_bitset_ct = 0;
}
@ -382,7 +505,7 @@ void OvmsVehicle::BmsResetCellVoltages(bool full /*=false*/)
m_bms_vmins[k] = 0;
m_bms_vmaxs[k] = 0;
m_bms_vdevmaxs[k] = 0;
m_bms_valerts[k] = 0;
m_bms_valerts[k] = OvmsStatus::OK;
}
m_bms_valerts_new = 0;
m_bms_vstddev_cnt = 0;
@ -409,7 +532,7 @@ void OvmsVehicle::BmsResetCellTemperatures(bool full /*=false*/)
m_bms_tmins[k] = 0;
m_bms_tmaxs[k] = 0;
m_bms_tdevmaxs[k] = 0;
m_bms_talerts[k] = 0;
m_bms_talerts[k] = OvmsStatus::OK;
}
m_bms_talerts_new = 0;
if (full) StandardMetrics.ms_v_bat_cell_temp->ClearValue();
@ -427,81 +550,217 @@ void OvmsVehicle::BmsResetCellStats()
BmsResetCellTemperatures(false);
}
void OvmsVehicle::BmsStatus(int verbosity, OvmsWriter* writer)
template<typename INT>
INT round_up_div(INT value, INT divis)
{
return (value + divis -1) / divis;
}
void OvmsVehicle::BmsStatus(int verbosity, OvmsWriter* writer, vehicle_bms_status_t statusmode)
{
auto check_max_cols = [](int total_cols, int maximum)
{
if (maximum <= 1)
return 1;
if (total_cols <= maximum)
return total_cols;
if (maximum >= 4 && total_cols % 4 == 0)
return 4;
if (total_cols % 3 == 0)
return 3;
if (maximum >= 5 && total_cols % 5 == 0)
return 5;
return maximum;
};
int c;
if ((! m_bms_has_voltages)||(! m_bms_has_temperatures))
bool show_voltage = m_bms_has_voltages;
bool show_temperature = m_bms_has_temperatures;
switch (statusmode)
{
writer->puts("No BMS status data available");
case vehicle_bms_status_t::Both:
break;
case vehicle_bms_status_t::Voltage:
show_temperature = false;
break;
case vehicle_bms_status_t::Temperature:
show_voltage = false;
break;
}
if ((!show_voltage) && (!show_temperature))
{
const char *datatype= "status";
switch (statusmode)
{
case vehicle_bms_status_t::Both:
break;
case vehicle_bms_status_t::Voltage:
datatype = "voltage";
break;
case vehicle_bms_status_t::Temperature:
datatype = "temperature";
break;
}
writer->printf("No BMS %s data available\n", datatype);
return;
}
metric_unit_t user_temp = UnitNotFound;
std::string temp_unit;
if (show_temperature)
{
user_temp = OvmsMetricGetUserUnit(GrpTemp, Celcius);
// (To Check: '°' is not SMS safe, so we only output 'C')
temp_unit = OvmsMetricUnitLabel(user_temp);
}
int vwarn=0, valert=0;
int twarn=0, talert=0;
for (c=0; c<m_bms_readings_v; c++) {
if (m_bms_valerts[c]==1) vwarn++;
if (m_bms_valerts[c]==2) valert++;
}
for (c=0; c<m_bms_readings_t; c++) {
if (m_bms_talerts[c]==1) twarn++;
if (m_bms_talerts[c]==2) talert++;
}
for (c=0; c<m_bms_readings_v; c++)
{
switch (m_bms_valerts[c])
{
case OvmsStatus::OK: break;
case OvmsStatus::Warn: vwarn++; break;
case OvmsStatus::Alert: valert++;break;
}
}
for (c=0; c<m_bms_readings_t; c++)
{
switch (m_bms_talerts[c])
{
case OvmsStatus::OK: break;
case OvmsStatus::Warn: twarn++; break;
case OvmsStatus::Alert: talert++; break;
}
}
if (show_voltage)
{
writer->puts("Voltage:");
writer->printf(" Average: %5.3fV [%5.3fV - %5.3fV]\n",
StdMetrics.ms_v_bat_pack_vavg->AsFloat(),
StdMetrics.ms_v_bat_pack_vmin->AsFloat(),
StdMetrics.ms_v_bat_pack_vmax->AsFloat());
writer->printf(" Deviation: SD %6.2fmV [max %.2fmV], %d warnings, %d alerts\n",
StdMetrics.ms_v_bat_pack_vstddev->AsFloat()*1000,
StdMetrics.ms_v_bat_pack_vstddev_max->AsFloat()*1000,
vwarn, valert);
}
writer->puts("Voltage:");
writer->printf(" Average: %5.3fV [%5.3fV - %5.3fV]\n",
StdMetrics.ms_v_bat_pack_vavg->AsFloat(),
StdMetrics.ms_v_bat_pack_vmin->AsFloat(),
StdMetrics.ms_v_bat_pack_vmax->AsFloat());
writer->printf(" Deviation: SD %6.2fmV [max %.2fmV], %d warnings, %d alerts\n",
StdMetrics.ms_v_bat_pack_vstddev->AsFloat()*1000,
StdMetrics.ms_v_bat_pack_vstddev_max->AsFloat()*1000,
vwarn, valert);
writer->puts("Temperature:");
writer->printf(" Average: %5.1fC [%5.1fC - %5.1fC]\n",
StdMetrics.ms_v_bat_pack_tavg->AsFloat(),
StdMetrics.ms_v_bat_pack_tmin->AsFloat(),
StdMetrics.ms_v_bat_pack_tmax->AsFloat());
writer->printf(" Deviation: SD %6.2fC [max %.2fC], %d warnings, %d alerts\n",
StdMetrics.ms_v_bat_pack_tstddev->AsFloat(),
StdMetrics.ms_v_bat_pack_tstddev_max->AsFloat(),
twarn, talert);
if (show_temperature)
{
writer->puts("Temperature:");
writer->printf(" Average: %5.1f%s [%5.1f%s - %5.1f%s]\n",
StdMetrics.ms_v_bat_pack_tavg->AsFloat(0, user_temp), temp_unit.c_str(),
StdMetrics.ms_v_bat_pack_tmin->AsFloat(0, user_temp), temp_unit.c_str(),
StdMetrics.ms_v_bat_pack_tmax->AsFloat(0, user_temp), temp_unit.c_str());
writer->printf(" Deviation: SD %6.2f%s [max %.2f%s], %d warnings, %d alerts\n",
StdMetrics.ms_v_bat_pack_tstddev->AsFloat(0, user_temp), temp_unit.c_str(),
StdMetrics.ms_v_bat_pack_tstddev_max->AsFloat(0, user_temp), temp_unit.c_str(),
twarn, talert);
}
writer->puts("Cells:");
int kv = 0;
int kt = 0;
for (int module = 0; module < ((m_bms_readings_v+m_bms_readingspermodule_v-1)/m_bms_readingspermodule_v); module++)
int module_count = 0;
if (show_voltage)
module_count = round_up_div(m_bms_readings_v,m_bms_readingspermodule_v);
if (show_temperature)
{
int temp_module_count = round_up_div(m_bms_readings_t,m_bms_readingspermodule_t);
if (temp_module_count > module_count)
module_count = temp_module_count;
}
int max_cols_v = 0;
if (show_voltage)
max_cols_v = check_max_cols(m_bms_readingspermodule_v, show_temperature?4:5);
int max_cols_t = 0;
if (show_temperature)
max_cols_t = check_max_cols(m_bms_readingspermodule_t, 5-max_cols_v);
for (int module = 0; module < module_count; ++module)
{
writer->printf(" +");
for (c=0;c<m_bms_readingspermodule_v;c++) { writer->printf("-------"); }
writer->printf("-+");
for (c=0;c<m_bms_readingspermodule_t;c++) { writer->printf("-------"); }
writer->puts("-+");
writer->printf("%3d |",module+1);
for (c=0; c<m_bms_readingspermodule_v; c++)
if (show_voltage)
{
if (kv < m_bms_readings_v)
writer->printf(" %5.3fV",m_bms_voltages[kv++]);
else
writer->printf(" ");
for (c=0;c<max_cols_v;c++) { writer->printf("-------"); }
writer->printf("-+");
}
writer->printf(" |");
for (c=0; c<m_bms_readingspermodule_t; c++)
if (show_temperature) {
for (c=0;c<max_cols_t;c++) { writer->printf("-------"); }
writer->printf("-+");
}
writer->puts("");
int rows_v = 0, rows_t = 0;
int reading_left_v = 0, reading_left_t = 0;
if (show_voltage)
{
if (kt < m_bms_readings_t)
writer->printf(" %5.1fC",m_bms_temperatures[kt++]);
else
writer->printf(" ");
int items_left_v = m_bms_readings_v - kv;
reading_left_v = std::min(items_left_v, m_bms_readingspermodule_v);
rows_v = round_up_div(reading_left_v, max_cols_v);
}
if (show_temperature)
{
int items_left_t = m_bms_readings_t - kt;
reading_left_t = std::min(items_left_t, m_bms_readingspermodule_t);
rows_t = round_up_div(reading_left_t, max_cols_t);
}
int rows = std::max(rows_v,rows_t);
for (int row = 0 ; row < rows; ++row)
{
if (row == 0)
writer->printf("%3d |",module+1);
else
writer->printf(" |");
if (show_voltage)
{
for (c=0; c<max_cols_v; c++)
{
if (kv < m_bms_readings_v && (reading_left_v > 0))
{
writer->printf(" %5.3fV",m_bms_voltages[kv]);
--reading_left_v;
++kv;
}
else
writer->printf(" ");
}
writer->printf(" |");
}
if (show_temperature)
{
for (c=0; c<m_bms_readingspermodule_t; c++)
{
if (kt < m_bms_readings_t && (reading_left_t > 0))
{
writer->printf(" %5.1f%s",UnitConvert(Celcius, user_temp, m_bms_temperatures[kt]), temp_unit.c_str());
--reading_left_t;
++kt;
}
else
writer->printf(" ");
}
writer->printf(" |");
}
writer->puts("");
}
writer->puts(" |");
}
writer->printf(" +");
for (c=0;c<m_bms_readingspermodule_v;c++) { writer->printf("-------"); }
writer->printf("-+");
for (c=0;c<m_bms_readingspermodule_t;c++) { writer->printf("-------"); }
writer->puts("-+");
if (show_voltage)
{
for (c=0;c<max_cols_v;c++) { writer->printf("-------"); }
writer->printf("-+");
}
if (show_temperature)
{
for (c=0;c<max_cols_t;c++) { writer->printf("-------"); }
writer->printf("-+");
}
writer->puts("");
}
bool OvmsVehicle::FormatBmsAlerts(int verbosity, OvmsWriter* writer, bool show_warnings)
@ -515,14 +774,18 @@ bool OvmsVehicle::FormatBmsAlerts(int verbosity, OvmsWriter* writer, bool show_w
writer->printf("Voltage: StdDev %dmV", (int)(StdMetrics.ms_v_bat_pack_vstddev_max->AsFloat() * 1000));
for (int i=0; i<m_bms_readings_v; i++)
{
int sts = StdMetrics.ms_v_bat_cell_valert->GetElemValue(i);
if (sts == 0) continue;
if (sts == 1 && !show_warnings) continue;
OvmsStatus sts = OvmsStatus(StdMetrics.ms_v_bat_cell_valert->GetElemValue(i));
switch (sts)
{
case OvmsStatus::OK: continue;
case OvmsStatus::Warn: if (!show_warnings) continue;
case OvmsStatus::Alert: ;
}
has_valerts++;
if (verbose || has_valerts <= 5)
{
int dev = StdMetrics.ms_v_bat_cell_vdevmax->GetElemValue(i) * 1000;
writer->printf("\n %c #%02d: %+4dmV", (sts==1) ? '?' : '!', i+1, dev);
writer->printf("\n %c #%02d: %+4dmV", (sts==OvmsStatus::Warn) ? '?' : '!', i+1, dev);
}
else
{
@ -533,18 +796,23 @@ bool OvmsVehicle::FormatBmsAlerts(int verbosity, OvmsWriter* writer, bool show_w
writer->printf("%s\n", has_valerts ? "" : ", cells OK");
// Temperatures:
// (Note: '°' is not SMS safe, so we only output 'C')
writer->printf("Temperature: StdDev %.1fC", StdMetrics.ms_v_bat_pack_tstddev_max->AsFloat());
metric_unit_t user_temp = OvmsMetricGetUserUnit(GrpTemp, Celcius);
std::string temp_unit = OvmsMetricUnitLabel(user_temp);
writer->printf("Temperature: StdDev %.1f%s", StdMetrics.ms_v_bat_pack_tstddev_max->AsFloat(0, user_temp), temp_unit.c_str());
for (int i=0; i<m_bms_readings_v; i++)
{
int sts = StdMetrics.ms_v_bat_cell_talert->GetElemValue(i);
if (sts == 0) continue;
if (sts == 1 && !show_warnings) continue;
OvmsStatus sts = OvmsStatus(StdMetrics.ms_v_bat_cell_talert->GetElemValue(i));
switch (sts)
{
case OvmsStatus::OK: continue;
case OvmsStatus::Warn: if (!show_warnings) continue;
case OvmsStatus::Alert: ;
}
has_talerts++;
if (verbose || has_talerts <= 5)
{
float dev = StdMetrics.ms_v_bat_cell_tdevmax->GetElemValue(i);
writer->printf("\n %c #%02d: %+3.1fC", (sts==1) ? '?' : '!', i+1, dev);
float dev = StdMetrics.ms_v_bat_cell_tdevmax->GetElemValue(i, user_temp);
writer->printf("\n %c #%02d: %+3.1f%s", (sts==OvmsStatus::Warn) ? '?' : '!', i+1, dev, temp_unit.c_str());
}
else
{

View file

@ -435,7 +435,14 @@ void OvmsVehicleFactory::bms_status(int verbosity, OvmsWriter* writer, OvmsComma
{
if (MyVehicleFactory.m_currentvehicle != NULL)
{
MyVehicleFactory.m_currentvehicle->BmsStatus(verbosity, writer);
OvmsVehicle::vehicle_bms_status_t statusmode = OvmsVehicle::vehicle_bms_status_t::Both;
const char* smode = cmd->GetName();
if (strcmp(smode,"volt")==0)
statusmode = OvmsVehicle::vehicle_bms_status_t::Voltage;
else if (strcmp(smode,"temp")==0)
statusmode = OvmsVehicle::vehicle_bms_status_t::Temperature;
MyVehicleFactory.m_currentvehicle->BmsStatus(verbosity, writer, statusmode);
}
else
{

View file

@ -1,35 +0,0 @@
=======================
Renault Zoe
=======================
Vehicle Type: **RZ2**
This vehicle type supports the Renault Zoe(PH2)
----------------
Support Overview
----------------
=========================== ==============
Function Support Status
=========================== ==============
Hardware OVMS v3 (or later)
Vehicle Cable OBD-II to DB9 Data Cable for OVMS (1441200 right, or 1139300 left)
GSM Antenna 1000500 Open Vehicles OVMS GSM Antenna (or any compatible antenna)
GPS Antenna 1020200 Universal GPS Antenna (SMA Connector) (or any compatible antenna)
SOC Display Yes
Range Display Yes
GPS Location Yes (from modem module GPS)
Speed Display Yes
Temperature Display Yes (External Temp and Battery)
BMS v+t Display Yes
TPMS Display Zoe No
TPMS Display Kangoo No
Charge Status Display Yes
Charge Interruption Alerts No
Charge Control No
Cabin Pre-heat/cool Control No
Lock/Unlock Vehicle No
Valet Mode Control No
Others
=========================== ==============

View file

@ -7,7 +7,7 @@
# please read the ESP-IDF documents if you need to do this.
#
ifdef CONFIG_OVMS_VEHICLE_RENAULTZOE_PH2
ifdef CONFIG_OVMS_VEHICLE_RENAULTZOE_PH2_CAN
COMPONENT_ADD_INCLUDEDIRS:=src
COMPONENT_SRCDIRS:=src
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

View file

@ -0,0 +1,49 @@
=======================================
Renault Zoe Phase 2 (CAN direct access)
=======================================
Vehicle Type: **RZ2C**
This vehicle type supports the Renault Zoe(PH2) through direct CAN connection.
You connect CAN1 interface of the OVMS module to M-CAN bus on your Zoe, you can intercept it at TCU, Radio (IVI) or Can Core Gateway behind glovebox.
----------------
Support Overview
----------------
=========================== ==============
Function Support Status
=========================== ==============
Hardware OVMS v3.3
Vehicle Cable DIY cable to intercept M-CAN on TCU, Radio/IVI or Can Core Gateway
GSM Antenna SMA to Fakra adapter, if you use Zoes antennas (disconnect TCU)
GPS Antenna SMA to Fakra adapter, if you use Zoes antennas (disconnect TCU)
SOC Display Yes
Range Display Yes
GPS Location Yes (from modem module GPS)
Speed Display Yes
Temperature Display Yes (Battery, Outside, Cabin, Motor, Inverter, Tyres)
BMS v+t Display Yes
TPMS Display Zoe Yes (Pressure and temperature)
Charge Status Display Yes
Charge Interruption Alerts Yes
Charge Control No
Cabin Pre-heat/cool Control No
Lock/Unlock Vehicle No
Valet Mode Control No
=========================== ==============
Others:
=================================== ==============
Door open/close Yes (exclude hood)
Battery full charge cycles Yes
Battery max charge, recd pwr Yes
Trip counter from Car No
Battery lifetime gauges Yes (Charged, Recd and Used kWh)
Heat pump power, rpm and hp press. Yes
Aux power gauges Yes (testing needed)
Charge type Yes (DC charge, testing needed)
Headlights Status Yes (lowbeam)
Charge efficiency calculation Yes (but only for AC, pf is measured with power analyzer and included as statics)
=================================== ==============

View file

@ -0,0 +1,224 @@
/*
; 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 "vehicle_renaultzoe_ph2_can.h"
void OvmsVehicleRenaultZoePh2CAN::IncomingBCM(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x6300: { // TPMS pressure - front left
if ((CAN_UINT(0) * 7.5) < 7672) {
StandardMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_FL, (float)CAN_UINT(0) * 7.5 / 10, kPa);
}
//ESP_LOGD(TAG, "6300 BCM tpms pressure FL: %f", CAN_UINT(0) * 7.5);
break;
}
case 0x6301: { // TPMS pressure - front right
if ((CAN_UINT(0) * 7.5) < 7672) {
StandardMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_FR, (float)CAN_UINT(0) * 7.5 / 10, kPa);
}
//ESP_LOGD(TAG, "6301 BCM tpms pressure FR: %f", CAN_UINT(0) * 7.5);
break;
}
case 0x6302: { // TPMS pressure - rear left
if ((CAN_UINT(0) * 7.5) < 7672) {
StandardMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_RL, (float)CAN_UINT(0) * 7.5 / 10, kPa);
}
//ESP_LOGD(TAG, "6302 BCM tpms pressure RL: %f", CAN_UINT(0) * 7.5);
break;
}
case 0x6303: { // TPMS pressure - rear right
if ((CAN_UINT(0) * 7.5) < 7672) {
StandardMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_RR, (float)CAN_UINT(0) * 7.5 / 10, kPa);
}
//ESP_LOGD(TAG, "6303 BCM tpms pressure RR: %f", CAN_UINT(0) * 7.5);
break;
}
case 0x6310: { // TPMS temp - front left
if (CAN_BYTE(0) < 127) {
StandardMetrics.ms_v_tpms_temp->SetElemValue(MS_V_TPMS_IDX_FL, CAN_BYTE(0) - 30, Celcius);
}
//ESP_LOGD(TAG, "6310 BCM tpms temp FL: %d", CAN_NIB(0));
break;
}
case 0x6311: { // TPMS temp - front right
if (CAN_BYTE(0) < 127) {
StandardMetrics.ms_v_tpms_temp->SetElemValue(MS_V_TPMS_IDX_FR, CAN_BYTE(0) - 30, Celcius);
}
//ESP_LOGD(TAG, "6311 BCM tpms temp FR: %d", CAN_NIBL(0));
break;
}
case 0x6312: { // TPMS temp - rear left
if (CAN_BYTE(0) < 127) {
StandardMetrics.ms_v_tpms_temp->SetElemValue(MS_V_TPMS_IDX_RL, CAN_BYTE(0) - 30, Celcius);
}
//ESP_LOGD(TAG, "6312 BCM tpms temp RL: %d", CAN_NIBH(0));
break;
}
case 0x6313: { // TPMS temp - rear right
if (CAN_BYTE(0) < 127) {
StandardMetrics.ms_v_tpms_temp->SetElemValue(MS_V_TPMS_IDX_RR, CAN_BYTE(0) - 30, Celcius);
}
//ESP_LOGD(TAG, "6313 BCM tpms temp RR: %d", CAN_BYTE(0));
break;
}
case 0x4109: { // TPMS alert - front left
if (CAN_UINT(0) == 0) {
StandardMetrics.ms_v_tpms_alert->SetElemValue(MS_V_TPMS_IDX_FL, 0);
}
if (CAN_UINT(0) == 1 || CAN_UINT(0) == 3 || CAN_UINT(0) == 5 || CAN_UINT(0) == 7) {
StandardMetrics.ms_v_tpms_alert->SetElemValue(MS_V_TPMS_IDX_FL, 2);
}
if (CAN_UINT(0) == 2 || CAN_UINT(0) == 4 || CAN_UINT(0) == 6 ) {
StandardMetrics.ms_v_tpms_alert->SetElemValue(MS_V_TPMS_IDX_FL, 1);
}
//ESP_LOGD(TAG, "40FF BCM tpms alert FL: %d", CAN_UINT(0));
break;
}
case 0x410A: { // TPMS alert - front right
if (CAN_UINT(0) == 0) {
StandardMetrics.ms_v_tpms_alert->SetElemValue(MS_V_TPMS_IDX_FR, 0);
}
if (CAN_UINT(0) == 1 || CAN_UINT(0) == 3 || CAN_UINT(0) == 5 || CAN_UINT(0) == 7) {
StandardMetrics.ms_v_tpms_alert->SetElemValue(MS_V_TPMS_IDX_FR, 2);
}
if (CAN_UINT(0) == 2 || CAN_UINT(0) == 4 || CAN_UINT(0) == 6 ) {
StandardMetrics.ms_v_tpms_alert->SetElemValue(MS_V_TPMS_IDX_FR, 1);
}
//ESP_LOGD(TAG, "40FF BCM tpms alert FR: %d", CAN_UINT(0));
break;
}
case 0x410B: { // TPMS alert - rear left
if (CAN_UINT(0) == 0) {
StandardMetrics.ms_v_tpms_alert->SetElemValue(MS_V_TPMS_IDX_RL, 0);
}
if (CAN_UINT(0) == 1 || CAN_UINT(0) == 3 || CAN_UINT(0) == 5 || CAN_UINT(0) == 7) {
StandardMetrics.ms_v_tpms_alert->SetElemValue(MS_V_TPMS_IDX_RL, 2);
}
if (CAN_UINT(0) == 2 || CAN_UINT(0) == 4 || CAN_UINT(0) == 6 ) {
StandardMetrics.ms_v_tpms_alert->SetElemValue(MS_V_TPMS_IDX_RL, 1);
}
//ESP_LOGD(TAG, "40FF BCM tpms alert RL: %d", CAN_UINT(0));
break;
}
case 0x410C: { // TPMS alert - rear right
if (CAN_UINT(0) == 0) {
StandardMetrics.ms_v_tpms_alert->SetElemValue(MS_V_TPMS_IDX_RR, 0);
}
if (CAN_UINT(0) == 1 || CAN_UINT(0) == 3 || CAN_UINT(0) == 5 || CAN_UINT(0) == 7) {
StandardMetrics.ms_v_tpms_alert->SetElemValue(MS_V_TPMS_IDX_RR, 2);
}
if (CAN_UINT(0) == 2 || CAN_UINT(0) == 4 || CAN_UINT(0) == 6 ) {
StandardMetrics.ms_v_tpms_alert->SetElemValue(MS_V_TPMS_IDX_RR, 1);
}
//ESP_LOGD(TAG, "40FF BCM tpms alert RR: %d", CAN_UINT(0));
break;
}
case 0x8004: { //Car secure aka vehicle locked
StandardMetrics.ms_v_env_locked->SetValue((bool)CAN_UINT(0));
//ESP_LOGD(TAG, "8004 BCM Car Secure S: %d", CAN_UINT(0));
break;
}
case 0x6026: { //Front left door
StandardMetrics.ms_v_door_fl->SetValue((bool)CAN_UINT(0));
//ESP_LOGD(TAG, "6026 BCM Front left door: %d", CAN_UINT(0));
break;
}
case 0x6027: { //Front right door
StandardMetrics.ms_v_door_fr->SetValue((bool)CAN_UINT(0));
//ESP_LOGD(TAG, "6027 BCM Front right door: %d", CAN_UINT(0));
break;
}
case 0x61B2: { //Rear left door
StandardMetrics.ms_v_door_rl->SetValue((bool)CAN_UINT(0));
//ESP_LOGD(TAG, "61B2 BCM Rear left door: %d", CAN_UINT(0));
break;
}
case 0x61B3: { //Rear right door
StandardMetrics.ms_v_door_rr->SetValue((bool)CAN_UINT(0));
//ESP_LOGD(TAG, "61B3 Rear right door: %d", CAN_UINT(0));
break;
}
case 0x609B: { //Tailgate
StandardMetrics.ms_v_door_trunk->SetValue((bool)CAN_UINT(0));
//ESP_LOGD(TAG, "609B Tailgate: %d", CAN_UINT(0));
break;
}
case 0x4186: { //Low beam lights
StandardMetrics.ms_v_env_headlights->SetValue((bool)CAN_UINT(0));
/*if ((bool)CAN_UINT(0)) {
ESP_LOGD(TAG, "4186 Low beam lights: active");
} else {
ESP_LOGD(TAG, "4186 Low beam lights: inactive");
}*/
break;
}
case 0x60C6: { //Ignition relay (switch)
/*if ((bool)CAN_UINT(0)) {
ESP_LOGD(TAG, "60C6 Ignition relay: active");
} else {
ESP_LOGD(TAG, "60C6 Ignition relay: inactive");
}*/
if (!CarIsCharging) { //Igniton while charging
StandardMetrics.ms_v_env_on->SetValue((bool)CAN_UINT(0));
StandardMetrics.ms_v_env_awake->SetValue((bool)CAN_UINT(0));
}
break;
}
case 0x4060: { //Vehicle identificaftion number
zoe_vin[0] = CAN_BYTE(0);
zoe_vin[1] = CAN_BYTE(1);
zoe_vin[2] = CAN_BYTE(2);
zoe_vin[3] = CAN_BYTE(3);
zoe_vin[4] = CAN_BYTE(4);
zoe_vin[5] = CAN_BYTE(5);
zoe_vin[6] = CAN_BYTE(6);
zoe_vin[7] = CAN_BYTE(7);
zoe_vin[8] = CAN_BYTE(8);
zoe_vin[9] = CAN_BYTE(9);
zoe_vin[10] = CAN_BYTE(10);
zoe_vin[11] = CAN_BYTE(11);
zoe_vin[12] = CAN_BYTE(12);
zoe_vin[13] = CAN_BYTE(13);
zoe_vin[14] = CAN_BYTE(14);
zoe_vin[15] = CAN_BYTE(15);
zoe_vin[16] = CAN_BYTE(16);
zoe_vin[17] = 0;
StandardMetrics.ms_v_vin->SetValue((string) zoe_vin);
break;
}
default: {
char *buf = NULL;
size_t rlen = len, offset = 0;
do {
rlen = FormatHexDump(&buf, data + offset, rlen, 16);
offset += 16;
ESP_LOGW(TAG, "OBD2: unhandled reply from BCM [%02x %02x]: %s", type, pid, buf ? buf : "-");
} while (rlen);
if (buf)
free(buf);
break;
}
}
}

View file

@ -0,0 +1,261 @@
/*
; 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 "vehicle_renaultzoe_ph2_can.h"
void OvmsVehicleRenaultZoePh2CAN::IncomingEVC(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x2006: { //Odometer (Total Vehicle Distance)
StandardMetrics.ms_v_pos_odometer->SetValue((float) CAN_UINT24(0), Kilometers);
//ESP_LOGD(TAG, "2006 EVC ms_v_pos_odometer: %d", CAN_UINT24(0));
break;
}
case 0x2003: { //Vehicle Speed
StandardMetrics.ms_v_pos_speed->SetValue((float) (CAN_UINT(0) * 0.01), KphPS);
//ESP_LOGD(TAG, "2003 EVC ms_v_pos_speed: %f", CAN_UINT(0) * 0.01);
break;
}
case 0x2005: { //12V Battery Voltage
StandardMetrics.ms_v_charge_12v_voltage->SetValue((float) (CAN_UINT(0) * 0.01), Volts);
//ESP_LOGD(TAG, "2005 EVC ms_v_charge_12v_voltage: %f", CAN_UINT(0) * 0.01);
break;
}
case 0x21CF: { //Inverter status
//ESP_LOGD(TAG, "21CF EVC mt_inv_status: %d", CAN_NIBL(0));
if (CAN_NIBL(0) == 1) {
mt_inv_status->SetValue("Inverter off");
} else if (CAN_NIBL(0) == 2) {
mt_inv_status->SetValue("Inverter on");
StandardMetrics.ms_v_door_chargeport->SetValue(false);
} else if (CAN_NIBL(0) == 3) {
mt_inv_status->SetValue("Inverter decharging");
} else if (CAN_NIBL(0) == 4) {
mt_inv_status->SetValue("Inverter alternator mode");
} else if (CAN_NIBL(0) == 5) {
mt_inv_status->SetValue("Inverter ready to sleep");
} else {
mt_inv_status->SetValue("Inverter state unknown");
}
break;
}
case 0x2218: { // Ambient temperature
StandardMetrics.ms_v_env_temp->SetValue((float) (CAN_UINT(0) * 0.1 - 273), Celcius);
//ESP_LOGD(TAG, "2218 EVC ms_v_env_temp: %f", (CAN_UINT(0) * 0.1 - 273));
break;
}
case 0x2A09: { // Power consumption by consumer
mt_bat_aux_power_consumer->SetValue((float) CAN_UINT(0) * 10, Watts);
//ESP_LOGD(TAG, "2A09 EVC mt_bat_aux_power_consumer: %d", CAN_UINT(0) * 10);
break;
}
case 0x2191: { // Power consumption by ptc
mt_bat_aux_power_ptc->SetValue((float) CAN_UINT(0) * 10, Watts);
//ESP_LOGD(TAG, "2191 EVC mt_bat_aux_power_ptc: %d", CAN_UINT(0) * 10);
break;
}
case 0x2B85: { // Charge plug preset
//ESP_LOGD(TAG, "2B85 EVC Charge plug present: %d", CAN_NIBL(0));
if (CAN_NIBL(0) == 1) {
StandardMetrics.ms_v_charge_pilot->SetValue(true);
if (!CarPluggedIn) {
ESP_LOGI(TAG, "Charge cable plugged in");
CarPluggedIn = true;
}
}
if (CAN_NIBL(0) == 0) {
StandardMetrics.ms_v_charge_pilot->SetValue(false);
if (CarPluggedIn) {
ESP_LOGI(TAG, "Charge cable plugged out");
CarPluggedIn = false;
StandardMetrics.ms_v_door_chargeport->SetValue(false);
}
}
break;
}
case 0x2B6D: { // Charge MMI States, will be polled every 30s even car is off, because free frames are unreliable
//ESP_LOGD(TAG, "2B6D Charge MMI States RAW: %d", CAN_NIBL(0));
if (CAN_NIBL(0) == 0) {
StandardMetrics.ms_v_charge_state->SetValue("stopped");
StandardMetrics.ms_v_charge_substate->SetValue("stopped");
StandardMetrics.ms_v_charge_inprogress->SetValue(false);
//ESP_LOGD(TAG, "2B6D Charge MMI States : No Charge");
}
if (CAN_NIBL(0) == 1) {
StandardMetrics.ms_v_charge_state->SetValue("timerwait");
StandardMetrics.ms_v_charge_substate->SetValue("timerwait");
//ESP_LOGD(TAG, "2B6D Charge MMI States : Waiting for a planned charge");
}
if (CAN_NIBL(0) == 2) {
StandardMetrics.ms_v_charge_state->SetValue("done");
StandardMetrics.ms_v_charge_substate->SetValue("stopped");
StandardMetrics.ms_v_charge_inprogress->SetValue(false);
//ESP_LOGD(TAG, "2B6D Charge MMI States : Ended charge");
}
if (CAN_NIBL(0) == 3) {
StandardMetrics.ms_v_charge_state->SetValue("charging");
StandardMetrics.ms_v_charge_substate->SetValue("onrequest");
StandardMetrics.ms_v_charge_inprogress->SetValue(true);
//ESP_LOGD(TAG, "2B6D Charge MMI States : Charge in progress");
}
if (CAN_NIBL(0) == 4) {
StandardMetrics.ms_v_charge_state->SetValue("stopped");
StandardMetrics.ms_v_charge_substate->SetValue("interrupted");
StandardMetrics.ms_v_charge_inprogress->SetValue(false);
//ESP_LOGD(TAG, "2B6D Charge MMI States : Charge failure");
}
if (CAN_NIBL(0) == 5) {
StandardMetrics.ms_v_charge_state->SetValue("stopped");
StandardMetrics.ms_v_charge_substate->SetValue("powerwait");
StandardMetrics.ms_v_charge_inprogress->SetValue(false);
//ESP_LOGD(TAG, "2B6D Charge MMI States : Waiting for current charge");
}
if (CAN_NIBL(0) == 6) {
StandardMetrics.ms_v_door_chargeport->SetValue(true);
//ESP_LOGD(TAG, "2B6D Charge MMI States : Chargeport opened");
ESP_LOGI(TAG, "Chargedoor opened");
}
if (CAN_NIBL(0) == 8) {
StandardMetrics.ms_v_charge_state->SetValue("prepare");
StandardMetrics.ms_v_charge_substate->SetValue("powerwait");
StandardMetrics.ms_v_charge_inprogress->SetValue(false);
//ESP_LOGD(TAG, "2B6D Charge MMI States : Charge preparation");
}
if (!mt_bus_awake->AsBool()) {
ZoeWakeUp();
}
break;
}
case 0x2B7A: { // Charge type
//ESP_LOGD(TAG, "2B7A EVC Charge type: %d", (CAN_NIBL(0)));
if (CAN_NIBL(0) == 0) {
StandardMetrics.ms_v_charge_type->SetValue("undefined");
}
if (CAN_NIBL(0) == 1 || CAN_NIBL(0) == 2) {
StandardMetrics.ms_v_charge_type->SetValue("type2");
StandardMetrics.ms_v_charge_mode->SetValue("standard");
}
if (CAN_NIBL(0) == 3) {
StandardMetrics.ms_v_charge_type->SetValue("chademo");
StandardMetrics.ms_v_charge_mode->SetValue("performance");
}
if (CAN_NIBL(0) == 4) {
StandardMetrics.ms_v_charge_type->SetValue("ccs");
StandardMetrics.ms_v_charge_mode->SetValue("performance");
}
break;
}
case 0x3064: { // Motor rpm
StandardMetrics.ms_v_mot_rpm->SetValue((float) (CAN_UINT(0)));
//ESP_LOGD(TAG, "3064 EVC ms_v_mot_rpm: %d", (CAN_UINT(0)));
break;
}
case 0x300F: { // AC charging power available
mt_main_power_available->SetValue((float) (CAN_UINT(0) * 0.025), kW);
//ESP_LOGD(TAG, "300F EVC mt_main_power_available: %f", (CAN_UINT(0) * 0.025));
break;
}
case 0x300D: { // AC input current
StandardMetrics.ms_v_charge_current->SetValue((float) (CAN_UINT(0) * 0.1), Amps);
//Power factor measured with a Janitza UMG512 Class A power analyser to get more precision
//Only three phases measurement at the moment
if (StandardMetrics.ms_v_charge_current->AsFloat() > 19.0f) {
ACInputPowerFactor = 1.0;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() > 18.0f) {
ACInputPowerFactor = 0.997;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() > 17.0f) {
ACInputPowerFactor = 0.99;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() > 16.0f) {
ACInputPowerFactor = 0.978;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() > 15.0f) {
ACInputPowerFactor = 0.948;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() > 14.0f) {
ACInputPowerFactor = 0.931;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() > 13.0f) {
ACInputPowerFactor = 0.916;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() > 12.0f) {
ACInputPowerFactor = 0.902;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() > 11.0f) {
ACInputPowerFactor = 0.888;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() > 10.0f) {
ACInputPowerFactor = 0.905;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() > 9.0f) {
ACInputPowerFactor = 0.929;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() > 8.0f) {
ACInputPowerFactor = 0.901;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() > 7.0f) {
ACInputPowerFactor = 0.775;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() < 6.0f && StandardMetrics.ms_v_charge_inprogress->AsBool(false)) {
ACInputPowerFactor = 0.05;
}
if (StandardMetrics.ms_v_charge_type->AsString() == "type2" && mt_main_phases_num->AsFloat() == 3 && StandardMetrics.ms_v_charge_inprogress->AsBool(false)) {
StandardMetrics.ms_v_charge_power->SetValue((StandardMetrics.ms_v_charge_current->AsFloat() * StandardMetrics.ms_v_charge_voltage->AsFloat() * ACInputPowerFactor * 1.732f) * 0.001, kW);
} else if (StandardMetrics.ms_v_charge_type->AsString() == "type2" && (mt_main_phases_num->AsFloat() == 2 || mt_main_phases_num->AsFloat() == 1)) {
StandardMetrics.ms_v_charge_power->SetValue((StandardMetrics.ms_v_charge_current->AsFloat() * StandardMetrics.ms_v_charge_voltage->AsFloat() * ACInputPowerFactor) * 0.001, kW);
} else if (StandardMetrics.ms_v_charge_type->AsString() == "type2") {
StandardMetrics.ms_v_charge_power->SetValue(0);
}
//ESP_LOGD(TAG, "300D EVC mt_main_current: %f", (CAN_UINT(0) * 0.1));
break;
}
case 0x300B: { // AC phases used
//ESP_LOGD(TAG, "300B EVC mt_main_phases: %d", (CAN_NIBL(0)));
if (CAN_NIBL(0) == 0) {
mt_main_phases->SetValue("one phase");
mt_main_phases_num->SetValue(1);
}
if (CAN_NIBL(0) == 1) {
mt_main_phases->SetValue("two phase");
mt_main_phases_num->SetValue(2);
}
if (CAN_NIBL(0) == 2) {
mt_main_phases->SetValue("three phase");
mt_main_phases_num->SetValue(3);
}
if (CAN_NIBL(0) == 3) {
mt_main_phases->SetValue("not detected");
mt_main_phases_num->SetValue(0);
}
break;
}
case 0x2B8A: { // AC mains voltage
StandardMetrics.ms_v_charge_voltage->SetValue((float) (CAN_UINT(0) * 0.5), Volts);
//ESP_LOGD(TAG, "2B8A EVC ms_v_charge_voltage: %f", (CAN_UINT(0) * 0.5));
break;
}
default: {
char *buf = NULL;
size_t rlen = len, offset = 0;
do {
rlen = FormatHexDump(&buf, data + offset, rlen, 16);
offset += 16;
ESP_LOGW(TAG, "OBD2: unhandled reply from EVC [%02x %02x]: %s", type, pid, buf ? buf : "-");
} while (rlen);
if (buf)
free(buf);
break;
}
}
}

View file

@ -0,0 +1,92 @@
/*
; 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 "vehicle_renaultzoe_ph2_can.h"
void OvmsVehicleRenaultZoePh2CAN::IncomingHVAC(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x4009: { //Cabin temperature
StandardMetrics.ms_v_env_cabintemp->SetValue(float((CAN_UINT(0) - 400) / 10), Celcius);
//ESP_LOGD(TAG, "4361 HVAC ms_v_env_cabintemp: %f", float((CAN_UINT(0) - 400) / 10));
break;
}
case 0x4360: { //Cabin setpoint
StandardMetrics.ms_v_env_cabinsetpoint->SetValue(float(((CAN_NIBL(0) + 32) / 2)), Celcius);
//ESP_LOGD(TAG, "4360 HVAC ms_v_env_cabinsetpoint: %d", (CAN_NIBL(0) + 32) / 2);
break;
}
case 0x43D8: { //Compressor speed
mt_hvac_compressor_speed->SetValue(float(CAN_UINT(0)));
//ESP_LOGD(TAG, "43D8 HVAC mt_hvac_compressor_speed: %d", CAN_UINT(0));
break;
}
case 0x4402: { //Compressor state
if (CAN_NIBL(0) == 1) {
mt_hvac_compressor_mode->SetValue("AC mode");
StandardMetrics.ms_v_env_hvac->SetValue(true);
}
if (CAN_NIBL(0) == 2) {
mt_hvac_compressor_mode->SetValue("De-ICE mode");
StandardMetrics.ms_v_env_hvac->SetValue(true);
}
if (CAN_NIBL(0) == 4) {
mt_hvac_compressor_mode->SetValue("Heat pump mode");
StandardMetrics.ms_v_env_hvac->SetValue(true);
}
if (CAN_NIBL(0) == 6) {
mt_hvac_compressor_mode->SetValue("Demisting mode");
StandardMetrics.ms_v_env_hvac->SetValue(true);
}
if (CAN_NIBL(0) == 7) {
mt_hvac_compressor_mode->SetValue("idle");
StandardMetrics.ms_v_env_hvac->SetValue(false);
}
//ESP_LOGD(TAG, "%d HVAC mt_hvac_compressor_mode: %d", pid, CAN_UINT(0));
break;
}
case 0x4369: { //Compressor pressure
mt_hvac_compressor_pressure->SetValue(float(CAN_UINT(0) * 0.1));
//ESP_LOGD(TAG, "%d HVAC mt_hvac_compressor_pressure: %f", pid, CAN_UINT(0) * 0.1);
break;
}
case 0x4436: { //Compressor power
mt_hvac_compressor_power->SetValue(float(CAN_UINT(0) * 25.0 / 100.0));
//ESP_LOGD(TAG, "%d HVAC mt_hvac_compressor_power: %f", pid, CAN_UINT(0) * 25.0 / 100.0);
break;
}
default: {
char *buf = NULL;
size_t rlen = len, offset = 0;
do {
rlen = FormatHexDump(&buf, data + offset, rlen, 16);
offset += 16;
ESP_LOGW(TAG, "OBD2: unhandled reply from HVAC [%02x %02x]: %s", type, pid, buf ? buf : "-");
} while (rlen);
if (buf)
free(buf);
break;
}
}
}

View file

@ -0,0 +1,72 @@
/*
; 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 "vehicle_renaultzoe_ph2_can.h"
void OvmsVehicleRenaultZoePh2CAN::IncomingINV(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x700C: { // Inverter temperature
StandardMetrics.ms_v_inv_temp->SetValue(float((CAN_UINT24(0) * 0.001953125) - 40), Celcius);
//ESP_LOGD(TAG, "700C INV ms_v_inv_temp RAW: %f", float(CAN_UINT24(0)));
//ESP_LOGD(TAG, "700C INV ms_v_inv_temp: %f", float((CAN_UINT24(0) * 0.001953125) - 40));
break;
}
case 0x700F: { // Motor, Stator1 temperature
StandardMetrics.ms_v_mot_temp->SetValue(float((CAN_UINT24(0) * 0.001953125) - 40), Celcius);
mt_mot_temp_stator1->SetValue(float((CAN_UINT24(0) * 0.001953125) - 40), Celcius);
//ESP_LOGD(TAG, "700F INV ms_v_mot_temp: %f", float((CAN_UINT24(0) * 0.001953125) - 40));
break;
}
case 0x7010: { // Stator 2 temperature
mt_mot_temp_stator2->SetValue(float((CAN_UINT24(0) * 0.001953125) - 40), Celcius);
//ESP_LOGD(TAG, "7010 INV mt_mot_temp_stator2: %f", float((CAN_UINT24(0) * 0.001953125) - 40));
break;
}
case 0x2004: { // Battery voltage sense
mt_inv_hv_voltage->SetValue(float(CAN_UINT(0) * 0.03125), Volts);
//ESP_LOGD(TAG, "2004 INV mt_inv_hv_voltage: %f", float(CAN_UINT(0) * 0.03125));
StandardMetrics.ms_v_inv_power->SetValue(float (mt_inv_hv_voltage->AsFloat() * mt_inv_hv_current->AsFloat()) * 0.001);
break;
}
case 0x7049: { // Battery current sense
mt_inv_hv_current->SetValue(float((CAN_UINT(0) * 0.03125) - 500), Amps);
//ESP_LOGD(TAG, "7049 INV mt_inv_hv_current: %f", float(CAN_UINT(0) * 0.003125) - 500);
StandardMetrics.ms_v_inv_power->SetValue(float (mt_inv_hv_current->AsFloat() * mt_inv_hv_voltage->AsFloat()) * 0.001);
break;
}
default: {
char *buf = NULL;
size_t rlen = len, offset = 0;
do {
rlen = FormatHexDump(&buf, data + offset, rlen, 16);
offset += 16;
ESP_LOGW(TAG, "OBD2: unhandled reply from INV [%02x %02x]: %s", type, pid, buf ? buf : "-");
} while (rlen);
if (buf)
free(buf);
break;
}
}
}

View file

@ -0,0 +1,641 @@
/*
; 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 "vehicle_renaultzoe_ph2_can.h"
void OvmsVehicleRenaultZoePh2CAN::IncomingLBC(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x9005: { //Battery voltage
StandardMetrics.ms_v_bat_voltage->SetValue((float) (CAN_UINT(0) * 0.1), Volts);
//ESP_LOGD(TAG, "9005 LBC ms_v_bat_voltage: %f", CAN_UINT(0) * 0.1);
StandardMetrics.ms_v_bat_power->SetValue(((CAN_UINT(0) * 0.1) * StandardMetrics.ms_v_bat_current->AsFloat()) * 0.001);
break;
}
case 0x925D: { //Battery current
StandardMetrics.ms_v_bat_current->SetValue((float) ((CAN_UINT(0) * 0.03125 - 1020) * -1.0f), Amps);
//ESP_LOGD(TAG, "925D LBC ms_v_bat_current: %f", (CAN_UINT(0) * 0.03125 - 1020));
StandardMetrics.ms_v_bat_power->SetValue((((CAN_UINT(0) * 0.03125 - 1020) * -1.0f) * StandardMetrics.ms_v_bat_voltage->AsFloat()) * 0.001);
break;
}
case 0x9012: { //Battery average temperature
StandardMetrics.ms_v_bat_temp->SetValue((float) (CAN_UINT(0) * 0.0625 - 40), Celcius);
//ESP_LOGD(TAG, "9012 LBC ms_v_bat_temp: %f", (CAN_UINT(0) * 0.0625 - 40));
break;
}
case 0x9002: { //Battery SOC
StandardMetrics.ms_v_bat_soc->SetValue((float) (CAN_UINT(0)) * 0.01, Percentage);
StandardMetrics.ms_v_bat_cac->SetValue(Bat_cell_capacity * CAN_UINT(0) * 0.0001);
//ESP_LOGD(TAG, "9002 LBC mt_bat_lbc_soc: %f", bat_soc);
break;
}
case 0x9003: { //Battery SOH
StandardMetrics.ms_v_bat_soh->SetValue((float) (CAN_UINT(0) * 0.01), Percentage);
//ESP_LOGD(TAG, "9003 LBC ms_v_bat_soh: %f", CAN_UINT(0) * 0.01);
break;
}
case 0x9243: { //Battery energy charged kWh
StandardMetrics.ms_v_charge_kwh_grid_total->SetValue((float) (CAN_UINT32(0) * 0.001), kWh);
//ESP_LOGD(TAG, "9243 LBC ms_v_charge_kwh_grid_total: %f", CAN_UINT32(0) * 0.001);
break;
}
case 0x9245: { //Battery energy discharged kWh
StandardMetrics.ms_v_bat_energy_used_total->SetValue((float) (CAN_UINT32(0) * 0.001), kWh);
//ESP_LOGD(TAG, "9244 LBC ms_v_bat_energy_used_total: %f", CAN_UINT32(0) * 0.001);
break;
}
case 0x9247: { //Battery energy regenerated kWh
StandardMetrics.ms_v_bat_energy_recd_total->SetValue((float) (CAN_UINT32(0) * 0.001), kWh);
//ESP_LOGD(TAG, "9246 LBC ms_v_bat_energy_recd_total: %f", CAN_UINT32(0) * 0.001);
break;
}
case 0x9210: { //Number of charge cycles
mt_bat_cycles->SetValue(CAN_UINT(0));
//ESP_LOGD(TAG, "9210 LBC mt_bat_cycles: %d", CAN_UINT(0));
break;
}
case 0x9018: { //Max charge power
mt_bat_max_charge_power->SetValue((float) (CAN_UINT(0) * 0.01), kW);
//ESP_LOGD(TAG, "9018 LBC mt_bat_max_charge_power: %f", CAN_UINT(0) * 0.01);
break;
}
case 0x91C8: { //Available charge in kWh
mt_bat_available_energy->SetValue(float(CAN_UINT24(0) * 0.001), kWh);
//ESP_LOGD(TAG, "91C8 LBC mt_bat_available_energy: %f", CAN_UINT24(0) * 0.001);
break;
}
case 0x9131: {
BmsSetCellTemperature(0, CAN_UINT(0) * 0.0625 - 40);
//ESP_LOGD(TAG, "%x: %f C", pid, CAN_UINT(0) * 0.0625 - 40);
break;
}
case 0x9132: {
BmsSetCellTemperature(1, CAN_UINT(0) * 0.0625 - 40);
//ESP_LOGD(TAG, "%x: %f C", pid, CAN_UINT(0) * 0.0625 - 40);
break;
}
case 0x9133: {
BmsSetCellTemperature(2, CAN_UINT(0) * 0.0625 - 40);
//ESP_LOGD(TAG, "%x: %f C", pid, CAN_UINT(0) * 0.0625 - 40);
break;
}
case 0x9134: {
BmsSetCellTemperature(3, CAN_UINT(0) * 0.0625 - 40);
//ESP_LOGD(TAG, "%x: %f C", pid, CAN_UINT(0) * 0.0625 - 40);
break;
}
case 0x9135: {
BmsSetCellTemperature(4, CAN_UINT(0) * 0.0625 - 40);
//ESP_LOGD(TAG, "%x: %f C", pid, CAN_UINT(0) * 0.0625 - 40);
break;
}
case 0x9136: {
BmsSetCellTemperature(5, CAN_UINT(0) * 0.0625 - 40);
//ESP_LOGD(TAG, "%x: %f C", pid, CAN_UINT(0) * 0.0625 - 40);
break;
}
case 0x9137: {
BmsSetCellTemperature(6, CAN_UINT(0) * 0.0625 - 40);
//ESP_LOGD(TAG, "%x: %f C", pid, CAN_UINT(0) * 0.0625 - 40);
break;
}
case 0x9138: {
BmsSetCellTemperature(7, CAN_UINT(0) * 0.0625 - 40);
//ESP_LOGD(TAG, "%x: %f C", pid, CAN_UINT(0) * 0.0625 - 40);
break;
}
case 0x9139: {
BmsSetCellTemperature(8, CAN_UINT(0) * 0.0625 - 40);
//ESP_LOGD(TAG, "%x: %f C", pid, CAN_UINT(0) * 0.0625 - 40);
break;
}
case 0x913A: {
BmsSetCellTemperature(9, CAN_UINT(0) * 0.0625 - 40);
//ESP_LOGD(TAG, "%x: %f C", pid, CAN_UINT(0) * 0.0625 - 40);
break;
}
case 0x913B: {
BmsSetCellTemperature(10, CAN_UINT(0) * 0.0625 - 40);
//ESP_LOGD(TAG, "%x: %f C", pid, CAN_UINT(0) * 0.0625 - 40);
break;
}
case 0x913C: {
BmsSetCellTemperature(11, CAN_UINT(0) * 0.0625 - 40);
//ESP_LOGD(TAG, "%x: %f C", pid, CAN_UINT(0) * 0.0625 - 40);
break;
}
case 0x9021: {
BmsSetCellVoltage(0, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9022: {
BmsSetCellVoltage(1, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9023: {
BmsSetCellVoltage(2, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9024: {
BmsSetCellVoltage(3, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9025: {
BmsSetCellVoltage(4, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9026: {
BmsSetCellVoltage(5, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9027: {
BmsSetCellVoltage(6, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9028: {
BmsSetCellVoltage(7, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9029: {
BmsSetCellVoltage(8, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x902A: {
BmsSetCellVoltage(9, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x902B: {
BmsSetCellVoltage(10, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x902C: {
BmsSetCellVoltage(11, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x902D: {
BmsSetCellVoltage(12, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x902E: {
BmsSetCellVoltage(13, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x902F: {
BmsSetCellVoltage(14, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9030: {
BmsSetCellVoltage(15, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9031: {
BmsSetCellVoltage(16, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9032: {
BmsSetCellVoltage(17, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9033: {
BmsSetCellVoltage(18, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9034: {
BmsSetCellVoltage(19, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9035: {
BmsSetCellVoltage(20, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9036: {
BmsSetCellVoltage(21, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9037: {
BmsSetCellVoltage(22, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9038: {
BmsSetCellVoltage(23, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9039: {
BmsSetCellVoltage(24, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x903A: {
BmsSetCellVoltage(25, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x903B: {
BmsSetCellVoltage(26, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x903C: {
BmsSetCellVoltage(27, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x903D: {
BmsSetCellVoltage(28, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x903E: {
BmsSetCellVoltage(29, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x903F: {
BmsSetCellVoltage(30, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9041: {
BmsSetCellVoltage(31, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9042: {
BmsSetCellVoltage(32, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9043: {
BmsSetCellVoltage(33, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9044: {
BmsSetCellVoltage(34, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9045: {
BmsSetCellVoltage(35, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9046: {
BmsSetCellVoltage(36, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9047: {
BmsSetCellVoltage(37, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9048: {
BmsSetCellVoltage(38, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9049: {
BmsSetCellVoltage(39, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x904A: {
BmsSetCellVoltage(40, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x904B: {
BmsSetCellVoltage(41, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x904C: {
BmsSetCellVoltage(42, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x904D: {
BmsSetCellVoltage(43, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x904E: {
BmsSetCellVoltage(44, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x904F: {
BmsSetCellVoltage(45, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9050: {
BmsSetCellVoltage(46, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9051: {
BmsSetCellVoltage(47, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9052: {
BmsSetCellVoltage(48, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9053: {
BmsSetCellVoltage(49, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9054: {
BmsSetCellVoltage(50, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9055: {
BmsSetCellVoltage(51, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9056: {
BmsSetCellVoltage(52, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9057: {
BmsSetCellVoltage(53, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9058: {
BmsSetCellVoltage(54, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9059: {
BmsSetCellVoltage(55, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x905A: {
BmsSetCellVoltage(56, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x905B: {
BmsSetCellVoltage(57, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x905C: {
BmsSetCellVoltage(58, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x905D: {
BmsSetCellVoltage(59, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x905E: {
BmsSetCellVoltage(60, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x905F: {
BmsSetCellVoltage(61, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9061: {
BmsSetCellVoltage(62, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9062: {
BmsSetCellVoltage(63, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9063: {
BmsSetCellVoltage(64, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9064: {
BmsSetCellVoltage(65, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9065: {
BmsSetCellVoltage(66, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9066: {
BmsSetCellVoltage(67, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9067: {
BmsSetCellVoltage(68, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9068: {
BmsSetCellVoltage(69, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9069: {
BmsSetCellVoltage(70, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x906A: {
BmsSetCellVoltage(71, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x906B: {
BmsSetCellVoltage(72, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x906C: {
BmsSetCellVoltage(73, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x906D: {
BmsSetCellVoltage(74, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x906E: {
BmsSetCellVoltage(75, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x906F: {
BmsSetCellVoltage(76, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9070: {
BmsSetCellVoltage(77, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9071: {
BmsSetCellVoltage(78, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9072: {
BmsSetCellVoltage(79, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9073: {
BmsSetCellVoltage(80, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9074: {
BmsSetCellVoltage(81, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9075: {
BmsSetCellVoltage(82, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9076: {
BmsSetCellVoltage(83, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9077: {
BmsSetCellVoltage(84, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9078: {
BmsSetCellVoltage(85, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9079: {
BmsSetCellVoltage(86, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x907A: {
BmsSetCellVoltage(87, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x907B: {
BmsSetCellVoltage(88, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x907C: {
BmsSetCellVoltage(89, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x907D: {
BmsSetCellVoltage(90, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x907E: {
BmsSetCellVoltage(91, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x907F: {
BmsSetCellVoltage(92, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9081: {
BmsSetCellVoltage(93, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9082: {
BmsSetCellVoltage(94, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
case 0x9083: {
BmsSetCellVoltage(95, CAN_UINT(0) * 0.001);
//ESP_LOGD(TAG, "%x: %f V", pid, CAN_UINT(0) * 0.001);
break;
}
default: {
char *buf = NULL;
size_t rlen = len, offset = 0;
do {
rlen = FormatHexDump(&buf, data + offset, rlen, 16);
offset += 16;
ESP_LOGW(TAG, "OBD2: unhandled reply from LBC [%02x %02x]: %s", type, pid, buf ? buf : "-");
} while (rlen);
if (buf)
free(buf);
break;
}
}
}

View file

@ -1,6 +1,6 @@
/*
; Project: Open Vehicle Monitor System
; Date: 15th Apr 2022
; Date: 19th Nov 2022
;
; (C) 2022 Carsten Schmiemann
;
@ -22,17 +22,16 @@
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
*/
#include "vehicle_renaultzoe_ph2_can.h"
#include "vehicle_renaultzoe_ph2.h"
void OvmsVehicleRenaultZoePh2::IncomingCLUSTER(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x2101: { // Cluster - trip counter
mt_pos_car_trip->SetValue(CAN_UINT32(0), Kilometers);
ESP_LOGD(TAG, "2101 CLUSTER mt_pos_car_trip: %f", float(CAN_UINT32(0)));
//working very unreliable
break;
}
void OvmsVehicleRenaultZoePh2CAN::IncomingUCM(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x6079: { //12V Battery Current
StandardMetrics.ms_v_charge_12v_current->SetValue((float) (CAN_UINT(0) * 0.1), Amps);
StandardMetrics.ms_v_bat_12v_current->SetValue((float) (CAN_UINT(0) * 0.1), Amps);
//ESP_LOGD(TAG, "6079 UCM ms_v_charge_12v_current: %f", CAN_UINT(0) * 0.1);
break;
}
default: {
char *buf = NULL;
@ -40,7 +39,7 @@ void OvmsVehicleRenaultZoePh2::IncomingCLUSTER(uint16_t type, uint16_t pid, cons
do {
rlen = FormatHexDump(&buf, data + offset, rlen, 16);
offset += 16;
ESP_LOGW(TAG, "OBD2: unhandled reply from CLUSTER [%02x %02x]: %s", type, pid, buf ? buf : "-");
ESP_LOGW(TAG, "OBD2: unhandled reply from UCM [%02x %02x]: %s", type, pid, buf ? buf : "-");
} while (rlen);
if (buf)
free(buf);

View file

@ -0,0 +1,252 @@
/*
; 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 <stdio.h>
#include <string>
#include <iomanip>
#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"
#include "ph2_poller.h"
void OvmsVehicleRenaultZoePh2CAN::CanInit()
{
OvmsCommand* cmd;
OvmsCommand* ph2;
ph2 = cmd_zoe_ph2->RegisterCommand("ph2", "Zoe Ph2 CAN tools");
cmd = ph2->RegisterCommand("request", "Send ISO-TP request, output response");
cmd->RegisterCommand("device", "Send ISO-TP request to a ECU", shell_can_request, "<txid> <rxid> <request>", 3, 3);
cmd->RegisterCommand("broadcast", "Send ISO-TP request as broadcast", shell_can_request, "<request>", 1, 1);
}
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;
}
void OvmsVehicleRenaultZoePh2CAN::shell_can_request(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
{
OvmsVehicleRenaultZoePh2CAN* zoe_ph2_can = GetInstance(writer);
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 = zoe_ph2_can->CanRequest(txid, rxid, request, response, 3000);
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);
}
OvmsVehicle::vehicle_command_t OvmsVehicleRenaultZoePh2CAN::CommandPreHeat(bool climatecontrolon) {
//Wake Up Packet
uint8_t data[8] = {0x83, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
canbus *obd;
obd = m_can1;
for (int i=0; i<40; i++)
{
obd->WriteStandard(0x55C, 8, data);
}
obd->WriteStandard(0x6f9, 8, data);
data[0] = 0x40;
data[1] = 0x80;
vTaskDelay(50 / portTICK_PERIOD_MS);
obd->WriteStandard(0x43c, 8, data);
data = {0xe4, 0x64, 0x70, 0x00, 0x28, 0x84, 0x07, 0x10};
obd->WriteStandard(0x46f, 8, data);
data = {0x84, 0xc7, 0x07, 0x55, 0xe0, 0x0d, 0x00, 0xc4};
obd->WriteStandard(0x47d, 8, data);
data = {0x03, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00};
obd->WriteStandard(0x46a, 8, data);
data = {0x03, 0x02, 0x04, 0x02, 0x06, 0x00, 0x20, 0x08};
obd->WriteStandard(0x46c, 8, data);
data = {0x10, 0xc9, 0x50, 0x21, 0x01, 0x20, 0x00, 0x00};
obd->WriteStandard(0x482, 8, data);
data = {0x00, 0x00, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00};
obd->WriteStandard(0x5b1, 8, data);
data = {0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00};
obd->WriteStandard(0x5ba, 8, data);
return Success;
}
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;
}

View file

@ -0,0 +1,224 @@
/*
; 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.
*/
#define SESSION_EXTDIAG 0x1003
#define SESSION_DEFAULT 0x1001
#define SESSION_AfterSales 0x10C0
// Pollstate 0 - POLLSTATE_OFF - car is off
// Pollstate 1 - POLLSTATE_ON - car is on
// Pollstate 2 - POLLSTATE_DRIVING - car is driving
// Pollstate 3 - POLLSTATE_CHARGING - car is charging
static const OvmsVehicle::poll_pid_t renault_zoe_polls[] = {
//***TX-ID, ***RX-ID, ***SID, ***PID, { Polltime (seconds) for Pollstate 0, 1, 2, 3}, ***CAN BUS Interface, ***FRAMETYPE
//Motor Inverter
//{ 0x18dadff1, 0x18daf1df, VEHICLE_POLL_TYPE_OBDIISESSION, SESSION_EXTDIAG, { 0, 60, 60, 60 }, 0, ISOTP_EXTFRAME }, // OBD Extended Diagnostic Session
{ 0x18dadff1, 0x18daf1df, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x700C, { 0, 10, 10, 10 }, 0, ISOTP_EXTFRAME }, // Inverter temperature
{ 0x18dadff1, 0x18daf1df, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x700F, { 0, 10, 10, 10 }, 0, ISOTP_EXTFRAME }, // Stator Temperature 1
{ 0x18dadff1, 0x18daf1df, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x7010, { 0, 10, 10, 10 }, 0, ISOTP_EXTFRAME }, // Stator Temperature 2
{ 0x18dadff1, 0x18daf1df, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2004, { 0, 60, 3, 60 }, 0, ISOTP_EXTFRAME }, // Battery voltage sense
{ 0x18dadff1, 0x18daf1df, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x7049, { 0, 60, 3, 60 }, 0, ISOTP_EXTFRAME }, // Current voltage sense
//EVC-HCM-VCM
//{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIISESSION, SESSION_EXTDIAG, { 0, 60, 60, 60 }, 0, ISOTP_EXTFRAME }, // OBD Extended Diagnostic Session
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2006, { 0, 10, 10, 300 }, 0, ISOTP_EXTFRAME }, // Odometer
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2003, { 0, 60, 2, 300 }, 0, ISOTP_EXTFRAME }, // Vehicle Speed
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2005, { 0, 10, 10, 10 }, 0, ISOTP_EXTFRAME }, // 12Battery Voltage
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x21CF, { 0, 2, 10, 300 }, 0, ISOTP_EXTFRAME }, // Inverter Status
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2218, { 0, 20, 20, 20 }, 0, ISOTP_EXTFRAME }, // Ambient air temperature
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2A09, { 0, 10, 10, 10 }, 0, ISOTP_EXTFRAME }, // Power usage by consumer
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2191, { 0, 10, 10, 10 }, 0, ISOTP_EXTFRAME }, // Power usage by ptc
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2B85, { 0, 2, 300, 10 }, 0, ISOTP_EXTFRAME }, // Charge plug present
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2B6D, { 0, 2, 300, 10 }, 0, ISOTP_EXTFRAME }, // Charge MMI states
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2B7A, { 0, 2, 300, 10 }, 0, ISOTP_EXTFRAME }, // Charge type
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x3064, { 0, 10, 3, 10 }, 0, ISOTP_EXTFRAME }, // Motor rpm
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x300F, { 0, 2, 300, 3 }, 0, ISOTP_EXTFRAME }, // AC charging power available
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x300D, { 0, 10, 300, 3 }, 0, ISOTP_EXTFRAME }, // AC mains current
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x300B, { 0, 2, 300, 10 }, 0, ISOTP_EXTFRAME }, // AC phases
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2B8A, { 0, 2, 300, 10 }, 0, ISOTP_EXTFRAME }, // AC mains voltage
//BCM
//{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIISESSION, SESSION_DEFAULT, { 0, 60, 60, 60 }, 0, ISOTP_STD }, // OBD Extended Diagnostic Session
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6300, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS pressure - front left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6301, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS pressure - front right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6302, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS pressure - rear left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6303, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS pressure - rear right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6310, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS temp - front left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6311, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS temp - front right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6312, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS temp - rear left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6313, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS temp - rear right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4109, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS alert - front left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x410A, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS alert - front right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x410B, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS alert - rear left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x410C, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS alert - rear right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x8004, { 0, 3, 3, 3 }, 0, ISOTP_STD }, // Car secure aka vehicle locked
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6026, { 0, 3, 3, 3 }, 0, ISOTP_STD }, // Front left door
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6027, { 0, 3, 3, 3 }, 0, ISOTP_STD }, // Front right door
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x61B2, { 0, 3, 3, 3 }, 0, ISOTP_STD }, // Rear left door
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x61B3, { 0, 3, 3, 3 }, 0, ISOTP_STD }, // Rear right door
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x609B, { 0, 3, 3, 3 }, 0, ISOTP_STD }, // Tailgate
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4186, { 0, 3, 3, 3 }, 0, ISOTP_STD }, // Low beam lights
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x60C6, { 0, 3, 6, 60 }, 0, ISOTP_STD }, // Ignition relay (switch), working but behavior on CHARING testing needed
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4060, { 0, 600, 0, 0 }, 0, ISOTP_STD }, // Vehicle identificaftion number
//LBC
//{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIISESSION, SESSION_DEFAULT, { 0, 60, 60, 60 }, 0, ISOTP_EXTFRAME }, // OBD Extended Diagnostic Session
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9002, { 0, 10, 10, 10 }, 0, ISOTP_EXTFRAME }, // SOC
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9003, { 0, 60, 60, 10 }, 0, ISOTP_EXTFRAME }, // SOH
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9005, { 0, 10, 3, 5 }, 0, ISOTP_EXTFRAME }, // Battery Voltage
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x925D, { 0, 10, 3, 5 }, 0, ISOTP_EXTFRAME }, // Battery Current
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9012, { 0, 10, 60, 5 }, 0, ISOTP_EXTFRAME }, // Battery Average Temperature
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x91C8, { 0, 60, 60, 5 }, 0, ISOTP_EXTFRAME }, // Battery Available Energy kWh
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9243, { 0, 60, 60, 10 }, 0, ISOTP_EXTFRAME }, // Energy charged kWh
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9245, { 0, 60, 10, 60 }, 0, ISOTP_EXTFRAME }, // Energy discharged kWh
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9247, { 0, 60, 10, 60 }, 0, ISOTP_EXTFRAME }, // Energy regenerated kWh
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9210, { 0, 60, 60, 60 }, 0, ISOTP_EXTFRAME }, // Number of complete cycles
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9018, { 0, 10, 60, 10 }, 0, ISOTP_EXTFRAME }, // Max Charge Power
//LBC Cell voltages and temperatures, OBD Grouppoll not working
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9131, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 1
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9132, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 2
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9133, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 3
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9134, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 4
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9135, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 5
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9136, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 6
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9137, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 7
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9138, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 8
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9139, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 9
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x913A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 10
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x913B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 11
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x913C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 12
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9021, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 1
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9022, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 2
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9023, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 3
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9024, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 4
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9025, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 5
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9026, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 6
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9027, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 7
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9028, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 8
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9029, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 9
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 10
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 11
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 12
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902D, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 13
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902E, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 14
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902F, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 15
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9030, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 16
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9031, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 17
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9032, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 18
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9033, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 19
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9034, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 20
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9035, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 21
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9036, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 22
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9037, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 23
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9038, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 24
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9039, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 25
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 26
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 27
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 28
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903D, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 29
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903E, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 30
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903F, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 31
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9041, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 32
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9042, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 33
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9043, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 34
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9044, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 35
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9045, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 36
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9046, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 37
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9047, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 38
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9048, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 39
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9049, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 40
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 41
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 42
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 43
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904D, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 44
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904E, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 45
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904F, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 46
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9050, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 47
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9051, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 48
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9052, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 49
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9053, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 50
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9054, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 51
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9055, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 52
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9056, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 53
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9057, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 54
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9058, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 55
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9059, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 56
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 57
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 58
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 59
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905D, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 60
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905E, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 61
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905F, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 62
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9061, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 63
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9062, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 64
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9063, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 65
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9064, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 66
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9065, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 67
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9066, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 68
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9067, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 69
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9068, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 70
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9069, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 71
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 72
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 73
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 74
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906D, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 75
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906E, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 76
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906F, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 77
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9070, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 78
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9071, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 79
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9072, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 80
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9073, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 81
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9074, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 82
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9075, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 83
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9076, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 84
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9077, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 85
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9078, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 86
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9079, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 87
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 88
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 89
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 90
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907D, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 91
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907E, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 92
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907F, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 93
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9081, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 94
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9082, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 95
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9083, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 96
//HVAC
//{ 0x744, 0x764, VEHICLE_POLL_TYPE_OBDIISESSION, SESSION_EXTDIAG, { 0, 60, 60, 60 }, 0, ISOTP_STD }, // OBD Extended Diagnostic Session
{ 0x744, 0x764, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4009, { 0, 10, 60, 10 }, 0, ISOTP_STD }, // Cabin temp
{ 0x744, 0x764, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4360, { 0, 60, 60, 60 }, 0, ISOTP_STD }, // Cabin setpoint
{ 0x744, 0x764, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x43D8, { 0, 10, 10, 5 }, 0, ISOTP_STD }, // Compressor speed
{ 0x744, 0x764, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4402, { 0, 10, 10, 10 }, 0, ISOTP_STD }, // Compressor state
{ 0x744, 0x764, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4369, { 0, 10, 10, 10 }, 0, ISOTP_STD }, // Compressor pressure in BAR
{ 0x744, 0x764, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4436, { 0, 10, 10, 10 }, 0, ISOTP_STD }, // Compressor power
//UCM
//{ 0x74D, 0x76D, VEHICLE_POLL_TYPE_OBDIISESSION, SESSION_AfterSales, { 0, 60, 60, 60 }, 0, ISOTP_STD }, // OBD Extended Diagnostic Session
{ 0x74D, 0x76D, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6079, { 0, 10, 10, 10 }, 0, ISOTP_STD }, // 12V Battery current
POLL_LIST_END
};

View file

@ -0,0 +1,135 @@
/*
; 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 <sdkconfig.h>
#ifdef CONFIG_OVMS_COMP_WEBSERVER
#include <stdio.h>
#include <string>
#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_webserver.h"
#include "vehicle_renaultzoe_ph2_can.h"
using namespace std;
#define _attr(text) (c.encode_html(text).c_str())
#define _html(text) (c.encode_html(text).c_str())
void OvmsVehicleRenaultZoePh2CAN::WebCfgCommon(PageEntry_t& p, PageContext_t& c)
{
std::string error, rangeideal, battcapacity;
bool UseBMScalculation;
if (c.method == "POST") {
rangeideal = c.getvar("rangeideal");
battcapacity = c.getvar("battcapacity");
UseBMScalculation = (c.getvar("UseBMScalculation") == "no");
if (!rangeideal.empty()) {
int v = atoi(rangeideal.c_str());
if (v < 90 || v > 500)
error += "<li data-input=\"rangeideal\">Range Ideal must be of 80…500 km</li>";
}
if (error == "") {
// store:
MyConfig.SetParamValue("xrz2c", "rangeideal", rangeideal);
MyConfig.SetParamValue("xrz2c", "battcapacity", battcapacity);
MyConfig.SetParamValueBool("xrz2c", "UseBMScalculation", UseBMScalculation);
c.head(200);
c.alert("success", "<p class=\"lead\">Renault Zoe Ph2 battery setup saved.</p>");
MyWebServer.OutputHome(p, c);
c.done();
return;
}
error = "<p class=\"lead\">Error!</p><ul class=\"errorlist\">" + error + "</ul>";
c.head(400);
c.alert("danger", error.c_str());
}
else {
// read configuration:
rangeideal = MyConfig.GetParamValue("xrz2c", "rangeideal", "350");
battcapacity = MyConfig.GetParamValue("xrz2c", "battcapacity", "52000");
UseBMScalculation = MyConfig.GetParamValueBool("xrz2c", "UseBMScalculation", false);
c.head(200);
}
c.panel_start("primary", "Renault Zoe Ph2 Battery Setup");
c.form_start(p.uri);
c.fieldset_start("Battery size and ideal range");
c.input_radio_start("Battery size", "battcapacity");
c.input_radio_option("battcapacity", "R240 (22kWh)", "22000", battcapacity == "22000");
c.input_radio_option("battcapacity", "ZE40 (41kWh)", "41000", battcapacity == "41000");
c.input_radio_option("battcapacity", "ZE50 (52kWh)", "52000", battcapacity == "52000");
c.input_radio_end("");
c.input_slider("Range Ideal", "rangeideal", 3, "km", -1, atoi(rangeideal.c_str()), 350, 80, 500, 1,
"<p>Default 350km. Ideal Range...</p>");
c.fieldset_start("Battery energy calculation");
c.input_radio_start("Which energy calculation?", "UseBMScalculation");
c.input_radio_option("UseBMScalculation", "OVMS energy calculation", "yes", UseBMScalculation == false);
c.input_radio_option("UseBMScalculation", "BMS-based calculation", "no", UseBMScalculation == true);
c.input_radio_end("");
c.fieldset_end();
c.print("<hr>");
c.input_button("default", "Save");
c.form_end();
c.panel_end();
c.done();
}
/**
* WebInit: register pages
*/
void OvmsVehicleRenaultZoePh2CAN::WebInit()
{
MyWebServer.RegisterPage("/xrz2c/battmon", "BMS View", OvmsWebServer::HandleBmsCellMonitor, PageMenu_Vehicle, PageAuth_Cookie);
MyWebServer.RegisterPage("/xrz2c/settings", "Setup", WebCfgCommon, PageMenu_Vehicle, PageAuth_Cookie);
}
/**
* WebDeInit: deregister pages
*/
void OvmsVehicleRenaultZoePh2CAN::WebDeInit()
{
MyWebServer.DeregisterPage("/xrz2c/battmon");
MyWebServer.DeregisterPage("/xrz2c/battery");
}
#endif //CONFIG_OVMS_COMP_WEBSERVER

View file

@ -0,0 +1,422 @@
/*
; 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 <stdio.h>
#include <string>
#include <iomanip>
#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"
#include "ph2_poller.h"
const char *OvmsVehicleRenaultZoePh2CAN::s_tag = "v-zoe-ph2-can";
OvmsVehicleRenaultZoePh2CAN::OvmsVehicleRenaultZoePh2CAN() {
ESP_LOGI(TAG, "Start Renault Zoe Ph2 (CAN) vehicle module");
//Init variables supressing push on boot up
StandardMetrics.ms_v_type->SetValue("RZ2");
StandardMetrics.ms_v_charge_inprogress->SetValue(false);
StandardMetrics.ms_v_charge_pilot->SetValue(false);
StandardMetrics.ms_v_charge_state->SetValue("stopped");
StandardMetrics.ms_v_charge_substate->SetValue("stopped");
StandardMetrics.ms_v_env_on->SetValue(false);
MyConfig.RegisterParam("xrz2c", "Renault Zoe Ph2 (CAN) configuration", true, true);
ConfigChanged(NULL);
// CAN1: Init Zoe Ph2 M-CAN bus
RegisterCanBus(1, CAN_MODE_ACTIVE, CAN_SPEED_500KBPS);
// CAN2: Init Zoe Ph2 EXT-CAN bus
RegisterCanBus(2, CAN_MODE_LISTEN, CAN_SPEED_500KBPS);
// CAN3: Init Zoe Ph2 V1-CAN bus
RegisterCanBus(3, CAN_MODE_LISTEN, CAN_SPEED_500KBPS);
cmd_zoe_ph2 = MyCommandApp.RegisterCommand("xrz2c", "Renault Zoe Phase 2");
CanInit();
POLLSTATE_OFF;
ESP_LOGI(TAG, "Pollstate switched to OFF");
PollSetPidList(m_can1, renault_zoe_polls);
PollSetThrottling(10);
PollSetResponseSeparationTime(20);
// Renault ZOE specific metrics
mt_bus_awake = MyMetrics.InitBool("zph2.v.bus.awake", SM_STALE_NONE, false);
mt_pos_odometer_start = MyMetrics.InitFloat("zph2.v.pos.odometer.start", SM_STALE_MID, 0, Kilometers, true);
mt_bat_used_start = MyMetrics.InitFloat("zph2.b.used.start", SM_STALE_MID, 0, kWh, true);
mt_bat_recd_start = MyMetrics.InitFloat("zph2.b.recd.start", SM_STALE_MID, 0, kWh, true);
mt_bat_chg_start = MyMetrics.InitFloat("zph2.b.chg.start", SM_STALE_MID, 0, kWh, true);
mt_bat_available_energy = MyMetrics.InitFloat("zph2.b.avail.energy", SM_STALE_NONE, 0, kWh, true);
mt_bat_aux_power_consumer = MyMetrics.InitFloat("zph2.b.aux.power.consumer", SM_STALE_MID, 0, Watts);
mt_bat_aux_power_ptc = MyMetrics.InitFloat("zph2.b.aux.power.ptc", SM_STALE_MID, 0, Watts);
mt_bat_max_charge_power = MyMetrics.InitFloat("zph2.b.max.charge.power", SM_STALE_MID, 0, kW, true);
mt_bat_cycles = MyMetrics.InitFloat("zph2.b.cycles", SM_STALE_MID, 0, Other, true);
mt_main_power_available = MyMetrics.InitFloat("zph2.c.main.power.available", SM_STALE_MIN, 0, kW);
mt_main_phases = MyMetrics.InitString("zph2.c.main.phases", SM_STALE_MIN, 0);
mt_main_phases_num = MyMetrics.InitFloat("zph2.c.main.phases.num", SM_STALE_MIN, 0);
mt_inv_status = MyMetrics.InitString("zph2.m.inverter.status", SM_STALE_NONE, 0);
mt_inv_hv_voltage = MyMetrics.InitFloat("zph2.i.voltage", SM_STALE_MID, 0, Volts, true);
mt_inv_hv_current = MyMetrics.InitFloat("zph2.i.current", SM_STALE_MID, 0, Amps);
mt_mot_temp_stator1 = MyMetrics.InitFloat("zph2.m.temp.stator1", SM_STALE_MID, 0, Celcius);
mt_mot_temp_stator2 = MyMetrics.InitFloat("zph2.m.temp.stator2", SM_STALE_MID, 0, Celcius);
mt_hvac_compressor_speed = MyMetrics.InitFloat("zph2.h.compressor.speed", SM_STALE_MID, 0);
mt_hvac_compressor_pressure = MyMetrics.InitFloat("zph2.h.compressor.pressure", SM_STALE_MID, 0);
mt_hvac_compressor_power = MyMetrics.InitFloat("zph2.h.compressor.power", SM_STALE_MID, 0, Watts);
mt_hvac_compressor_mode = MyMetrics.InitString("zph2.h.compressor.mode", SM_STALE_MID, 0, Other);
// BMS configuration:
BmsSetCellArrangementVoltage(96, 1);
BmsSetCellArrangementTemperature(12, 1);
BmsSetCellLimitsVoltage(2.0, 5.0);
BmsSetCellLimitsTemperature(-39, 200);
BmsSetCellDefaultThresholdsVoltage(0.030, 0.050);
BmsSetCellDefaultThresholdsTemperature(4.0, 5.0);
#ifdef CONFIG_OVMS_COMP_WEBSERVER
WebInit();
#endif
}
OvmsVehicleRenaultZoePh2CAN::~OvmsVehicleRenaultZoePh2CAN() {
ESP_LOGI(TAG, "Stop Renault Zoe Ph2 (CAN) vehicle module");
}
void OvmsVehicleRenaultZoePh2CAN::ConfigChanged(OvmsConfigParam* param) {
if (param && param->GetName() != "xrz2c")
return;
// get values from config store
m_range_ideal = MyConfig.GetParamValueInt("xrz2c", "rangeideal", 350);
m_battery_capacity = MyConfig.GetParamValueInt("xrz2c", "battcapacity", 52000);
m_UseBMScalculation = MyConfig.GetParamValueBool("xrz2c", "UseBMScalculation", false);
StandardMetrics.ms_v_bat_range_ideal->SetValue(m_range_ideal, Kilometers);
if (m_battery_capacity == 52000) {
Bat_cell_capacity = 78.0 * 2 * (StandardMetrics.ms_v_bat_soh->AsFloat() / 100.0);
}
if (m_battery_capacity == 41000) {
Bat_cell_capacity = 65.6 * 2 * (StandardMetrics.ms_v_bat_soh->AsFloat() / 100.0);
}
if (m_battery_capacity == 22000) {
Bat_cell_capacity = 36.0 * 2 * (StandardMetrics.ms_v_bat_soh->AsFloat() / 100.0);
}
ESP_LOGI(TAG, "Renault Zoe Ph2 (CAN) reload configuration: Range ideal: %d, Battery capacity: %d, Use BMS as energy counter: %s", m_range_ideal, m_battery_capacity, m_UseBMScalculation ? "yes" : "no");
}
void OvmsVehicleRenaultZoePh2CAN::ZoeWakeUp() {
ESP_LOGI(TAG,"Zoe woke up (CAN Bus activity detected)");
mt_bus_awake->SetValue(true);;
StandardMetrics.ms_v_env_charging12v->SetValue(true);
POLLSTATE_ON;
ESP_LOGI(TAG, "Pollstate switched to ON");
}
/**
* Handles incoming CAN-frames on bus 1, connected to M-CAN
*/
void OvmsVehicleRenaultZoePh2CAN::IncomingFrameCan1(CAN_frame_t* p_frame) {
//uint8_t *data = p_frame->data.u8;
//ESP_LOGI(TAG, "M-CAN: PID:%x DATA: %02x %02x %02x %02x %02x %02x %02x %02x", p_frame->MsgID, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
//tooooooo many packets, OVMS will crash with logging
//If a CAN message comes in, start polling
if (!mt_bus_awake->AsBool()) {
ZoeWakeUp();
}
//Reset CAN1 inactivity timer, inactivity after 5 sec detected to stop polling
m_can1_activity_timer = 5;
}
/**
* Handles incoming CAN-frames on bus 2, connected to EXT-CAN
*/
void OvmsVehicleRenaultZoePh2CAN::IncomingFrameCan2(CAN_frame_t* p_frame) {
//uint8_t *data = p_frame->data.u8;
//ESP_LOGI(TAG, "V1-CAN: PID:%x DATA: %02x %02x %02x %02x %02x %02x %02x %02x", p_frame->MsgID, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
//tooooooo many packets, OVMS will crash with logging
//ToDo: Analyse PIDs and copy data to useful metrics
}
/**
* Handles incoming CAN-frames on bus 3, connected to V1-CAN
*/
void OvmsVehicleRenaultZoePh2CAN::IncomingFrameCan3(CAN_frame_t* p_frame) {
//uint8_t *data = p_frame->data.u8;
//ESP_LOGI(TAG, "V1-CAN: PID:%x DATA: %02x %02x %02x %02x %02x %02x %02x %02x", p_frame->MsgID, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
//tooooooo many packets, OVMS will crash with logging
//ToDo: Analyse PIDs and copy data to useful metrics
}
/**
* Handles incoming poll results
*/
void OvmsVehicleRenaultZoePh2CAN::IncomingPollReply(canbus* bus, uint16_t type, uint16_t pid, uint8_t* data, uint8_t length, uint16_t remain) {
string& rxbuf = zoe_can1_rxbuf;
//ESP_LOGV(TAG, "pid: %04x length: %d m_poll_ml_remain: %d m_poll_ml_frame: %d", pid, length, m_poll_ml_remain, m_poll_ml_frame);
// init / fill rx buffer:
if (m_poll_ml_frame == 0) {
rxbuf.clear();
rxbuf.reserve(length + remain);
}
rxbuf.append((char*)data, length);
if (remain)
return;
switch (m_poll_moduleid_low) {
// ****** INV *****
case 0x18daf1df:
IncomingINV(type, pid, rxbuf.data(), rxbuf.size());
break;
// ****** EVC *****
case 0x18daf1da:
IncomingEVC(type, pid, rxbuf.data(), rxbuf.size());
break;
// ****** BCM *****
case 0x765:
IncomingBCM(type, pid, rxbuf.data(), rxbuf.size());
break;
// ****** LBC *****
case 0x18daf1db:
IncomingLBC(type, pid, rxbuf.data(), rxbuf.size());
break;
// ****** HVAC *****
case 0x764:
IncomingHVAC(type, pid, rxbuf.data(), rxbuf.size());
break;
// ****** UCM *****
case 0x76D:
IncomingUCM(type, pid, rxbuf.data(), rxbuf.size());
break;
// ****** CLUSTER *****
//case 0x763:
// IncomingCLUSTER(type, pid, rxbuf.data(), rxbuf.size());
// break;
}
}
int OvmsVehicleRenaultZoePh2CAN::calcMinutesRemaining(float charge_voltage, float charge_current) {
float bat_soc = StandardMetrics.ms_v_bat_soc->AsFloat(100);
float remaining_wh = m_battery_capacity - (m_battery_capacity * bat_soc / 100.0);
float remaining_hours = remaining_wh / (charge_current * charge_voltage);
float remaining_mins = remaining_hours * 60.0;
//ESP_LOGD(TAG, "SOC: %f, BattCap:%d, Current: %f, Voltage: %f, RemainWH: %f, RemainHour: %f, RemainMin: %f", bat_soc, m_battery_capacity, charge_current, charge_voltage, remaining_wh, remaining_hours, remaining_mins);
return MIN( 1440, (int)remaining_mins );
}
//Handle Charging values
void OvmsVehicleRenaultZoePh2CAN::ChargeStatistics() {
float charge_current = fabs(StandardMetrics.ms_v_bat_current->AsFloat(0, Amps));
float charge_voltage = StandardMetrics.ms_v_bat_voltage->AsFloat(0, Volts);
float battery_power = fabs(StandardMetrics.ms_v_bat_power->AsFloat(0, kW));
float charger_power = StandardMetrics.ms_v_charge_power->AsFloat(0, kW);
float ac_current = StandardMetrics.ms_v_charge_current->AsFloat(0, Amps);
string ac_phases = mt_main_phases->AsString();
if (CarIsCharging != CarLastCharging) {
if (CarIsCharging) {
StandardMetrics.ms_v_charge_kwh->SetValue(0);
StandardMetrics.ms_v_charge_inprogress->SetValue(CarIsCharging);
if (m_UseBMScalculation) {
mt_bat_chg_start->SetValue(StandardMetrics.ms_v_charge_kwh_grid_total->AsFloat());
}
} else {
StandardMetrics.ms_v_charge_inprogress->SetValue(CarIsCharging);
}
}
CarLastCharging = CarIsCharging;
if (!StandardMetrics.ms_v_charge_pilot->AsBool() || !StandardMetrics.ms_v_charge_inprogress->AsBool() || (charge_current <= 0.0) ) {
return;
}
if (charge_voltage > 0 && charge_current > 0) {
float power = charge_voltage * charge_current / 1000.0;
float energy = power / 3600.0 * 10.0;
float c_efficiency;
if (m_UseBMScalculation) {
StandardMetrics.ms_v_charge_kwh->SetValue(StandardMetrics.ms_v_charge_kwh_grid_total->AsFloat() - mt_bat_chg_start->AsFloat());
} else {
StandardMetrics.ms_v_charge_kwh->SetValue( StandardMetrics.ms_v_charge_kwh->AsFloat() + energy);
}
int minsremaining = calcMinutesRemaining(charge_voltage, charge_current);
StandardMetrics.ms_v_charge_duration_full->SetValue(minsremaining, Minutes);
if (StandardMetrics.ms_v_charge_type->AsString() == "type2") {
c_efficiency = (battery_power / charger_power) * 100.0;
if (c_efficiency < 100.0) {
StandardMetrics.ms_v_charge_efficiency->SetValue(c_efficiency);
}
ESP_LOGI(TAG, "Charge time remaining: %d mins, AC Charge at %.2f kW with %.1f amps, %s at %.1f efficiency, %.2f powerfactor", minsremaining, charger_power, ac_current, ac_phases.c_str(), StandardMetrics.ms_v_charge_efficiency->AsFloat(100), ACInputPowerFactor);
} else if (StandardMetrics.ms_v_charge_type->AsString() == "ccs" || StandardMetrics.ms_v_charge_type->AsString() == "chademo") {
StandardMetrics.ms_v_charge_power->SetValue(battery_power);
ESP_LOGI(TAG, "Charge time remaining: %d mins, DC Charge at %.2f kW", minsremaining, battery_power);
}
}
}
void OvmsVehicleRenaultZoePh2CAN::EnergyStatisticsOVMS() {
float voltage = StandardMetrics.ms_v_bat_voltage->AsFloat(0, Volts);
float current = StandardMetrics.ms_v_bat_current->AsFloat(0, Amps);
float power = voltage * current / 1000.0;
if (power != 0.0 && StandardMetrics.ms_v_env_on->AsBool()) {
float energy = power / 3600.0 * 10.0;
if (energy > 0.0f)
StandardMetrics.ms_v_bat_energy_used->SetValue( StandardMetrics.ms_v_bat_energy_used->AsFloat() + energy);
else
StandardMetrics.ms_v_bat_energy_recd->SetValue( StandardMetrics.ms_v_bat_energy_recd->AsFloat() - energy);
}
}
void OvmsVehicleRenaultZoePh2CAN::EnergyStatisticsBMS() {
StandardMetrics.ms_v_bat_energy_used->SetValue(StandardMetrics.ms_v_bat_energy_used_total->AsFloat() - mt_bat_used_start->AsFloat());
StandardMetrics.ms_v_bat_energy_recd->SetValue(StandardMetrics.ms_v_bat_energy_recd_total->AsFloat() - mt_bat_recd_start->AsFloat());
}
void OvmsVehicleRenaultZoePh2CAN::Ticker10(uint32_t ticker) {
if (StandardMetrics.ms_v_charge_pilot->AsBool() && !StandardMetrics.ms_v_env_on->AsBool()) {
ChargeStatistics();
} else {
if (m_UseBMScalculation) {
EnergyStatisticsBMS();
} else {
EnergyStatisticsOVMS();
}
}
}
void OvmsVehicleRenaultZoePh2CAN::Ticker1(uint32_t ticker) {
//Stop polling after 5 seconds of inactivity on CAN1 (M-CAN)
if (m_can1_activity_timer > 0) {
if (--m_can1_activity_timer == 0) {
ESP_LOGI(TAG,"Zoe has gone to sleep (No messages on M-CAN for 10 seconds)");
mt_bus_awake->SetValue(false);
StandardMetrics.ms_v_env_awake->SetValue(false);
StandardMetrics.ms_v_env_charging12v->SetValue(false);
StandardMetrics.ms_v_pos_speed->SetValue( 0 );
StandardMetrics.ms_v_bat_12v_current->SetValue( 0 );
StandardMetrics.ms_v_charge_12v_current->SetValue( 0 );
StandardMetrics.ms_v_bat_current->SetValue( 0 );
POLLSTATE_OFF;
ESP_LOGI(TAG, "Pollstate switched to OFF");
//Check if car is locked, if not send notify
if (!StandardMetrics.ms_v_env_locked->AsBool()) {
MyNotify.NotifyString("alert", "vehicle.lock", "Vehicle is not locked");
}
}
}
if (StandardMetrics.ms_v_env_on->AsBool() && !CarIsDriving) {
CarIsDriving = true;
//Start trip after power on
StandardMetrics.ms_v_pos_trip->SetValue(0);
mt_pos_odometer_start->SetValue(StandardMetrics.ms_v_pos_odometer->AsFloat(0));
StandardMetrics.ms_v_bat_energy_used->SetValue(0);
StandardMetrics.ms_v_bat_energy_recd->SetValue(0);
if (m_UseBMScalculation) {
mt_bat_used_start->SetValue(StandardMetrics.ms_v_bat_energy_used_total->AsFloat());
mt_bat_recd_start->SetValue(StandardMetrics.ms_v_bat_energy_recd_total->AsFloat());
}
}
if (CarIsDriving && !CarIsDrivingInit) {
CarIsDrivingInit = true;
ESP_LOGI(TAG, "Pollstate switched to DRIVING");
POLLSTATE_DRIVING;
}
if (!StandardMetrics.ms_v_env_on->AsBool() && CarIsDriving) {
CarIsDriving = false;
CarIsDrivingInit = false;
ESP_LOGI(TAG, "Pollstate switched to ON");
POLLSTATE_ON;
}
if (StandardMetrics.ms_v_charge_pilot->AsBool() && StandardMetrics.ms_v_charge_inprogress->AsBool() && !CarIsCharging) {
CarIsCharging = true;
ESP_LOGI(TAG, "Pollstate switched to CHARGING");
POLLSTATE_CHARGING;
} else if (!StandardMetrics.ms_v_charge_pilot->AsBool() && CarIsCharging) {
CarIsCharging = false;
StandardMetrics.ms_v_charge_duration_full->SetValue(0);
ESP_LOGI(TAG, "Pollstate switched to ON");
POLLSTATE_ON;
} else if (StandardMetrics.ms_v_charge_pilot->AsBool() && CarIsCharging && StandardMetrics.ms_v_charge_substate->AsString() == "powerwait") {
CarIsCharging = false;
StandardMetrics.ms_v_charge_duration_full->SetValue(0);
ESP_LOGI(TAG, "Pollstate switched to OFF, Wait for power...");
POLLSTATE_OFF;
} else if (StandardMetrics.ms_v_charge_pilot->AsBool() && CarIsCharging && StandardMetrics.ms_v_charge_state->AsString() == "done") {
CarIsCharging = false;
StandardMetrics.ms_v_charge_duration_full->SetValue(0);
ESP_LOGI(TAG, "Pollstate switched to OFF, done charging...");
POLLSTATE_OFF;
}
if (StandardMetrics.ms_v_env_on->AsBool()) {
StandardMetrics.ms_v_pos_trip->SetValue(StandardMetrics.ms_v_pos_odometer->AsFloat(0) - mt_pos_odometer_start->AsFloat(0));
}
StandardMetrics.ms_v_bat_range_est->SetValue((m_range_ideal * (StandardMetrics.ms_v_bat_soc->AsFloat(1) * 0.01)), Kilometers);
}
OvmsVehicleRenaultZoePh2CAN* OvmsVehicleRenaultZoePh2CAN::GetInstance(OvmsWriter* writer /*=NULL*/)
{
OvmsVehicleRenaultZoePh2CAN* zoe_ph2_can = (OvmsVehicleRenaultZoePh2CAN*) MyVehicleFactory.ActiveVehicle();
string type = StdMetrics.ms_v_type->AsString();
if (!zoe_ph2_can || type != "RZ2C") {
if (writer)
writer->puts("Error: Renault Zoe Ph2 (CAN) vehicle module not selected");
return NULL;
}
return zoe_ph2_can;
}
class OvmsVehicleRenaultZoePh2CANInit {
public: OvmsVehicleRenaultZoePh2CANInit();
} MyOvmsVehicleRenaultZoePh2CANInit __attribute__ ((init_priority (9000)));
OvmsVehicleRenaultZoePh2CANInit::OvmsVehicleRenaultZoePh2CANInit()
{
ESP_LOGI(TAG, "Registering Vehicle: Renault Zoe Ph2 (CAN) (9000)");
MyVehicleFactory.RegisterVehicle<OvmsVehicleRenaultZoePh2CAN>("RZ2C","Renault Zoe Ph2 (CAN)");
}

View file

@ -0,0 +1,152 @@
/*
; 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.
*/
#ifndef __VEHICLE_RENAULTZOE_PH2_OBD_H__
#define __VEHICLE_RENAULTZOE_PH2_OBD_H__
#include <atomic>
#include "can.h"
#include "vehicle.h"
#include "ovms_log.h"
#include "ovms_config.h"
#include "ovms_metrics.h"
#include "ovms_command.h"
#include "freertos/timers.h"
#ifdef CONFIG_OVMS_COMP_WEBSERVER
#include "ovms_webserver.h"
#endif
// CAN buffer access macros: b=byte# 0..7 / n=nibble# 0..15
#define CAN_BYTE(b) data[b]
#define CAN_UINT(b) (((UINT)CAN_BYTE(b) << 8) | CAN_BYTE(b+1))
#define CAN_UINT24(b) (((uint32_t)CAN_BYTE(b) << 16) | ((UINT)CAN_BYTE(b+1) << 8) | CAN_BYTE(b+2))
#define CAN_UINT32(b) (((uint32_t)CAN_BYTE(b) << 24) | ((uint32_t)CAN_BYTE(b+1) << 16) | ((UINT)CAN_BYTE(b+2) << 8) | CAN_BYTE(b+3))
#define CAN_NIBL(b) (data[b] & 0x0f)
#define CAN_NIBH(b) (data[b] >> 4)
#define CAN_NIB(n) (((n)&1) ? CAN_NIBL((n)>>1) : CAN_NIBH((n)>>1))
#define POLLSTATE_OFF PollSetState(0);
#define POLLSTATE_ON PollSetState(1);
#define POLLSTATE_DRIVING PollSetState(2);
#define POLLSTATE_CHARGING PollSetState(3);
using namespace std;
#define TAG (OvmsVehicleRenaultZoePh2CAN::s_tag)
class OvmsVehicleRenaultZoePh2CAN : public OvmsVehicle {
public:
static const char *s_tag;
public:
OvmsVehicleRenaultZoePh2CAN();
~OvmsVehicleRenaultZoePh2CAN();
static void WebCfgCommon(PageEntry_t& p, PageContext_t& c);
void ConfigChanged(OvmsConfigParam* param);
void ZoeWakeUp();
void IncomingFrameCan1(CAN_frame_t* p_frame);
void IncomingFrameCan2(CAN_frame_t* p_frame);
void IncomingFrameCan3(CAN_frame_t* p_frame);
void IncomingPollReply(canbus* bus, uint16_t type, uint16_t pid, uint8_t* data, uint8_t length, uint16_t remain);
void WebInit();
void WebDeInit();
bool CarIsCharging = false;
bool CarPluggedIn = false;
bool CarLastCharging = false;
bool CarIsDriving = false;
bool CarIsDrivingInit = false;
float ACInputPowerFactor = 0.0;
float Bat_cell_capacity = 0.0;
protected:
int m_range_ideal;
int m_battery_capacity;
bool m_UseCarTrip = false;
bool m_UseBMScalculation = false;
char zoe_vin[18] = "";
void IncomingINV(uint16_t type, uint16_t pid, const char* data, uint16_t len);
void IncomingEVC(uint16_t type, uint16_t pid, const char* data, uint16_t len);
void IncomingBCM(uint16_t type, uint16_t pid, const char* data, uint16_t len);
void IncomingLBC(uint16_t type, uint16_t pid, const char* data, uint16_t len);
void IncomingHVAC(uint16_t type, uint16_t pid, const char* data, uint16_t len);
void IncomingUCM(uint16_t type, uint16_t pid, const char* data, uint16_t len);
//void IncomingCLUSTER(uint16_t type, uint16_t pid, const char* data, uint16_t len);
int calcMinutesRemaining(float charge_voltage, float charge_current);
void ChargeStatistics();
void EnergyStatisticsOVMS();
void EnergyStatisticsBMS();
virtual void Ticker10(uint32_t ticker);//Handle charge, energy statistics
virtual void Ticker1(uint32_t ticker); //Handle trip counter
private:
unsigned int m_can1_activity_timer;
// Renault ZOE specific metrics
OvmsMetricBool *mt_bus_awake; //CAN bus awake status
OvmsMetricFloat *mt_pos_odometer_start; //ODOmeter at trip start
OvmsMetricFloat *mt_bat_used_start; //Used battery kWh at trip start
OvmsMetricFloat *mt_bat_recd_start; //Recd battery kWh at trip start
OvmsMetricFloat *mt_bat_chg_start; //Charge battery kWh at charge start
OvmsMetricFloat *mt_bat_available_energy; //Available energy in battery
OvmsMetricFloat *mt_bat_aux_power_consumer; //Power usage by consumer
OvmsMetricFloat *mt_bat_aux_power_ptc; //Power usage by PTCs
OvmsMetricFloat *mt_bat_max_charge_power; //Battery max allowed charge, recd power
OvmsMetricFloat *mt_bat_cycles; //Battery full charge cycles
OvmsMetricFloat *mt_main_power_available; //Mains power available
OvmsMetricString *mt_main_phases; //Mains phases used
OvmsMetricFloat *mt_main_phases_num; //Mains phases used
OvmsMetricString *mt_inv_status; //Inverter status string
OvmsMetricFloat *mt_inv_hv_voltage; //Inverter battery voltage sense, used for motor power calc
OvmsMetricFloat *mt_inv_hv_current; //Inverter battery current sense, used for motor power calc
OvmsMetricFloat *mt_mot_temp_stator1; //Temp of motor stator 1
OvmsMetricFloat *mt_mot_temp_stator2; //Temp of motor stator 2
OvmsMetricFloat *mt_hvac_compressor_speed; //Compressor speed
OvmsMetricFloat *mt_hvac_compressor_pressure;//Compressor high-side pressure in BAR
OvmsMetricFloat *mt_hvac_compressor_power; //Compressor power usage
OvmsMetricString *mt_hvac_compressor_mode; //Compressor mode
OvmsMetricFloat *mt_v_env_pressure; //Ambient air pressure
//CAN Commands section
public:
static OvmsVehicleRenaultZoePh2CAN* GetInstance(OvmsWriter* writer=NULL);
void CanInit();
int CanRequest(uint16_t txid, uint16_t rxid, string request, string& response, int timeout_ms);
static void shell_can_request(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv);
virtual vehicle_command_t CommandPreHeat(bool climatecontrolon);
virtual vehicle_command_t CommandWakeup();
virtual vehicle_command_t CommandLock(const char* pin);
virtual vehicle_command_t CommandUnlock(const char* pin);
protected:
OvmsCommand *cmd_zoe_ph2;
string zoe_can1_rxbuf;
OvmsSemaphore zoe_can1_rxwait;
uint16_t zoe_can1_rxerr;
OvmsMutex zoe_can1_request;
};
#endif //#ifndef __VEHICLE_RENAULTZOE_PH2_OBD_H__

View file

@ -0,0 +1,14 @@
#
# Main component makefile.
#
# This Makefile can be left empty. By default, it will take the sources in the
# src/ directory, compile them and link them into lib(subdirectory_name).a
# in the build directory. This behaviour is entirely configurable,
# please read the ESP-IDF documents if you need to do this.
#
ifdef CONFIG_OVMS_VEHICLE_RENAULTZOE_PH2_OBD
COMPONENT_ADD_INCLUDEDIRS:=src
COMPONENT_SRCDIRS:=src
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
endif

View file

@ -0,0 +1,49 @@
==============================
Renault Zoe Phase 2 (OBD port)
==============================
Vehicle Type: **RZ2O**
This vehicle type supports the Renault Zoe(PH2) through OBD connection.
All values are read-only, no control possible because of CAN Gateway.
----------------
Support Overview
----------------
=========================== ==============
Function Support Status
=========================== ==============
Hardware OVMS v3.3
Vehicle Cable OBD-II to DB9 Data Cable for OVMS (Generic OBD2 Cable (left))
GSM Antenna 1000500 Open Vehicles OVMS GSM Antenna (or any compatible antenna)
GPS Antenna 1020200 Universal GPS Antenna (SMA Connector) (or any compatible antenna)
SOC Display Yes
Range Display Yes
GPS Location Yes (from modem module GPS)
Speed Display Yes
Temperature Display Yes (Battery, Outside, Cabin, Motor, Inverter, Tyres)
BMS v+t Display Yes
TPMS Display Zoe Yes (Pressure and temperature)
Charge Status Display Yes
Charge Interruption Alerts Yes
Charge Control No (planned, but access to CAN after gw neccessary)
Cabin Pre-heat/cool Control No (planned, but access to CAN after gw neccessary)
Lock/Unlock Vehicle Yes (display only)
Valet Mode Control No
=========================== ==============
Others:
=================================== ==============
Door open/close Yes (exclude hood)
Battery full charge cycles Yes
Battery max charge, recd pwr Yes
Trip counter from Car No (was included, but datapoint is extremly unreliable)
Battery lifetime gauges Yes (Charged, Recd and Used kWh)
Heat pump power, rpm and hp press. Yes
Aux power gauges Yes (testing needed)
Charge type Yes (DC charge, testing needed)
Headlights Status Yes (lowbeam)
Charge efficiency calculation Yes (but only for AC, pf is measured with power analyzer and included as statics)
=================================== ==============

View file

@ -23,48 +23,64 @@
; THE SOFTWARE.
*/
#include "vehicle_renaultzoe_ph2.h"
#include "vehicle_renaultzoe_ph2_obd.h"
void OvmsVehicleRenaultZoePh2::IncomingBCM(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
void OvmsVehicleRenaultZoePh2OBD::IncomingBCM(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x4204: { // TPMS pressure - front left
StandardMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_FL, (float)CAN_UINT(0) * 7.5 / 10, kPa);
//ESP_LOGD(TAG, "4204 BCM tpms pressure FL: %f", CAN_UINT(0) * 7.5);
case 0x6300: { // TPMS pressure - front left
if ((CAN_UINT(0) * 7.5) < 7672) {
StandardMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_FL, (float)CAN_UINT(0) * 7.5 / 10, kPa);
}
//ESP_LOGD(TAG, "6300 BCM tpms pressure FL: %f", CAN_UINT(0) * 7.5);
break;
}
case 0x4205: { // TPMS pressure - front right
StandardMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_FR, (float)CAN_UINT(0) * 7.5 / 10, kPa);
//ESP_LOGD(TAG, "4205 BCM tpms pressure FR: %f", CAN_UINT(0) * 7.5);
case 0x6301: { // TPMS pressure - front right
if ((CAN_UINT(0) * 7.5) < 7672) {
StandardMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_FR, (float)CAN_UINT(0) * 7.5 / 10, kPa);
}
//ESP_LOGD(TAG, "6301 BCM tpms pressure FR: %f", CAN_UINT(0) * 7.5);
break;
}
case 0x4206: { // TPMS pressure - rear left
StandardMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_RL, (float)CAN_UINT(0) * 7.5 / 10, kPa);
//ESP_LOGD(TAG, "4206 BCM tpms pressure RL: %f", CAN_UINT(0) * 7.5);
case 0x6302: { // TPMS pressure - rear left
if ((CAN_UINT(0) * 7.5) < 7672) {
StandardMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_RL, (float)CAN_UINT(0) * 7.5 / 10, kPa);
}
//ESP_LOGD(TAG, "6302 BCM tpms pressure RL: %f", CAN_UINT(0) * 7.5);
break;
}
case 0x4207: { // TPMS pressure - rear right
StandardMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_RR, (float)CAN_UINT(0) * 7.5 / 10, kPa);
//ESP_LOGD(TAG, "4207 BCM tpms pressure RR: %f", CAN_UINT(0) * 7.5);
case 0x6303: { // TPMS pressure - rear right
if ((CAN_UINT(0) * 7.5) < 7672) {
StandardMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_RR, (float)CAN_UINT(0) * 7.5 / 10, kPa);
}
//ESP_LOGD(TAG, "6303 BCM tpms pressure RR: %f", CAN_UINT(0) * 7.5);
break;
}
case 0x420C: { // TPMS temp - front left
StandardMetrics.ms_v_tpms_temp->SetElemValue(MS_V_TPMS_IDX_FL, (float)(CAN_UINT(0) - 30) * 0.001, Celcius);
//ESP_LOGD(TAG, "420C BCM tpms temp FL: %f", (CAN_UINT(0) - 30) * 0.001);
case 0x6310: { // TPMS temp - front left
if (CAN_BYTE(0) < 127) {
StandardMetrics.ms_v_tpms_temp->SetElemValue(MS_V_TPMS_IDX_FL, CAN_BYTE(0) - 30, Celcius);
}
//ESP_LOGD(TAG, "6310 BCM tpms temp FL: %d", CAN_NIB(0));
break;
}
case 0x420D: { // TPMS temp - front right
StandardMetrics.ms_v_tpms_temp->SetElemValue(MS_V_TPMS_IDX_FR, (float)(CAN_UINT(0) - 30) * 0.001, Celcius);
//ESP_LOGD(TAG, "420D BCM tpms temp FR: %f", (CAN_UINT(0) - 30) * 0.001);
case 0x6311: { // TPMS temp - front right
if (CAN_BYTE(0) < 127) {
StandardMetrics.ms_v_tpms_temp->SetElemValue(MS_V_TPMS_IDX_FR, CAN_BYTE(0) - 30, Celcius);
}
//ESP_LOGD(TAG, "6311 BCM tpms temp FR: %d", CAN_NIBL(0));
break;
}
case 0x420E: { // TPMS temp - rear left
StandardMetrics.ms_v_tpms_temp->SetElemValue(MS_V_TPMS_IDX_RL, (float)(CAN_UINT(0) - 30) * 0.001, Celcius);
//ESP_LOGD(TAG, "420E BCM tpms temp RL: %f", (CAN_UINT(0) - 30) * 0.001);
case 0x6312: { // TPMS temp - rear left
if (CAN_BYTE(0) < 127) {
StandardMetrics.ms_v_tpms_temp->SetElemValue(MS_V_TPMS_IDX_RL, CAN_BYTE(0) - 30, Celcius);
}
//ESP_LOGD(TAG, "6312 BCM tpms temp RL: %d", CAN_NIBH(0));
break;
}
case 0x420F: { // TPMS temp - rear right
StandardMetrics.ms_v_tpms_temp->SetElemValue(MS_V_TPMS_IDX_RR, (float)(CAN_UINT(0) - 30) * 0.001, Celcius);
//ESP_LOGD(TAG, "420F BCM tpms temp RR: %f", (CAN_UINT(0) - 30) * 0.001);
case 0x6313: { // TPMS temp - rear right
if (CAN_BYTE(0) < 127) {
StandardMetrics.ms_v_tpms_temp->SetElemValue(MS_V_TPMS_IDX_RR, CAN_BYTE(0) - 30, Celcius);
}
//ESP_LOGD(TAG, "6313 BCM tpms temp RR: %d", CAN_BYTE(0));
break;
}
case 0x4109: { // TPMS alert - front left
@ -149,6 +165,49 @@ void OvmsVehicleRenaultZoePh2::IncomingBCM(uint16_t type, uint16_t pid, const ch
//ESP_LOGD(TAG, "609B Tailgate: %d", CAN_UINT(0));
break;
}
case 0x4186: { //Low beam lights
StandardMetrics.ms_v_env_headlights->SetValue((bool)CAN_UINT(0));
/*if ((bool)CAN_UINT(0)) {
ESP_LOGD(TAG, "4186 Low beam lights: active");
} else {
ESP_LOGD(TAG, "4186 Low beam lights: inactive");
}*/
break;
}
case 0x60C6: { //Ignition relay (switch)
/*if ((bool)CAN_UINT(0)) {
ESP_LOGD(TAG, "60C6 Ignition relay: active");
} else {
ESP_LOGD(TAG, "60C6 Ignition relay: inactive");
}*/
if (!CarIsCharging) { //Igniton while charging
StandardMetrics.ms_v_env_on->SetValue((bool)CAN_UINT(0));
StandardMetrics.ms_v_env_awake->SetValue((bool)CAN_UINT(0));
}
break;
}
case 0x4060: { //Vehicle identificaftion number
zoe_vin[0] = CAN_BYTE(0);
zoe_vin[1] = CAN_BYTE(1);
zoe_vin[2] = CAN_BYTE(2);
zoe_vin[3] = CAN_BYTE(3);
zoe_vin[4] = CAN_BYTE(4);
zoe_vin[5] = CAN_BYTE(5);
zoe_vin[6] = CAN_BYTE(6);
zoe_vin[7] = CAN_BYTE(7);
zoe_vin[8] = CAN_BYTE(8);
zoe_vin[9] = CAN_BYTE(9);
zoe_vin[10] = CAN_BYTE(10);
zoe_vin[11] = CAN_BYTE(11);
zoe_vin[12] = CAN_BYTE(12);
zoe_vin[13] = CAN_BYTE(13);
zoe_vin[14] = CAN_BYTE(14);
zoe_vin[15] = CAN_BYTE(15);
zoe_vin[16] = CAN_BYTE(16);
zoe_vin[17] = 0;
StandardMetrics.ms_v_vin->SetValue((string) zoe_vin);
break;
}
default: {
char *buf = NULL;

View file

@ -23,9 +23,9 @@
; THE SOFTWARE.
*/
#include "vehicle_renaultzoe_ph2.h"
#include "vehicle_renaultzoe_ph2_obd.h"
void OvmsVehicleRenaultZoePh2::IncomingEVC(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
void OvmsVehicleRenaultZoePh2OBD::IncomingEVC(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x2006: { //Odometer (Total Vehicle Distance)
StandardMetrics.ms_v_pos_odometer->SetValue((float) CAN_UINT24(0), Kilometers);
@ -42,29 +42,20 @@ void OvmsVehicleRenaultZoePh2::IncomingEVC(uint16_t type, uint16_t pid, const ch
//ESP_LOGD(TAG, "2005 EVC ms_v_charge_12v_voltage: %f", CAN_UINT(0) * 0.01);
break;
}
case 0x21CF: { //Inverter status --- Main switch for polling and driving state
case 0x21CF: { //Inverter status
//ESP_LOGD(TAG, "21CF EVC mt_inv_status: %d", CAN_NIBL(0));
if (CAN_NIBL(0) == 1) {
mt_inv_status->SetValue("Inverter off");
StandardMetrics.ms_v_env_on->SetValue(false);
StandardMetrics.ms_v_env_awake->SetValue(false);
} else if (CAN_NIBL(0) == 2) {
mt_inv_status->SetValue("Inverter on");
StandardMetrics.ms_v_env_on->SetValue(true);
StandardMetrics.ms_v_env_awake->SetValue(true);
StandardMetrics.ms_v_door_chargeport->SetValue(false);
} else if (CAN_NIBL(0) == 3) {
mt_inv_status->SetValue("Inverter decharging");
StandardMetrics.ms_v_env_on->SetValue(false);
StandardMetrics.ms_v_env_awake->SetValue(false);
} else if (CAN_NIBL(0) == 4) {
mt_inv_status->SetValue("Inverter alternator mode");
StandardMetrics.ms_v_env_on->SetValue(false);
StandardMetrics.ms_v_env_awake->SetValue(false);
} else if (CAN_NIBL(0) == 5) {
mt_inv_status->SetValue("Inverter ready to sleep");
StandardMetrics.ms_v_env_on->SetValue(false);
StandardMetrics.ms_v_env_awake->SetValue(false);
} else {
mt_inv_status->SetValue("Inverter state unknown");
}
@ -76,12 +67,12 @@ void OvmsVehicleRenaultZoePh2::IncomingEVC(uint16_t type, uint16_t pid, const ch
break;
}
case 0x2A09: { // Power consumption by consumer
//mt_bat_aux_power_consumer->SetValue((float) CAN_UINT(0) * 10, Watts);
mt_bat_aux_power_consumer->SetValue((float) CAN_UINT(0) * 10, Watts);
//ESP_LOGD(TAG, "2A09 EVC mt_bat_aux_power_consumer: %d", CAN_UINT(0) * 10);
break;
}
case 0x2191: { // Power consumption by ptc
//mt_bat_aux_power_ptc->SetValue((float) CAN_UINT(0) * 10, Watts);
mt_bat_aux_power_ptc->SetValue((float) CAN_UINT(0) * 10, Watts);
//ESP_LOGD(TAG, "2191 EVC mt_bat_aux_power_ptc: %d", CAN_UINT(0) * 10);
break;
}
@ -144,6 +135,7 @@ void OvmsVehicleRenaultZoePh2::IncomingEVC(uint16_t type, uint16_t pid, const ch
if (CAN_NIBL(0) == 6) {
StandardMetrics.ms_v_door_chargeport->SetValue(true);
//ESP_LOGD(TAG, "2B6D Charge MMI States : Chargeport opened");
ESP_LOGI(TAG, "Chargedoor opened");
}
if (CAN_NIBL(0) == 8) {
StandardMetrics.ms_v_charge_state->SetValue("prepare");
@ -217,7 +209,6 @@ void OvmsVehicleRenaultZoePh2::IncomingEVC(uint16_t type, uint16_t pid, const ch
ACInputPowerFactor = 0.775;
} else if (StandardMetrics.ms_v_charge_current->AsFloat() < 6.0f && StandardMetrics.ms_v_charge_inprogress->AsBool(false)) {
ACInputPowerFactor = 0.05;
ESP_LOGE(TAG, "Warning charge current too low, charging not possible");
}
if (StandardMetrics.ms_v_charge_type->AsString() == "type2" && mt_main_phases_num->AsFloat() == 3 && StandardMetrics.ms_v_charge_inprogress->AsBool(false)) {
StandardMetrics.ms_v_charge_power->SetValue((StandardMetrics.ms_v_charge_current->AsFloat() * StandardMetrics.ms_v_charge_voltage->AsFloat() * ACInputPowerFactor * 1.732f) * 0.001, kW);
@ -254,16 +245,6 @@ void OvmsVehicleRenaultZoePh2::IncomingEVC(uint16_t type, uint16_t pid, const ch
//ESP_LOGD(TAG, "2B8A EVC ms_v_charge_voltage: %f", (CAN_UINT(0) * 0.5));
break;
}
case 0x2009: { // Ambient air pressure
mt_v_env_pressure->SetValue((float) (CAN_UINT(0)));
//ESP_LOGD(TAG, "2009 EVC mt_v_env_pressure: %d mbar", CAN_UINT(0));
break;
}
case 0x21CD: { // User SOC
mt_bat_user_soc->SetValue((float) (CAN_UINT(0)), Percentage);
//ESP_LOGD(TAG, "21CD EVC mt_bat_user_soc: %f", (float) CAN_UINT(0));
break;
}
default: {
char *buf = NULL;

View file

@ -22,9 +22,9 @@
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
*/
#include "vehicle_renaultzoe_ph2.h"
#include "vehicle_renaultzoe_ph2_obd.h"
void OvmsVehicleRenaultZoePh2::IncomingHVAC(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
void OvmsVehicleRenaultZoePh2OBD::IncomingHVAC(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x4009: { //Cabin temperature
StandardMetrics.ms_v_env_cabintemp->SetValue(float((CAN_UINT(0) - 400) / 10), Celcius);

View file

@ -23,9 +23,9 @@
; THE SOFTWARE.
*/
#include "vehicle_renaultzoe_ph2.h"
#include "vehicle_renaultzoe_ph2_obd.h"
void OvmsVehicleRenaultZoePh2::IncomingINV(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
void OvmsVehicleRenaultZoePh2OBD::IncomingINV(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x700C: { // Inverter temperature
StandardMetrics.ms_v_inv_temp->SetValue(float((CAN_UINT24(0) * 0.001953125) - 40), Celcius);
@ -44,11 +44,6 @@ void OvmsVehicleRenaultZoePh2::IncomingINV(uint16_t type, uint16_t pid, const ch
//ESP_LOGD(TAG, "7010 INV mt_mot_temp_stator2: %f", float((CAN_UINT24(0) * 0.001953125) - 40));
break;
}
//case 0x2001: { // Motor rpm
// StandardMetrics.ms_v_mot_rpm->SetValue(float(CAN_UINT24(0) / 2) - 16000);
// ESP_LOGD(TAG, "2001 INV ms_v_mot_rpm: %f", float(CAN_UINT24(0) / 2) - 16000);
// break;
//}
case 0x2004: { // Battery voltage sense
mt_inv_hv_voltage->SetValue(float(CAN_UINT(0) * 0.03125), Volts);
//ESP_LOGD(TAG, "2004 INV mt_inv_hv_voltage: %f", float(CAN_UINT(0) * 0.03125));

View file

@ -23,9 +23,9 @@
; THE SOFTWARE.
*/
#include "vehicle_renaultzoe_ph2.h"
#include "vehicle_renaultzoe_ph2_obd.h"
void OvmsVehicleRenaultZoePh2::IncomingLBC(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
void OvmsVehicleRenaultZoePh2OBD::IncomingLBC(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x9005: { //Battery voltage
StandardMetrics.ms_v_bat_voltage->SetValue((float) (CAN_UINT(0) * 0.1), Volts);
@ -45,9 +45,9 @@ void OvmsVehicleRenaultZoePh2::IncomingLBC(uint16_t type, uint16_t pid, const ch
break;
}
case 0x9002: { //Battery SOC
mt_bat_lbc_soc->SetValue((float) (CAN_UINT(0)) * 0.01, Percentage);
StandardMetrics.ms_v_bat_soc->SetValue((float) (CAN_UINT(0)) * 0.01, Percentage);
StandardMetrics.ms_v_bat_cac->SetValue(Bat_cell_capacity * CAN_UINT(0) * 0.0001);
//ESP_LOGD(TAG, "9002 LBC mt_bat_lbc_soc: %f", CAN_UINT(0) * 0.01);
//ESP_LOGD(TAG, "9002 LBC mt_bat_lbc_soc: %f", bat_soc);
break;
}
case 0x9003: { //Battery SOH

View file

@ -22,9 +22,9 @@
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
; THE SOFTWARE.
*/
#include "vehicle_renaultzoe_ph2.h"
#include "vehicle_renaultzoe_ph2_obd.h"
void OvmsVehicleRenaultZoePh2::IncomingUCM(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
void OvmsVehicleRenaultZoePh2OBD::IncomingUCM(uint16_t type, uint16_t pid, const char* data, uint16_t len) {
switch (pid) {
case 0x6079: { //12V Battery Current
StandardMetrics.ms_v_charge_12v_current->SetValue((float) (CAN_UINT(0) * 0.1), Amps);

View file

@ -51,7 +51,7 @@ static const OvmsVehicle::poll_pid_t renault_zoe_polls[] = {
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2218, { 0, 20, 20, 20 }, 0, ISOTP_EXTFRAME }, // Ambient air temperature
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2A09, { 0, 10, 10, 10 }, 0, ISOTP_EXTFRAME }, // Power usage by consumer
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2191, { 0, 10, 10, 10 }, 0, ISOTP_EXTFRAME }, // Power usage by ptc
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2B85, { 0, 2, 300, 10 }, 0, ISOTP_EXTFRAME }, // Charge plug preset
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2B85, { 0, 2, 300, 10 }, 0, ISOTP_EXTFRAME }, // Charge plug present
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2B6D, { 0, 2, 300, 10 }, 0, ISOTP_EXTFRAME }, // Charge MMI states
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2B7A, { 0, 2, 300, 10 }, 0, ISOTP_EXTFRAME }, // Charge type
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x3064, { 0, 10, 3, 10 }, 0, ISOTP_EXTFRAME }, // Motor rpm
@ -59,19 +59,17 @@ static const OvmsVehicle::poll_pid_t renault_zoe_polls[] = {
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x300D, { 0, 10, 300, 3 }, 0, ISOTP_EXTFRAME }, // AC mains current
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x300B, { 0, 2, 300, 10 }, 0, ISOTP_EXTFRAME }, // AC phases
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2B8A, { 0, 2, 300, 10 }, 0, ISOTP_EXTFRAME }, // AC mains voltage
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2009, { 0, 20, 20, 20 }, 0, ISOTP_EXTFRAME }, // Ambient air pressure
{ 0x18dadaf1, 0x18daf1da, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x21CD, { 0, 10, 10, 10 }, 0, ISOTP_EXTFRAME }, // User SOC
//BCM
//{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIISESSION, SESSION_DEFAULT, { 0, 60, 60, 60 }, 0, ISOTP_STD }, // OBD Extended Diagnostic Session
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4204, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS pressure - front left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4205, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS pressure - front right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4206, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS pressure - rear left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4207, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS pressure - rear right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x420C, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS temp - front left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x420D, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS temp - front right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x420E, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS temp - rear left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x420F, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS temp - rear right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6300, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS pressure - front left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6301, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS pressure - front right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6302, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS pressure - rear left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6303, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS pressure - rear right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6310, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS temp - front left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6311, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS temp - front right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6312, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS temp - rear left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6313, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS temp - rear right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4109, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS alert - front left
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x410A, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS alert - front right
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x410B, { 0, 300, 300, 300 }, 0, ISOTP_STD }, // TPMS alert - rear left
@ -82,6 +80,9 @@ static const OvmsVehicle::poll_pid_t renault_zoe_polls[] = {
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x61B2, { 0, 3, 3, 3 }, 0, ISOTP_STD }, // Rear left door
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x61B3, { 0, 3, 3, 3 }, 0, ISOTP_STD }, // Rear right door
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x609B, { 0, 3, 3, 3 }, 0, ISOTP_STD }, // Tailgate
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4186, { 0, 3, 3, 3 }, 0, ISOTP_STD }, // Low beam lights
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x60C6, { 0, 3, 6, 60 }, 0, ISOTP_STD }, // Ignition relay (switch), working but behavior on CHARING testing needed
{ 0x745, 0x765, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x4060, { 0, 600, 0, 0 }, 0, ISOTP_STD }, // Vehicle identificaftion number
//LBC
//{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIISESSION, SESSION_DEFAULT, { 0, 60, 60, 60 }, 0, ISOTP_EXTFRAME }, // OBD Extended Diagnostic Session
@ -97,114 +98,114 @@ static const OvmsVehicle::poll_pid_t renault_zoe_polls[] = {
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9210, { 0, 60, 60, 60 }, 0, ISOTP_EXTFRAME }, // Number of complete cycles
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9018, { 0, 10, 60, 10 }, 0, ISOTP_EXTFRAME }, // Max Charge Power
//LBC Cell voltages and temperatures, OBD Grouppoll not working
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9131, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 1
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9132, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 2
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9133, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 3
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9134, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 4
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9135, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 5
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9136, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 6
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9137, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 7
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9138, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 8
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9139, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 9
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x913A, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 10
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x913B, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 11
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x913C, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 12
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9021, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 1
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9022, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 2
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9023, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 3
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9024, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 4
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9025, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 5
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9026, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 6
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9027, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 7
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9028, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 8
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9029, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 9
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902A, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 10
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902B, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 11
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902C, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 12
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902D, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 13
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902E, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 14
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902F, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 15
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9030, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 16
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9031, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 17
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9032, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 18
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9033, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 19
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9034, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 20
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9035, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 21
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9036, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 22
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9037, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 23
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9038, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 24
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9039, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 25
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903A, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 26
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903B, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 27
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903C, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 28
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903D, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 29
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903E, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 30
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903F, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 31
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9041, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 32
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9042, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 33
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9043, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 34
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9044, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 35
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9045, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 36
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9046, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 37
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9047, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 38
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9048, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 39
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9049, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 40
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904A, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 41
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904B, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 42
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904C, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 43
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904D, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 44
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904E, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 45
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904F, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 46
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9050, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 47
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9051, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 48
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9052, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 49
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9053, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 50
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9054, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 51
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9055, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 52
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9056, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 53
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9057, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 54
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9058, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 55
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9059, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 56
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905A, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 57
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905B, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 58
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905C, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 59
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905D, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 60
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905E, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 61
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905F, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 62
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9061, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 63
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9062, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 64
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9063, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 65
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9064, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 66
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9065, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 67
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9066, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 68
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9067, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 69
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9068, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 70
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9069, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 71
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906A, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 72
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906B, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 73
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906C, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 74
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906D, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 75
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906E, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 76
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906F, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 77
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9070, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 78
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9071, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 79
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9072, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 80
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9073, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 81
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9074, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 82
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9075, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 83
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9076, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 84
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9077, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 85
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9078, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 86
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9079, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 87
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907A, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 88
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907B, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 89
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907C, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 90
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907D, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 91
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907E, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 92
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907F, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 93
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9081, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 94
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9082, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 95
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9083, { 0, 60, 600, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 96
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9131, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 1
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9132, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 2
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9133, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 3
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9134, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 4
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9135, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 5
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9136, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 6
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9137, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 7
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9138, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 8
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9139, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 9
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x913A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 10
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x913B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 11
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x913C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Pack temperature 12
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9021, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 1
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9022, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 2
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9023, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 3
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9024, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 4
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9025, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 5
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9026, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 6
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9027, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 7
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9028, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 8
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9029, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 9
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 10
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 11
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 12
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902D, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 13
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902E, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 14
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x902F, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 15
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9030, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 16
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9031, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 17
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9032, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 18
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9033, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 19
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9034, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 20
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9035, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 21
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9036, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 22
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9037, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 23
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9038, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 24
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9039, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 25
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 26
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 27
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 28
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903D, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 29
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903E, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 30
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x903F, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 31
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9041, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 32
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9042, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 33
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9043, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 34
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9044, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 35
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9045, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 36
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9046, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 37
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9047, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 38
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9048, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 39
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9049, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 40
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 41
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 42
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 43
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904D, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 44
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904E, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 45
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x904F, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 46
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9050, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 47
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9051, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 48
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9052, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 49
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9053, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 50
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9054, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 51
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9055, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 52
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9056, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 53
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9057, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 54
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9058, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 55
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9059, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 56
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 57
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 58
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 59
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905D, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 60
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905E, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 61
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x905F, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 62
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9061, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 63
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9062, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 64
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9063, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 65
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9064, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 66
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9065, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 67
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9066, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 68
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9067, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 69
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9068, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 70
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9069, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 71
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 72
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 73
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 74
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906D, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 75
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906E, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 76
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x906F, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 77
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9070, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 78
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9071, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 79
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9072, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 80
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9073, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 81
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9074, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 82
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9075, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 83
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9076, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 84
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9077, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 85
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9078, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 86
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9079, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 87
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907A, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 88
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907B, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 89
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907C, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 90
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907D, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 91
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907E, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 92
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x907F, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 93
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9081, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 94
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9082, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 95
{ 0x18dadbf1, 0x18daf1db, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x9083, { 0, 60, 300, 60 }, 0, ISOTP_EXTFRAME }, // Cell voltage 96
//HVAC
//{ 0x744, 0x764, VEHICLE_POLL_TYPE_OBDIISESSION, SESSION_EXTDIAG, { 0, 60, 60, 60 }, 0, ISOTP_STD }, // OBD Extended Diagnostic Session
@ -219,8 +220,5 @@ static const OvmsVehicle::poll_pid_t renault_zoe_polls[] = {
//{ 0x74D, 0x76D, VEHICLE_POLL_TYPE_OBDIISESSION, SESSION_AfterSales, { 0, 60, 60, 60 }, 0, ISOTP_STD }, // OBD Extended Diagnostic Session
{ 0x74D, 0x76D, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x6079, { 0, 10, 10, 10 }, 0, ISOTP_STD }, // 12V Battery current
//TDB
//{ 0x743, 0x763, VEHICLE_POLL_TYPE_OBDIISESSION, SESSION_AfterSales, { 0, 60, 60, 60 }, 0, ISOTP_STD }, // OBD Extended Diagnostic Session
{ 0x743, 0x763, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x2101, { 0, 30, 10, 300 }, 0, ISOTP_STD }, // Cluster - trip counter
POLL_LIST_END
};

View file

@ -36,24 +36,22 @@
#include "ovms_notify.h"
#include "ovms_webserver.h"
#include "vehicle_renaultzoe_ph2.h"
#include "vehicle_renaultzoe_ph2_obd.h"
using namespace std;
#define _attr(text) (c.encode_html(text).c_str())
#define _html(text) (c.encode_html(text).c_str())
void OvmsVehicleRenaultZoePh2::WebCfgCommon(PageEntry_t& p, PageContext_t& c)
void OvmsVehicleRenaultZoePh2OBD::WebCfgCommon(PageEntry_t& p, PageContext_t& c)
{
std::string error, rangeideal, battcapacity;
bool UseCarTrip, UseBMScalculation, UseBMSsoc;
bool UseBMScalculation;
if (c.method == "POST") {
rangeideal = c.getvar("rangeideal");
battcapacity = c.getvar("battcapacity");
UseCarTrip = (c.getvar("UseCarTrip") == "no");
UseBMScalculation = (c.getvar("UseBMScalculation") == "no");
UseBMSsoc = (c.getvar("UseBMSsoc") == "no");
if (!rangeideal.empty()) {
int v = atoi(rangeideal.c_str());
@ -62,11 +60,9 @@ void OvmsVehicleRenaultZoePh2::WebCfgCommon(PageEntry_t& p, PageContext_t& c)
}
if (error == "") {
// store:
MyConfig.SetParamValue("xrz2", "rangeideal", rangeideal);
MyConfig.SetParamValue("xrz2", "battcapacity", battcapacity);
MyConfig.SetParamValueBool("xrz2", "UseCarTrip", UseCarTrip);
MyConfig.SetParamValueBool("xrz2", "UseBMScalculation", UseBMScalculation);
MyConfig.SetParamValueBool("xrz2", "UseBMSsoc", UseBMSsoc);
MyConfig.SetParamValue("xrz2o", "rangeideal", rangeideal);
MyConfig.SetParamValue("xrz2o", "battcapacity", battcapacity);
MyConfig.SetParamValueBool("xrz2o", "UseBMScalculation", UseBMScalculation);
c.head(200);
c.alert("success", "<p class=\"lead\">Renault Zoe Ph2 battery setup saved.</p>");
@ -81,11 +77,9 @@ void OvmsVehicleRenaultZoePh2::WebCfgCommon(PageEntry_t& p, PageContext_t& c)
}
else {
// read configuration:
rangeideal = MyConfig.GetParamValue("xrz2", "rangeideal", "350");
battcapacity = MyConfig.GetParamValue("xrz2", "battcapacity", "52000");
UseCarTrip = MyConfig.GetParamValueBool("xrz2", "UseCarTrip", false);
UseBMScalculation = MyConfig.GetParamValueBool("xrz2", "UseBMScalculation", false);
UseBMSsoc = MyConfig.GetParamValueBool("xrz2", "UseBMSsoc", false);
rangeideal = MyConfig.GetParamValue("xrz2o", "rangeideal", "350");
battcapacity = MyConfig.GetParamValue("xrz2o", "battcapacity", "52000");
UseBMScalculation = MyConfig.GetParamValueBool("xrz2o", "UseBMScalculation", false);
c.head(200);
}
@ -102,15 +96,7 @@ void OvmsVehicleRenaultZoePh2::WebCfgCommon(PageEntry_t& p, PageContext_t& c)
c.input_slider("Range Ideal", "rangeideal", 3, "km", -1, atoi(rangeideal.c_str()), 350, 80, 500, 1,
"<p>Default 350km. Ideal Range...</p>");
c.fieldset_start("Trip counter settings");
c.input_radio_start("Which tripcounter to use?", "UseCarTrip");
c.input_radio_option("UseCarTrip", "Internal (New trip everytime car starts)", "yes", UseCarTrip == false);
c.input_radio_option("UseCarTrip", "Car trip counter from Cluster", "no", UseCarTrip == true);
c.input_radio_end("");
c.fieldset_end();
c.fieldset_start("Battery energy calculation");
c.input_radio_start("Which energy calculation?", "UseBMScalculation");
@ -118,11 +104,6 @@ void OvmsVehicleRenaultZoePh2::WebCfgCommon(PageEntry_t& p, PageContext_t& c)
c.input_radio_option("UseBMScalculation", "BMS-based calculation", "no", UseBMScalculation == true);
c.input_radio_end("");
c.input_radio_start("State of Charge - Display", "UseBMSsoc");
c.input_radio_option("UseBMSsoc", "User SOC from EVC", "yes", UseBMSsoc == false);
c.input_radio_option("UseBMSsoc", "Real SOC from BMS", "no", UseBMSsoc == true);
c.input_radio_end("");
c.fieldset_end();
c.print("<hr>");
@ -135,19 +116,19 @@ void OvmsVehicleRenaultZoePh2::WebCfgCommon(PageEntry_t& p, PageContext_t& c)
/**
* WebInit: register pages
*/
void OvmsVehicleRenaultZoePh2::WebInit()
void OvmsVehicleRenaultZoePh2OBD::WebInit()
{
MyWebServer.RegisterPage("/xrz2/battmon", "BMS View", OvmsWebServer::HandleBmsCellMonitor, PageMenu_Vehicle, PageAuth_Cookie);
MyWebServer.RegisterPage("/xrz2/settings", "Setup", WebCfgCommon, PageMenu_Vehicle, PageAuth_Cookie);
MyWebServer.RegisterPage("/xrz2o/battmon", "BMS View", OvmsWebServer::HandleBmsCellMonitor, PageMenu_Vehicle, PageAuth_Cookie);
MyWebServer.RegisterPage("/xrz2o/settings", "Setup", WebCfgCommon, PageMenu_Vehicle, PageAuth_Cookie);
}
/**
* WebDeInit: deregister pages
*/
void OvmsVehicleRenaultZoePh2::WebDeInit()
void OvmsVehicleRenaultZoePh2OBD::WebDeInit()
{
MyWebServer.DeregisterPage("/xrz2/battmon");
MyWebServer.DeregisterPage("/xrz2/battery");
MyWebServer.DeregisterPage("/xrz2o/battmon");
MyWebServer.DeregisterPage("/xrz2o/battery");
}

View file

@ -39,11 +39,13 @@
#include "ovms_peripherals.h"
#include "ovms_netmanager.h"
#include "vehicle_renaultzoe_ph2.h"
#include "vehicle_renaultzoe_ph2_obd.h"
#include "ph2_poller.h"
OvmsVehicleRenaultZoePh2::OvmsVehicleRenaultZoePh2() {
ESP_LOGI(TAG, "Start Renault Zoe Ph2 vehicle module");
const char *OvmsVehicleRenaultZoePh2OBD::s_tag = "v-zoe-ph2-obd";
OvmsVehicleRenaultZoePh2OBD::OvmsVehicleRenaultZoePh2OBD() {
ESP_LOGI(TAG, "Start Renault Zoe Ph2 (OBD) vehicle module");
//Init variables supressing push on boot up
StandardMetrics.ms_v_type->SetValue("RZ2");
@ -53,7 +55,7 @@ OvmsVehicleRenaultZoePh2::OvmsVehicleRenaultZoePh2() {
StandardMetrics.ms_v_charge_substate->SetValue("stopped");
StandardMetrics.ms_v_env_on->SetValue(false);
MyConfig.RegisterParam("xrz2", "Renault Zoe Ph2 configuration", true, true);
MyConfig.RegisterParam("xrz2o", "Renault Zoe Ph2 (OBD) configuration", true, true);
ConfigChanged(NULL);
// Init Zoe Ph2 OBD Connection (CAN Gateway)
@ -68,31 +70,27 @@ OvmsVehicleRenaultZoePh2::OvmsVehicleRenaultZoePh2() {
// Renault ZOE specific metrics
mt_bus_awake = MyMetrics.InitBool("zph2.v.bus.awake", SM_STALE_NONE, false);
mt_pos_odometer_start = MyMetrics.InitFloat("zph2.v.pos.odometer.start", SM_STALE_MID, 0, Kilometers);
mt_pos_car_trip = MyMetrics.InitFloat("zph2.v.pos.car.trip", SM_STALE_NONE, 0, Kilometers);
mt_bat_used_start = MyMetrics.InitFloat("zph2.b.used.start", SM_STALE_MID, 0, kWh);
mt_bat_recd_start = MyMetrics.InitFloat("zph2.b.recd.start", SM_STALE_MID, 0, kWh);
mt_bat_chg_start = MyMetrics.InitFloat("zph2.b.chg.start", SM_STALE_MID, 0, kWh);
mt_bat_available_energy = MyMetrics.InitFloat("zph2.b.avail.energy", SM_STALE_NONE, 0, kWh);
mt_pos_odometer_start = MyMetrics.InitFloat("zph2.v.pos.odometer.start", SM_STALE_MID, 0, Kilometers, true);
mt_bat_used_start = MyMetrics.InitFloat("zph2.b.used.start", SM_STALE_MID, 0, kWh, true);
mt_bat_recd_start = MyMetrics.InitFloat("zph2.b.recd.start", SM_STALE_MID, 0, kWh, true);
mt_bat_chg_start = MyMetrics.InitFloat("zph2.b.chg.start", SM_STALE_MID, 0, kWh, true);
mt_bat_available_energy = MyMetrics.InitFloat("zph2.b.avail.energy", SM_STALE_NONE, 0, kWh, true);
mt_bat_aux_power_consumer = MyMetrics.InitFloat("zph2.b.aux.power.consumer", SM_STALE_MID, 0, Watts);
mt_bat_aux_power_ptc = MyMetrics.InitFloat("zph2.b.aux.power.ptc", SM_STALE_MID, 0, Watts);
mt_bat_max_charge_power = MyMetrics.InitFloat("zph2.b.max.charge.power", SM_STALE_MID, 0, kW);
mt_bat_max_charge_power = MyMetrics.InitFloat("zph2.b.max.charge.power", SM_STALE_MID, 0, kW, true);
mt_bat_cycles = MyMetrics.InitFloat("zph2.b.cycles", SM_STALE_MID, 0, Other, true);
mt_main_power_available = MyMetrics.InitFloat("zph2.c.main.power.available", SM_STALE_MIN, 0, kW);
mt_main_phases = MyMetrics.InitString("zph2.c.main.phases", SM_STALE_MIN, 0);
mt_main_phases_num = MyMetrics.InitFloat("zph2.c.main.phases.num", SM_STALE_MIN, 0);
mt_inv_status = MyMetrics.InitString("zph2.m.inverter.status", SM_STALE_NONE, 0);
mt_inv_hv_voltage = MyMetrics.InitFloat("zph2.i.voltage", SM_STALE_MID, 0, Volts);
mt_inv_hv_voltage = MyMetrics.InitFloat("zph2.i.voltage", SM_STALE_MID, 0, Volts, true);
mt_inv_hv_current = MyMetrics.InitFloat("zph2.i.current", SM_STALE_MID, 0, Amps);
mt_mot_temp_stator1 = MyMetrics.InitFloat("zph2.m.temp.stator1", SM_STALE_MID, 0, Celcius);
mt_mot_temp_stator2 = MyMetrics.InitFloat("zph2.m.temp.stator2", SM_STALE_MID, 0, Celcius);
mt_hvac_compressor_speed = MyMetrics.InitFloat("zph2.h.compressor.speed", SM_STALE_MID, 0, rpm);
mt_hvac_compressor_pressure = MyMetrics.InitFloat("zph2.h.compressor.pressure", SM_STALE_MID, 0, Bar);
mt_hvac_compressor_speed = MyMetrics.InitFloat("zph2.h.compressor.speed", SM_STALE_MID, 0);
mt_hvac_compressor_pressure = MyMetrics.InitFloat("zph2.h.compressor.pressure", SM_STALE_MID, 0);
mt_hvac_compressor_power = MyMetrics.InitFloat("zph2.h.compressor.power", SM_STALE_MID, 0, Watts);
mt_hvac_compressor_mode = MyMetrics.InitString("zph2.h.compressor.mode", SM_STALE_MID, 0, Other);
mt_v_env_pressure = MyMetrics.InitFloat("zph2.v.e.pressure", SM_STALE_MIN, 0, mBar);
mt_bat_lbc_soc = MyMetrics.InitFloat("zph2.b.lbc.soc", SM_STALE_MAX, 0, Percentage, true);
mt_bat_user_soc = MyMetrics.InitFloat("zph2.b.user.soc", SM_STALE_MAX, 0, Percentage, true);
// BMS configuration:
BmsSetCellArrangementVoltage(96, 1);
@ -107,20 +105,18 @@ OvmsVehicleRenaultZoePh2::OvmsVehicleRenaultZoePh2() {
#endif
}
OvmsVehicleRenaultZoePh2::~OvmsVehicleRenaultZoePh2() {
ESP_LOGI(TAG, "Stop Renault Zoe Ph2 vehicle module");
OvmsVehicleRenaultZoePh2OBD::~OvmsVehicleRenaultZoePh2OBD() {
ESP_LOGI(TAG, "Stop Renault Zoe Ph2 (OBD) vehicle module");
}
void OvmsVehicleRenaultZoePh2::ConfigChanged(OvmsConfigParam* param) {
if (param && param->GetName() != "xrz2")
void OvmsVehicleRenaultZoePh2OBD::ConfigChanged(OvmsConfigParam* param) {
if (param && param->GetName() != "xrz2o")
return;
// get values from config store
m_range_ideal = MyConfig.GetParamValueInt("xrz2", "rangeideal", 350);
m_battery_capacity = MyConfig.GetParamValueInt("xrz2", "battcapacity", 52000);
m_UseCarTrip = MyConfig.GetParamValueBool("xrz2", "UseCarTrip", false);
m_UseBMScalculation = MyConfig.GetParamValueBool("xrz2", "UseBMScalculation", false);
m_UseBMSsoc = MyConfig.GetParamValueBool("xrz2", "UseBMSsoc", false);
m_range_ideal = MyConfig.GetParamValueInt("xrz2o", "rangeideal", 350);
m_battery_capacity = MyConfig.GetParamValueInt("xrz2o", "battcapacity", 52000);
m_UseBMScalculation = MyConfig.GetParamValueBool("xrz2o", "UseBMScalculation", false);
StandardMetrics.ms_v_bat_range_ideal->SetValue(m_range_ideal, Kilometers);
if (m_battery_capacity == 52000) {
Bat_cell_capacity = 78.0 * 2 * (StandardMetrics.ms_v_bat_soh->AsFloat() / 100.0);
@ -131,10 +127,10 @@ void OvmsVehicleRenaultZoePh2::ConfigChanged(OvmsConfigParam* param) {
if (m_battery_capacity == 22000) {
Bat_cell_capacity = 36.0 * 2 * (StandardMetrics.ms_v_bat_soh->AsFloat() / 100.0);
}
ESP_LOGI(TAG, "Renault Zoe Ph2 reload configuration: Range ideal: %d, Battery capacity: %d, Use Car trip counter: %s, Use BMS as energy counter: %s, Use BMS for SOC: %s", m_range_ideal, m_battery_capacity, m_UseCarTrip ? "Yes" : "No", m_UseBMScalculation ? "yes" : "no", m_UseBMSsoc ? "yes" : "no");
ESP_LOGI(TAG, "Renault Zoe Ph2 (OBD) reload configuration: Range ideal: %d, Battery capacity: %d, Use BMS as energy counter: %s", m_range_ideal, m_battery_capacity, m_UseBMScalculation ? "yes" : "no");
}
void OvmsVehicleRenaultZoePh2::ZoeWakeUp() {
void OvmsVehicleRenaultZoePh2OBD::ZoeWakeUp() {
ESP_LOGI(TAG,"Zoe woke up (CAN Bus activity detected)");
mt_bus_awake->SetValue(true);;
StandardMetrics.ms_v_env_charging12v->SetValue(true);
@ -145,7 +141,7 @@ void OvmsVehicleRenaultZoePh2::ZoeWakeUp() {
/**
* Handles incoming CAN-frames on bus 1
*/
void OvmsVehicleRenaultZoePh2::IncomingFrameCan1(CAN_frame_t* p_frame) {
void OvmsVehicleRenaultZoePh2OBD::IncomingFrameCan1(CAN_frame_t* p_frame) {
uint8_t *data = p_frame->data.u8;
//ESP_LOGI(TAG, "PID:%x DATA: %02x %02x %02x %02x %02x %02x %02x %02x", p_frame->MsgID, data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7]);
//ESP_LOGD(TAG, "Status CAN Bus: %s", mt_bus_awake->AsBool() ? "true" : "false");
@ -162,6 +158,10 @@ void OvmsVehicleRenaultZoePh2::IncomingFrameCan1(CAN_frame_t* p_frame) {
StandardMetrics.ms_v_bat_current->SetValue( 0 );
POLLSTATE_OFF;
ESP_LOGI(TAG, "Pollstate switched to OFF");
//Check if car is locked, if not send notify
if (!StandardMetrics.ms_v_env_locked->AsBool()) {
MyNotify.NotifyString("alert", "vehicle.lock", "Vehicle is not locked");
}
} else if (!mt_bus_awake->AsBool()) {
ZoeWakeUp();
}
@ -174,7 +174,7 @@ void OvmsVehicleRenaultZoePh2::IncomingFrameCan1(CAN_frame_t* p_frame) {
/**
* Handles incoming poll results
*/
void OvmsVehicleRenaultZoePh2::IncomingPollReply(canbus* bus, uint16_t type, uint16_t pid, uint8_t* data, uint8_t length, uint16_t remain) {
void OvmsVehicleRenaultZoePh2OBD::IncomingPollReply(canbus* bus, uint16_t type, uint16_t pid, uint8_t* data, uint8_t length, uint16_t remain) {
string& rxbuf = zoe_obd_rxbuf;
//ESP_LOGV(TAG, "pid: %04x length: %d m_poll_ml_remain: %d m_poll_ml_frame: %d", pid, length, m_poll_ml_remain, m_poll_ml_frame);
@ -215,16 +215,16 @@ void OvmsVehicleRenaultZoePh2::IncomingPollReply(canbus* bus, uint16_t type, uin
IncomingUCM(type, pid, rxbuf.data(), rxbuf.size());
break;
// ****** CLUSTER *****
case 0x763:
IncomingCLUSTER(type, pid, rxbuf.data(), rxbuf.size());
break;
//case 0x763:
// IncomingCLUSTER(type, pid, rxbuf.data(), rxbuf.size());
// break;
}
}
int OvmsVehicleRenaultZoePh2::calcMinutesRemaining(float charge_voltage, float charge_current) {
float bat_soc = mt_bat_lbc_soc->AsFloat(100);
int OvmsVehicleRenaultZoePh2OBD::calcMinutesRemaining(float charge_voltage, float charge_current) {
float bat_soc = StandardMetrics.ms_v_bat_soc->AsFloat(100);
float remaining_wh = m_battery_capacity * bat_soc / 100.0;
float remaining_wh = m_battery_capacity - (m_battery_capacity * bat_soc / 100.0);
float remaining_hours = remaining_wh / (charge_current * charge_voltage);
float remaining_mins = remaining_hours * 60.0;
//ESP_LOGD(TAG, "SOC: %f, BattCap:%d, Current: %f, Voltage: %f, RemainWH: %f, RemainHour: %f, RemainMin: %f", bat_soc, m_battery_capacity, charge_current, charge_voltage, remaining_wh, remaining_hours, remaining_mins);
@ -232,7 +232,7 @@ int OvmsVehicleRenaultZoePh2::calcMinutesRemaining(float charge_voltage, float c
}
//Handle Charging values
void OvmsVehicleRenaultZoePh2::ChargeStatistics() {
void OvmsVehicleRenaultZoePh2OBD::ChargeStatistics() {
float charge_current = fabs(StandardMetrics.ms_v_bat_current->AsFloat(0, Amps));
float charge_voltage = StandardMetrics.ms_v_bat_voltage->AsFloat(0, Volts);
float battery_power = fabs(StandardMetrics.ms_v_bat_power->AsFloat(0, kW));
@ -260,6 +260,7 @@ void OvmsVehicleRenaultZoePh2::ChargeStatistics() {
if (charge_voltage > 0 && charge_current > 0) {
float power = charge_voltage * charge_current / 1000.0;
float energy = power / 3600.0 * 10.0;
float c_efficiency;
if (m_UseBMScalculation) {
StandardMetrics.ms_v_charge_kwh->SetValue(StandardMetrics.ms_v_charge_kwh_grid_total->AsFloat() - mt_bat_chg_start->AsFloat());
@ -271,7 +272,10 @@ void OvmsVehicleRenaultZoePh2::ChargeStatistics() {
StandardMetrics.ms_v_charge_duration_full->SetValue(minsremaining, Minutes);
if (StandardMetrics.ms_v_charge_type->AsString() == "type2") {
StandardMetrics.ms_v_charge_efficiency->SetValue((battery_power / charger_power) * 100.0);
c_efficiency = (battery_power / charger_power) * 100.0;
if (c_efficiency < 100.0) {
StandardMetrics.ms_v_charge_efficiency->SetValue(c_efficiency);
}
ESP_LOGI(TAG, "Charge time remaining: %d mins, AC Charge at %.2f kW with %.1f amps, %s at %.1f efficiency, %.2f powerfactor", minsremaining, charger_power, ac_current, ac_phases.c_str(), StandardMetrics.ms_v_charge_efficiency->AsFloat(100), ACInputPowerFactor);
} else if (StandardMetrics.ms_v_charge_type->AsString() == "ccs" || StandardMetrics.ms_v_charge_type->AsString() == "chademo") {
StandardMetrics.ms_v_charge_power->SetValue(battery_power);
@ -280,7 +284,7 @@ void OvmsVehicleRenaultZoePh2::ChargeStatistics() {
}
}
void OvmsVehicleRenaultZoePh2::EnergyStatisticsOVMS() {
void OvmsVehicleRenaultZoePh2OBD::EnergyStatisticsOVMS() {
float voltage = StandardMetrics.ms_v_bat_voltage->AsFloat(0, Volts);
float current = StandardMetrics.ms_v_bat_current->AsFloat(0, Amps);
@ -295,12 +299,12 @@ void OvmsVehicleRenaultZoePh2::EnergyStatisticsOVMS() {
}
}
void OvmsVehicleRenaultZoePh2::EnergyStatisticsBMS() {
void OvmsVehicleRenaultZoePh2OBD::EnergyStatisticsBMS() {
StandardMetrics.ms_v_bat_energy_used->SetValue(StandardMetrics.ms_v_bat_energy_used_total->AsFloat() - mt_bat_used_start->AsFloat());
StandardMetrics.ms_v_bat_energy_recd->SetValue(StandardMetrics.ms_v_bat_energy_recd_total->AsFloat() - mt_bat_recd_start->AsFloat());
}
void OvmsVehicleRenaultZoePh2::Ticker10(uint32_t ticker) {
void OvmsVehicleRenaultZoePh2OBD::Ticker10(uint32_t ticker) {
if (StandardMetrics.ms_v_charge_pilot->AsBool() && !StandardMetrics.ms_v_env_on->AsBool()) {
ChargeStatistics();
} else {
@ -312,8 +316,8 @@ void OvmsVehicleRenaultZoePh2::Ticker10(uint32_t ticker) {
}
}
void OvmsVehicleRenaultZoePh2::Ticker1(uint32_t ticker) {
if (StandardMetrics.ms_v_env_on->AsBool() && !CarIsDriving && !m_UseCarTrip) {
void OvmsVehicleRenaultZoePh2OBD::Ticker1(uint32_t ticker) {
if (StandardMetrics.ms_v_env_on->AsBool() && !CarIsDriving) {
CarIsDriving = true;
//Start trip after power on
StandardMetrics.ms_v_pos_trip->SetValue(0);
@ -350,30 +354,27 @@ void OvmsVehicleRenaultZoePh2::Ticker1(uint32_t ticker) {
StandardMetrics.ms_v_charge_duration_full->SetValue(0);
ESP_LOGI(TAG, "Pollstate switched to OFF, Wait for power...");
POLLSTATE_OFF;
} else if (StandardMetrics.ms_v_charge_pilot->AsBool() && CarIsCharging && StandardMetrics.ms_v_charge_state->AsString() == "done") {
CarIsCharging = false;
StandardMetrics.ms_v_charge_duration_full->SetValue(0);
ESP_LOGI(TAG, "Pollstate switched to OFF, done charging...");
POLLSTATE_OFF;
}
if (StandardMetrics.ms_v_env_on->AsBool() && !m_UseCarTrip) {
if (StandardMetrics.ms_v_env_on->AsBool()) {
StandardMetrics.ms_v_pos_trip->SetValue(StandardMetrics.ms_v_pos_odometer->AsFloat(0) - mt_pos_odometer_start->AsFloat(0));
}
if (m_UseCarTrip) {
StandardMetrics.ms_v_pos_trip->SetValue(mt_pos_car_trip->AsFloat());
}
StandardMetrics.ms_v_bat_range_est->SetValue((m_range_ideal * (StandardMetrics.ms_v_bat_soc->AsFloat(1) * 0.01)), Kilometers);
if(m_UseBMSsoc) {
StandardMetrics.ms_v_bat_soc->SetValue(mt_bat_lbc_soc->AsFloat(0));
} else {
StandardMetrics.ms_v_bat_soc->SetValue(mt_bat_user_soc->AsFloat(0));
}
}
class OvmsVehicleRenaultZoePh2Init {
public: OvmsVehicleRenaultZoePh2Init();
} MyOvmsVehicleRenaultZoePh2Init __attribute__ ((init_priority (9000)));
class OvmsVehicleRenaultZoePh2OBDInit {
public: OvmsVehicleRenaultZoePh2OBDInit();
} MyOvmsVehicleRenaultZoePh2OBDInit __attribute__ ((init_priority (9000)));
OvmsVehicleRenaultZoePh2Init::OvmsVehicleRenaultZoePh2Init()
OvmsVehicleRenaultZoePh2OBDInit::OvmsVehicleRenaultZoePh2OBDInit()
{
ESP_LOGI(TAG, "Registering Vehicle: Renault Zoe Ph2 (9000)");
MyVehicleFactory.RegisterVehicle<OvmsVehicleRenaultZoePh2>("RZ2","Renault Zoe Ph2");
ESP_LOGI(TAG, "Registering Vehicle: Renault Zoe Ph2 (OBD) (9000)");
MyVehicleFactory.RegisterVehicle<OvmsVehicleRenaultZoePh2OBD>("RZ2O","Renault Zoe Ph2 (OBD)");
}

View file

@ -23,10 +23,8 @@
; THE SOFTWARE.
*/
#ifndef __VEHICLE_RENAULTZOE_PH2_H__
#define __VEHICLE_RENAULTZOE_PH2_H__
static const char *TAG = "v-zoe-ph2";
#ifndef __VEHICLE_RENAULTZOE_PH2_OBD_H__
#define __VEHICLE_RENAULTZOE_PH2_OBD_H__
#include <atomic>
@ -58,11 +56,15 @@ static const char *TAG = "v-zoe-ph2";
using namespace std;
class OvmsVehicleRenaultZoePh2 : public OvmsVehicle {
#define TAG (OvmsVehicleRenaultZoePh2OBD::s_tag)
class OvmsVehicleRenaultZoePh2OBD : public OvmsVehicle {
public:
static const char *s_tag;
public:
OvmsVehicleRenaultZoePh2();
~OvmsVehicleRenaultZoePh2();
OvmsVehicleRenaultZoePh2OBD();
~OvmsVehicleRenaultZoePh2OBD();
static void WebCfgCommon(PageEntry_t& p, PageContext_t& c);
void ConfigChanged(OvmsConfigParam* param);
void ZoeWakeUp();
@ -83,14 +85,14 @@ class OvmsVehicleRenaultZoePh2 : public OvmsVehicle {
int m_battery_capacity;
bool m_UseCarTrip = false;
bool m_UseBMScalculation = false;
bool m_UseBMSsoc = false;
char zoe_vin[18] = "";
void IncomingINV(uint16_t type, uint16_t pid, const char* data, uint16_t len);
void IncomingEVC(uint16_t type, uint16_t pid, const char* data, uint16_t len);
void IncomingBCM(uint16_t type, uint16_t pid, const char* data, uint16_t len);
void IncomingLBC(uint16_t type, uint16_t pid, const char* data, uint16_t len);
void IncomingHVAC(uint16_t type, uint16_t pid, const char* data, uint16_t len);
void IncomingUCM(uint16_t type, uint16_t pid, const char* data, uint16_t len);
void IncomingCLUSTER(uint16_t type, uint16_t pid, const char* data, uint16_t len);
//void IncomingCLUSTER(uint16_t type, uint16_t pid, const char* data, uint16_t len);
int calcMinutesRemaining(float charge_voltage, float charge_current);
void ChargeStatistics();
void EnergyStatisticsOVMS();
@ -102,7 +104,6 @@ class OvmsVehicleRenaultZoePh2 : public OvmsVehicle {
// Renault ZOE specific metrics
OvmsMetricBool *mt_bus_awake; //CAN bus awake status
OvmsMetricFloat *mt_pos_odometer_start; //ODOmeter at trip start
OvmsMetricFloat *mt_pos_car_trip; //Cluster tripcounter
OvmsMetricFloat *mt_bat_used_start; //Used battery kWh at trip start
OvmsMetricFloat *mt_bat_recd_start; //Recd battery kWh at trip start
OvmsMetricFloat *mt_bat_chg_start; //Charge battery kWh at charge start
@ -111,8 +112,6 @@ class OvmsVehicleRenaultZoePh2 : public OvmsVehicle {
OvmsMetricFloat *mt_bat_aux_power_ptc; //Power usage by PTCs
OvmsMetricFloat *mt_bat_max_charge_power; //Battery max allowed charge, recd power
OvmsMetricFloat *mt_bat_cycles; //Battery full charge cycles
OvmsMetricFloat *mt_bat_lbc_soc; //Battery real SOC by BMS
OvmsMetricFloat *mt_bat_user_soc; //Battery user SOC by Cluster
OvmsMetricFloat *mt_main_power_available; //Mains power available
OvmsMetricString *mt_main_phases; //Mains phases used
OvmsMetricFloat *mt_main_phases_num; //Mains phases used
@ -132,4 +131,4 @@ class OvmsVehicleRenaultZoePh2 : public OvmsVehicle {
string zoe_obd_rxbuf;
};
#endif //#ifndef __VEHICLE_RENAULTZOE_PH2_H__
#endif //#ifndef __VEHICLE_RENAULTZOE_PH2_OBD_H__

View file

@ -353,13 +353,20 @@ config OVMS_VEHICLE_CAN_RX_QUEUE_SIZE
help
The size of the CAN bus RX queue (at the vehicle component).
config OVMS_VEHICLE_RENAULTZOE_PH2
bool "Include support for Renault Zoe PH2 vehicles"
config OVMS_VEHICLE_RENAULTZOE_PH2_OBD
bool "Include support for Renault Zoe PH2 vehicles via OBD port (read-only)"
default y
depends on OVMS
help
Enable to include support for Renault Zoe Ph2 vehicles.
Enable to include support for Renault Zoe Ph2 vehicles via OBD port (read-only).
config OVMS_VEHICLE_RENAULTZOE_PH2_CAN
bool "Include support for Renault Zoe PH2 vehicles via direct CAN access after Core Can Gateway"
default y
depends on OVMS
help
Enable to include support for Renault Zoe Ph2 vehicles via direct CAN access after Core Can Gateway.
endmenu # Vehicle Support

View file

@ -191,7 +191,7 @@ void ConsoleAsync::HandleDeviceEvent(void* pEvent)
uart_flush(EX_UART_NUM);
break;
default:
ESP_LOGE(TAG, "uart event type: %d", event.type);
break;
}
}

View file

@ -257,6 +257,8 @@ MetricsStandard::MetricsStandard()
ms_v_pos_gpsmode = new OvmsMetricString(MS_V_POS_GPSMODE, SM_STALE_MIN);
ms_v_pos_gpshdop = new OvmsMetricFloat(MS_V_POS_GPSHDOP, SM_STALE_MIN);
ms_v_pos_satcount= new OvmsMetricInt(MS_V_POS_SATCOUNT, SM_STALE_MIN);
ms_v_pos_gpssq = new OvmsMetricInt(MS_V_POS_GPSSQ, SM_STALE_MIN, Percentage);
ms_v_pos_gpstime = new OvmsMetricInt(MS_V_POS_GPSTIME, SM_STALE_MIN, Seconds);
ms_v_pos_latitude = new OvmsMetricFloat(MS_V_POS_LATITUDE, SM_STALE_MIN, Other, true);
ms_v_pos_longitude = new OvmsMetricFloat(MS_V_POS_LONGITUDE, SM_STALE_MIN, Other, true);
ms_v_pos_location = new OvmsMetricString(MS_V_POS_LOCATION, SM_STALE_MID);

View file

@ -230,6 +230,8 @@
#define MS_V_POS_GPSMODE "v.p.gpsmode"
#define MS_V_POS_GPSHDOP "v.p.gpshdop"
#define MS_V_POS_SATCOUNT "v.p.satcount"
#define MS_V_POS_GPSSQ "v.p.gpssq"
#define MS_V_POS_GPSTIME "v.p.gpstime"
#define MS_V_POS_LATITUDE "v.p.latitude"
#define MS_V_POS_LONGITUDE "v.p.longitude"
#define MS_V_POS_LOCATION "v.p.location"
@ -491,6 +493,8 @@ class MetricsStandard
OvmsMetricString* ms_v_pos_gpsmode; // <GPS><GLONASS>; N/A/D/E (None/Autonomous/Differential/Estimated)
OvmsMetricFloat* ms_v_pos_gpshdop; // Horizontal dilution of precision (smaller=better)
OvmsMetricInt* ms_v_pos_satcount;
OvmsMetricInt* ms_v_pos_gpssq; // GPS signal quality [%] (<30 unusable, >50 good, >80 excellent)
OvmsMetricInt* ms_v_pos_gpstime; // Time (UTC) of GPS coordinates [Seconds]
OvmsMetricFloat* ms_v_pos_latitude;
OvmsMetricFloat* ms_v_pos_longitude;
OvmsMetricString* ms_v_pos_location; // Name of current location if defined

View file

@ -34,6 +34,7 @@ static const char *TAG = "boot";
#include "freertos/FreeRTOS.h"
#include "freertos/xtensa_api.h"
#include "rom/rtc.h"
#include "rom/uart.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_system.h"
#include "esp_panic.h"
@ -165,10 +166,18 @@ void boot_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc,
writer->printf("\nLast crash: ");
if (boot_data.crash_data.is_abort)
{
// Software controlled panic:
writer->printf("abort() was called on core %d\n", boot_data.crash_data.core_id);
if (boot_data.stack_overflow_taskname[0])
writer->printf(" Stack overflow in task %s\n", boot_data.stack_overflow_taskname);
else if (boot_data.curr_task[0].name[0] && !boot_data.curr_task[0].stackfree)
writer->printf(" Pending stack overflow in task %s\n", boot_data.curr_task[0].name);
else if (boot_data.curr_task[1].name[0] && !boot_data.curr_task[1].stackfree)
writer->printf(" Pending stack overflow in task %s\n", boot_data.curr_task[1].name);
}
else
{
// Hardware exception:
int exccause = boot_data.crash_data.reg[19];
writer->printf("%s exception on core %d\n",
(exccause < NUM_EDESCS) ? edesc[exccause] : "Unknown", boot_data.crash_data.core_id);
@ -176,18 +185,29 @@ void boot_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc,
for (int i=0; i<24; i++)
writer->printf(" %s: 0x%08lx%s", sdesc[i], boot_data.crash_data.reg[i], ((i+1)%4) ? "" : "\n");
}
for (int core = 0; core < portNUM_PROCESSORS; core++)
{
if (boot_data.curr_task[core].name[0])
writer->printf(" Current task on core %d: %s, %u stack bytes free\n",
core, boot_data.curr_task[core].name, boot_data.curr_task[core].stackfree);
}
writer->printf(" Backtrace:\n ");
for (int i=0; i<OVMS_BT_LEVELS && boot_data.crash_data.bt[i].pc; i++)
writer->printf(" 0x%08lx", boot_data.crash_data.bt[i].pc);
if (boot_data.curr_event_name[0])
{
writer->printf("\n Event: %s@%s %u secs", boot_data.curr_event_name, boot_data.curr_event_handler,
boot_data.curr_event_runtime);
}
if (MyBoot.GetResetReason() == ESP_RST_TASK_WDT)
{
writer->printf("\n WDT tasks: %s", boot_data.wdt_tasknames);
}
writer->printf("\n Version: %s\n", StdMetrics.ms_m_version->AsString("").c_str());
writer->printf("\n Hardware: %s\n", StdMetrics.ms_m_hardware->AsString("").c_str());
}
@ -239,7 +259,7 @@ Boot::Boot()
uint32_t adc_level = 0;
for (int i = 0; i < 5; i++)
adc_level += adc1_get_raw(ADC1_CHANNEL_0);
float level_12v = adc_level / 5 / 195.7;
float level_12v = (float) adc_level / 5 / 195.7;
ESP_LOGI(TAG, "12V level: ~%.1fV", level_12v);
if (level_12v > 11.0)
ESP_LOGI(TAG, "12V level sufficient, proceeding with boot");
@ -265,14 +285,12 @@ Boot::Boot()
boot_data.boot_count++;
ESP_LOGI(TAG, "Boot #%d reasons for CPU0=%d and CPU1=%d",boot_data.boot_count,cpu0,cpu1);
m_resetreason = boot_data.reset_hint;
ESP_LOGI(TAG, "Reset reason %s (%d)", GetResetReasonName(), GetResetReason());
if (boot_data.soft_reset)
{
boot_data.crash_count_total = 0;
boot_data.crash_count_early = 0;
m_bootreason = BR_SoftReset;
m_resetreason = ESP_RST_SW;
ESP_LOGI(TAG, "Soft reset by user");
}
else if (boot_data.firmware_update)
@ -281,6 +299,7 @@ Boot::Boot()
boot_data.crash_count_early = 0;
m_bootreason = BR_FirmwareUpdate;
ESP_LOGI(TAG, "Firmware update reset");
m_resetreason = ESP_RST_SW;
}
else if (!boot_data.stable_reached)
{
@ -288,24 +307,33 @@ Boot::Boot()
boot_data.crash_count_early++;
m_bootreason = BR_EarlyCrash;
ESP_LOGE(TAG, "Early crash #%d detected", boot_data.crash_count_early);
m_resetreason = boot_data.reset_hint;
ESP_LOGI(TAG, "Reset reason %s (%d)", GetResetReasonName(), GetResetReason());
}
else
{
boot_data.crash_count_total++;
m_bootreason = BR_Crash;
ESP_LOGE(TAG, "Crash #%d detected", boot_data.crash_count_total);
m_resetreason = boot_data.reset_hint;
ESP_LOGI(TAG, "Reset reason %s (%d)", GetResetReasonName(), GetResetReason());
}
}
m_crash_count_early = boot_data.crash_count_early;
m_stack_overflow = boot_data.stack_overflow;
if (!m_stack_overflow)
boot_data.stack_overflow_taskname[0] = 0;
boot_data.bootreason_cpu0 = cpu0;
boot_data.bootreason_cpu1 = cpu1;
boot_data.reset_hint = ESP_RST_UNKNOWN;
// reset flags:
boot_data.soft_reset = false;
boot_data.firmware_update = false;
boot_data.stable_reached = false;
boot_data.stack_overflow = false;
boot_data.crc = boot_data.calc_crc();
@ -351,6 +379,8 @@ const char* Boot::GetBootReasonName()
const char* Boot::GetResetReasonName()
{
if (m_stack_overflow)
return "Stack overflow";
return (m_resetreason >= 0 && m_resetreason < NUM_RESETREASONS)
? resetreason_name[m_resetreason]
: "Unknown reset reason";
@ -448,6 +478,52 @@ bool Boot::IsShuttingDown()
return m_shutting_down;
}
/*
* Direct UART output utils borrowed from esp32/panic.c
*/
static void panicPutChar(char c)
{
while (((READ_PERI_REG(UART_STATUS_REG(CONFIG_CONSOLE_UART_NUM)) >> UART_TXFIFO_CNT_S)&UART_TXFIFO_CNT) >= 126) ;
WRITE_PERI_REG(UART_FIFO_REG(CONFIG_CONSOLE_UART_NUM), c);
}
static void panicPutStr(const char *c)
{
int x = 0;
while (c[x] != 0)
{
panicPutChar(c[x]);
x++;
}
}
/*
* This function is called by task_wdt_isr function (ISR for when TWDT times out).
* It can be redefined in user code to handle twdt events.
* Note: It has the same limitations as the interrupt function.
* Do not use ESP_LOGI functions inside.
*/
extern "C" void esp_task_wdt_isr_user_handler(void)
{
panicPutStr("\r\n[OVMS] ***TWDT***\r\n");
// Save TWDT task info:
esp_task_wdt_get_trigger_tasknames(boot_data.wdt_tasknames, sizeof(boot_data.wdt_tasknames));
}
/*
* This function is called if FreeRTOS detects a stack overflow.
*/
extern "C" void vApplicationStackOverflowHook( TaskHandle_t xTask, signed char *pcTaskName )
{
panicPutStr("\r\n[OVMS] ***ERROR*** A stack overflow in task ");
panicPutStr((char *)pcTaskName);
panicPutStr(" has been detected.\r\n");
strlcpy(boot_data.stack_overflow_taskname, (const char*)pcTaskName, sizeof(boot_data.stack_overflow_taskname));
boot_data.stack_overflow = true;
abort();
}
void Boot::ErrorCallback(XtExcFrame *frame, int core_id, bool is_abort)
{
boot_data.reset_hint = ovms_reset_reason_get_hint();
@ -495,8 +571,28 @@ void Boot::ErrorCallback(XtExcFrame *frame, int core_id, bool is_abort)
boot_data.curr_event_runtime = 0;
}
// Save TWDT task info:
esp_task_wdt_get_trigger_tasknames(boot_data.wdt_tasknames, sizeof(boot_data.wdt_tasknames));
// Save current tasks:
panicPutStr("\r\n[OVMS] Current tasks: ");
for (int core=0; core<portNUM_PROCESSORS; core++)
{
TaskHandle_t task = xTaskGetCurrentTaskHandleForCPU(core);
if (task)
{
char *name = pcTaskGetTaskName(task);
uint32_t stackfree = uxTaskGetStackHighWaterMark(task);
if (core > 0) panicPutChar('|');
panicPutStr(name);
strlcpy(boot_data.curr_task[core].name, name, sizeof(boot_data.curr_task[core].name));
boot_data.curr_task[core].stackfree = stackfree;
if (!stackfree) boot_data.stack_overflow = true;
}
else
{
boot_data.curr_task[core].name[0] = 0;
boot_data.curr_task[core].stackfree = 0;
}
}
panicPutStr("\r\n");
boot_data.crc = boot_data.calc_crc();
}
@ -514,6 +610,8 @@ void Boot::NotifyDebugCrash()
// ,<curr_event_name>,<curr_event_handler>,<curr_event_runtime>
// ,<wdt_tasknames>
// ,<hardware_info>
// ,<stack_overflow_task>
// ,<core0_task>,<core0_stackfree>,<core1_task>,<core1_stackfree>
StringWriter buf;
buf.reserve(2048);
@ -557,6 +655,20 @@ void Boot::NotifyDebugCrash()
buf.append(",");
buf.append(mp_encode(StdMetrics.ms_m_hardware->AsString("")));
// Stack overflow task:
std::string name = boot_data.stack_overflow_taskname;
buf.append(",");
buf.append(mp_encode(name));
// Current tasks:
for (int i = 0; i < 2; i++)
{
name = boot_data.curr_task[i].name;
buf.append(",");
buf.append(mp_encode(name));
buf.printf(",%u", boot_data.curr_task[i].stackfree);
}
MyNotify.NotifyString("data", "debug.crash", buf.c_str());
}
}

View file

@ -62,6 +62,12 @@ typedef struct
} bt[OVMS_BT_LEVELS];
} crash_data_t;
typedef struct
{
char name[16];
uint32_t stackfree;
} task_info_t;
typedef struct
{
// data consistency:
@ -83,6 +89,9 @@ typedef struct
char curr_event_handler[16]; // … MyEvents.m_current_callback->m_caller
uint16_t curr_event_runtime; // … monotonictime-MyEvents.m_current_started
char wdt_tasknames[32]; // Pipe (|) separated list of the tasks that triggered the TWDT
bool stack_overflow;
char stack_overflow_taskname[16];
task_info_t curr_task[portNUM_PROCESSORS];
} boot_data_t;
extern boot_data_t boot_data;
@ -128,6 +137,7 @@ class Boot
bootreason_t m_bootreason;
esp_reset_reason_t m_resetreason;
unsigned int m_crash_count_early;
bool m_stack_overflow;
};
extern Boot MyBoot;

Some files were not shown because too many files have changed in this diff Show more