Skip to content

Commit 5a19e47

Browse files
committed
Vehicle: added optional automatic trip report generation
Details: https://docs.openvehicles.com/en/latest/userguide/notifications.html New configs: [notify] report.trip.enable -- Send trip report on vehicle off (bool, default no) [notify] report.trip.minlength -- … minimum trip length in km, default 0.2 km New command: stat trip -- Output statistics for current/finished trip
1 parent bc2fd96 commit 5a19e47

File tree

6 files changed

+246
-3
lines changed

6 files changed

+246
-3
lines changed

docs/source/userguide/notifications.rst

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ info charge.stopped ``stat`` on planned charge stop
127127
alert charge.stopped ``stat`` on unplanned charge stop
128128
data debug.crash Transmit crash backtraces (→ ``*-OVM-DebugCrash``)
129129
data debug.tasks Transmit task statistics (→ ``*-OVM-DebugTasks``)
130+
info drive.trip.report Trip driving statistics (see `Trip report`_)
130131
alert flatbed.moved Vehicle is being transported while parked - possible theft/flatbed
131132
info heating.started ``stat`` on start of heating (battery)
132133
data log.grid Grid (charge/generator) history log (see below) (→ ``*-LOG-Grid``)
@@ -278,3 +279,32 @@ Already stored log entries will be kept on the server until expiry or manual del
278279
* tpms_health_min
279280
* tpms_health_max
280281

282+
283+
-----------
284+
Trip report
285+
-----------
286+
287+
The trip report outputs some core statistics of the current or most recent trip (drive
288+
cycle). It can be queried any time using the ``stat trip`` command, or be configured to
289+
be sent automatically on turning the vehicle off:
290+
291+
Use the web UI (Config → Notifications) or set config variable ``notify report.trip.enable`` to
292+
``yes`` and optionally a minimum trip length in ``notify log.trip.minlength`` (defaults to 0.2 km).
293+
If your vehicle does not support the ``v.p.trip`` metric, set the minimum trip length to 0.
294+
295+
The statistics available depend on your vehicle type (i.e. metrics support of that vehicle
296+
adaption). Vehicles also may override the report to provide custom statistics. By default,
297+
a full trip report will contain:
298+
299+
- Trip length
300+
- Average driving speed
301+
- Overall altitude difference (start to end point)
302+
- Energy consumption in Wh per km/Mi
303+
- Recuperation percentage (in relation to energy used)
304+
- SOC difference & new SOC
305+
- Estimated range difference & new range
306+
- Average acceleration & deceleration
307+
308+
The average driving speed is calculated only from speeds above 5 kph (3 mph)
309+
(to exclude slow speed rolling), and the acceleration & deceleration averages
310+
exclude values below 2.5 kph/s (1.6 mph/s) (constant speed cruising).

vehicle/OVMS.V3/changes.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
Open Vehicle Monitor System v3 - Change log
22

33
????-??-?? ??? ??????? OTA release
4+
- Vehicle: added optional automatic trip report generation
5+
Details: https://docs.openvehicles.com/en/latest/userguide/notifications.html
6+
New configs:
7+
[notify] report.trip.enable -- Send trip report on vehicle off (bool, default no)
8+
[notify] report.trip.minlength -- … minimum trip length in km, default 0.2 km
9+
New command:
10+
stat trip -- Output statistics for current/finished trip
411
- VW e-Up: added acceleration support; OBD by reading from ECU,
512
T26 deriving from speed changes
613

vehicle/OVMS.V3/components/ovms_webserver/src/web_cfg.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,6 +1600,8 @@ void OvmsWebServer::HandleCfgNotifications(PageEntry_t& p, PageContext_t& c)
16001600
std::string error;
16011601
std::string vehicle_minsoc, vehicle_stream;
16021602
std::string log_trip_storetime, log_trip_minlength, log_grid_storetime;
1603+
bool report_trip_enable;
1604+
std::string report_trip_minlength;
16031605

16041606
if (c.method == "POST") {
16051607
// process form submission:
@@ -1608,6 +1610,8 @@ void OvmsWebServer::HandleCfgNotifications(PageEntry_t& p, PageContext_t& c)
16081610
log_trip_storetime = c.getvar("log_trip_storetime");
16091611
log_trip_minlength = c.getvar("log_trip_minlength");
16101612
log_grid_storetime = c.getvar("log_grid_storetime");
1613+
report_trip_enable = (c.getvar("report_trip_enable") == "yes");
1614+
report_trip_minlength = c.getvar("report_trip_minlength");
16111615

16121616
if (vehicle_minsoc != "") {
16131617
if (atoi(vehicle_minsoc.c_str()) < 0 || atoi(vehicle_minsoc.c_str()) > 100) {
@@ -1636,6 +1640,12 @@ void OvmsWebServer::HandleCfgNotifications(PageEntry_t& p, PageContext_t& c)
16361640
}
16371641
}
16381642

1643+
if (report_trip_minlength != "") {
1644+
if (atoi(report_trip_minlength.c_str()) < 0) {
1645+
error += "<li data-input=\"report_trip_minlength\">Trip report min length must not be negative</li>";
1646+
}
1647+
}
1648+
16391649
if (error == "") {
16401650
// success:
16411651
if (vehicle_minsoc == "")
@@ -1660,6 +1670,12 @@ void OvmsWebServer::HandleCfgNotifications(PageEntry_t& p, PageContext_t& c)
16601670
else
16611671
MyConfig.SetParamValue("notify", "log.grid.storetime", log_grid_storetime);
16621672

1673+
MyConfig.SetParamValueBool("notify", "report.trip.enable", report_trip_enable);
1674+
if (report_trip_minlength == "")
1675+
MyConfig.DeleteInstance("notify", "report.trip.minlength");
1676+
else
1677+
MyConfig.SetParamValue("notify", "report.trip.minlength", report_trip_minlength);
1678+
16631679
c.head(200);
16641680
c.alert("success", "<p class=\"lead\">Notifications configured.</p>");
16651681
OutputHome(p, c);
@@ -1680,6 +1696,8 @@ void OvmsWebServer::HandleCfgNotifications(PageEntry_t& p, PageContext_t& c)
16801696
log_trip_storetime = MyConfig.GetParamValue("notify", "log.trip.storetime");
16811697
log_trip_minlength = MyConfig.GetParamValue("notify", "log.trip.minlength");
16821698
log_grid_storetime = MyConfig.GetParamValue("notify", "log.grid.storetime");
1699+
report_trip_enable = MyConfig.GetParamValueBool("notify", "report.trip.enable");
1700+
report_trip_minlength = MyConfig.GetParamValue("notify", "report.trip.minlength");
16831701

16841702
// generate form:
16851703
c.head(200);
@@ -1701,6 +1719,17 @@ void OvmsWebServer::HandleCfgNotifications(PageEntry_t& p, PageContext_t& c)
17011719

17021720
c.fieldset_end();
17031721

1722+
c.fieldset_start("Vehicle Reports");
1723+
1724+
c.input_checkbox("Enable trip report", "report_trip_enable", report_trip_enable,
1725+
"<p>This will send a textual report on driving statistics after each trip.</p>");
1726+
c.input("number", "Report min trip length", "report_trip_minlength", report_trip_minlength.c_str(), "Default: 0.2 km",
1727+
"<p>Only trips over at least this distance will produce a report. If your vehicle does not support the "
1728+
"<code>v.p.trip</code> metric, set this to 0.</p>",
1729+
"min=\"0\" step=\"0.1\"", "km");
1730+
1731+
c.fieldset_end();
1732+
17041733
c.fieldset_start("Data Log Storage Times");
17051734

17061735
c.input("number", "Trip history log", "log_trip_storetime", log_trip_storetime.c_str(), "Default: empty/0 = disabled",

vehicle/OVMS.V3/components/vehicle/vehicle.cpp

Lines changed: 153 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ OvmsVehicleFactory::OvmsVehicleFactory()
8585
cmd_charge->RegisterCommand("current","Limit charge current",vehicle_charge_current,"<amps>",1,1);
8686
cmd_charge->RegisterCommand("cooldown","Start a vehicle cooldown",vehicle_charge_cooldown);
8787

88-
MyCommandApp.RegisterCommand("stat","Show vehicle status",vehicle_stat);
88+
OvmsCommand* cmd_stat = MyCommandApp.RegisterCommand("stat","Show vehicle status",vehicle_stat);
89+
cmd_stat->RegisterCommand("trip","Show trip status",vehicle_stat_trip);
8990

9091
OvmsCommand* cmd_bms = MyCommandApp.RegisterCommand("bms","BMS framework");
9192
cmd_bms->RegisterCommand("status","Show BMS status",bms_status);
@@ -248,6 +249,16 @@ OvmsVehicle::OvmsVehicle()
248249
m_last_gentime = 0;
249250
m_last_parktime = 0;
250251

252+
m_drive_startsoc = StdMetrics.ms_v_bat_soc->AsFloat();
253+
m_drive_startrange = StdMetrics.ms_v_bat_range_est->AsFloat();
254+
m_drive_startaltitude = StdMetrics.ms_v_pos_altitude->AsFloat();
255+
m_drive_speedcnt = 0;
256+
m_drive_speedsum = 0;
257+
m_drive_accelcnt = 0;
258+
m_drive_accelsum = 0;
259+
m_drive_decelcnt = 0;
260+
m_drive_decelsum = 0;
261+
251262
m_ticker = 0;
252263
m_12v_ticker = 0;
253264
m_chargestate_ticker = 0;
@@ -1083,6 +1094,96 @@ OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStat(int verbosity, OvmsWrite
10831094
return Success;
10841095
}
10851096

1097+
/**
1098+
* CommandStatTrip: default implementation of vehicle trip status report
1099+
*/
1100+
OvmsVehicle::vehicle_command_t OvmsVehicle::CommandStatTrip(int verbosity, OvmsWriter* writer)
1101+
{
1102+
metric_unit_t rangeUnit = (MyConfig.GetParamValue("vehicle", "units.distance") == "M") ? Miles : Kilometers;
1103+
metric_unit_t speedUnit = (rangeUnit == Miles) ? Mph : Kph;
1104+
metric_unit_t accelUnit = (rangeUnit == Miles) ? MphPS : KphPS;
1105+
metric_unit_t energyUnit = (rangeUnit == Miles) ? WattHoursPM : WattHoursPK;
1106+
metric_unit_t altitudeUnit = (rangeUnit == Miles) ? Feet : Meters;
1107+
const char* rangeUnitLabel = OvmsMetricUnitLabel(rangeUnit);
1108+
const char* speedUnitLabel = OvmsMetricUnitLabel(speedUnit);
1109+
const char* accelUnitLabel = OvmsMetricUnitLabel(accelUnit);
1110+
const char* energyUnitLabel = OvmsMetricUnitLabel(energyUnit);
1111+
const char* altitudeUnitLabel = OvmsMetricUnitLabel(altitudeUnit);
1112+
1113+
float trip_length = StdMetrics.ms_v_pos_trip->AsFloat(0, rangeUnit);
1114+
1115+
float speed_avg = (m_drive_speedcnt > 0)
1116+
? UnitConvert(Kph, speedUnit, (float)(m_drive_speedsum / m_drive_speedcnt))
1117+
: 0;
1118+
float accel_avg = (m_drive_accelcnt > 0)
1119+
? UnitConvert(MetersPSS, accelUnit, (float)(m_drive_accelsum / m_drive_accelcnt))
1120+
: 0;
1121+
float decel_avg = (m_drive_decelcnt > 0)
1122+
? UnitConvert(MetersPSS, accelUnit, (float)(m_drive_decelsum / m_drive_decelcnt))
1123+
: 0;
1124+
1125+
float energy_used = StdMetrics.ms_v_bat_energy_used->AsFloat();
1126+
float energy_recd = StdMetrics.ms_v_bat_energy_recd->AsFloat();
1127+
float energy_recd_perc = (energy_used > 0) ? energy_recd / energy_used * 100 : 0;
1128+
float wh_per_rangeunit = (trip_length > 0) ? (energy_used - energy_recd) * 1000 / trip_length : 0;
1129+
1130+
float soc = StdMetrics.ms_v_bat_soc->AsFloat();
1131+
float soc_diff = soc - m_drive_startsoc;
1132+
float range = StdMetrics.ms_v_bat_range_est->AsFloat();
1133+
float range_diff = range - m_drive_startrange;
1134+
float alt = StdMetrics.ms_v_pos_altitude->AsFloat();
1135+
float alt_diff = UnitConvert(Meters, altitudeUnit, alt - m_drive_startaltitude);
1136+
1137+
std::ostringstream buf;
1138+
buf
1139+
<< "Trip "
1140+
<< std::fixed
1141+
<< std::setprecision(1)
1142+
<< trip_length << rangeUnitLabel
1143+
<< " Avg "
1144+
<< std::setprecision(0)
1145+
<< speed_avg << speedUnitLabel
1146+
<< " Alt "
1147+
<< ((alt_diff >= 0) ? "+" : "")
1148+
<< alt_diff << altitudeUnitLabel
1149+
;
1150+
if (wh_per_rangeunit != 0)
1151+
{
1152+
buf
1153+
<< "\nEnergy "
1154+
<< wh_per_rangeunit << energyUnitLabel
1155+
<< ", "
1156+
<< energy_recd_perc << "% recd"
1157+
;
1158+
}
1159+
buf
1160+
<< std::setprecision(1)
1161+
<< "\nSOC "
1162+
<< ((soc_diff >= 0) ? "+" : "")
1163+
<< soc_diff << "%"
1164+
<< " = "
1165+
<< soc << "%"
1166+
<< "\nRange "
1167+
<< ((range_diff >= 0) ? "+" : "")
1168+
<< range_diff << rangeUnitLabel
1169+
<< " = "
1170+
<< range << rangeUnitLabel
1171+
;
1172+
if (accel_avg > 0)
1173+
{
1174+
buf
1175+
<< "\nAccel +"
1176+
<< accel_avg
1177+
<< " / "
1178+
<< decel_avg << accelUnitLabel
1179+
;
1180+
}
1181+
1182+
writer->puts(buf.str().c_str());
1183+
1184+
return Success;
1185+
}
1186+
10861187
void OvmsVehicle::VehicleConfigChanged(std::string event, void* data)
10871188
{
10881189
OvmsConfigParam* param = (OvmsConfigParam*) data;
@@ -1125,6 +1226,15 @@ void OvmsVehicle::MetricModified(OvmsMetric* metric)
11251226
{
11261227
if (StandardMetrics.ms_v_env_on->AsBool())
11271228
{
1229+
m_drive_startsoc = StdMetrics.ms_v_bat_soc->AsFloat();
1230+
m_drive_startrange = StdMetrics.ms_v_bat_range_est->AsFloat();
1231+
m_drive_startaltitude = StdMetrics.ms_v_pos_altitude->AsFloat();
1232+
m_drive_speedcnt = 0;
1233+
m_drive_speedsum = 0;
1234+
m_drive_accelcnt = 0;
1235+
m_drive_accelsum = 0;
1236+
m_drive_decelcnt = 0;
1237+
m_drive_decelsum = 0;
11281238
MyEvents.SignalEvent("vehicle.on",NULL);
11291239
if (m_autonotifications)
11301240
{
@@ -1364,10 +1474,35 @@ void OvmsVehicle::MetricModified(OvmsMetric* metric)
13641474
if (m_autonotifications)
13651475
NotifyGenState();
13661476
}
1477+
else if (metric == StandardMetrics.ms_v_pos_speed)
1478+
{
1479+
// Collect data for trip speed average:
1480+
const float min_speed = 5.0; // slow speed exclusion [kph]
1481+
float speed = StandardMetrics.ms_v_pos_speed->AsFloat();
1482+
if (speed > min_speed)
1483+
{
1484+
m_drive_speedcnt++;
1485+
m_drive_speedsum += speed;
1486+
}
1487+
}
13671488
else if (metric == StandardMetrics.ms_v_pos_acceleration)
13681489
{
13691490
if (m_brakelight_enable)
13701491
CheckBrakelight();
1492+
1493+
// Collect data for trip acceleration/deceleration average:
1494+
const float min_accel = 2.5 / 3.6; // cruising range exclusion [m/s²]
1495+
float accel = StandardMetrics.ms_v_pos_acceleration->AsFloat();
1496+
if (accel > min_accel)
1497+
{
1498+
m_drive_accelcnt++;
1499+
m_drive_accelsum += accel;
1500+
}
1501+
else if (accel < -min_accel)
1502+
{
1503+
m_drive_decelcnt++;
1504+
m_drive_decelsum += accel;
1505+
}
13711506
}
13721507
else if (metric == StandardMetrics.ms_v_bat_power)
13731508
{
@@ -1628,9 +1763,11 @@ void OvmsVehicle::NotifyVehicleOn()
16281763

16291764
void OvmsVehicle::NotifyVehicleOff()
16301765
{
1631-
float min_trip_length = MyConfig.GetParamValueFloat("notify", "log.trip.minlength", 0.2);
1632-
if (StdMetrics.ms_v_pos_trip->AsFloat() >= min_trip_length)
1766+
float trip = StdMetrics.ms_v_pos_trip->AsFloat();
1767+
if (trip >= MyConfig.GetParamValueFloat("notify", "log.trip.minlength", 0.2))
16331768
NotifyTripLog();
1769+
if (trip >= MyConfig.GetParamValueFloat("notify", "report.trip.minlength", 0.2))
1770+
NotifyTripReport();
16341771
NotifiedVehicleOff();
16351772
}
16361773

@@ -1719,6 +1856,19 @@ void OvmsVehicle::NotifyTripLog()
17191856
MyNotify.NotifyString("data", "log.trip", buf.str().c_str());
17201857
}
17211858

1859+
void OvmsVehicle::NotifyTripReport()
1860+
{
1861+
// Send trip report notification
1862+
// Notification type "info", subtype "drive.trip.report"
1863+
bool send_report = MyConfig.GetParamValueBool("notify", "report.trip.enable", false);
1864+
if (send_report)
1865+
{
1866+
StringWriter buf(200);
1867+
CommandStatTrip(COMMAND_RESULT_NORMAL, &buf);
1868+
MyNotify.NotifyString("info", "drive.trip.report", buf.c_str());
1869+
}
1870+
}
1871+
17221872
OvmsVehicle::vehicle_mode_t OvmsVehicle::VehicleModeKey(const std::string code)
17231873
{
17241874
vehicle_mode_t key;

vehicle/OVMS.V3/components/vehicle/vehicle.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,16 @@ class OvmsVehicle : public InternalRamAllocated
348348
int m_last_chargetime; // duration of current/most recent charge [s]
349349
int m_last_gentime; // duration of current/most recent generator run [s]
350350

351+
float m_drive_startsoc; // SOC at drive start (vehicle.on)
352+
float m_drive_startrange; // Range estimation at drive start (vehicle.on)
353+
float m_drive_startaltitude; // Altitude at drive start (vehicle.on)
354+
uint32_t m_drive_speedcnt; // Driving speed average data
355+
double m_drive_speedsum; // Driving speed average data
356+
uint32_t m_drive_accelcnt; // Driving acceleration average data
357+
double m_drive_accelsum; // Driving acceleration average data
358+
uint32_t m_drive_decelcnt; // Driving deceleration average data
359+
double m_drive_decelsum; // Driving deceleration average data
360+
351361
protected:
352362
uint32_t m_ticker;
353363
int m_12v_ticker;
@@ -388,6 +398,7 @@ class OvmsVehicle : public InternalRamAllocated
388398
protected:
389399
virtual void NotifyGridLog();
390400
virtual void NotifyTripLog();
401+
virtual void NotifyTripReport();
391402

392403
protected:
393404
virtual int GetNotifyVehicleStateDelay(const std::string& state) { return 3; }
@@ -493,6 +504,7 @@ class OvmsVehicle : public InternalRamAllocated
493504

494505
public:
495506
virtual vehicle_command_t CommandStat(int verbosity, OvmsWriter* writer);
507+
virtual vehicle_command_t CommandStatTrip(int verbosity, OvmsWriter* writer);
496508
virtual vehicle_command_t ProcessMsgCommand(std::string &result, int command, const char* args);
497509

498510
public:
@@ -736,6 +748,7 @@ class OvmsVehicleFactory
736748
static void vehicle_charge_stop(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv);
737749
static void vehicle_charge_cooldown(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv);
738750
static void vehicle_stat(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv);
751+
static void vehicle_stat_trip(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv);
739752
static void bms_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv);
740753
static void bms_reset(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv);
741754
static void bms_alerts(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv);

vehicle/OVMS.V3/components/vehicle/vehicle_shell.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,20 @@ void OvmsVehicleFactory::vehicle_stat(int verbosity, OvmsWriter* writer, OvmsCom
417417
}
418418
}
419419

420+
void OvmsVehicleFactory::vehicle_stat_trip(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
421+
{
422+
if (MyVehicleFactory.m_currentvehicle==NULL)
423+
{
424+
writer->puts("Error: No vehicle module selected");
425+
return;
426+
}
427+
428+
if (MyVehicleFactory.m_currentvehicle->CommandStatTrip(verbosity, writer) == OvmsVehicle::NotImplemented)
429+
{
430+
writer->puts("Error: Functionality not available");
431+
}
432+
}
433+
420434
void OvmsVehicleFactory::bms_status(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv)
421435
{
422436
if (MyVehicleFactory.m_currentvehicle != NULL)

0 commit comments

Comments
 (0)