All chargers compiled

This commit is contained in:
Jaakko Haakana 2025-05-10 06:50:15 +03:00
parent 1d733fa78f
commit 517731c4c3
14 changed files with 316 additions and 274 deletions

View file

@ -101,9 +101,7 @@ void setup() {
init_precharge_control();
#endif // PRECHARGE_CONTROL
#if defined(CHARGER_SELECTED)
setup_charger();
#endif
#if defined(CAN_INVERTER_SELECTED) || defined(MODBUS_INVERTER_SELECTED) || defined(RS485_INVERTER_SELECTED)
setup_inverter();

View file

@ -8,6 +8,8 @@
#include "../datalayer/datalayer_extended.h" //For "More battery info" webpage
#include "../devboard/utils/events.h"
#include "../charger/CanCharger.h"
/* Do not change code below unless you are sure what you are doing */
static unsigned long previousMillis10 = 0; // will store last time a 10ms CAN Message was send
static unsigned long previousMillis100 = 0; // will store last time a 100ms CAN Message was send
@ -1199,13 +1201,14 @@ void transmit_can_battery(unsigned long currentMillis) {
break;
}
//Only send this message when NISSANLEAF_CHARGER is not defined (otherwise it will collide!)
#ifndef NISSANLEAF_CHARGER
//Only send this message when NISSANLEAF_CHARGER is not defined (otherwise it will collide!)
if (!charger || charger->type() != ChargerType::NissanLeaf) {
transmit_can_frame(&LEAF_1F2, can_config.battery);
#ifdef DOUBLE_BATTERY
transmit_can_frame(&LEAF_1F2, can_config.battery_double);
#endif // DOUBLE_BATTERY
#endif
}
mprun10r = (mprun10r + 1) % 20; // 0x1F2 patter repeats after 20 messages. 0-1..19-0

View file

@ -1,19 +1,9 @@
#include "../include.h"
#ifdef SELECTED_CHARGER_CLASS
static CanCharger* charger;
void map_can_frame_to_variable_charger(CAN_frame rx_frame) {
charger->map_can_frame_to_variable(rx_frame);
}
void transmit_can_charger(unsigned long currentMillis) {
charger->transmit_can(currentMillis);
}
CanCharger* charger = nullptr;
void setup_charger() {
#ifdef SELECTED_CHARGER_CLASS
charger = new SELECTED_CHARGER_CLASS();
}
#endif
}

View file

@ -2,16 +2,14 @@
#define CHARGERS_H
#include "../../USER_SETTINGS.h"
#ifdef CHEVYVOLT_CHARGER
#include "CHEVY-VOLT-CHARGER.h"
#endif
#ifdef NISSANLEAF_CHARGER
#include "NISSAN-LEAF-CHARGER.h"
#endif
void map_can_frame_to_variable_charger(CAN_frame rx_frame);
void transmit_can_charger(unsigned long currentMillis);
// Constructs the global charger object based on build-time selection of charger type.
// Safe to call even though no charger is selected.
void setup_charger();
// The selected charger or null if no charger in use.
extern CanCharger* charger;
#endif

View file

@ -1,7 +1,6 @@
#include "../include.h"
#ifdef CHEVYVOLT_CHARGER
#include "../datalayer/datalayer.h"
#include "CHEVY-VOLT-CHARGER.h"
#include "../datalayer/datalayer.h"
#include "../include.h"
/* This implements Chevy Volt / Ampera charger support (2011-2015 model years).
*
@ -157,4 +156,3 @@ void ChevyVoltCharger::transmit_can(unsigned long currentMillis) {
}
#endif
}
#endif

View file

@ -1,29 +1,43 @@
#ifndef CHEVYVOLT_CHARGER_H
#define CHEVYVOLT_CHARGER_H
#include <Arduino.h>
#include "../datalayer/datalayer.h"
#include "../include.h"
#include "CanCharger.h"
#define CHARGER_SELECTED
#ifdef CHEVYVOLT_CHARGER
#define SELECTED_CHARGER_CLASS ChevyVoltCharger
#endif
/* Charger hardware limits
class ChevyVoltCharger : public CanCharger {
public:
ChevyVoltCharger() : CanCharger(ChargerType::ChevyVolt) {}
const char* name() { return "Chevy Volt Gen1 Charger"; }
void map_can_frame_to_variable(CAN_frame rx_frame);
void transmit_can(unsigned long currentMillis);
float outputPowerDC() {
return static_cast<float>(datalayer.charger.charger_stat_HVcur * datalayer.charger.charger_stat_HVvol);
}
bool efficiencySupported() { return true; }
float efficiency() {
float chgPwrAC = static_cast<float>(datalayer.charger.charger_stat_ACcur * datalayer.charger.charger_stat_ACvol);
return outputPowerDC() / chgPwrAC * 100;
}
private:
/* Charger hardware limits
*
* Relative to runtime settings, expectations are:
* hw minimum <= setting minimum <= setting maximum <= hw max
*/
#define CHEVYVOLT_MAX_HVDC 420.0
#define CHEVYVOLT_MIN_HVDC 200.0
#define CHEVYVOLT_MAX_AMP 11.5
#define CHEVYVOLT_MAX_POWER 3300
const float CHEVYVOLT_MAX_HVDC = 420.0;
const float CHEVYVOLT_MIN_HVDC = 200.0;
const float CHEVYVOLT_MAX_AMP = 11.5;
const float CHEVYVOLT_MAX_POWER = 3300;
class ChevyVoltCharger : public CanCharger {
public:
void map_can_frame_to_variable(CAN_frame rx_frame);
void transmit_can(unsigned long currentMillis);
private:
/* CAN cycles and timers */
unsigned long previousMillis30ms = 0; // 30ms cycle for keepalive frames
unsigned long previousMillis200ms = 0; // 200ms cycle for commanding I/V targets

View file

@ -3,10 +3,46 @@
#include "src/devboard/utils/types.h"
class CanCharger {
#include "../datalayer/datalayer.h"
enum class ChargerType { NissanLeaf, ChevyVolt };
// Generic base class for all chargers
class Charger {
public:
ChargerType type() { return m_type; }
virtual const char* name() = 0;
virtual float outputPowerDC() = 0;
virtual float HVDC_output_voltage() { return datalayer.charger.charger_stat_HVvol; }
virtual float HVDC_output_current() { return datalayer.charger.charger_stat_HVcur; }
virtual float LVDC_output_voltage() { return datalayer.charger.charger_stat_LVvol; }
virtual float LVDC_output_current() { return datalayer.charger.charger_stat_LVcur; }
virtual float AC_input_voltage() { return datalayer.charger.charger_stat_ACvol; }
virtual float AC_input_current() { return datalayer.charger.charger_stat_ACcur; }
virtual bool efficiencySupported() { return false; }
virtual float efficiency() { return 0; }
protected:
Charger(ChargerType type) : m_type(type) {}
private:
ChargerType m_type;
};
// Base class for chargers on a CAN bus
class CanCharger : public Charger {
public:
virtual void map_can_frame_to_variable(CAN_frame rx_frame) = 0;
virtual void transmit_can(unsigned long currentMillis) = 0;
protected:
CanCharger(ChargerType type) : Charger(type) {}
};
#endif

View file

@ -1,7 +1,6 @@
#include "../include.h"
#ifdef NISSANLEAF_CHARGER
#include "../datalayer/datalayer.h"
#include "NISSAN-LEAF-CHARGER.h"
#include "../datalayer/datalayer.h"
#include "../include.h"
/* This implements Nissan LEAF PDM charger support. 2013-2024 Gen2/3 PDMs are supported
*
@ -184,4 +183,3 @@ void NissanLeafCharger::transmit_can(unsigned long currentMillis) {
#endif
}
}
#endif

View file

@ -4,14 +4,31 @@
#include "../include.h"
#include "CanCharger.h"
#define CHARGER_SELECTED
#ifdef NISSANLEAF_CHARGER
#define SELECTED_CHARGER_CLASS NissanLeafCharger
#endif
class NissanLeafCharger : public CanCharger {
public:
NissanLeafCharger() : CanCharger(ChargerType::NissanLeaf) {}
const char* name() { return "Nissan LEAF 2013-2024 PDM charger"; }
void map_can_frame_to_variable(CAN_frame rx_frame);
void transmit_can(unsigned long currentMillis);
float outputPowerDC() { return static_cast<float>(datalayer.charger.charger_stat_HVcur * 100); }
float HVDC_output_current() {
// P/U=I
if (datalayer.battery.status.voltage_dV > 0) {
return outputPowerDC() / (datalayer.battery.status.voltage_dV / 10);
}
return 0;
}
float HVDC_output_voltage() { return static_cast<float>(datalayer.battery.status.voltage_dV / 10); }
private:
/* CAN cycles and timers */
unsigned long previousMillis10ms = 0;

View file

@ -119,9 +119,9 @@ void transmit_can(unsigned long currentMillis) {
transmit_can_inverter(currentMillis);
#endif // CAN_INVERTER_SELECTED
#ifdef CHARGER_SELECTED
transmit_can_charger(currentMillis);
#endif // CHARGER_SELECTED
if (charger) {
charger->transmit_can(currentMillis);
}
#ifdef CAN_SHUNT_SELECTED
transmit_can_shunt(currentMillis);
@ -330,10 +330,8 @@ void map_can_frame_to_variable(CAN_frame* rx_frame, int interface) {
handle_incoming_can_frame_battery2(*rx_frame);
#endif
}
if (interface == can_config.charger) {
#ifdef CHARGER_SELECTED
map_can_frame_to_variable_charger(*rx_frame);
#endif
if (interface == can_config.charger && charger) {
charger->map_can_frame_to_variable(*rx_frame);
}
if (interface == can_config.shunt) {
#ifdef CAN_SHUNT_SELECTED

View file

@ -1,7 +1,9 @@
#ifndef _DATALAYER_H_
#define _DATALAYER_H_
#include "../include.h"
#include "../../USER_SETTINGS.h"
#include "../devboard/utils/types.h"
#include "../system_settings.h"
typedef struct {
/** uint32_t */

View file

@ -231,7 +231,7 @@ void update_machineryprotection() {
}
#endif //CAN_INVERTER_SELECTED
#ifdef CHARGER_SELECTED
if (charger) {
// Check if the charger is still sending CAN messages. If we go 60s without messages we raise a warning
if (!datalayer.charger.CAN_charger_still_alive) {
set_event(EVENT_CAN_CHARGER_MISSING, can_config.charger);
@ -239,7 +239,7 @@ void update_machineryprotection() {
datalayer.charger.CAN_charger_still_alive--;
clear_event(EVENT_CAN_CHARGER_MISSING);
}
#endif //CHARGER_SELECTED
}
#ifdef DOUBLE_BATTERY // Additional Double-Battery safeties are checked here
// Check if the Battery 2 BMS is still sending CAN messages. If we go 60s without messages we raise a warning

View file

@ -1,5 +1,6 @@
#include "settings_html.h"
#include <Arduino.h>
#include "../../charger/CHARGERS.h"
#include "../../datalayer/datalayer.h"
String settings_processor(const String& var) {
@ -143,8 +144,7 @@ String settings_processor(const String& var) {
content += "</div>";
#endif
#if defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
if (charger) {
// Start a new block with orange background color
content += "<div style='background-color: #FF6E00; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
@ -164,16 +164,16 @@ String settings_processor(const String& var) {
}
content += " <button onclick='editChargerAux12vEnabled()'>Edit</button></h4>";
content +=
"<h4 style='color: white;'>Charger Voltage Setpoint: " + String(datalayer.charger.charger_setpoint_HV_VDC, 1) +
content += "<h4 style='color: white;'>Charger Voltage Setpoint: " +
String(datalayer.charger.charger_setpoint_HV_VDC, 1) +
" V </span> <button onclick='editChargerSetpointVDC()'>Edit</button></h4>";
content +=
"<h4 style='color: white;'>Charger Current Setpoint: " + String(datalayer.charger.charger_setpoint_HV_IDC, 1) +
content += "<h4 style='color: white;'>Charger Current Setpoint: " +
String(datalayer.charger.charger_setpoint_HV_IDC, 1) +
" A </span> <button onclick='editChargerSetpointIDC()'>Edit</button></h4>";
// Close the block
content += "</div>";
#endif
}
content += "<script>"; // Note, this section is minified to improve performance
content += "function editComplete(){if(this.status==200){window.location.reload();}}";
@ -305,14 +305,15 @@ String settings_processor(const String& var) {
"between 0 and 1000');}}}";
#endif
#if defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
if (charger) {
content +=
"function editChargerHVDCEnabled(){var value=prompt('Enable or disable HV DC output. Enter 1 for enabled, 0 "
"for disabled');if(value!==null){if(value==0||value==1){var xhr=new "
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
"updateChargerHvEnabled?value='+value,true);xhr.send();}}else{alert('Invalid value. Please enter 1 or 0');}}";
content +=
"function editChargerAux12vEnabled(){var value=prompt('Enable or disable low voltage 12v auxiliary DC output. "
"function editChargerAux12vEnabled(){var value=prompt('Enable or disable low voltage 12v auxiliary DC "
"output. "
"Enter 1 for enabled, 0 for disabled');if(value!==null){if(value==0||value==1){var xhr=new "
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
"updateChargerAux12vEnabled?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter 1 or "
@ -322,23 +323,26 @@ String settings_processor(const String& var) {
"inverter and/or charger configuration parameters, but use sensible values like 200 to "
"420.');if(value!==null){if(value>=0&&value<=1000){var xhr=new "
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
"updateChargeSetpointV?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between "
"updateChargeSetpointV?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
"between "
"0 and 1000');}}}";
content +=
"function editChargerSetpointIDC(){var value=prompt('Set charging amperage. Input will be validated against "
"inverter and/or charger configuration parameters, but use sensible values like 6 to "
"48.');if(value!==null){if(value>=0&&value<=1000){var xhr=new "
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
"updateChargeSetpointA?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between "
"updateChargeSetpointA?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value "
"between "
"0 and 100');}}}";
content +=
"function editChargerSetpointEndI(){var value=prompt('Set amperage that terminates charge as being "
"sufficiently complete. Input will be validated against inverter and/or charger configuration parameters, but "
"sufficiently complete. Input will be validated against inverter and/or charger configuration parameters, "
"but "
"use sensible values like 1-5.');if(value!==null){if(value>=0&&value<=1000){var xhr=new "
"XMLHttpRequest();xhr.onload=editComplete;xhr.onerror=editError;xhr.open('GET','/"
"updateChargeEndA?value='+value,true);xhr.send();}else{alert('Invalid value. Please enter a value between 0 "
"and 100');}}}";
#endif
}
content += "</script>";
content += "<script>";

View file

@ -752,7 +752,7 @@ void init_webserver() {
});
#endif
#if defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
if (charger) {
// Route for editing ChargerTargetV
server.on("/updateChargeSetpointV", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
@ -839,7 +839,7 @@ void init_webserver() {
request->send(400, "text/plain", "Bad Request");
}
});
#endif // defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
}
// Send a GET request to <ESP_IP>/update
server.on("/debug", HTTP_GET, [](AsyncWebServerRequest* request) {
@ -1018,16 +1018,11 @@ String processor(const String& var) {
content += "</h4>";
#endif
#if defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
if (charger) {
content += "<h4 style='color: white;'>Charger protocol: ";
#ifdef CHEVYVOLT_CHARGER
content += "Chevy Volt Gen1 Charger";
#endif // CHEVYVOLT_CHARGER
#ifdef NISSANLEAF_CHARGER
content += "Nissan LEAF 2013-2024 PDM charger";
#endif // NISSANLEAF_CHARGER
content += charger->name();
content += "</h4>";
#endif // defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
}
// Close the block
content += "</div>";
@ -1428,7 +1423,7 @@ String processor(const String& var) {
content += "</div>";
#endif // DOUBLE_BATTERY
#if defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
if (charger) {
// Start a new block with orange background color
content += "<div style='background-color: #FF6E00; padding: 10px; margin-bottom: 10px;border-radius: 50px'>";
@ -1447,42 +1442,33 @@ String processor(const String& var) {
content += "<span style='color: red;'>&#10005;</span>";
}
content += "</h4>";
#ifdef CHEVYVOLT_CHARGER
float chgPwrDC = static_cast<float>(datalayer.charger.charger_stat_HVcur * datalayer.charger.charger_stat_HVvol);
float chgPwrAC = static_cast<float>(datalayer.charger.charger_stat_ACcur * datalayer.charger.charger_stat_ACvol);
float chgEff = chgPwrDC / chgPwrAC * 100;
float ACcur = datalayer.charger.charger_stat_ACcur;
float ACvol = datalayer.charger.charger_stat_ACvol;
float HVvol = datalayer.charger.charger_stat_HVvol;
float HVcur = datalayer.charger.charger_stat_HVcur;
float LVvol = datalayer.charger.charger_stat_LVvol;
float LVcur = datalayer.charger.charger_stat_LVcur;
auto chgPwrDC = charger->outputPowerDC();
auto chgEff = charger->efficiency();
content += formatPowerValue("Charger Output Power", chgPwrDC, "", 1);
if (charger->efficiencySupported()) {
content += "<h4 style='color: white;'>Charger Efficiency: " + String(chgEff) + "%</h4>";
}
float HVvol = charger->HVDC_output_voltage();
float HVcur = charger->HVDC_output_current();
float LVvol = charger->LVDC_output_voltage();
float LVcur = charger->LVDC_output_current();
content += "<h4 style='color: white;'>Charger HVDC Output V: " + String(HVvol, 2) + " V</h4>";
content += "<h4 style='color: white;'>Charger HVDC Output I: " + String(HVcur, 2) + " A</h4>";
content += "<h4 style='color: white;'>Charger LVDC Output I: " + String(LVcur, 2) + "</h4>";
content += "<h4 style='color: white;'>Charger LVDC Output V: " + String(LVvol, 2) + "</h4>";
float ACcur = charger->AC_input_current();
float ACvol = charger->AC_input_voltage();
content += "<h4 style='color: white;'>Charger AC Input V: " + String(ACvol, 2) + " VAC</h4>";
content += "<h4 style='color: white;'>Charger AC Input I: " + String(ACcur, 2) + " A</h4>";
#endif // CHEVYVOLT_CHARGER
#ifdef NISSANLEAF_CHARGER
float chgPwrDC = static_cast<float>(datalayer.charger.charger_stat_HVcur * 100);
datalayer.charger.charger_stat_HVcur = chgPwrDC / (datalayer.battery.status.voltage_dV / 10); // P/U=I
datalayer.charger.charger_stat_HVvol = static_cast<float>(datalayer.battery.status.voltage_dV / 10);
float ACvol = datalayer.charger.charger_stat_ACvol;
float HVvol = datalayer.charger.charger_stat_HVvol;
float HVcur = datalayer.charger.charger_stat_HVcur;
content += formatPowerValue("Charger Output Power", chgPwrDC, "", 1);
content += "<h4 style='color: white;'>Charger HVDC Output V: " + String(HVvol, 2) + " V</h4>";
content += "<h4 style='color: white;'>Charger HVDC Output I: " + String(HVcur, 2) + " A</h4>";
content += "<h4 style='color: white;'>Charger AC Input V: " + String(ACvol, 2) + " VAC</h4>";
#endif // NISSANLEAF_CHARGER
// Close the block
content += "</div>";
#endif // defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
}
if (emulator_pause_request_ON)
content += "<button onclick='PauseBattery(false)'>Resume charge/discharge</button> ";