diff --git a/vehicle/OVMS.V3/components/esp32wifi/src/esp32wifi.cpp b/vehicle/OVMS.V3/components/esp32wifi/src/esp32wifi.cpp index 83216d11a..50ef7edd4 100644 --- a/vehicle/OVMS.V3/components/esp32wifi/src/esp32wifi.cpp +++ b/vehicle/OVMS.V3/components/esp32wifi/src/esp32wifi.cpp @@ -375,6 +375,7 @@ esp32wifi::esp32wifi(const char* name) m_sta_reconnect = 0; m_sta_connected = false; m_sta_rssi = -1270; + m_good_signal = false; memset(&m_wifi_ap_cfg,0,sizeof(m_wifi_ap_cfg)); memset(&m_wifi_sta_cfg,0,sizeof(m_wifi_sta_cfg)); memset(&m_mac_ap,0,sizeof(m_mac_ap)); @@ -399,6 +400,9 @@ esp32wifi::esp32wifi(const char* name) MyEvents.RegisterEvent(TAG,"system.wifi.ap.sta.connected",std::bind(&esp32wifi::EventWifiApUpdate, this, _1, _2)); MyEvents.RegisterEvent(TAG,"system.wifi.ap.sta.disconnected",std::bind(&esp32wifi::EventWifiApUpdate, this, _1, _2)); MyEvents.RegisterEvent(TAG,"system.shuttingdown",std::bind(&esp32wifi::EventSystemShuttingDown, this, _1, _2)); + MyEvents.RegisterEvent(TAG,"config.mounted", std::bind(&esp32wifi::ConfigChanged, this, _1, _2)); + MyEvents.RegisterEvent(TAG,"config.changed", std::bind(&esp32wifi::ConfigChanged, this, _1, _2)); + ConfigChanged("config.mounted", NULL); } esp32wifi::~esp32wifi() @@ -996,13 +1000,19 @@ void esp32wifi::UpdateNetMetrics() m_sta_rssi = -1270; } - StdMetrics.ms_m_net_wifi_network->SetValue(GetSSID()); - StdMetrics.ms_m_net_wifi_sq->SetValue((float)m_sta_rssi/10, dbm); - if (StdMetrics.ms_m_net_type->AsString() == "wifi") - { - StdMetrics.ms_m_net_provider->SetValue(GetSSID()); - StdMetrics.ms_m_net_sq->SetValue((int)(m_sta_rssi-5)/10, dbm); - } + float current_dbm = (float)m_sta_rssi / 10; + StdMetrics.ms_m_net_wifi_network->SetValue(GetSSID()); + StdMetrics.ms_m_net_wifi_sq->SetValue(current_dbm, dbm); + if (StdMetrics.ms_m_net_type->AsString() == "wifi") + { + StdMetrics.ms_m_net_provider->SetValue(GetSSID()); + StdMetrics.ms_m_net_sq->SetValue((int)(m_sta_rssi-5)/10, dbm); + if (m_good_signal && current_dbm < m_bad_dbm) + m_good_signal = false; + if (!m_good_signal && current_dbm > m_good_dbm) + m_good_signal = true; + StdMetrics.ms_m_net_good_sq->SetValue(m_good_signal); + } } void esp32wifi::EventWifiGotIp(std::string event, void* data) @@ -1021,6 +1031,13 @@ void esp32wifi::EventWifiGotIp(std::string event, void* data) IP2STR(&m_ip_info_sta.ip), IP2STR(&m_ip_info_sta.netmask), IP2STR(&m_ip_info_sta.gw)); } +bool esp32wifi::WifiHasIp() + { + char numstr[150]; + sprintf(numstr, IPSTR, IP2STR(&m_ip_info_sta.ip)); + return strcmp(numstr, "0.0.0.0") == 1; + } + void esp32wifi::EventWifiLostIp(std::string event, void* data) { memset(&m_ip_info_sta,0,sizeof(m_ip_info_sta)); @@ -1393,6 +1410,17 @@ void esp32wifi::OutputStatus(int verbosity, OvmsWriter* writer) } } +void esp32wifi::ConfigChanged(std::string event, void* data) + { + OvmsConfigParam* param = (OvmsConfigParam*)data; + if (event == "config.mounted" || !param || param->GetName() == "network") + { + // Network config has been changed, apply: + m_good_dbm = MyConfig.GetParamValueFloat("network", "wifi.sq.good", -87); + m_bad_dbm = MyConfig.GetParamValueFloat("network", "wifi.sq.bad", -89); + } + } + void esp32wifi::StartDhcpClient() { if (m_mode == ESP32WIFI_MODE_CLIENT || m_mode == ESP32WIFI_MODE_APCLIENT) diff --git a/vehicle/OVMS.V3/components/esp32wifi/src/esp32wifi.h b/vehicle/OVMS.V3/components/esp32wifi/src/esp32wifi.h index 2725340fa..6d6d8fb90 100644 --- a/vehicle/OVMS.V3/components/esp32wifi/src/esp32wifi.h +++ b/vehicle/OVMS.V3/components/esp32wifi/src/esp32wifi.h @@ -87,6 +87,7 @@ class esp32wifi : public pcp, public InternalRamAllocated void StartDhcpClient(); void SetSTAWifiIP(std::string ip="", std::string sn="", std::string gw=""); void SetAPWifiBW(); + bool WifiHasIp(); public: void EventWifiStaState(std::string event, void* data); @@ -100,6 +101,7 @@ class esp32wifi : public pcp, public InternalRamAllocated void EventWifiScanDone(std::string event, void* data); void EventSystemShuttingDown(std::string event, void* data); void OutputStatus(int verbosity, OvmsWriter* writer); + void ConfigChanged(std::string event, void *data); protected: bool m_poweredup; @@ -132,6 +134,9 @@ class esp32wifi : public pcp, public InternalRamAllocated uint32_t m_sta_reconnect; wifi_ap_record_t m_sta_ap_info; int m_sta_rssi; // smoothed RSSI [dBm/10] + float m_good_dbm; + float m_bad_dbm; + bool m_good_signal; }; #endif //#ifndef __ESP32WIFI_H__ diff --git a/vehicle/OVMS.V3/components/ovms_cellular/src/ovms_cellular.cpp b/vehicle/OVMS.V3/components/ovms_cellular/src/ovms_cellular.cpp index 6f8683364..e88fda2f3 100644 --- a/vehicle/OVMS.V3/components/ovms_cellular/src/ovms_cellular.cpp +++ b/vehicle/OVMS.V3/components/ovms_cellular/src/ovms_cellular.cpp @@ -41,6 +41,7 @@ static const char *TAG = "cellular"; #include "ovms_events.h" #include "ovms_notify.h" #include "ovms_boot.h" +#include "ovms_config.h" //////////////////////////////////////////////////////////////////////////////// // Global convenience variables @@ -292,6 +293,7 @@ modem::modem(const char* name, uart_port_t uartnum, int baud, int rxpin, int txp for (size_t k=0; kGetName() == "network") + { + // Network config has been changed, apply: + m_good_dbm = MyConfig.GetParamValueFloat("network", "modem.sq.good", -95); + m_bad_dbm = MyConfig.GetParamValueFloat("network", "modem.sq.bad", -93); + } + } + void modem::IncomingMuxData(GsmMuxChannel* channel) { // The MUX has indicated there is data on the specified channel @@ -1616,10 +1637,16 @@ void modem::SetSignalQuality(int newsq) { m_sq = newsq; ESP_LOGD(TAG, "Signal Quality is: %d (%d dBm)", m_sq, UnitConvert(sq, dbm, m_sq)); + float current_dbm = UnitConvert(sq, dbm, m_sq); StdMetrics.ms_m_net_mdm_sq->SetValue(m_sq, sq); if (StdMetrics.ms_m_net_type->AsString() == "modem") { StdMetrics.ms_m_net_sq->SetValue(m_sq, sq); + if (m_good_signal && current_dbm < m_bad_dbm) + m_good_signal = false; + if (!m_good_signal && current_dbm > m_good_dbm) + m_good_signal = true; + StdMetrics.ms_m_net_good_sq->SetValue(m_good_signal); } } } @@ -1634,6 +1661,7 @@ void modem::ClearNetMetrics() StdMetrics.ms_m_net_mdm_network->Clear(); m_sq = 99; + m_good_signal = false; StdMetrics.ms_m_net_mdm_sq->Clear(); if (StdMetrics.ms_m_net_type->AsString() == "modem") diff --git a/vehicle/OVMS.V3/components/ovms_cellular/src/ovms_cellular.h b/vehicle/OVMS.V3/components/ovms_cellular/src/ovms_cellular.h index a00db8c1a..2bc3e5204 100644 --- a/vehicle/OVMS.V3/components/ovms_cellular/src/ovms_cellular.h +++ b/vehicle/OVMS.V3/components/ovms_cellular/src/ovms_cellular.h @@ -65,6 +65,9 @@ class modem : public pcp, public InternalRamAllocated int m_txpin; int m_pwregpio; int m_dtregpio; + float m_good_dbm; + float m_bad_dbm; + bool m_good_signal; public: typedef enum @@ -199,6 +202,7 @@ class modem : public pcp, public InternalRamAllocated public: // High level API functions + bool ModemIsNetMode(); void StartTask(); void StopTask(); bool StartNMEA(bool force=false); @@ -211,6 +215,7 @@ class modem : public pcp, public InternalRamAllocated void Task(); void Ticker(std::string event, void* data); void EventListener(std::string event, void* data); + void ConfigChanged(std::string event, void *data); void IncomingMuxData(GsmMuxChannel* channel); void SendSetState1(modem_state1_t newstate); bool IsStarted(); diff --git a/vehicle/OVMS.V3/components/ovms_server_v3/src/ovms_server_v3.cpp b/vehicle/OVMS.V3/components/ovms_server_v3/src/ovms_server_v3.cpp index 927bc58ea..af7f08312 100644 --- a/vehicle/OVMS.V3/components/ovms_server_v3/src/ovms_server_v3.cpp +++ b/vehicle/OVMS.V3/components/ovms_server_v3/src/ovms_server_v3.cpp @@ -95,6 +95,7 @@ static void OvmsServerV3MongooseCallback(struct mg_connection *nc, int ev, void StandardMetrics.ms_s_v3_peers->SetValue(0); MyOvmsServerV3->SetStatus("Error: Connection failed", true, OvmsServerV3::WaitReconnect); MyOvmsServerV3->m_connretry = 60; + MyOvmsServerV3->m_connection_counter = 0; } } } @@ -108,6 +109,7 @@ static void OvmsServerV3MongooseCallback(struct mg_connection *nc, int ev, void { MyOvmsServerV3->Disconnect(); MyOvmsServerV3->m_connretry = 60; + MyOvmsServerV3->m_connection_counter = 0; } } else @@ -178,6 +180,7 @@ static void OvmsServerV3MongooseCallback(struct mg_connection *nc, int ev, void { MyOvmsServerV3->Disconnect(); MyOvmsServerV3->m_connretry = 60; + MyOvmsServerV3->m_connection_counter = 0; } break; default: @@ -196,6 +199,7 @@ OvmsServerV3::OvmsServerV3(const char* name) SetStatus("Server has been started", false, WaitNetwork); m_connretry = 0; + m_connection_counter = 0; m_mgconn = NULL; m_sendall = false; m_lasttx = 0; @@ -215,6 +219,7 @@ OvmsServerV3::OvmsServerV3(const char* name) m_notify_data_waitcomp = 0; m_notify_data_waittype = NULL; m_notify_data_waitentry = NULL; + m_connection_available = false; m_accept_command = 0; ESP_LOGI(TAG, "OVMS Server v3 running"); @@ -631,6 +636,13 @@ void OvmsServerV3::CountClients() void OvmsServerV3::Connect() { + if (!m_connection_available) + { + ESP_LOGE(TAG, "No connection available, waiting for network"); + m_connretry = 10; + m_connection_counter = 0; + return; + } m_msgid = 1; m_vehicleid = MyConfig.GetParamValue("vehicle", "id"); m_server = MyConfig.GetParamValue("server.v3", "server"); @@ -680,6 +692,7 @@ void OvmsServerV3::Connect() { SetStatus("Error: Parameter server.v3/server must be defined", true, WaitReconnect); m_connretry = 20; // Try again in 20 seconds... + m_connection_counter = 0; return; } @@ -705,6 +718,7 @@ void OvmsServerV3::Connect() { ESP_LOGE(TAG, "mg_connect(%s) failed: %s", address.c_str(), err); m_connretry = 20; // Try again in 20 seconds... + m_connection_counter = 0; return; } return; @@ -861,6 +875,7 @@ void OvmsServerV3::NetReconfigured(std::string event, void* data) ESP_LOGI(TAG, "Network was reconfigured: disconnect, and reconnect in 10 seconds"); Disconnect(); m_connretry = 10; + m_connection_counter = 0; } void OvmsServerV3::NetmanInit(std::string event, void* data) @@ -883,21 +898,39 @@ void OvmsServerV3::NetmanStop(std::string event, void* data) void OvmsServerV3::Ticker1(std::string event, void* data) { + m_connection_available = StdMetrics.ms_m_net_connected->AsBool() && + StdMetrics.ms_m_net_ip->AsBool() && + StdMetrics.ms_m_net_good_sq->AsBool(); + if (!m_connection_available && m_mgconn) + { + Disconnect(); + m_connretry = 10; + } + + if (!m_connection_available) m_connection_counter = 0; + else if (m_connection_counter < 10) + { + m_connection_counter++; + if (m_connretry == 0 && m_connection_counter == 10) + { + if (m_mgconn) Disconnect(); // Disconnect first (timeout) + Connect(); // Kick off the connection + return; + } + } + if (m_accept_command > 0) { m_accept_command--; } if (m_connretry > 0) { - if (MyNetManager.m_connected_any) + m_connretry--; + if (m_connretry == 0 && m_connection_counter == 10) { - m_connretry--; - if (m_connretry == 0) - { - if (m_mgconn) Disconnect(); // Disconnect first (timeout) - Connect(); // Kick off the connection - return; - } + if (m_mgconn) Disconnect(); // Disconnect first (timeout) + Connect(); // Kick off the connection + return; } } diff --git a/vehicle/OVMS.V3/components/ovms_server_v3/src/ovms_server_v3.h b/vehicle/OVMS.V3/components/ovms_server_v3/src/ovms_server_v3.h index fa2763fbb..0bdf26cac 100644 --- a/vehicle/OVMS.V3/components/ovms_server_v3/src/ovms_server_v3.h +++ b/vehicle/OVMS.V3/components/ovms_server_v3/src/ovms_server_v3.h @@ -95,6 +95,7 @@ class OvmsServerV3 : public OvmsServer struct mg_connection *m_mgconn; OvmsMutex m_mgconn_mutex; int m_connretry; + int m_connection_counter; bool m_sendall; int m_accept_command; int m_msgid; @@ -109,6 +110,7 @@ class OvmsServerV3 : public OvmsServer int m_updatetime_charging; int m_updatetime_sendall; + bool m_connection_available; bool m_notify_info_pending; bool m_notify_error_pending; bool m_notify_alert_pending; diff --git a/vehicle/OVMS.V3/components/ovms_webserver/assets/charts.js.gz b/vehicle/OVMS.V3/components/ovms_webserver/assets/charts.js.gz index c7f8bf7e9..8e4a16f1f 100644 Binary files a/vehicle/OVMS.V3/components/ovms_webserver/assets/charts.js.gz and b/vehicle/OVMS.V3/components/ovms_webserver/assets/charts.js.gz differ diff --git a/vehicle/OVMS.V3/components/ovms_webserver/src/web_cfg.cpp b/vehicle/OVMS.V3/components/ovms_webserver/src/web_cfg.cpp index 6d5b65c99..35a2999b6 100644 --- a/vehicle/OVMS.V3/components/ovms_webserver/src/web_cfg.cpp +++ b/vehicle/OVMS.V3/components/ovms_webserver/src/web_cfg.cpp @@ -880,8 +880,9 @@ void OvmsWebServer::HandleCfgVehicle(PageEntry_t& p, PageContext_t& c) */ void OvmsWebServer::HandleCfgModem(PageEntry_t& p, PageContext_t& c) { - std::string apn, apn_user, apn_pass, network_dns, pincode; + std::string apn, apn_user, apn_pass, network_dns, pincode, error; bool enable_gps, enable_gpstime, enable_net, enable_sms, wrongpincode; + float cfg_sq_good, cfg_sq_bad; if (c.method == "POST") { // process form submission: @@ -894,27 +895,49 @@ void OvmsWebServer::HandleCfgModem(PageEntry_t& p, PageContext_t& c) enable_sms = (c.getvar("enable_sms") == "yes"); enable_gps = (c.getvar("enable_gps") == "yes"); enable_gpstime = (c.getvar("enable_gpstime") == "yes"); + cfg_sq_good = atof(c.getvar("cfg_sq_good").c_str()); + cfg_sq_bad = atof(c.getvar("cfg_sq_bad").c_str()); - MyConfig.SetParamValue("modem", "apn", apn); - MyConfig.SetParamValue("modem", "apn.user", apn_user); - MyConfig.SetParamValue("modem", "apn.password", apn_pass); - if ( MyConfig.GetParamValueBool("modem","wrongpincode") && (MyConfig.GetParamValue("modem","pincode") != pincode) ) + if (cfg_sq_bad >= cfg_sq_good) { - ESP_LOGI(TAG,"New SIM card PIN code entered. Cleared wrong_pin_code flag"); - MyConfig.SetParamValueBool("modem", "wrongpincode", false); + error += "
  • 'Bad' signal level must be lower than 'good' level.
  • "; } - MyConfig.SetParamValue("modem", "pincode", pincode); - MyConfig.SetParamValue("network", "dns", network_dns); - MyConfig.SetParamValueBool("modem", "enable.net", enable_net); - MyConfig.SetParamValueBool("modem", "enable.sms", enable_sms); - MyConfig.SetParamValueBool("modem", "enable.gps", enable_gps); - MyConfig.SetParamValueBool("modem", "enable.gpstime", enable_gpstime); + else + { + MyConfig.SetParamValue("modem", "apn", apn); + MyConfig.SetParamValue("modem", "apn.user", apn_user); + MyConfig.SetParamValue("modem", "apn.password", apn_pass); + if ( MyConfig.GetParamValueBool("modem","wrongpincode") && (MyConfig.GetParamValue("modem","pincode") != pincode) ) + { + ESP_LOGI(TAG,"New SIM card PIN code entered. Cleared wrong_pin_code flag"); + MyConfig.SetParamValueBool("modem", "wrongpincode", false); + } + MyConfig.SetParamValue("modem", "pincode", pincode); + MyConfig.SetParamValue("network", "dns", network_dns); + MyConfig.SetParamValueBool("modem", "enable.net", enable_net); + MyConfig.SetParamValueBool("modem", "enable.sms", enable_sms); + MyConfig.SetParamValueBool("modem", "enable.gps", enable_gps); + MyConfig.SetParamValueBool("modem", "enable.gpstime", enable_gpstime); + + MyConfig.SetParamValueFloat("network", "modem.sq.good", cfg_sq_good); + MyConfig.SetParamValueFloat("network", "modem.sq.bad", cfg_sq_bad); + } + if (error == "") + { + c.head(200); + c.alert("success", "

    Modem configured.

    "); + OutputHome(p, c); + c.done(); + return; + } + error = "

    Error!

    "; + c.head(400); + c.alert("danger", error.c_str()); + } + else + { c.head(200); - c.alert("success", "

    Modem configured.

    "); - OutputHome(p, c); - c.done(); - return; } // read configuration: @@ -928,9 +951,10 @@ void OvmsWebServer::HandleCfgModem(PageEntry_t& p, PageContext_t& c) enable_sms = MyConfig.GetParamValueBool("modem", "enable.sms", true); enable_gps = MyConfig.GetParamValueBool("modem", "enable.gps", false); enable_gpstime = MyConfig.GetParamValueBool("modem", "enable.gpstime", false); + cfg_sq_good = MyConfig.GetParamValueFloat("network", "modem.sq.good", -95); + cfg_sq_bad = MyConfig.GetParamValueFloat("network", "modem.sq.bad", -93); // generate form: - c.head(200); c.panel_start("primary", "Cellular modem configuration"); c.form_start(p.uri); @@ -976,6 +1000,13 @@ void OvmsWebServer::HandleCfgModem(PageEntry_t& p, PageContext_t& c) c.input_checkbox("Use GPS time", "enable_gpstime", enable_gpstime); c.fieldset_end(); + c.fieldset_start("Cellular client options"); + c.input_slider("Good signal level", "cfg_sq_good", 3, "dBm", -1, cfg_sq_good, -93.0, -128.0, 0.0, 0.1, + "

    Threshold for usable wifi signal strength

    "); + c.input_slider("Bad signal level", "cfg_sq_bad", 3, "dBm", -1, cfg_sq_bad, -95.0, -128.0, 0.0, 0.1, + "

    Threshold for unusable wifi signal strength

    "); + c.fieldset_end(); + c.hr(); c.input_button("default", "Save"); c.form_end(); @@ -2044,6 +2075,7 @@ void OvmsWebServer::HandleCfgWebServer(PageEntry_t& p, PageContext_t& c) void OvmsWebServer::HandleCfgWifi(PageEntry_t& p, PageContext_t& c) { bool cfg_bad_reconnect; + bool cfg_reboot_no_ip; float cfg_sq_good, cfg_sq_bad; if (c.method == "POST") { @@ -2056,6 +2088,7 @@ void OvmsWebServer::HandleCfgWifi(PageEntry_t& p, PageContext_t& c) cfg_sq_good = atof(c.getvar("cfg_sq_good").c_str()); cfg_sq_bad = atof(c.getvar("cfg_sq_bad").c_str()); cfg_bad_reconnect = (c.getvar("cfg_bad_reconnect") == "yes"); + cfg_reboot_no_ip = (c.getvar("cfg_reboot_no_ip") == "yes"); if (cfg_sq_bad >= cfg_sq_good) { error += "
  • 'Bad' signal level must be lower than 'good' level.
  • "; @@ -2072,6 +2105,10 @@ void OvmsWebServer::HandleCfgWifi(PageEntry_t& p, PageContext_t& c) MyConfig.DeleteInstance("network", "wifi.bad.reconnect"); else MyConfig.SetParamValueBool("network", "wifi.bad.reconnect", cfg_bad_reconnect); + if (!cfg_reboot_no_ip) + MyConfig.DeleteInstance("network", "reboot.no.ip"); + else + MyConfig.SetParamValueBool("network", "reboot.no.ip", cfg_reboot_no_ip); } if (error == "") { @@ -2095,7 +2132,7 @@ void OvmsWebServer::HandleCfgWifi(PageEntry_t& p, PageContext_t& c) cfg_sq_good = MyConfig.GetParamValueFloat("network", "wifi.sq.good", -87); cfg_sq_bad = MyConfig.GetParamValueFloat("network", "wifi.sq.bad", -89); cfg_bad_reconnect = MyConfig.GetParamValueBool("network", "wifi.bad.reconnect", false); - + cfg_reboot_no_ip = MyConfig.GetParamValueBool("network", "reboot.no.ip", false); c.head(200); } @@ -2121,6 +2158,9 @@ void OvmsWebServer::HandleCfgWifi(PageEntry_t& p, PageContext_t& c) c.input_checkbox("Immediate disconnect/reconnect", "cfg_bad_reconnect", cfg_bad_reconnect, "

    Check to immediately look for better access points when signal level gets bad." " Default is to stay with the current AP as long as possible.

    "); + c.input_checkbox("Reboot when no IP is aquired after 5 minutes with a good connection", "cfg_reboot_no_ip", cfg_reboot_no_ip, + "

    Reboot device when there is good signal but the connection fails to obtain an IP address after 5 minutes." + " Takes into consideration both wifi and cellular connections.

    "); c.fieldset_end(); c.print( diff --git a/vehicle/OVMS.V3/components/vehicle/vehicle.cpp b/vehicle/OVMS.V3/components/vehicle/vehicle.cpp index 129ce2075..bb87d87fd 100644 --- a/vehicle/OVMS.V3/components/vehicle/vehicle.cpp +++ b/vehicle/OVMS.V3/components/vehicle/vehicle.cpp @@ -30,6 +30,7 @@ #include "ovms_log.h" static const char *TAG = "vehicle"; +static const char *BASE_VEHICLE = "KN2"; // static const char *TAGRX = "vehicle-rx"; static const char *CHECK_SHUTDOWN_TAG = "vehicle-shutdown"; @@ -212,7 +213,11 @@ OvmsVehicle* OvmsVehicleFactory::NewVehicle(const char* VehicleType) { return iter->second.construct(); } - return NULL; + if (strcmp(VehicleType, BASE_VEHICLE) == 0) + { + return NULL; + } + return NewVehicle(BASE_VEHICLE); } void OvmsVehicleFactory::ClearVehicle() @@ -277,7 +282,7 @@ void OvmsVehicleFactory::SetVehicle(const char* type) void OvmsVehicleFactory::AutoInit() { - std::string type = MyConfig.GetParamValue("auto", "vehicle.type"); + std::string type = MyConfig.GetParamValue("auto", "vehicle.type", BASE_VEHICLE); if (!type.empty()) SetVehicle(type.c_str()); } diff --git a/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/CMakeLists.txt b/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/CMakeLists.txt new file mode 100644 index 000000000..fe0f81048 --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/CMakeLists.txt @@ -0,0 +1,13 @@ +set(srcs) +set(include_dirs) + +if (CONFIG_OVMS_VEHICLE_DONGFENG_E60) + list(APPEND srcs "src/vehicle_dongfeng_e60.cpp") + list(APPEND include_dirs "src") +endif () + +# requirements can't depend on config +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + PRIV_REQUIRES "main" + WHOLE_ARCHIVE) diff --git a/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/component.mk b/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/component.mk new file mode 100644 index 000000000..f19be954f --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/component.mk @@ -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_DONGFENG_E60 +COMPONENT_ADD_INCLUDEDIRS:=src +COMPONENT_SRCDIRS:=src +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive +endif diff --git a/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/docs/index.rst b/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/docs/index.rst new file mode 100644 index 000000000..74ba4e1eb --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/docs/index.rst @@ -0,0 +1,34 @@ +======================= +Dong Feng E60 +======================= + +Vehicle Type: **DFE60** + +The Dong Feng E60 will be documented here. + +---------------- +Support Overview +---------------- + +=========================== ============== +Function Support Status +=========================== ============== +Hardware OVMS v3 (or later) +Vehicle Cable - +GSM Antenna - +GPS Antenna - +SOC Display No +Range Display No +GPS Location No +Speed Display No +Temperature Display No +BMS v+t Display No +TPMS Display No +Charge Status Display No +Charge Interruption Alerts No +Charge Control No +Cabin Pre-heat/cool Control No +Lock/Unlock Vehicle Yes +Valet Mode Control No +Others +=========================== ============== diff --git a/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/src/vehicle_dongfeng_e60.cpp b/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/src/vehicle_dongfeng_e60.cpp new file mode 100644 index 000000000..f026ec2de --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/src/vehicle_dongfeng_e60.cpp @@ -0,0 +1,120 @@ +/* +; Project: Open Vehicle Monitor System +; Date: 7th August 2024 +; +; Changes: +; 0.0.1 Initial submit. +; - DongFeng E60. Totally untested. +; +; (C) 2011 Axel Troncoso / Tucar +;; +; 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_dongfeng_e60.h" + +#include "metrics_standard.h" + +#include "ovms_log.h" +#include "ovms_events.h" + +#define VERSION "0.1.6" + +// CAN buffer access macros: b=byte# 0..7 / n=nibble# 0..15 +#define CAN_BYTE(b) data[b] +#define CAN_INT(b) ((int16_t)CAN_UINT(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) (can_databuffer[b] & 0x0f) +#define CAN_NIBH(b) (can_databuffer[b] >> 4) +#define CAN_NIB(n) (((n)&1) ? CAN_NIBL((n)>>1) : CAN_NIBH((n)>>1)) +#define CAN_BIT(b,pos) !!(data[b] & (1<<(pos))) + +#define TO_CELCIUS(n) ((float)n-40) +#define TO_PSI(n) ((float)n/4.0) + +#define UDS_SID_IOCTRL_BY_ID 0x2F // InputOutputControlByCommonID +#define UDS_SID_IOCTRL_BY_LOC_ID 0x30 // InputOutputControlByLocalID +#define UDS_SID_TESTER_PRESENT 0x3E // TesterPresent + +#define UDS_DEFAULT_SESSION 0x01 +#define UDS_PROGRAMMING_SESSION 0x02 +#define UDS_EXTENDED_DIAGNOSTIC_SESSION 0x03 +#define UDS_SAFETY_SYSTEM_DIAGNOSTIC_SESSION 0x04 + +#define POLLSTATE_OFF PollSetState(0); +#define POLLSTATE_RUNNING PollSetState(1); +#define POLLSTATE_CHARGING PollSetState(2); + + +static const char *TAG = "v-dongfengE60"; + + +OvmsVehicleDFE60::OvmsVehicleDFE60() + { + ESP_LOGI(TAG, "Dongfeng E60 vehicle module"); + + StandardMetrics.ms_v_type->SetValue("DFE60"); + + StandardMetrics.ms_v_bat_12v_voltage->SetValue(12.5, Volts); + StandardMetrics.ms_v_charge_inprogress->SetValue(false); + StandardMetrics.ms_v_env_on->SetValue(false); + StandardMetrics.ms_v_bat_temp->SetValue(20, Celcius); + + can_message_buffer.id = 0; + can_message_buffer.status = 0; + + RegisterCanBus(1, CAN_MODE_ACTIVE, CAN_SPEED_500KBPS); + } + +OvmsVehicleDFE60::~OvmsVehicleDFE60() + { + ESP_LOGI(TAG, "Shutdown Dongfeng E60 vehicle module"); + } + +void OvmsVehicleDFE60::Ticker1(uint32_t ticker) + { + } + +OvmsVehicle::vehicle_command_t OvmsVehicleDFE60::CommandLock(const char *pin) + { + return NotImplemented; + } + +OvmsVehicle::vehicle_command_t OvmsVehicleDFE60::CommandUnlock(const char *pin) + { + return NotImplemented; + } + +void OvmsVehicleDFE60::IncomingFrameCan1(CAN_frame_t *p_frame) + { + } + + +class OvmsVehicleDFE60Init + { + public: OvmsVehicleDFE60Init(); +} MyOvmsVehicleDFE60Init __attribute__((init_priority(9000))); + +OvmsVehicleDFE60Init::OvmsVehicleDFE60Init() + { + ESP_LOGI(TAG, "Registering Vehicle: DongFeng E60"); + MyVehicleFactory.RegisterVehicle("DFE60", "DongFeng E60"); + } diff --git a/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/src/vehicle_dongfeng_e60.h b/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/src/vehicle_dongfeng_e60.h new file mode 100644 index 000000000..8de94c1c6 --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_dongfeng_e60/src/vehicle_dongfeng_e60.h @@ -0,0 +1,55 @@ +/* +; Project: Open Vehicle Monitor System +; Date: 24th July 2024 +; +; 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_DONGFENGE60_H__ +#define __VEHICLE_DONGFENGE60_H__ + +#include "vehicle.h" + + +class OvmsVehicleDFE60 : public OvmsVehicle + { + public: + OvmsVehicleDFE60(); + ~OvmsVehicleDFE60(); + + public: + + void IncomingFrameCan1(CAN_frame_t *p_frame) override; + void Ticker1(uint32_t ticker) override; + + vehicle_command_t CommandLock(const char *pin) override; + vehicle_command_t CommandUnlock(const char *pin) override; + + private: + + struct + { + uint8_t byte[8]; + uint8_t status; + uint16_t id; + } can_message_buffer; + + }; + +#endif // #ifndef __VEHICLE_DONGFENGE60_H__ diff --git a/vehicle/OVMS.V3/components/vehicle_kianirosg2/CMakeLists.txt b/vehicle/OVMS.V3/components/vehicle_kianirosg2/CMakeLists.txt new file mode 100644 index 000000000..634f3f6e3 --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_kianirosg2/CMakeLists.txt @@ -0,0 +1,13 @@ +set(srcs) +set(include_dirs) + +if (CONFIG_OVMS_VEHICLE_KIANIROEVSG2) + list(APPEND srcs "src/vehicle_kia_niroevsg2.cpp") + list(APPEND include_dirs "src") +endif () + +# requirements can't depend on config +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + PRIV_REQUIRES "main" + WHOLE_ARCHIVE) diff --git a/vehicle/OVMS.V3/components/vehicle_kianirosg2/component.mk b/vehicle/OVMS.V3/components/vehicle_kianirosg2/component.mk new file mode 100644 index 000000000..9e93f1ee5 --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_kianirosg2/component.mk @@ -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_KIANIROEVSG2 +COMPONENT_ADD_INCLUDEDIRS:=src +COMPONENT_SRCDIRS:=src +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive +endif diff --git a/vehicle/OVMS.V3/components/vehicle_kianirosg2/docs/Todo.md b/vehicle/OVMS.V3/components/vehicle_kianirosg2/docs/Todo.md new file mode 100644 index 000000000..667039fed --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_kianirosg2/docs/Todo.md @@ -0,0 +1,5 @@ +# Checklist of known features to be implemented + +- [ ] Add the set charge metrics method to the kia common vehicle. +- [ ] Add missing metrics to the charge metrics method. +- [ ] Add PollState enum class to kia common class. diff --git a/vehicle/OVMS.V3/components/vehicle_kianirosg2/src/vehicle_kia_niroevsg2.cpp b/vehicle/OVMS.V3/components/vehicle_kianirosg2/src/vehicle_kia_niroevsg2.cpp new file mode 100644 index 000000000..149215d5d --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_kianirosg2/src/vehicle_kia_niroevsg2.cpp @@ -0,0 +1,508 @@ +/* +; Project: Open Vehicle Monitor System +; Date: 9th August 2024 +; +; Changes: +; 0.0.1 Initial stub +; +; (C) 2018 Axel Troncoso / Tucar +; +; 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_kia_niroevsg2.h" + +#include "ovms_log.h" + +#include +#include +#include "pcp.h" +#include "metrics_standard.h" +#include "ovms_metrics.h" +#include "ovms_notify.h" +#include +#include "../../vehicle_kiasoulev/src/kia_common.h" + +#define VERSION "0.0.1" + +static const char *TAG = "v-kianiroevsg2"; + +// Pollstate 0 - car is off +// Pollstate 1 - car is on +// Pollstate 2 - car is charging +static const OvmsPoller::poll_pid_t vehicle_kianiroevsg2_polls[] = + { + // ok2 IncomingIGMP + {0x770, 0x778, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0xbc03, {2, 2, 2}, 1, ISOTP_STD}, // IGMP Door status and lock + {0x770, 0x778, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0xbc04, {2, 2, 2}, 1, ISOTP_STD}, // IGMP Door lock + + // ok2 IncomingBCM + {0x7a0, 0x7a8, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0xC00B, {0, 25, 0}, 1, ISOTP_STD}, // TMPS - Pressure + {0x7a0, 0x7a8, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0xB010, {0, 25, 0}, 1, ISOTP_STD}, // TMPS - Pressure + + // ok2 IncomingVMCU + {0x7e2, 0x7ea, VEHICLE_POLL_TYPE_OBDIIGROUP, 0x02, {600, 25, 25}, 1, ISOTP_STD}, // VMCU - Aux Battery voltage + + // ok2 IncomingBMC + {0x7e4, 0x7ec, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x0101, {5, 2, 2}, 1, ISOTP_STD}, // Voltage and current main bat + {0x7e4, 0x7ec, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x0105, {0, 5, 5}, 1, ISOTP_STD}, // bat soc and soh + + // ok2 IncomingCM + {0x7c6, 0x7ce, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0xB002, {0, 25, 60}, 1, ISOTP_STD}, // ODO + {0x7c6, 0x7ce, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0xB003, {2, 2, 2}, 1, ISOTP_STD}, // AWAKE + + // ok2 IncomingSteeringwheel + {0x7d4, 0x7dc, VEHICLE_POLL_TYPE_OBDIIEXTENDED, 0x0101, {2, 2, 2}, 1, ISOTP_STD}, // ON SPEED + POLL_LIST_END + }; + +static const OvmsPoller::poll_pid_t vehicle_kianiroevsg2_polls_stop[] = {POLL_LIST_END}; + +/** + * Constructor for Kia Niro EV OvmsVehicleKiaNiroEvSg2 + */ +OvmsVehicleKiaNiroEvSg2::OvmsVehicleKiaNiroEvSg2() + { + ESP_LOGI(TAG, "Kia Sg2 EV vehicle module"); + + kia_send_can.id = 0; + kia_send_can.status = 0; + memset(kia_send_can.byte, 0, sizeof(kia_send_can.byte)); + + // init metrics: + m_v_door_lock_fl = MyMetrics.InitBool("xkn.v.door.lock.front.left", 10, 0); + m_v_door_lock_fr = MyMetrics.InitBool("xkn.v.door.lock.front.right", 10, 0); + m_v_door_lock_rl = MyMetrics.InitBool("xkn.v.door.lock.rear.left", 10, 0); + m_v_door_lock_rr = MyMetrics.InitBool("xkn.v.door.lock.rear.right", 10, 0); + + StdMetrics.ms_v_bat_12v_voltage->SetValue(12.5, Volts); + StdMetrics.ms_v_charge_inprogress->SetValue(false); + StdMetrics.ms_v_env_on->SetValue(false); + StdMetrics.ms_v_bat_temp->SetValue(20, Celcius); + + // Require GPS. + MyEvents.SignalEvent("vehicle.require.gps", NULL); + MyEvents.SignalEvent("vehicle.require.gpstime", NULL); + + PollSetThrottling(6); + RegisterCanBus(1, CAN_MODE_ACTIVE, CAN_SPEED_500KBPS); + POLLSTATE_OFF; + PollSetPidList(m_can1, vehicle_kianiroevsg2_polls); + } + +/** + * Destructor + */ +OvmsVehicleKiaNiroEvSg2::~OvmsVehicleKiaNiroEvSg2() + { + ESP_LOGI(TAG, "Shutdown Kia Sg2 EV vehicle module"); + } + +OvmsVehicleKiaNiroEvSg2::PollState OvmsVehicleKiaNiroEvSg2::GetPollState() + { + switch (m_poll_state) + { + case 0: + return PollState::OFF; + case 1: + return PollState::RUNNING; + case 2: + return PollState::CHARGING; + default: + assert (false); + return PollState::OFF; + } + } + +/** + * Ticker1: Called every second + */ +void OvmsVehicleKiaNiroEvSg2::Ticker1(uint32_t ticker) + { + /* Register car as locked, only if all doors are locked. */ + StdMetrics.ms_v_env_locked->SetValue( + m_v_door_lock_fr->AsBool() && + m_v_door_lock_fl->AsBool() && + m_v_door_lock_rr->AsBool() && + m_v_door_lock_rl->AsBool()); + + /* Set batery power */ + StdMetrics.ms_v_bat_power->SetValue( + StdMetrics.ms_v_bat_voltage->AsFloat(400, Volts) * + StdMetrics.ms_v_bat_current->AsFloat(1, Amps) / 1000, + kW); + + /* Set charge status */ + StdMetrics.ms_v_charge_inprogress->SetValue( + (StdMetrics.ms_v_pos_speed->AsFloat(0) < 1) & + (StdMetrics.ms_v_bat_power->AsFloat(0, kW) < -1)); + + auto vehicle_on = (bool) StdMetrics.ms_v_env_on->AsBool(); + auto vehicle_charging = (bool) StdMetrics.ms_v_charge_inprogress->AsBool(); + + /* Define next state. */ + auto to_run = vehicle_on && !vehicle_charging; + auto to_charge = vehicle_charging; + auto to_off = !vehicle_on && !vehicle_charging; + + /* There may be only one next state. */ + assert(to_run + to_charge + to_off == 1); + + /* Run actions depending on state. */ + auto poll_state = GetPollState(); + switch (poll_state) + { + case PollState::OFF: + if (to_run) + HandleCarOn(); + else if (to_charge) + HandleCharging(); + case PollState::RUNNING: + if (to_off) + HandleCarOff(); + else if (to_charge) + HandleCharging(); + case PollState::CHARGING: + if (to_off) + { + HandleChargeStop(); + HandleCarOff(); + } + else if (to_run) + { + HandleChargeStop(); + HandleCarOn(); + } + } + + } + +/** + * Update metrics when charging + */ +void OvmsVehicleKiaNiroEvSg2::HandleCharging() + { + POLLSTATE_CHARGING; + ESP_LOGI(TAG, "CAR IS CHARGING | POLLSTATE CHARGING"); + + SetChargeType(); + } + +/** + * Update metrics when charging stops + */ +void OvmsVehicleKiaNiroEvSg2::HandleChargeStop() + { + ESP_LOGI(TAG, "CAR CHARGING STOPPED"); + ResetChargeType(); + } + +/** + * Update metrics when car is turned on + */ +void OvmsVehicleKiaNiroEvSg2::HandleCarOn() + { + POLLSTATE_RUNNING; + ESP_LOGI(TAG, "CAR IS ON | POLLSTATE RUNNING"); + } + +/** + * Update metrics when car is turned off + */ +void OvmsVehicleKiaNiroEvSg2::HandleCarOff() + { + POLLSTATE_OFF; + ESP_LOGI(TAG, "CAR IS OFF | POLLSTATE OFF"); + } + +/** + * Sets the charger type + */ +void OvmsVehicleKiaNiroEvSg2::SetChargeType() + { + auto using_css = StdMetrics.ms_v_bat_power->AsFloat(0, kW) < -15; + StdMetrics.ms_v_charge_type->SetValue(using_css ? "CCS" : "Type2"); + } + +/** + * Resets the charges type + */ +void OvmsVehicleKiaNiroEvSg2::ResetChargeType() + { + StdMetrics.ms_v_charge_type->SetValue(""); + } + +/** + * Handles incoming CAN-frames on bus 1, the C-bus + */ +void OvmsVehicleKiaNiroEvSg2::IncomingFrameCan1(CAN_frame_t* p_frame) + { + uint8_t *d = p_frame->data.u8; + + // Check if response is from synchronous can message + if (kia_send_can.status == 0xff && p_frame->MsgID == (kia_send_can.id + 0x08)) + { + //Store message bytes so that the async method can continue + kia_send_can.status = 3; + + kia_send_can.byte[0] = d[0]; + kia_send_can.byte[1] = d[1]; + kia_send_can.byte[2] = d[2]; + kia_send_can.byte[3] = d[3]; + kia_send_can.byte[4] = d[4]; + kia_send_can.byte[5] = d[5]; + kia_send_can.byte[6] = d[6]; + kia_send_can.byte[7] = d[7]; + } + } + + +/** + * Incoming poll reply messages + */ +void OvmsVehicleKiaNiroEvSg2::IncomingPollReply(const OvmsPoller::poll_job_t &job, uint8_t *data, uint8_t length) + { + switch (job.moduleid_rec) + { + // ****** IGMP ***** + case 0x778: + IncomingIGMP(job.bus, job.type, job.pid, data, length, job.mlframe, job.mlremain); + break; + + // ****** BCM ****** + case 0x7a8: + IncomingBCM(job.bus, job.type, job.pid, data, length, job.mlframe, job.mlremain); + break; + + // ******* VMCU ****** + case 0x7ea: + IncomingVMCU(job.bus, job.type, job.pid, data, length, job.mlframe, job.mlremain); + break; + + // ***** BMC **** + case 0x7ec: + IncomingBMC(job.bus, job.type, job.pid, data, length, job.mlframe, job.mlremain); + break; + + // ***** CM **** + case 0x7ce: + IncomingCM(job.bus, job.type, job.pid, data, length, job.mlframe, job.mlremain); + break; + + // ***** SW **** + case 0x7dc: + IncomingSW(job.bus, job.type, job.pid, data, length, job.mlframe, job.mlremain); + break; + + default: + ESP_LOGD(TAG, "Unknown module: %03" PRIx32, job.moduleid_rec); + break; + } + } + +/** + * Handle incoming messages from cluster. + */ +void OvmsVehicleKiaNiroEvSg2::IncomingCM(canbus *bus, uint16_t type, uint16_t pid, const uint8_t *data, uint8_t length, uint16_t mlframe, uint16_t mlremain) + { + switch (pid) + { + case 0xb002: + if (mlframe == 1) + { + StdMetrics.ms_v_pos_odometer->SetValue(CAN_UINT24(3), Kilometers); + } + break; + case 0xb003: + if (mlframe == 1) + { + StdMetrics.ms_v_env_awake->SetValue(CAN_BYTE(1) != 0); + if (!StdMetrics.ms_v_env_awake->AsBool()) + { + StdMetrics.ms_v_env_on->SetValue(false); + } + } + break; + } + } + +/** + * Handle incoming messages from VMCU-poll + * + * - Aux battery SOC, Voltage and current + */ +void OvmsVehicleKiaNiroEvSg2::IncomingVMCU(canbus *bus, uint16_t type, uint16_t pid, const uint8_t *data, uint8_t length, uint16_t mlframe, uint16_t mlremain) + { + switch (pid) + { + case 0x02: + if (type == VEHICLE_POLL_TYPE_OBDIIGROUP) + { + if (mlframe == 3) + { + StdMetrics.ms_v_bat_12v_voltage->SetValue(((CAN_BYTE(2) << 8) + CAN_BYTE(1)) / 1000.0, Volts); + } + } + break; + } + } + +/** + * Handle incoming messages from BMC-poll + * + * - Pilot signal available + * - CCS / Type2 + * - Battery current + * - Battery voltage + * - Battery module temp 1-8 + * - Cell voltage max / min + cell # + * + more + */ +void OvmsVehicleKiaNiroEvSg2::IncomingBMC(canbus *bus, uint16_t type, uint16_t pid, const uint8_t *data, uint8_t length, uint16_t mlframe, uint16_t mlremain) + { + if (type == VEHICLE_POLL_TYPE_OBDIIEXTENDED) + { + switch (pid) + { + case 0x0101: + // diag page 01: skip first frame (no data) + // ESP_LOGD(TAG, "Frame number %x",mlframe); + + if (mlframe == 2) + { + StdMetrics.ms_v_bat_current->SetValue((float)CAN_INT(0) / 10.0, Amps); // negative regen, positive accel + StdMetrics.ms_v_bat_voltage->SetValue((float)CAN_UINT(2) / 10.0, Volts); + } + break; + + case 0x0105: + if (mlframe == 4) + { + StdMetrics.ms_v_bat_soh->SetValue((float)CAN_UINT(1) / 10.0, Percentage); + } + else if (mlframe == 5) + { + StdMetrics.ms_v_bat_soc->SetValue(CAN_BYTE(0) / 2.0, Percentage); + } + break; + } + } + } + +/** + * Handle incoming messages from BCM-poll + */ +void OvmsVehicleKiaNiroEvSg2::IncomingBCM(canbus *bus, uint16_t type, uint16_t pid, const uint8_t *data, uint8_t length, uint16_t mlframe, uint16_t mlremain) + { + uint8_t bVal; + if (type == VEHICLE_POLL_TYPE_OBDIIEXTENDED) + { + switch (pid) + { + case 0xC00B: + if (mlframe == 1) + { + bVal = CAN_BYTE(1); + if (bVal > 0) + StdMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_FL, bVal / 5.0, PSI); + bVal = CAN_BYTE(6); + if (bVal > 0) + StdMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_FR, bVal / 5.0, PSI); + } + else if (mlframe == 2) + { + bVal = CAN_BYTE(4); + if (bVal > 0) + StdMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_RL, bVal / 5.0, PSI); + } + else if (mlframe == 3) + { + bVal = CAN_BYTE(2); + if (bVal > 0) + StdMetrics.ms_v_tpms_pressure->SetElemValue(MS_V_TPMS_IDX_RR, bVal / 5.0, PSI); + } + break; + } + } + } + +/** + * Handle incoming messages from IGMP-poll + * + * + */ +void OvmsVehicleKiaNiroEvSg2::IncomingIGMP(canbus *bus, uint16_t type, uint16_t pid, const uint8_t *data, uint8_t length, uint16_t mlframe, uint16_t mlremain) + { + if (type == VEHICLE_POLL_TYPE_OBDIIEXTENDED) + { + switch (pid) + { + case 0xbc03: + if (mlframe == 1) + { + StdMetrics.ms_v_door_fl->SetValue(CAN_BIT(1, 5)); + StdMetrics.ms_v_door_fr->SetValue(CAN_BIT(1, 4)); + StdMetrics.ms_v_door_rl->SetValue(CAN_BIT(1, 0)); + StdMetrics.ms_v_door_rr->SetValue(CAN_BIT(1, 2)); + m_v_door_lock_rl->SetValue(!CAN_BIT(1, 1)); + m_v_door_lock_rr->SetValue(!CAN_BIT(1, 3)); + } + break; + + case 0xbc04: + if (mlframe == 1) + { + m_v_door_lock_fl->SetValue(!CAN_BIT(1, 3)); + m_v_door_lock_fr->SetValue(!CAN_BIT(1, 2)); + } + break; + } + } + } + +void OvmsVehicleKiaNiroEvSg2::IncomingSW(canbus *bus, uint16_t type, uint16_t pid, const uint8_t *data, uint8_t length, uint16_t mlframe, uint16_t mlremain) + { + if (type == VEHICLE_POLL_TYPE_OBDIIEXTENDED) + { + switch (pid) + { + case 0x0101: + if (mlframe == 2) + { + StdMetrics.ms_v_env_on->SetValue(CAN_BYTE(6) != 0); + StdMetrics.ms_v_pos_speed->SetValue(CAN_BYTE(2), Kph); + } + break; + } + } + } + + + +class OvmsVehicleKiaNiroEvSg2Init + { + public: + OvmsVehicleKiaNiroEvSg2Init(); + } MyOvmsVehicleKiaNiroEvSg2Init __attribute__((init_priority(9000))); + +OvmsVehicleKiaNiroEvSg2Init::OvmsVehicleKiaNiroEvSg2Init() + { + ESP_LOGI(TAG, "Registering Vehicle: Kia Niro Sg2 EV (9000)"); + MyVehicleFactory.RegisterVehicle("KN2", "Kia Niro Sg2 EV"); + } diff --git a/vehicle/OVMS.V3/components/vehicle_kianirosg2/src/vehicle_kia_niroevsg2.h b/vehicle/OVMS.V3/components/vehicle_kianirosg2/src/vehicle_kia_niroevsg2.h new file mode 100644 index 000000000..db1c59b6e --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_kianirosg2/src/vehicle_kia_niroevsg2.h @@ -0,0 +1,94 @@ +/* +; Project: Open Vehicle Monitor System +; Date: 9th August 2024 +; +; Changes: +; 0.0.1 Initial stub +; +; (C) 2018 Axel Troncoso / Tucar +; +; 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_KIANIROEVSG2_H__ +#define __VEHICLE_KIANIROEVSG2_H__ + +#include "../../vehicle_kiasoulev/src/kia_common.h" +#include "vehicle.h" + + +using namespace std; + +class OvmsVehicleKiaNiroEvSg2 : public KiaVehicle + { + public: + OvmsVehicleKiaNiroEvSg2(); + ~OvmsVehicleKiaNiroEvSg2(); + + public: + void IncomingFrameCan1(CAN_frame_t *p_frame) override; + void Ticker1(uint32_t ticker) override; + void IncomingPollReply(const OvmsPoller::poll_job_t &job, uint8_t *data, uint8_t length) override; + void SendTesterPresent(uint16_t id, uint8_t length); + bool SetSessionMode(uint16_t id, uint8_t mode); + void SendCanMessage(uint16_t id, uint8_t count, + uint8_t serviceId, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, + uint8_t b5, uint8_t b6); + void SendCanMessageTriple(uint16_t id, uint8_t count, + uint8_t serviceId, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, + uint8_t b5, uint8_t b6); + bool SendCanMessage_sync(uint16_t id, uint8_t count, + uint8_t serviceId, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, + uint8_t b5, uint8_t b6); + bool SendCommandInSessionMode(uint16_t id, uint8_t count, + uint8_t serviceId, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, + uint8_t b5, uint8_t b6, uint8_t mode); + + protected: + void IncomingVMCU(canbus *bus, uint16_t type, uint16_t pid, const uint8_t *data, uint8_t length, uint16_t mlframe, uint16_t mlremain); + void IncomingBMC(canbus* bus, uint16_t type, uint16_t pid, const uint8_t* data, uint8_t length, uint16_t mlframe, uint16_t mlremain); + void IncomingBCM(canbus* bus, uint16_t type, uint16_t pid, const uint8_t* data, uint8_t length, uint16_t mlframe, uint16_t mlremain); + void IncomingIGMP(canbus* bus, uint16_t type, uint16_t pid, const uint8_t* data, uint8_t length, uint16_t mlframe, uint16_t mlremain); + void IncomingCM(canbus* bus, uint16_t type, uint16_t pid, const uint8_t* data, uint8_t length, uint16_t mlframe, uint16_t mlremain); + void IncomingSW(canbus* bus, uint16_t type, uint16_t pid, const uint8_t* data, uint8_t length, uint16_t mlframe, uint16_t mlremain); + void SendTesterPresentMessages(); + void StopTesterPresentMessages(); + + private: + + enum class PollState + { + OFF, + RUNNING, + CHARGING + }; + + void HandleCharging(); + void HandleChargeStop(); + void HandleCarOn(); + void HandleCarOff(); + + void SetChargeType(); + void ResetChargeType(); + + PollState GetPollState(); + + }; + +#endif //#ifndef __VEHICLE_KIANIROEVSG2_H__ diff --git a/vehicle/OVMS.V3/components/vehicle_maple60s/CMakeLists.txt b/vehicle/OVMS.V3/components/vehicle_maple60s/CMakeLists.txt new file mode 100644 index 000000000..445c99f7d --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_maple60s/CMakeLists.txt @@ -0,0 +1,13 @@ +set(srcs) +set(include_dirs) + +if (CONFIG_OVMS_VEHICLE_MAPLE60S) + list(APPEND srcs "src/vehicle_maple60s.cpp") + list(APPEND include_dirs "src") +endif () + +# requirements can't depend on config +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + PRIV_REQUIRES "main" + WHOLE_ARCHIVE) \ No newline at end of file diff --git a/vehicle/OVMS.V3/components/vehicle_maple60s/component.mk b/vehicle/OVMS.V3/components/vehicle_maple60s/component.mk new file mode 100644 index 000000000..040f9e492 --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_maple60s/component.mk @@ -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_MAPLE60S +COMPONENT_ADD_INCLUDEDIRS:=src +COMPONENT_SRCDIRS:=src +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive +endif diff --git a/vehicle/OVMS.V3/components/vehicle_maple60s/src/vehicle_maple_60s.cpp b/vehicle/OVMS.V3/components/vehicle_maple60s/src/vehicle_maple_60s.cpp new file mode 100644 index 000000000..08acae91a --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_maple60s/src/vehicle_maple_60s.cpp @@ -0,0 +1,195 @@ +/* +; Project: Open Vehicle Monitor System +; Date: 16th August 2024 +; +; Changes: +; 1.0 Initial stub +; +; (C) 2024 Jaime Middleton / Tucar +; (C) 2024 Axel Troncoso / Tucar +; +; 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_maple_60s.h" + +#include +#include + +#include "metrics_standard.h" +#include "ovms_log.h" +#include "ovms_metrics.h" + +#define VERSION "0.0.1" + +static const char *TAG = "v-maple60s"; + +// CAN buffer access macros: b=byte# 0..7 / n=nibble# 0..15 +#define CAN_BYTE(b) data[b] +#define CAN_INT(b) ((int16_t)CAN_UINT(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) (can_databuffer[b] & 0x0f) +#define CAN_NIBH(b) (can_databuffer[b] >> 4) +#define CAN_NIB(n) (((n)&1) ? CAN_NIBL((n)>>1) : CAN_NIBH((n)>>1)) +#define CAN_BIT(b,pos) !!(data[b] & (1<<(pos))) + +OvmsVehicleMaple60S::OvmsVehicleMaple60S() +{ + ESP_LOGI(TAG, "Maple 60s vehicle module"); + + m_door_lock_status.fill(false); + + StdMetrics.ms_v_charge_inprogress->SetValue(false); + StdMetrics.ms_v_env_on->SetValue(false); + StdMetrics.ms_v_env_locked->SetValue(false); + + // Require GPS. + MyEvents.SignalEvent("vehicle.require.gps", NULL); + MyEvents.SignalEvent("vehicle.require.gpstime", NULL); + + RegisterCanBus(1, CAN_MODE_LISTEN, CAN_SPEED_500KBPS); +} + +OvmsVehicleMaple60S::~OvmsVehicleMaple60S() +{ + ESP_LOGI(TAG, "Shutdown Maple 60S vehicle module"); +} + +void OvmsVehicleMaple60S::Ticker1(uint32_t ticker) +{ +} + +void OvmsVehicleMaple60S::IncomingFrameCan1(CAN_frame_t *p_frame) +{ + /* + BASIC METRICS + StdMetrics.ms_v_pos_speed ok + StdMetrics.ms_v_bat_soc ok + StdMetrics.ms_v_pos_odometer ok + + StdMetrics.ms_v_door_fl ok + StdMetrics.ms_v_door_fr ok + StdMetrics.ms_v_door_rl ok + StdMetrics.ms_v_door_rr ok + StdMetrics.ms_v_env_locked ok + + StdMetrics.ms_v_bat_current NA + StdMetrics.ms_v_bat_voltage NA + StdMetrics.ms_v_bat_power ok + + StdMetrics.ms_v_charge_inprogress rev + + StdMetrics.ms_v_env_on ok + StdMetrics.ms_v_env_awake ok + + StdMetrics.ms_v_env_aux12v rev + */ + + uint8_t *data = p_frame->data.u8; + + switch (p_frame->MsgID) + { + case 0x250: + StdMetrics.ms_v_charge_inprogress->SetValue(CAN_BIT(7, 0)); + break; + case 0x3F1: + StdMetrics.ms_v_pos_odometer->SetValue(CAN_UINT24(0) / 10.0, Kilometers); + break; + case 0x125: + StdMetrics.ms_v_pos_speed->SetValue((CAN_BYTE(1) * 2) + (2 * (CAN_BYTE(2) - 1) / 250.0)); + break; + case 0x162: // awake, on, off and power + { + StdMetrics.ms_v_env_awake->SetValue(CAN_BIT(5, 0)); + StdMetrics.ms_v_env_on->SetValue(CAN_BIT(3, 7) && CAN_BIT(5, 0)); + StdMetrics.ms_v_bat_power->SetValue(CAN_BYTE(4) - 100); + + auto usingCcsCharger = StdMetrics.ms_v_bat_power->AsFloat(0, kW) < -15; + StdMetrics.ms_v_charge_type->SetValue(usingCcsCharger ? "ccs" : "type2"); + break; + } + case 0x2F4: // Charge state + StdMetrics.ms_v_bat_soc->SetValue((100 * CAN_BYTE(1)) / 255, Percentage); + break; + case 0x235: + StdMetrics.ms_v_env_aux12v->SetValue((CAN_BYTE(7) + 67)/15); + break; + case 0x284: + { + StdMetrics.ms_v_door_trunk->SetValue(CAN_BIT(1, 2)); + break; + } + case 0x285: + { + StdMetrics.ms_v_door_fl->SetValue(CAN_BIT(4, 0)); + StdMetrics.ms_v_door_rl->SetValue(CAN_BIT(4, 1)); + + /* It's unclear which bit is associated with which door. + * However this doesn't matter since to consider the + * vehicle locked, all must be locked. */ + m_door_lock_status[0] = CAN_BIT(4, 2); + m_door_lock_status[1] = CAN_BIT(4, 4); + + auto vehicle_locked = std::accumulate( + m_door_lock_status.begin(), + m_door_lock_status.end(), + true, + [](bool a, bool b) + { return a && b; }); + + StdMetrics.ms_v_env_locked->SetValue(vehicle_locked); + break; + } + case 0x286: + { + StdMetrics.ms_v_door_fr->SetValue(CAN_BIT(4, 0)); + StdMetrics.ms_v_door_rr->SetValue(CAN_BIT(4, 1)); + + /* It's unclear which bit is associated with which door. + * However this doesn't matter since to consider the + * vehicle locked, all must be locked. */ + m_door_lock_status[2] = CAN_BIT(4, 2); + m_door_lock_status[3] = CAN_BIT(4, 4); + + auto vehicle_locked = std::accumulate( + m_door_lock_status.begin(), + m_door_lock_status.end(), + true, + [](bool a, bool b) + { return a && b; }); + + StdMetrics.ms_v_env_locked->SetValue(vehicle_locked); + break; + } + } +} + +class OvmsVehicleMaple60SInit +{ + public: + OvmsVehicleMaple60SInit(); +} MyOvmsVehicleMaple60SInit __attribute__((init_priority(9000))); + +OvmsVehicleMaple60SInit::OvmsVehicleMaple60SInit() +{ + ESP_LOGI(TAG, "Registering Vehicle: Maple 60S (9000)"); + MyVehicleFactory.RegisterVehicle("MPL60S", "Maple 60S"); +} diff --git a/vehicle/OVMS.V3/components/vehicle_maple60s/src/vehicle_maple_60s.h b/vehicle/OVMS.V3/components/vehicle_maple60s/src/vehicle_maple_60s.h new file mode 100644 index 000000000..945638ac6 --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_maple60s/src/vehicle_maple_60s.h @@ -0,0 +1,51 @@ +/* +; Project: Open Vehicle Monitor System +; Date: 16th August 2024 +; +; Changes: +; 1.0 Initial stub +; +; (C) 2024 Jaime Middleton / Tucar +; (C) 2024 Axel Troncoso / Tucar +; +; 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_MAPLE60S_H__ +#define __VEHICLE_MAPLE60S_H__ + +#include "vehicle.h" + +using namespace std; + +class OvmsVehicleMaple60S : public OvmsVehicle +{ +public: + OvmsVehicleMaple60S(); + ~OvmsVehicleMaple60S(); + +public: + void IncomingFrameCan1(CAN_frame_t *p_frame) override; + void Ticker1(uint32_t ticker) override; + +private: + std::array m_door_lock_status; +}; + +#endif // #ifndef __VEHICLE_MAPLE60S_H__ \ No newline at end of file diff --git a/vehicle/OVMS.V3/components/vehicle_maxus_euniq6/CMakeLists.txt b/vehicle/OVMS.V3/components/vehicle_maxus_euniq6/CMakeLists.txt new file mode 100644 index 000000000..1c2ad99a3 --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_maxus_euniq6/CMakeLists.txt @@ -0,0 +1,13 @@ +set(srcs) +set(include_dirs) + +if (CONFIG_OVMS_VEHICLE_MAXE6) + list(APPEND srcs "vehicle_me6.cpp") + list(APPEND include_dirs "src") +endif () + +# requirements can't depend on config +idf_component_register(SRCS ${srcs} + INCLUDE_DIRS ${include_dirs} + PRIV_REQUIRES "main" + WHOLE_ARCHIVE) diff --git a/vehicle/OVMS.V3/components/vehicle_maxus_euniq6/component.mk b/vehicle/OVMS.V3/components/vehicle_maxus_euniq6/component.mk new file mode 100644 index 000000000..095e6f1e3 --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_maxus_euniq6/component.mk @@ -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_MAXE6 +COMPONENT_ADD_INCLUDEDIRS:=src +COMPONENT_SRCDIRS:=src +COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive +endif diff --git a/vehicle/OVMS.V3/components/vehicle_maxus_euniq6/src/vehicle_me6.cpp b/vehicle/OVMS.V3/components/vehicle_maxus_euniq6/src/vehicle_me6.cpp new file mode 100644 index 000000000..6d748a478 --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_maxus_euniq6/src/vehicle_me6.cpp @@ -0,0 +1,172 @@ +/* +; Project: Open Vehicle Monitor System +; Date: 14th August 2024 +; +; Changes: +; 1.0 Initial release +; +; (C) 2021 Jaime Middleton / Tucar +; (C) 2021 Axel Troncoso / Tucar +; +; 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 = "v-maxe6"; + +#include +#include + +#include "vehicle_me6.h" +#include "ovms_notify.h" +#include +#include "metrics_standard.h" + +// CAN buffer access macros: b=byte# 0..7 / n=nibble# 0..15 +#define CAN_BYTE(b) data[b] +#define CAN_INT(b) ((int16_t)CAN_UINT(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) (can_databuffer[b] & 0x0f) +#define CAN_NIBH(b) (can_databuffer[b] >> 4) +#define CAN_NIB(n) (((n)&1) ? CAN_NIBL((n)>>1) : CAN_NIBH((n)>>1)) +#define CAN_BIT(b,pos) !!(data[b] & (1<<(pos))) + +OvmsVehicleMaxe6::OvmsVehicleMaxe6() +{ + ESP_LOGI(TAG, "Start Maxus Euniq 6 vehicle module"); + + // Init CAN: + RegisterCanBus(1,CAN_MODE_ACTIVE,CAN_SPEED_500KBPS); + + // Init Energy: + StdMetrics.ms_v_charge_mode->SetValue("standard"); + + // Require GPS: + MyEvents.SignalEvent("vehicle.require.gps", NULL); + MyEvents.SignalEvent("vehicle.require.gpstime", NULL); +} + +OvmsVehicleMaxe6::~OvmsVehicleMaxe6() +{ + ESP_LOGI(TAG, "Stop Euniq 6 vehicle module"); +} + +void OvmsVehicleMaxe6::IncomingFrameCan1(CAN_frame_t *p_frame) +{ + /* + BASIC METRICS + StdMetrics.ms_v_pos_speed ok + StdMetrics.ms_v_bat_soc ok + StdMetrics.ms_v_pos_odometer ok + + StdMetrics.ms_v_door_fl ok + StdMetrics.ms_v_door_fr ok + StdMetrics.ms_v_door_rl ok + StdMetrics.ms_v_door_rr ok + StdMetrics.ms_v_trunk ok + StdMetrics.ms_v_env_locked ok + + StdMetrics.ms_v_bat_current - + StdMetrics.ms_v_bat_voltage - + StdMetrics.ms_v_bat_power wip (percentage units) + + StdMetrics.ms_v_charge_inprogress rev + + StdMetrics.ms_v_env_on ok + StdMetrics.ms_v_env_awake ok + + StdMetrics.ms_v_env_aux12v rev + */ + + uint8_t *data = p_frame->data.u8; + + switch (p_frame->MsgID) + { + case 0x0c9: + { + StdMetrics.ms_v_charge_inprogress->SetValue( + CAN_BIT(2,0) && CAN_BIT(2,1) && CAN_BIT(2,2) + ); + break; + } + case 0x281: + { + StdMetrics.ms_v_env_locked->SetValue( + CAN_BIT(1,0) && CAN_BIT(1,2) && CAN_BIT(1,4) && CAN_BIT(1,6) && + !StdMetrics.ms_v_door_fl->AsBool() && + !StdMetrics.ms_v_door_fr->AsBool() && + !StdMetrics.ms_v_door_rl->AsBool() && + !StdMetrics.ms_v_door_rr->AsBool() && + !StdMetrics.ms_v_door_trunk->AsBool()); + break; + } + case 0x46a: + { + StdMetrics.ms_v_door_fl->SetValue(CAN_BIT(0, 0)); + StdMetrics.ms_v_door_fr->SetValue(CAN_BIT(0, 3)); + StdMetrics.ms_v_door_rr->SetValue(CAN_BIT(0, 5)); + StdMetrics.ms_v_door_rl->SetValue(CAN_BIT(0, 7)); + StdMetrics.ms_v_door_trunk->SetValue(CAN_BIT(1, 1)); + break; + } + case 0x540: + { + StdMetrics.ms_v_pos_odometer->SetValue(CAN_UINT24(0)); + break; + } + case 0x6f0: + { + StdMetrics.ms_v_pos_speed->SetValue(CAN_BYTE(4)); + break; + } + case 0x6f1: + { + StdMetrics.ms_v_env_awake->SetValue(CAN_BIT(4,7)); + StdMetrics.ms_v_env_on->SetValue(CAN_BIT(1,4) && CAN_BIT(4,7)); + break; + } + case 0x6f2: + { + // Units percentage. + StdMetrics.ms_v_bat_soc->SetValue(CAN_BYTE(1)); + StdMetrics.ms_v_bat_power->SetValue((CAN_BYTE(2) - 100) * 120, kW); + break; + } + default: + break; + } +} + +void OvmsVehicleMaxe6::Ticker1(uint32_t ticker) +{ +} + +class OvmsVehicleMaxe6Init +{ +public: OvmsVehicleMaxe6Init(); +} OvmsVehicleMaxe6Init __attribute__ ((init_priority (9000))); + +OvmsVehicleMaxe6Init::OvmsVehicleMaxe6Init() +{ + ESP_LOGI(TAG, "Registering Vehicle: Maxus Euniq 6 (9000)"); + + MyVehicleFactory.RegisterVehicle("ME6","Maxus Euniq 6"); +} diff --git a/vehicle/OVMS.V3/components/vehicle_maxus_euniq6/src/vehicle_me6.h b/vehicle/OVMS.V3/components/vehicle_maxus_euniq6/src/vehicle_me6.h new file mode 100644 index 000000000..18f11bc99 --- /dev/null +++ b/vehicle/OVMS.V3/components/vehicle_maxus_euniq6/src/vehicle_me6.h @@ -0,0 +1,58 @@ +/* +; Project: Open Vehicle Monitor System +; Date: 14th August 2024 +; +; Changes: +; 1.0 Initial release +; +; (C) 2021 Jaime Middleton / Tucar +; (C) 2021 Axel Troncoso / Tucar +; +; 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_ME6_H__ +#define __VEHICLE_ME6_H__ + +#include "vehicle.h" +#include "metrics_standard.h" + +#include "freertos/timers.h" + +#include + + +using namespace std; + +class OvmsVehicleMaxe6 : public OvmsVehicle +{ +public: + + OvmsVehicleMaxe6(); + ~OvmsVehicleMaxe6(); + +protected: + void Ticker1(uint32_t ticker) override; + +private: + void IncomingFrameCan1(CAN_frame_t* p_frame) override; +}; + +#endif //#ifndef __VEHICLE_ME6_H__ + diff --git a/vehicle/OVMS.V3/components/vehicle_vweup/src/vehicle_vweup.cpp b/vehicle/OVMS.V3/components/vehicle_vweup/src/vehicle_vweup.cpp index c243fbb59..a59cbc013 100644 --- a/vehicle/OVMS.V3/components/vehicle_vweup/src/vehicle_vweup.cpp +++ b/vehicle/OVMS.V3/components/vehicle_vweup/src/vehicle_vweup.cpp @@ -444,16 +444,15 @@ OvmsVehicleVWeUp::vehicle_command_t OvmsVehicleVWeUp::MsgCommandCA(std::string & // Handle changed T26 settings: if (vweup_enable_t26) { + bool soc_above_max = StdMetrics.ms_v_bat_soc->AsFloat() >= StdMetrics.ms_v_charge_limit_soc->AsInt(); if (vweup_chg_soclimit_new != StdMetrics.ms_v_charge_limit_soc->AsInt()) { - bool socswitch = StdMetrics.ms_v_charge_limit_soc->AsInt() <= StdMetrics.ms_v_bat_soc->AsFloat(); - StdMetrics.ms_v_charge_limit_soc->SetValue(vweup_chg_soclimit_new); - if (vweup_chg_soclimit_new == 0 || vweup_chg_soclimit_new > StdMetrics.ms_v_bat_soc->AsInt()) { + if (vweup_chg_soclimit_new == 0 || vweup_chg_soclimit_new > StdMetrics.ms_v_bat_soc->AsFloat()) { ESP_LOGD(TAG, "ConfigChanged: SoC limit changed to above current SoC"); if (IsCharging()) { ESP_LOGD(TAG, "ConfigChanged: already charging, nothing to do"); StdMetrics.ms_v_charge_state->SetValue("charging"); // switch from topoff mode to charging } - else if (socswitch) { // only start charge when previous max SoC was < SoC + else if (soc_above_max) { // only start charge when previous max SoC was <= SoC ESP_LOGD(TAG, "ConfigChanged: trying to start charge..."); fakestop = true; StartStopChargeT26(true); @@ -461,10 +460,11 @@ OvmsVehicleVWeUp::vehicle_command_t OvmsVehicleVWeUp::MsgCommandCA(std::string & } else { ESP_LOGD(TAG, "ConfigChanged: SoC limit changed to below current SoC"); - if (IsCharging()) { + if (IsCharging() && !soc_above_max) { // only stop charge when previous max SoC was > SoC ESP_LOGD(TAG, "ConfigChanged: stopping charge..."); StartStopChargeT26(false); } + StdMetrics.ms_v_charge_limit_soc->SetValue(vweup_chg_soclimit_new); } } if (vweup_charge_current_new != profile0_charge_current && profile0_charge_current != 0) { @@ -498,8 +498,8 @@ OvmsVehicleVWeUp::vehicle_command_t OvmsVehicleVWeUp::MsgCommandCA(std::string & } if (chg_autostop_new != chg_autostop) { chg_autostop = chg_autostop_new; - if (!chg_autostop_new) { - ESP_LOGD(TAG, "ConfigChanged: charge autostop disabled, trying to start charge..."); + if (!chg_autostop_new && soc_above_max && StdMetrics.ms_v_bat_soc->AsFloat() < 100) { + ESP_LOGD(TAG, "ConfigChanged: charge autostop disabled & SoC above max SoC, trying to start charge..."); fakestop = true; StartStopChargeT26(true); } @@ -610,12 +610,10 @@ void OvmsVehicleVWeUp::Ticker1(uint32_t ticker) } } } - if (HasT26() && m_chargestate_lastsoc > suff_soc && soc < suff_soc) { - ESP_LOGI(TAG, "Ticker1: SOC fell below sufficient SOC limit (%d%%), restarting charge", suff_soc); + if (m_chargestate_lastsoc > suff_soc && soc < suff_soc) { + ESP_LOGI(TAG, "Ticker1: SOC fell below sufficient SOC limit (%d%%), trying to restart charge...", suff_soc); fakestop = true; - ESP_LOGD(TAG, "Ticker1: trying to start charge..."); - fakestop = true; - StartStopChargeT26(true); + StartStopChargeT26(true); } m_chargestate_lastsoc = soc; @@ -1082,9 +1080,8 @@ int OvmsVehicleVWeUp::CalcChargeTime(float capacity, float max_pwr, int from_soc /** * UpdateChargeTimes: update all charge time predictions - * This is called by Ticker60() and by IncomingPollReply(), and on config changes. - * While charging, the car delivers a CTP for the current SOC limit if set, or 100%, - * but only if the OBD connection is available. + * This is called by Ticker60() and by IncomingPollReply(), and on config changes + */ void OvmsVehicleVWeUp::UpdateChargeTimes() { diff --git a/vehicle/OVMS.V3/components/vehicle_vweup/src/vehicle_vweup.h b/vehicle/OVMS.V3/components/vehicle_vweup/src/vehicle_vweup.h index 579026fb0..66e03bc26 100644 --- a/vehicle/OVMS.V3/components/vehicle_vweup/src/vehicle_vweup.h +++ b/vehicle/OVMS.V3/components/vehicle_vweup/src/vehicle_vweup.h @@ -251,7 +251,6 @@ class OvmsVehicleVWeUp : public OvmsVehicle int m_timermode_ticker; bool m_timermode_new; - // -------------------------------------------------------------------------- // Web UI Subsystem // - implementation: vweup_web.(h,cpp) @@ -356,7 +355,9 @@ class OvmsVehicleVWeUp : public OvmsVehicle bool wakeup_success; bool charge_timeout; uint8_t lever; - + uint8_t lever0_cnt; + bool p_problem; + private: RemoteCommand vweup_remote_command; // command to send, see RemoteCommandTimer() TimerHandle_t m_sendOcuHeartbeat; diff --git a/vehicle/OVMS.V3/components/vehicle_vweup/src/vweup_t26.cpp b/vehicle/OVMS.V3/components/vehicle_vweup/src/vweup_t26.cpp index a980578e9..8aa38d266 100644 --- a/vehicle/OVMS.V3/components/vehicle_vweup/src/vweup_t26.cpp +++ b/vehicle/OVMS.V3/components/vehicle_vweup/src/vweup_t26.cpp @@ -163,7 +163,7 @@ void OvmsVehicleVWeUp::T26Init() memset(m_vin, 0, sizeof(m_vin)); RegisterCanBus(3, CAN_MODE_ACTIVE, CAN_SPEED_100KBPS); - + MyConfig.RegisterParam("xvu", "VW e-Up", true, true); vin_part1 = false; vin_part2 = false; @@ -207,6 +207,8 @@ void OvmsVehicleVWeUp::T26Init() wakeup_success = false; charge_timeout = false; lever = 0x20; // assume position "P" on startup so climatecontrol works + lever0_cnt = 0; + p_problem = false; StdMetrics.ms_v_env_locked->SetValue(true); StdMetrics.ms_v_env_headlights->SetValue(false); @@ -336,8 +338,24 @@ void OvmsVehicleVWeUp::IncomingFrameCan3(CAN_frame_t *p_frame) } } if (lever != d[3]) { - ESP_LOGI(TAG, "Drive mode lever switched to %d", d[3]); + ESP_LOGI(TAG, "Drive mode lever switched to 0x%02x", d[3]); lever = d[3]; + if (lever != 0) { + lever0_cnt = 0; + p_problem = false; + } + } + if (lever == 0) { // detect "P-problem": when 12V battery is detected as defective, lever byte sometimes returns 0 when car is off. Then charge can't start since lever has to be in P position (0x20) for that... + if (lever0_cnt < 10) // wait for 10 occurences to suppress ghost triggering + lever0_cnt++; + else if (lever0_cnt == 10) { + lever0_cnt++; + if (HasOBD() && StdMetrics.ms_v_door_chargeport->AsBool()) { + ESP_LOGE(TAG, "T26: invalid selector lever value, charging impossible! Microswitch possibly defective?"); + MyNotify.NotifyStringf("alert", "Drive Mode Selector", "Invalid selector lever value, charging impossible! Microswitch possibly defective?"); + } + p_problem = true; + } } break; @@ -925,6 +943,12 @@ OvmsVehicle::vehicle_command_t OvmsVehicleVWeUp::CommandStartCharge() xSemaphoreGive(xChargeSemaphore); return Fail; } + else if (p_problem) { + ESP_LOGE(TAG, "T26: invalid selector lever value, can't start charge! Microswitch possibly defective?"); + MyNotify.NotifyStringf("alert", "Drive Mode Selector", "Invalid selector lever value, can't start charge! Microswitch possibly defective?"); + p_problem = false; + return Fail; + } fakestop = false; // reset possible workaround charge stop xSemaphoreTake(xChargeSemaphore, 0); StartStopChargeT26(true); @@ -958,6 +982,11 @@ OvmsVehicle::vehicle_command_t OvmsVehicleVWeUp::CommandStopCharge() ESP_LOGE(TAG, "Vehicle is not charging!"); return Fail; } + else if (StdMetrics.ms_v_env_on->AsBool() && !chg_workaround) { + ESP_LOGE(TAG, "Vehicle is on, can't stop charge without workaround!"); + MyNotify.NotifyStringf("alert", "Charge control", "Vehicle is on, can't stop charge without workaround!"); + return Fail; + } xSemaphoreTake(xChargeSemaphore, 0); StartStopChargeT26(false); if (xSemaphoreTake(xChargeSemaphore, pdMS_TO_TICKS(2*profile0_retries*profile0_delay))) diff --git a/vehicle/OVMS.V3/main/Kconfig b/vehicle/OVMS.V3/main/Kconfig index e37cd6fab..d8935bcbb 100644 --- a/vehicle/OVMS.V3/main/Kconfig +++ b/vehicle/OVMS.V3/main/Kconfig @@ -429,6 +429,14 @@ config OVMS_VEHICLE_MAXE56 depends on OVMS_COMP_POLLER help Enable to include support for Maxus Euniq 5 6-seats vehicle. + +config OVMS_VEHICLE_MAXE6 + bool "Include support for Maxus Euniq 6 vehicle" + default y + depends on OVMS + depends on OVMS_COMP_POLLER + help + Enable to include support for Maxus Euniq 6 vehicle. config OVMS_VEHICLE_KIASOULEV bool "Include support for Kia Soul EV vehicles" @@ -593,6 +601,7 @@ config OVMS_VEHICLE_TOYOTARAV4EV depends on OVMS help Enable to include support for Toyota RAV4 EV vehicles. + config OVMS_VEHICLE_BYD_ATTO3 bool "Include support for BYD Atto 3" default y @@ -600,6 +609,29 @@ config OVMS_VEHICLE_BYD_ATTO3 depends on OVMS_COMP_POLLER help Enable support for BYD Atto 3 +config OVMS_VEHICLE_KIANIROEVSG2 + bool "Include support for Kia Niro EV SG2" + default y + depends on OVMS + depends on OVMS_COMP_POLLER + help + Enable support for Kia Niro EV SG2 + +config OVMS_VEHICLE_DONGFENG_E60 + bool "Include support for Dong Feng E60" + default y + depends on OVMS + depends on OVMS_COMP_POLLER + help + Enable support for Dong Feng E60 + +config OVMS_VEHICLE_MAPLE60S + bool "Include support for Maple 60s" + default y + depends on OVMS + depends on OVMS_COMP_POLLER + help + Enable support for Maple 60s config OVMS_VEHICLE_RXTASK_STACK int "Stack size for ISOTP Poller RX task" diff --git a/vehicle/OVMS.V3/main/metrics_standard.cpp b/vehicle/OVMS.V3/main/metrics_standard.cpp index ddcb3ab50..458d176b8 100644 --- a/vehicle/OVMS.V3/main/metrics_standard.cpp +++ b/vehicle/OVMS.V3/main/metrics_standard.cpp @@ -48,6 +48,9 @@ MetricsStandard::MetricsStandard() ms_m_net_type = new OvmsMetricString(MS_N_TYPE, SM_STALE_MAX); ms_m_net_sq = new OvmsMetricInt(MS_N_SQ, SM_STALE_MAX, dbm); ms_m_net_provider = new OvmsMetricString(MS_N_PROVIDER, SM_STALE_MAX); + ms_m_net_connected = new OvmsMetricBool(MS_N_CONNECTED); + ms_m_net_ip = new OvmsMetricBool(MS_N_IP); + ms_m_net_good_sq = new OvmsMetricBool(MS_N_GOOD_SQ); ms_m_net_wifi_sq = new OvmsMetricFloat(MS_N_WIFI_SQ, SM_STALE_MAX, dbm); ms_m_net_wifi_network = new OvmsMetricString(MS_N_WIFI_NETWORK, SM_STALE_MAX); ms_m_net_mdm_sq = new OvmsMetricFloat(MS_N_MDM_SQ, SM_STALE_MAX, dbm); @@ -224,11 +227,14 @@ MetricsStandard::MetricsStandard() ms_v_env_awake = new OvmsMetricBool(MS_V_ENV_AWAKE, SM_STALE_MID, Other, true); ms_v_env_on = new OvmsMetricBool(MS_V_ENV_ON, SM_STALE_MID, Other, true); ms_v_env_drivemode = new OvmsMetricInt(MS_V_ENV_DRIVEMODE, SM_STALE_MID, Other, true); + ms_v_env_efficiencymode = new OvmsMetricString(MS_V_ENV_EFFICIENCYMODE, SM_STALE_MID, Other, true); + ms_v_env_regenlevel = new OvmsMetricFloat(MS_V_ENV_REGENLEVEL, SM_STALE_MID, Percentage, true); ms_v_env_gear = new OvmsMetricInt(MS_V_ENV_GEAR, SM_STALE_MID); ms_v_env_throttle = new OvmsMetricFloat(MS_V_ENV_THROTTLE, SM_STALE_MID, Percentage); ms_v_env_footbrake = new OvmsMetricFloat(MS_V_ENV_FOOTBRAKE, SM_STALE_MID, Percentage); ms_v_env_handbrake = new OvmsMetricBool(MS_V_ENV_HANDBRAKE, SM_STALE_MID); ms_v_env_regenbrake = new OvmsMetricBool(MS_V_ENV_REGENBRAKE, SM_STALE_MID); + ms_v_env_onepedal = new OvmsMetricBool(MS_V_ENV_ONEPEDAL, SM_STALE_MID); ms_v_env_cooling = new OvmsMetricBool(MS_V_ENV_COOLING, SM_STALE_MID); ms_v_env_heating = new OvmsMetricBool(MS_V_ENV_HEATING, SM_STALE_MID); ms_v_env_hvac = new OvmsMetricBool(MS_V_ENV_HVAC, SM_STALE_MID); diff --git a/vehicle/OVMS.V3/main/metrics_standard.h b/vehicle/OVMS.V3/main/metrics_standard.h index d0246f79e..4e3a50dfa 100644 --- a/vehicle/OVMS.V3/main/metrics_standard.h +++ b/vehicle/OVMS.V3/main/metrics_standard.h @@ -50,6 +50,9 @@ #define MS_N_TYPE "m.net.type" #define MS_N_SQ "m.net.sq" +#define MS_N_CONNECTED "m.net.connected" +#define MS_N_IP "m.net.ip" +#define MS_N_GOOD_SQ "m.net.good.sq" #define MS_N_PROVIDER "m.net.provider" #define MS_N_MDM_ICCID "m.net.mdm.iccid" #define MS_N_MDM_MODEL "m.net.mdm.model" @@ -197,11 +200,14 @@ #define MS_V_DOOR_TRUNK "v.d.trunk" #define MS_V_ENV_DRIVEMODE "v.e.drivemode" +#define MS_V_ENV_EFFICIENCYMODE "v.e.efficiencymode" +#define MS_V_ENV_REGENLEVEL "v.e.regenlevel" #define MS_V_ENV_GEAR "v.e.gear" #define MS_V_ENV_THROTTLE "v.e.throttle" #define MS_V_ENV_FOOTBRAKE "v.e.footbrake" #define MS_V_ENV_HANDBRAKE "v.e.handbrake" #define MS_V_ENV_REGENBRAKE "v.e.regenbrake" +#define MS_V_ENV_ONEPEDAL "v.e.onepedal" #define MS_V_ENV_AWAKE "v.e.awake" #define MS_V_ENV_CHARGING12V "v.e.charging12v" #define MS_V_ENV_AUX12V "v.e.aux12v" @@ -297,6 +303,9 @@ class MetricsStandard OvmsMetricString* ms_m_net_mdm_iccid; // ICCID of SIM card in modem OvmsMetricString* ms_m_net_mdm_model; // Model of modem discovered OvmsMetricString* ms_m_net_mdm_mode; // Cellular connection mode and status + OvmsMetricBool* ms_m_net_connected; // True = connected_any is true + OvmsMetricBool* ms_m_net_ip; // True = device has ip available + OvmsMetricBool* ms_m_net_good_sq; // True = sq is above the configured threshold for sq usability #ifdef CONFIG_OVMS_COMP_MAX7317 OvmsMetricBitset<10,0>* ms_m_egpio_input; // EGPIO (MAX7317) input port state (ports 0…9) @@ -466,11 +475,14 @@ class MetricsStandard OvmsMetricBool* ms_v_env_awake; // Vehicle is fully awake (switched on by the user) OvmsMetricBool* ms_v_env_on; // Vehicle is in "ignition" state (drivable) OvmsMetricInt* ms_v_env_drivemode; // Active drive profile number [1] + OvmsMetricString* ms_v_env_efficiencymode; // Active efficiency profile number [1] OvmsMetricInt* ms_v_env_gear; // Gear/direction; negative=reverse, 0=neutral [1] + OvmsMetricFloat* ms_v_env_regenlevel; // regen level activaed OvmsMetricFloat* ms_v_env_throttle; // Drive pedal state [%] OvmsMetricFloat* ms_v_env_footbrake; // Brake pedal state [%] OvmsMetricBool* ms_v_env_handbrake; // Handbrake state OvmsMetricBool* ms_v_env_regenbrake; // Regenerative braking state + OvmsMetricBool* ms_v_env_onepedal; // If one pedal driving is active OvmsMetricBool* ms_v_env_cooling; OvmsMetricBool* ms_v_env_heating; OvmsMetricBool* ms_v_env_hvac; // Climate control system state diff --git a/vehicle/OVMS.V3/main/ovms_config.cpp b/vehicle/OVMS.V3/main/ovms_config.cpp index 06bca7b71..344fd5a24 100644 --- a/vehicle/OVMS.V3/main/ovms_config.cpp +++ b/vehicle/OVMS.V3/main/ovms_config.cpp @@ -46,6 +46,8 @@ static const char *TAG = "config"; #include "ovms_boot.h" #include "ovms_semaphore.h" #include "ovms_vfs.h" +#include "ovms_module.h" + #ifdef CONFIG_OVMS_SC_ZIP #include "zip_archive.h" @@ -1189,7 +1191,16 @@ void OvmsConfigParam::RewriteConfig() path.append(m_name); FILE* f = fopen(path.c_str(), "w"); if (!f) + { ESP_LOGE(TAG, "RewriteConfig: can't open '%s': %s", path.c_str(), strerror(errno)); + // in case /store/ovms_config/ is not accesible factory reset. + // This is a workaround in case config is corrupted + if (startsWith(path, "/store/ovms_config/")) + { + ESP_LOGE(TAG, "RewriteConfig: factory reset"); + ExecuteDriverFactoryReset(); + } + } else { #ifdef OVMS_PERSIST_METADATA diff --git a/vehicle/OVMS.V3/main/ovms_events.cpp b/vehicle/OVMS.V3/main/ovms_events.cpp index b758eae34..d158a55e6 100644 --- a/vehicle/OVMS.V3/main/ovms_events.cpp +++ b/vehicle/OVMS.V3/main/ovms_events.cpp @@ -225,7 +225,7 @@ OvmsEvents::OvmsEvents() cmd_eventtrace->RegisterCommand("off","Turn event tracing OFF",event_trace); m_taskqueue = xQueueCreate(CONFIG_OVMS_HW_EVENT_QUEUE_SIZE,sizeof(event_queue_t)); - xTaskCreatePinnedToCore(EventLaunchTask, "OVMS Events", 8192, (void*)this, 8, &m_taskid, CORE(1)); + xTaskCreatePinnedToCore(EventLaunchTask, "OVMS Events", 12288, (void*)this, 8, &m_taskid, CORE(1)); AddTaskToMap(m_taskid); #ifdef CONFIG_OVMS_SC_JAVASCRIPT_DUKTAPE @@ -246,6 +246,7 @@ OvmsEvents::~OvmsEvents() void OvmsEvents::EventTask() { event_queue_t msg; + int detect_event_loop_blockage = 0; esp_task_wdt_add(NULL); // WATCHDOG is active for this task while(1) @@ -259,7 +260,25 @@ void OvmsEvents::EventTask() break; case EVENT_signal: m_current_event = msg.body.signal.event; - HandleQueueSignalEvent(&msg); + if (startsWith(m_current_event, "ticker.") && uxQueueSpacesAvailable(m_taskqueue) < CONFIG_OVMS_HW_EVENT_QUEUE_SIZE/5) + { + ESP_LOGE(TAG, "Droped %s, counter %i", m_current_event.c_str(), detect_event_loop_blockage); + FreeQueueSignalEvent(&msg); + detect_event_loop_blockage++; + if (detect_event_loop_blockage > 30) + { + ESP_LOGE(TAG, "Timer service / ticker timer has died => aborting"); + MyBoot.Restart(); + } + } + else + { + HandleQueueSignalEvent(&msg); + if (startsWith(m_current_event, "ticker.") && detect_event_loop_blockage > 0) + { + detect_event_loop_blockage--; + } + } esp_task_wdt_reset(); // Reset WATCHDOG timer for this task m_current_event.clear(); break; diff --git a/vehicle/OVMS.V3/main/ovms_housekeeping.cpp b/vehicle/OVMS.V3/main/ovms_housekeeping.cpp index a42af1d63..530daffca 100644 --- a/vehicle/OVMS.V3/main/ovms_housekeeping.cpp +++ b/vehicle/OVMS.V3/main/ovms_housekeeping.cpp @@ -202,6 +202,7 @@ void Housekeeping::Init(std::string event, void* data) else if (MyBoot.GetEarlyCrashCount() >= AUTO_INIT_INHIBIT_CRASHCOUNT) { ESP_LOGE(TAG, "Auto init inhibited: too many early crashes (%d)", MyBoot.GetEarlyCrashCount()); + ExecuteDriverFactoryReset(); } else { diff --git a/vehicle/OVMS.V3/main/ovms_main.cpp b/vehicle/OVMS.V3/main/ovms_main.cpp index d91afaa18..5c150abc6 100644 --- a/vehicle/OVMS.V3/main/ovms_main.cpp +++ b/vehicle/OVMS.V3/main/ovms_main.cpp @@ -47,7 +47,7 @@ static class FrameworkInit }; ESP_ERROR_CHECK(esp_task_wdt_init(&config)); #else - esp_task_wdt_init(120, true); + esp_task_wdt_init(250, true); #endif #else ESP_LOGI(TAG, "WATCHDOG already initialized..."); diff --git a/vehicle/OVMS.V3/main/ovms_module.cpp b/vehicle/OVMS.V3/main/ovms_module.cpp index a9a016805..fc7ce5735 100644 --- a/vehicle/OVMS.V3/main/ovms_module.cpp +++ b/vehicle/OVMS.V3/main/ovms_module.cpp @@ -1134,6 +1134,11 @@ bool module_factory_reset_yesno(OvmsWriter* writer, void* ctx, char ch) return false; } +void ExecuteDriverFactoryReset() + { + module_perform_factoryreset(NULL); + } + static void module_factory_reset(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, int argc, const char* const* argv) { if (argc > 0) diff --git a/vehicle/OVMS.V3/main/ovms_module.h b/vehicle/OVMS.V3/main/ovms_module.h index 27cdc8c9d..5ffb06456 100644 --- a/vehicle/OVMS.V3/main/ovms_module.h +++ b/vehicle/OVMS.V3/main/ovms_module.h @@ -31,6 +31,7 @@ #ifndef __OVMS_MODULE_H__ extern void AddTaskToMap(TaskHandle_t task); +extern void ExecuteDriverFactoryReset(); #define __OVMS_MODULE_H__ diff --git a/vehicle/OVMS.V3/main/ovms_netmanager.cpp b/vehicle/OVMS.V3/main/ovms_netmanager.cpp index 2265a8c70..93e5afbbd 100644 --- a/vehicle/OVMS.V3/main/ovms_netmanager.cpp +++ b/vehicle/OVMS.V3/main/ovms_netmanager.cpp @@ -49,6 +49,7 @@ static const char *TAG = "netmanager"; #include "ovms_command.h" #include "ovms_config.h" #include "ovms_module.h" +#include "ovms_boot.h" #ifdef CONFIG_OVMS_DEV_NETMANAGER_PING #include "ping/ping_sock.h" #endif // CONFIG_OVMS_DEV_NETMANAGER_PING @@ -343,6 +344,7 @@ void network_connections(int verbosity, OvmsWriter* writer, OvmsCommand* cmd, in OvmsNetManager::OvmsNetManager() { ESP_LOGI(TAG, "Initialising NETMANAGER (8999)"); + m_not_connected_counter = 0; m_connected_wifi = false; m_connected_modem = false; m_connected_any = false; @@ -399,6 +401,7 @@ OvmsNetManager::OvmsNetManager() MyEvents.RegisterEvent(TAG,"system.wifi.ap.sta.disconnected", std::bind(&OvmsNetManager::WifiApStaDisconnect, this, _1, _2)); #endif // #ifdef CONFIG_OVMS_COMP_WIFI + MyEvents.RegisterEvent(TAG, "ticker.1", std::bind(&OvmsNetManager::Ticker1, this, _1, _2)); MyEvents.RegisterEvent(TAG,"system.modem.gotip", std::bind(&OvmsNetManager::ModemUp, this, _1, _2)); MyEvents.RegisterEvent(TAG,"system.modem.stop", std::bind(&OvmsNetManager::ModemDown, this, _1, _2)); MyEvents.RegisterEvent(TAG,"system.modem.down", std::bind(&OvmsNetManager::ModemDown, this, _1, _2)); @@ -652,6 +655,26 @@ void OvmsNetManager::WifiApStaDisconnect(std::string event, void* data) #endif // #ifdef CONFIG_OVMS_COMP_WIFI +void OvmsNetManager::Ticker1(std::string event, void *data) + { + if (m_cfg_reboot_no_connection) + { + if (m_connected_any && !m_has_ip && StdMetrics.ms_m_net_good_sq->AsBool()) + { + ESP_LOGI(TAG, "Connection available with good signal, but no IP address; rebooting in %i seconds", 300-m_not_connected_counter); + m_not_connected_counter++; + if (m_not_connected_counter > 300) + { + MyBoot.Restart(); + } + } + else + { + m_not_connected_counter = 0; + } + } + } + void OvmsNetManager::ModemUp(std::string event, void* data) { m_connected_modem = true; @@ -723,8 +746,9 @@ void OvmsNetManager::ConfigChanged(std::string event, void* data) if (!param || param->GetName() == "network") { // Network config has been changed, apply: + m_cfg_reboot_no_connection = MyConfig.GetParamValueBool("network", "reboot.no.ip", false); m_cfg_wifi_sq_good = MyConfig.GetParamValueFloat("network", "wifi.sq.good", -87); - m_cfg_wifi_sq_bad = MyConfig.GetParamValueFloat("network", "wifi.sq.bad", -89); + m_cfg_wifi_sq_bad = MyConfig.GetParamValueFloat("network", "wifi.sq.bad", -89); if (m_cfg_wifi_sq_good < m_cfg_wifi_sq_bad) { float x = m_cfg_wifi_sq_good; @@ -854,6 +878,8 @@ void OvmsNetManager::DoSafePrioritiseAndIndicate() // A convenient place to keep track of connectivity in general m_connected_any = m_connected_wifi || m_connected_modem; m_network_any = m_connected_wifi || m_connected_modem || m_wifi_ap; + StdMetrics.ms_m_net_connected->SetValue(m_connected_any); + // Priority order... if (m_connected_wifi) @@ -862,6 +888,7 @@ void OvmsNetManager::DoSafePrioritiseAndIndicate() SetNetType("wifi"); search = "st"; dns = m_dns_wifi; + m_has_ip = MyPeripherals->m_esp32wifi->WifiHasIp(); } else if (m_connected_modem) { @@ -869,7 +896,11 @@ void OvmsNetManager::DoSafePrioritiseAndIndicate() SetNetType("modem"); search = "pp"; dns = m_dns_modem; + m_has_ip = MyPeripherals->m_cellular_modem->ModemIsNetMode() && + MyPeripherals->m_cellular_modem->m_mux->IsMuxUp() && + MyPeripherals->m_cellular_modem->m_ppp->m_connected; } + StdMetrics.ms_m_net_ip->SetValue(m_has_ip); if (search == NULL) { diff --git a/vehicle/OVMS.V3/main/ovms_netmanager.h b/vehicle/OVMS.V3/main/ovms_netmanager.h index e647937ec..da329ceea 100644 --- a/vehicle/OVMS.V3/main/ovms_netmanager.h +++ b/vehicle/OVMS.V3/main/ovms_netmanager.h @@ -116,6 +116,7 @@ class OvmsNetManager void WifiApStaDisconnect(std::string event, void* data); #endif // CONFIG_OVMS_COMP_WIFI + void Ticker1(std::string event, void *data); void ModemUp(std::string event, void* data); void ModemDown(std::string event, void* data); void InterfaceUp(std::string event, void* data); @@ -137,9 +138,11 @@ class OvmsNetManager void SetDNSServer(ip_addr_t* dnsstore); public: + int m_not_connected_counter; bool m_connected_wifi; bool m_connected_modem; bool m_connected_any; + bool m_has_ip; bool m_wifi_sta; bool m_wifi_good; bool m_wifi_ap; @@ -150,6 +153,7 @@ class OvmsNetManager char m_previous_name[2]; protected: + bool m_cfg_reboot_no_connection; float m_cfg_wifi_sq_good; // config network wifi.sq.good [dBm] default -87 float m_cfg_wifi_sq_bad; // config network wifi.sq.bad [dBm] default -89 diff --git a/vehicle/OVMS.V3/main/ovms_version.cpp b/vehicle/OVMS.V3/main/ovms_version.cpp index f53783214..315e39c80 100644 --- a/vehicle/OVMS.V3/main/ovms_version.cpp +++ b/vehicle/OVMS.V3/main/ovms_version.cpp @@ -207,7 +207,9 @@ void Version(std::string event, void* data) metric.append(" (build "); metric.append(GetOVMSBuild()); metric.append(")"); - StandardMetrics.ms_m_version->SetValue(metric.c_str()); + // StandardMetrics.ms_m_version->SetValue(metric.c_str()); + // CAMBIAR CON DIFERENTES VERSIONES + StandardMetrics.ms_m_version->SetValue("OVMS TUCAR V2.0"); metric = GetOVMSHardware(); StandardMetrics.ms_m_hardware->SetValue(metric.c_str());