Battery and inverter can bus can be selected in the UI

This commit is contained in:
Jaakko Haakana 2025-06-29 20:33:45 +03:00
parent c63ae6eb23
commit e3de4e546c
29 changed files with 224 additions and 65 deletions

View file

@ -72,10 +72,6 @@ Logging logging;
void setup() {
init_hal();
if (!led_init()) {
return;
}
init_serial();
// We print this after setting up serial, such that is also printed to serial with DEBUG_VIA_USB set.
@ -87,18 +83,18 @@ void setup() {
#ifdef WIFI
xTaskCreatePinnedToCore((TaskFunction_t)&connectivity_loop, "connectivity_loop", 4096, NULL, TASK_CONNECTIVITY_PRIO,
&connectivity_loop_task, WIFI_CORE);
&connectivity_loop_task, esp32hal->WIFICORE());
#endif
if (!led_init()) {
return;
}
#if defined(LOG_CAN_TO_SD) || defined(LOG_TO_SD)
xTaskCreatePinnedToCore((TaskFunction_t)&logging_loop, "logging_loop", 4096, NULL, TASK_CONNECTIVITY_PRIO,
&logging_loop_task, WIFI_CORE);
&logging_loop_task, esp32hal->WIFICORE());
#endif
if (!init_CAN()) {
return;
}
if (!init_contactors()) {
return;
}
@ -114,6 +110,12 @@ void setup() {
}
setup_battery();
setup_can_shunt();
if (!init_CAN()) {
return;
}
if (!init_rs485()) {
return;
}
@ -122,7 +124,6 @@ void setup() {
return;
}
setup_can_shunt();
// BOOT button at runtime is used as an input for various things
pinMode(0, INPUT_PULLUP);
@ -131,7 +132,7 @@ void setup() {
// Initialize Task Watchdog for subscribed tasks
esp_task_wdt_config_t wdt_config = {
.timeout_ms = INTERVAL_5_S, // If task hangs for longer than this, reboot
.idle_core_mask = (1 << esp32hal->CORE_FUNCTION_CORE()) | (1 << esp32hal->WIFI_CORE()), // Watch both cores
.idle_core_mask = (1 << esp32hal->CORE_FUNCTION_CORE()) | (1 << esp32hal->WIFICORE()), // Watch both cores
.trigger_panic = true // Enable panic reset on timeout
};
@ -141,7 +142,7 @@ void setup() {
init_mqtt();
xTaskCreatePinnedToCore((TaskFunction_t)&mqtt_loop, "mqtt_loop", 4096, NULL, TASK_MQTT_PRIO, &mqtt_loop_task,
esp32hal->WIFI_CORE());
esp32hal->WIFICORE());
#endif
xTaskCreatePinnedToCore((TaskFunction_t)&core_loop, "core_loop", 4096, NULL, TASK_CORE_PRIO, &main_loop_task,

View file

@ -5,7 +5,7 @@
#include "RS485Battery.h"
#if !defined(COMMON_IMAGE) && !defined(SELECTED_BATTERY_CLASS)
#error No battery selected! Choose one from the USER_SETTINGS.h file
#error No battery selected! Choose one from the USER_SETTINGS.h file or build COMMON_IMAGE.
#endif
Battery* battery = nullptr;
@ -21,7 +21,7 @@ std::vector<BatteryType> supported_battery_types() {
return types;
}
extern const char* name_for_chemistry(battery_chemistry_enum chem) {
const char* name_for_chemistry(battery_chemistry_enum chem) {
switch (chem) {
case battery_chemistry_enum::LFP:
return "LFP";
@ -34,7 +34,26 @@ extern const char* name_for_chemistry(battery_chemistry_enum chem) {
}
}
extern const char* name_for_battery_type(BatteryType type) {
const char* name_for_comm_interface(comm_interface comm) {
switch (comm) {
case comm_interface::Modbus:
return "Modbus";
case comm_interface::RS485:
return "RS485";
case comm_interface::CanNative:
return "Native CAN";
case comm_interface::CanFdNative:
return "Native CAN FD";
case comm_interface::CanAddonMcp2515:
return "CAN MCP 2515 add-on";
case comm_interface::CanFdAddonMcp2518:
return "CAN FD MCP 2518 add-on";
default:
return nullptr;
}
}
const char* name_for_battery_type(BatteryType type) {
switch (type) {
case BatteryType::None:
return "None";
@ -119,7 +138,7 @@ const battery_chemistry_enum battery_chemistry_default = battery_chemistry_enum:
const battery_chemistry_enum battery_chemistry_default = battery_chemistry_enum::NMC;
#endif
extern battery_chemistry_enum user_selected_battery_chemistry;
battery_chemistry_enum user_selected_battery_chemistry = battery_chemistry_default;
#ifdef COMMON_IMAGE
#ifdef SELECTED_BATTERY_CLASS

View file

@ -188,12 +188,12 @@ void BmwPhevBattery::wake_battery_via_canbus() {
// Followed by a Recessive interval of at least ~3µs (min) and at most ~10µs (max)
// Then a second dominant pulse of similar timing.
CAN_cfg.speed = CAN_SPEED_100KBPS; //Slow down canbus to achieve wakeup timings
ESP32Can.CANInit(); // ReInit native CAN module at new speed
slow_down_can();
transmit_can_frame(&BMW_PHEV_BUS_WAKEUP_REQUEST, can_config.battery);
transmit_can_frame(&BMW_PHEV_BUS_WAKEUP_REQUEST, can_config.battery);
CAN_cfg.speed = CAN_SPEED_500KBPS; //Resume fullspeed
ESP32Can.CANInit(); // ReInit native CAN module at new speed
resume_full_speed();
#ifdef DEBUG_LOG
logging.println("Sent magic wakeup packet to SME at 100kbps...");

View file

@ -2,6 +2,7 @@
#define BATTERY_H
#include <vector>
#include "src/devboard/utils/types.h"
#include "src/devboard/webserver/BatteryHtmlRenderer.h"
enum class BatteryType {
@ -48,6 +49,7 @@ enum class BatteryType {
extern std::vector<BatteryType> supported_battery_types();
extern const char* name_for_battery_type(BatteryType type);
extern const char* name_for_chemistry(battery_chemistry_enum chem);
extern const char* name_for_comm_interface(comm_interface comm);
extern BatteryType user_selected_battery_type;
extern bool user_selected_second_battery;

View file

@ -75,7 +75,7 @@ void ChademoBattery::process_vehicle_charging_session(CAN_frame rx_frame) {
vehicle_can_initialized = true;
vehicle_permission = digitalRead(CHADEMO_PIN_4);
vehicle_permission = digitalRead(pin4);
x102_chg_session.ControlProtocolNumberEV = rx_frame.data.u8[0];
@ -936,7 +936,7 @@ void ChademoBattery::handle_chademo_sequence() {
void ChademoBattery::setup(void) { // Performs one time setup at startup
if (!esp32hal->alloc_pins("CHADEMO", pin2, pin10, pin4, pin7, pin_lock)) {
if (!esp32hal->alloc_pins(Name, pin2, pin10, pin4, pin7, pin_lock)) {
return;
}

View file

@ -6,3 +6,11 @@ CanBattery::CanBattery() {
register_transmitter(this);
register_can_receiver(this, can_interface);
}
void CanBattery::slow_down_can() {
::slow_down_can(can_interface);
}
void CanBattery::resume_full_speed() {
::resume_full_speed(can_interface);
}

View file

@ -5,6 +5,7 @@
#include "src/communication/Transmitter.h"
#include "src/communication/can/CanReceiver.h"
#include "src/communication/can/comm_can.h"
#include "src/devboard/utils/types.h"
// Abstract base class for batteries using the CAN bus
@ -29,6 +30,9 @@ class CanBattery : public Battery, Transmitter, CanReceiver {
register_transmitter(this);
register_can_receiver(this, can_interface);
}
void slow_down_can();
void resume_full_speed();
};
#endif

View file

@ -3,7 +3,6 @@
#include "../datalayer/datalayer.h"
#include "../devboard/utils/events.h"
#include "../include.h"
#include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
const unsigned char crc8_table[256] =
{ // CRC8_SAE_J1850_ZER0 formula,0x1D Poly,initial value 0x3F,Final XOR value varies

View file

@ -2,11 +2,8 @@
#define KIA_E_GMP_BATTERY_H
#include <Arduino.h>
#include "../include.h"
#include "../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#include "CanBattery.h"
extern ACAN2517FD canfd;
#define ESTIMATE_SOC_FROM_CELLVOLTAGE
#ifdef KIA_E_GMP_BATTERY

View file

@ -1,6 +1,8 @@
#include "comm_can.h"
#include <algorithm>
#include <map>
#include "../../include.h"
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "../../lib/pierremolinaro-ACAN2517FD/ACAN2517FD.h"
#include "../../lib/pierremolinaro-acan2515/ACAN2515.h"
#include "src/devboard/sdcard/sdcard.h"
@ -295,7 +297,7 @@ void receive_frame_canfd_addon() { // This section checks if we have a complete
rx_frame.ID = MCP2518frame.id;
rx_frame.ext_ID = MCP2518frame.ext;
rx_frame.DLC = MCP2518frame.len;
memcpy(rx_frame.data.u8, MCP2518frame.data, MIN(rx_frame.DLC, 64));
memcpy(rx_frame.data.u8, MCP2518frame.data, std::min(rx_frame.DLC, (uint8_t)64));
//message incoming, pass it on to the handler
map_can_frame_to_variable(&rx_frame, CANFD_ADDON_MCP2518);
map_can_frame_to_variable(&rx_frame, CANFD_NATIVE);
@ -416,3 +418,17 @@ void restart_can() {
canfd->begin(*settings2517, [] { can2515->isr(); });
}
}
void slow_down_can(CAN_Interface interface) {
if (interface == CAN_Interface::CAN_NATIVE) {
CAN_cfg.speed = CAN_SPEED_100KBPS; //Slow down canbus to achieve wakeup timings
ESP32Can.CANInit(); // ReInit native CAN module at new speed
}
}
void resume_full_speed(CAN_Interface interface) {
if (interface == CAN_Interface::CAN_NATIVE) {
CAN_cfg.speed = CAN_SPEED_500KBPS; //Resume fullspeed
ESP32Can.CANInit(); // ReInit native CAN module at new speed
}
}

View file

@ -1,13 +1,7 @@
#ifndef _COMM_CAN_H_
#define _COMM_CAN_H_
#include "../../include.h"
#include "../../datalayer/datalayer.h"
#include "../../devboard/utils/events.h"
#include "../../devboard/utils/value_mapping.h"
#include "../../lib/ESP32Async-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#include "../../devboard/utils/types.h"
extern bool use_canfd_as_can;
@ -79,4 +73,7 @@ void stop_can();
// Restart CAN communication for all interfaces
void restart_can();
void slow_down_can(CAN_Interface interface);
void resume_full_speed(CAN_Interface interface);
#endif

View file

@ -21,7 +21,7 @@ bool init_equipment_stop_button();
*/
void monitor_equipment_stop_button();
enum class STOP_BUTTON_BEHAVIOR { NOT_CONNECTED = 0, LATCHING_SWITCH = 1, MOMENTARY_SWITCH = 2 };
enum class STOP_BUTTON_BEHAVIOR { NOT_CONNECTED = 0, LATCHING_SWITCH = 1, MOMENTARY_SWITCH = 2, Highest };
extern STOP_BUTTON_BEHAVIOR equipment_stop_behavior;

View file

@ -79,6 +79,28 @@ void init_stored_settings() {
user_selected_inverter_protocol = (InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None);
user_selected_charger_type = (ChargerType)settings.getUInt("CHGTYPE", (int)ChargerType::None);
auto readIf = [](const char* settingName) {
auto batt1If = (comm_interface)settings.getUInt(settingName, (int)comm_interface::CanNative);
switch (batt1If) {
case comm_interface::CanNative:
return CAN_Interface::CAN_NATIVE;
case comm_interface::CanFdNative:
return CAN_Interface::CANFD_NATIVE;
case comm_interface::CanAddonMcp2515:
return CAN_Interface::CAN_ADDON_MCP2515;
case comm_interface::CanFdAddonMcp2518:
return CAN_Interface::CANFD_ADDON_MCP2518;
}
return CAN_Interface::CAN_NATIVE;
};
can_config.battery = readIf("BATTCOMM");
can_config.battery_double = readIf("BATT2COMM");
can_config.inverter = readIf("INVCOMM");
can_config.charger = readIf("CHGCOMM");
can_config.shunt = readIf("SHUNTCOMM");
equipment_stop_behavior = (STOP_BUTTON_BEHAVIOR)settings.getUInt("EQSTOP", (int)STOP_BUTTON_BEHAVIOR::NOT_CONNECTED);
user_selected_second_battery = settings.getBool("DBLBTR", false);
contactor_control_enabled = settings.getBool("CNTCTRL", false);

View file

@ -7,7 +7,7 @@
#include "hw_lilygo.h"
#include "hw_stark.h"
extern Esp32Hal* esp32hal;
Esp32Hal* esp32hal = nullptr;
void init_hal() {
#if defined(HW_LILYGO)

View file

@ -3,6 +3,8 @@
#include <soc/gpio_num.h>
#include <chrono>
#include <unordered_map>
#include "../../../src/devboard/utils/events.h"
#include "../../../src/devboard/utils/types.h"
// Hardware Abstraction Layer base class.
@ -19,7 +21,7 @@ class Esp32Hal {
// Core assignment
virtual int CORE_FUNCTION_CORE() { return 1; }
virtual int MODBUS_CORE() { return 0; }
virtual int WIFI_CORE() { return 0; }
virtual int WIFICORE() { return 0; }
template <typename... Pins>
bool alloc_pins(const char* name, Pins... pins) {
@ -27,6 +29,7 @@ class Esp32Hal {
for (gpio_num_t pin : requested_pins) {
if (pin < 0) {
set_event(EVENT_GPIO_NOT_DEFINED, (int)pin);
// Event: {name} attempted to allocate pin that wasn't defined for the selected HW.
return false;
}
@ -35,6 +38,7 @@ class Esp32Hal {
if (it != allocated_pins.end()) {
// Event: GPIO conflict for pin {pin} between name and it->second.
//std::cerr << "Pin " << pin << " already allocated to \"" << it->second << "\".\n";
set_event(EVENT_GPIO_CONFLICT, (int)pin);
return false;
}
}
@ -136,12 +140,18 @@ class Esp32Hal {
virtual gpio_num_t WUP_PIN1() { return GPIO_NUM_NC; }
virtual gpio_num_t WUP_PIN2() { return GPIO_NUM_NC; }
// Returns the available comm interfaces on this HW
virtual std::vector<comm_interface> available_interfaces() = 0;
private:
std::unordered_map<gpio_num_t, std::string> allocated_pins;
};
extern Esp32Hal* esp32hal;
// Needed for AsyncTCPSock library.
#define WIFI_CORE (esp32hal->WIFICORE())
void init_hal();
#endif

View file

@ -72,6 +72,10 @@ class ThreeLBHal : public Esp32Hal {
// Battery wake up pins
virtual gpio_num_t WUP_PIN1() { return GPIO_NUM_25; }
virtual gpio_num_t WUP_PIN2() { return GPIO_NUM_32; }
std::vector<comm_interface> available_interfaces() {
return {comm_interface::Modbus, comm_interface::RS485, comm_interface::CanNative};
}
};
#endif

View file

@ -66,6 +66,14 @@ class DevKitHal : public Esp32Hal {
// Battery wake up pins
virtual gpio_num_t WUP_PIN1() { return GPIO_NUM_25; }
virtual gpio_num_t WUP_PIN2() { return GPIO_NUM_32; }
std::vector<comm_interface> available_interfaces() {
return {
comm_interface::Modbus,
comm_interface::RS485,
comm_interface::CanNative,
};
}
};
#endif // __HW_DEVKIT_H__

View file

@ -76,6 +76,11 @@ class LilyGoHal : public Esp32Hal {
// Battery wake up pins
virtual gpio_num_t WUP_PIN1() { return GPIO_NUM_25; }
virtual gpio_num_t WUP_PIN2() { return GPIO_NUM_32; }
std::vector<comm_interface> available_interfaces() {
return {comm_interface::Modbus, comm_interface::RS485, comm_interface::CanNative, comm_interface::CanAddonMcp2515,
comm_interface::CanFdAddonMcp2518};
}
};
#define HalClass LilyGoHal

View file

@ -69,6 +69,10 @@ class StarkHal : public Esp32Hal {
// Battery wake up pins
virtual gpio_num_t WUP_PIN1() { return GPIO_NUM_25; }
virtual gpio_num_t WUP_PIN2() { return GPIO_NUM_32; }
std::vector<comm_interface> available_interfaces() {
return {comm_interface::Modbus, comm_interface::RS485, comm_interface::CanNative, comm_interface::CanFdNative};
}
};
#endif // __HW_STARK_H__

View file

@ -1,4 +1,6 @@
#include "safety.h"
#include "../../datalayer/datalayer.h"
#include "../../include.h"
#include "../utils/events.h"
static uint16_t cell_deviation_mV = 0;

View file

@ -1,8 +1,6 @@
#ifndef SAFETY_H
#define SAFETY_H
#include <Arduino.h>
#include <string>
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
#define MAX_CAN_FAILURES 50

View file

@ -1,4 +1,5 @@
#include "sdcard.h"
#include "../../include.h"
#include "freertos/ringbuf.h"
File can_log_file;
@ -183,13 +184,11 @@ void init_logging_buffers() {
}
bool init_sdcard() {
auto miso_pin = esp32hal->SD_MISO_PIN();
auto mosi_pin = esp32hal->SD_MOSI_PIN();
auto miso_pin = esp32hal->SD_MISO_PIN();
auto sclk_pin = esp32hal->SD_SCLK_PIN();
if (!esp32hal->alloc_pins("SD Card", miso_pin, mosi_pin)) {
if (!esp32hal->alloc_pins("SD Card", miso_pin, mosi_pin, sclk_pin)) {
return false;
}
@ -201,7 +200,7 @@ bool init_sdcard() {
#ifdef DEBUG_LOG
logging.println("SD Card initialization failed!");
#endif // DEBUG_LOG
return;
return false;
}
clear_event(EVENT_SD_INIT_FAILED);

View file

@ -1,5 +1,6 @@
#include "events.h"
#include "../../datalayer/datalayer.h"
#include "../../include.h"
#include "../../../USER_SETTINGS.h"
@ -132,6 +133,9 @@ void init_events(void) {
events.entries[EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS].level = EVENT_LEVEL_INFO;
events.entries[EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_BATTERY_TEMP_DEVIATION_HIGH].level = EVENT_LEVEL_WARNING;
events.entries[EVENT_GPIO_CONFLICT].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_GPIO_NOT_DEFINED].level = EVENT_LEVEL_ERROR;
events.entries[EVENT_BATTERY_TEMP_DEVIATION_HIGH].level = EVENT_LEVEL_WARNING;
}
void set_event(EVENTS_ENUM_TYPE event, uint8_t data) {
@ -373,6 +377,10 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
case EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED:
return "Failed to syncronise with the NTP Server. BMS will reset every 24 hours from when the emulator was "
"powered on";
case EVENT_GPIO_CONFLICT:
return "There is a GPIO pin conflict between SW components.";
case EVENT_GPIO_NOT_DEFINED:
return "SW module requires GPIO that is not defined for this hardware.";
default:
return "";
}

View file

@ -1,8 +1,7 @@
#ifndef __EVENTS_H__
#define __EVENTS_H__
#ifndef UNIT_TEST
#include "../../include.h"
#endif
#include <stdint.h>
#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,
@ -107,6 +106,8 @@
XX(EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS) \
XX(EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED) \
XX(EVENT_BATTERY_TEMP_DEVIATION_HIGH) \
XX(EVENT_GPIO_NOT_DEFINED) \
XX(EVENT_GPIO_CONFLICT) \
XX(EVENT_NOF_EVENTS)
typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE;

View file

@ -9,7 +9,18 @@ using duration = std::chrono::duration<unsigned long, std::ratio<1, 1000>>;
enum bms_status_enum { STANDBY = 0, INACTIVE = 1, DARKSTART = 2, ACTIVE = 3, FAULT = 4, UPDATING = 5 };
enum real_bms_status_enum { BMS_DISCONNECTED = 0, BMS_STANDBY = 1, BMS_ACTIVE = 2, BMS_FAULT = 3 };
enum battery_chemistry_enum { NCA, NMC, LFP };
enum battery_chemistry_enum { NCA = 1, NMC = 2, LFP = 3, Highest };
enum class comm_interface {
Modbus = 1,
RS485 = 2,
CanNative = 3,
CanFdNative = 4,
CanAddonMcp2515 = 5,
CanFdAddonMcp2518 = 6,
Highest
};
enum led_color { GREEN, YELLOW, RED, BLUE };
enum led_mode_enum { CLASSIC, FLOW, HEARTBEAT };
enum PrechargeState {

View file

@ -27,7 +27,8 @@ std::vector<EnumType> enum_values() {
}
template <typename EnumType, typename Func>
std::vector<std::pair<String, EnumType>> enum_values_and_names(Func name_for_type) {
std::vector<std::pair<String, EnumType>> enum_values_and_names(Func name_for_type,
const EnumType* noneValue = nullptr) {
auto values = enum_values<EnumType>();
std::vector<std::pair<String, EnumType>> pairs;
@ -41,15 +42,31 @@ std::vector<std::pair<String, EnumType>> enum_values_and_names(Func name_for_typ
std::sort(pairs.begin(), pairs.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
pairs.insert(pairs.begin(), std::pair(name_for_type(EnumType::None), EnumType::None));
if (noneValue) {
pairs.insert(pairs.begin(), std::pair(name_for_type(*noneValue), *noneValue));
}
return pairs;
}
template <typename TEnum, typename Func>
String options_for_enum_with_none(TEnum selected, Func name_for_type, TEnum noneValue) {
String options;
TEnum none = noneValue;
auto values = enum_values_and_names<TEnum>(name_for_type, &none);
for (const auto& [name, type] : values) {
options +=
("<option value=\"" + String(static_cast<int>(type)) + "\"" + (selected == type ? " selected" : "") + ">");
options += name;
options += "</option>";
}
return options;
}
template <typename TEnum, typename Func>
String options_for_enum(TEnum selected, Func name_for_type) {
String options;
auto values = enum_values_and_names<TEnum>(name_for_type);
auto values = enum_values_and_names<TEnum>(name_for_type, nullptr);
for (const auto& [name, type] : values) {
options +=
("<option value=\"" + String(static_cast<int>(type)) + "\"" + (selected == type ? " selected" : "") + ">");
@ -106,7 +123,7 @@ String settings_processor(const String& var) {
"onclick='editPassword()'>Edit</button></h4>";
#ifdef COMMON_IMAGE
BatteryEmulatorSettingsStore settings;
BatteryEmulatorSettingsStore settings(true);
// It's important that we read/write settings directly to settings store instead of the run-time values
// since the run-time values may have direct effect on operation.
@ -118,29 +135,40 @@ String settings_processor(const String& var) {
"align-items: center;'>";
content += "<label>Battery: </label><select style='max-width: 250px;' name='battery'>";
content +=
options_for_enum((BatteryType)settings.getUInt("BATTTYPE", (int)BatteryType::None), name_for_battery_type);
content += options_for_enum_with_none((BatteryType)settings.getUInt("BATTTYPE", (int)BatteryType::None),
name_for_battery_type, BatteryType::None);
content += "</select>";
content += "<label>Battery chemistry: </label><select style='max-width: 250px;' name='battery'>";
content += "<label>Battery comm I/F: </label><select style='max-width: 250px;' name='BATTCOMM'>";
content += options_for_enum((comm_interface)settings.getUInt("BATTCOMM", (int)comm_interface::CanNative),
name_for_comm_interface);
content += "</select>";
content += "<label>Battery chemistry: </label><select style='max-width: 250px;' name='BATTCHEM'>";
content += options_for_enum((battery_chemistry_enum)settings.getUInt("BATTCHEM", (int)battery_chemistry_enum::NCA),
name_for_chemistry);
content += "</select>";
content += "<label>Inverter protocol: </label><select style='max-width: 250px;' name='inverter'>";
content += options_for_enum((InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None),
name_for_inverter_type);
content +=
options_for_enum_with_none((InverterProtocolType)settings.getUInt("INVTYPE", (int)InverterProtocolType::None),
name_for_inverter_type, InverterProtocolType::None);
content += "</select>";
content += "<label>Inverter comm I/F: </label><select style='max-width: 250px;' name='INVCOMM'>";
content += options_for_enum((comm_interface)settings.getUInt("INVCOMM", (int)comm_interface::CanNative),
name_for_comm_interface);
content += "</select>";
content += "<label>Charger: </label><select style='max-width: 250px;' name='charger'>";
content +=
options_for_enum((ChargerType)settings.getUInt("CHGTYPE", (int)ChargerType::None), name_for_charger_type);
content += options_for_enum_with_none((ChargerType)settings.getUInt("CHGTYPE", (int)ChargerType::None),
name_for_charger_type, ChargerType::None);
content += "</select>";
content += "<label>Equipment stop button: </label><select style='max-width: 250px;' name='EQSTOP'>";
content +=
options_for_enum((STOP_BUTTON_BEHAVIOR)settings.getUInt("EQSTOP", (int)STOP_BUTTON_BEHAVIOR::NOT_CONNECTED),
name_for_button_type);
content += options_for_enum_with_none(
(STOP_BUTTON_BEHAVIOR)settings.getUInt("EQSTOP", (int)STOP_BUTTON_BEHAVIOR::NOT_CONNECTED),
name_for_button_type, STOP_BUTTON_BEHAVIOR::NOT_CONNECTED);
content += "</select>";
// TODO: Generalize settings: define settings in one place and use the definitions to render

View file

@ -417,12 +417,30 @@ void init_webserver() {
} else if (p->name() == "battery") {
auto type = static_cast<BatteryType>(atoi(p->value().c_str()));
settings.saveUInt("BATTTYPE", (int)type);
} else if (p->name() == "BATTCHEM") {
auto type = static_cast<battery_chemistry_enum>(atoi(p->value().c_str()));
settings.saveUInt("BATTCHEM", (int)type);
} else if (p->name() == "charger") {
auto type = static_cast<ChargerType>(atoi(p->value().c_str()));
settings.saveUInt("CHGTYPE", (int)type);
} else if (p->name() == "EQSTOP") {
auto type = static_cast<STOP_BUTTON_BEHAVIOR>(atoi(p->value().c_str()));
settings.saveUInt("EQSTOP", (int)type);
} else if (p->name() == "BATTCOMM") {
auto type = static_cast<comm_interface>(atoi(p->value().c_str()));
settings.saveUInt("BATTCOMM", (int)type);
} else if (p->name() == "BATT2COMM") {
auto type = static_cast<comm_interface>(atoi(p->value().c_str()));
settings.saveUInt("BATT2COMM", (int)type);
} else if (p->name() == "INVCOMM") {
auto type = static_cast<comm_interface>(atoi(p->value().c_str()));
settings.saveUInt("INVCOMM", (int)type);
} else if (p->name() == "CHGCOMM") {
auto type = static_cast<comm_interface>(atoi(p->value().c_str()));
settings.saveUInt("CHGCOMM", (int)type);
} else if (p->name() == "SHUNTCOMM") {
auto type = static_cast<comm_interface>(atoi(p->value().c_str()));
settings.saveUInt("SHUNTCOMM", (int)type);
}
for (auto& boolSetting : boolSettings) {

View file

@ -19,7 +19,7 @@
/* - ERROR CHECKS BELOW, DON'T TOUCH - */
#if !defined(HW_LILYGO) || !defined(HW_STARK) || !defined(HW_3LB) || !defined(HW_DEVKIT)
#if !defined(HW_LILYGO) && !defined(HW_STARK) && !defined(HW_3LB) && !defined(HW_DEVKIT)
#error You must select a target hardware in the USER_SETTINGS.h file!
#endif

View file

@ -11,8 +11,6 @@
class SmaBydHInverter : public SmaInverterBase {
public:
SmaBydHInverter();
const char* name() override { return Name; }
void update_values();
void transmit_can(unsigned long currentMillis);