diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt index 73150270dc..6a644589c7 100644 --- a/modules/CMakeLists.txt +++ b/modules/CMakeLists.txt @@ -31,6 +31,7 @@ ev_add_module(OCPPExtensionExample) ev_add_module(DummyTokenValidator) ev_add_module(DummyTokenProvider) ev_add_module(DummyTokenProviderManual) +ev_add_module(PhyVersoBSP) add_subdirectory(examples) add_subdirectory(simulation) diff --git a/modules/EvseManager/EvseManager.cpp b/modules/EvseManager/EvseManager.cpp index 923ff2de31..19d578365a 100644 --- a/modules/EvseManager/EvseManager.cpp +++ b/modules/EvseManager/EvseManager.cpp @@ -60,7 +60,7 @@ void EvseManager::init() { } // Use SLAC MAC address for Autocharge if configured. - if (config.autocharge_use_slac_instead_of_hlc and slac_enabled) { + if (config.autocharge_use_slac_instead_of_hlc and slac_enabled and config.enable_autocharge) { r_slac[0]->subscribe_ev_mac_address([this](const std::string& token) { p_token_provider->publish_provided_token(create_autocharge_token(token, config.connector_id)); }); @@ -474,7 +474,9 @@ void EvseManager::ready() { r_hlc[0]->call_authorization_response(types::authorization::AuthorizationStatus::Accepted, types::authorization::CertificateStatus::NoCertificateAvailable); } else { - p_token_provider->publish_provided_token(autocharge_token); + if (config.enable_autocharge) { + p_token_provider->publish_provided_token(autocharge_token); + } Everest::scoped_lock_timeout lock(hlc_mutex, Everest::MutexDescription::EVSE_publish_provided_token); hlc_waiting_for_auth_eim = true; hlc_waiting_for_auth_pnc = false; diff --git a/modules/EvseManager/EvseManager.hpp b/modules/EvseManager/EvseManager.hpp index df31ecf35b..2ddafae195 100644 --- a/modules/EvseManager/EvseManager.hpp +++ b/modules/EvseManager/EvseManager.hpp @@ -79,6 +79,7 @@ struct Conf { bool hack_pause_imd_during_precharge; bool hack_allow_bpt_with_iso2; bool autocharge_use_slac_instead_of_hlc; + bool enable_autocharge; std::string logfile_suffix; double soft_over_current_tolerance_percent; double soft_over_current_measurement_noise_A; @@ -204,7 +205,6 @@ class EvseManager : public Everest::ModuleBase { powersupply_capabilities.max_import_current_A = caps.max_import_current_A; powersupply_capabilities.max_import_power_W = caps.max_import_power_W; } - // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1 protected: diff --git a/modules/EvseManager/manifest.yaml b/modules/EvseManager/manifest.yaml index 83427cd628..6ceffbe1b3 100644 --- a/modules/EvseManager/manifest.yaml +++ b/modules/EvseManager/manifest.yaml @@ -146,6 +146,11 @@ config: description: Use slac ev mac address for autocharge instead of EVCCID from HLC type: boolean default: false + enable_autocharge: + description: >- + Enables Autocharge (i.e. Mac address for authorization). Disabled by default as PnC should be used instead. + type: boolean + default: false logfile_suffix: description: Use the string given for the log folder name. Special string session_uuid will be replaced with session uuid. type: string diff --git a/modules/PhyVersoBSP/CMakeLists.txt b/modules/PhyVersoBSP/CMakeLists.txt new file mode 100644 index 0000000000..2817652328 --- /dev/null +++ b/modules/PhyVersoBSP/CMakeLists.txt @@ -0,0 +1,55 @@ +# +# AUTO GENERATED - MARKED REGIONS WILL BE KEPT +# template version 3 +# + +# module setup: +# - ${MODULE_NAME}: module name +ev_setup_cpp_module() + +# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1 +# insert your custom targets and additional config variables here +add_subdirectory(phyverso_mcu_comms) +add_subdirectory(phyverso_cli) +add_subdirectory(phyverso_config) + +target_include_directories(${MODULE_NAME} + PRIVATE + "common" + "phyverso_mcu_comms" + "phyverso_mcu_comms/nanopb" + "phyverso_mcu_comms/protobuf" + "phyverso_config" +) + +target_link_libraries(${MODULE_NAME} + PRIVATE + Pal::Sigslot + phyverso_mcu_comms + phyverso_config +) +# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1 + +target_sources(${MODULE_NAME} + PRIVATE + "connector_1/evse_board_supportImpl.cpp" + "connector_2/evse_board_supportImpl.cpp" + "rcd_1/ac_rcdImpl.cpp" + "rcd_2/ac_rcdImpl.cpp" + "connector_lock_1/connector_lockImpl.cpp" + "connector_lock_2/connector_lockImpl.cpp" +) + +# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1 +target_sources(${MODULE_NAME} + PRIVATE + "board_support_common.cpp" +) + +# install MCU configs +install( + DIRECTORY "." + DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/everest" + FILES_MATCHING PATTERN "*.json" +) +# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1 diff --git a/modules/PhyVersoBSP/PhyVersoBSP.cpp b/modules/PhyVersoBSP/PhyVersoBSP.cpp new file mode 100644 index 0000000000..019987897d --- /dev/null +++ b/modules/PhyVersoBSP/PhyVersoBSP.cpp @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "PhyVersoBSP.hpp" +#include + +namespace module { + +void PhyVersoBSP::init() { + // initialize serial driver + if (!serial.open_device(config.serial_port.c_str(), config.baud_rate)) { + EVLOG_AND_THROW(EVEXCEPTION(Everest::EverestConfigError, "Could not open serial port ", config.serial_port, + " with baud rate ", config.baud_rate)); + return; + } + + invoke_init(*p_connector_1); + invoke_init(*p_connector_2); + invoke_init(*p_rcd_1); + invoke_init(*p_rcd_2); + invoke_init(*p_connector_lock_1); + invoke_init(*p_connector_lock_2); + + std::filesystem::path mcu_config_file = config.mcu_config_file; + + if (mcu_config_file.is_relative()) { + // Add everest config folder prefix if relative path is given + mcu_config_file = info.paths.etc / mcu_config_file; + } + + if (!verso_config.open_file(mcu_config_file.string())) { + EVLOG_AND_THROW(EVEXCEPTION(Everest::EverestConfigError, "Could not open config file ", mcu_config_file)); + } + + serial.signal_config_request.connect([&]() { + serial.send_config(verso_config); + EVLOG_info << "Sent config packet to MCU"; + }); +} + +void PhyVersoBSP::ready() { + serial.run(); + + invoke_ready(*p_connector_1); + invoke_ready(*p_connector_2); + invoke_ready(*p_rcd_1); + invoke_ready(*p_rcd_2); + invoke_ready(*p_connector_lock_1); + invoke_ready(*p_connector_lock_2); +} + +} // namespace module diff --git a/modules/PhyVersoBSP/PhyVersoBSP.hpp b/modules/PhyVersoBSP/PhyVersoBSP.hpp new file mode 100644 index 0000000000..91aafe6a04 --- /dev/null +++ b/modules/PhyVersoBSP/PhyVersoBSP.hpp @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef PHY_VERSO_BSP_HPP +#define PHY_VERSO_BSP_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 2 +// + +#include "ld-ev.hpp" + +// headers for provided interface implementations +#include +#include +#include + +// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1 +// insert your custom include headers here +#include "phyverso_mcu_comms/evSerial.h" +// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1 + +namespace module { + +struct Conf { + std::string serial_port; + int baud_rate; + int reset_gpio; + std::string mcu_config_file; + int conn1_max_current_A_import; + int conn1_min_current_A_import; + int conn1_min_phase_count_import; + int conn1_max_phase_count_import; + int conn1_min_current_A_export; + int conn1_max_current_A_export; + int conn1_min_phase_count_export; + int conn1_max_phase_count_export; + bool conn1_has_socket; + bool conn1_dc; + int conn2_max_current_A_import; + int conn2_min_current_A_import; + int conn2_min_phase_count_import; + int conn2_max_phase_count_import; + int conn2_min_current_A_export; + int conn2_max_current_A_export; + int conn2_min_phase_count_export; + int conn2_max_phase_count_export; + bool conn2_has_socket; + bool conn2_dc; +}; + +class PhyVersoBSP : public Everest::ModuleBase { +public: + PhyVersoBSP() = delete; + PhyVersoBSP(const ModuleInfo& info, Everest::MqttProvider& mqtt_provider, Everest::TelemetryProvider& telemetry, + std::unique_ptr p_connector_1, + std::unique_ptr p_connector_2, std::unique_ptr p_rcd_1, + std::unique_ptr p_rcd_2, std::unique_ptr p_connector_lock_1, + std::unique_ptr p_connector_lock_2, Conf& config) : + ModuleBase(info), + mqtt(mqtt_provider), + telemetry(telemetry), + p_connector_1(std::move(p_connector_1)), + p_connector_2(std::move(p_connector_2)), + p_rcd_1(std::move(p_rcd_1)), + p_rcd_2(std::move(p_rcd_2)), + p_connector_lock_1(std::move(p_connector_lock_1)), + p_connector_lock_2(std::move(p_connector_lock_2)), + config(config){}; + + Everest::MqttProvider& mqtt; + Everest::TelemetryProvider& telemetry; + const std::unique_ptr p_connector_1; + const std::unique_ptr p_connector_2; + const std::unique_ptr p_rcd_1; + const std::unique_ptr p_rcd_2; + const std::unique_ptr p_connector_lock_1; + const std::unique_ptr p_connector_lock_2; + const Conf& config; + + // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1 + // insert your public definitions here + evSerial serial; + // ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1 + +protected: + // ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1 + // insert your protected definitions here + // ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1 + +private: + friend class LdEverest; + void init(); + void ready(); + + // ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1 + // insert your private definitions here + evConfig verso_config; + // ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1 +}; + +// ev@087e516b-124c-48df-94fb-109508c7cda9:v1 +// insert other definitions here +// ev@087e516b-124c-48df-94fb-109508c7cda9:v1 + +} // namespace module + +#endif // PHY_VERSO_BSP_HPP diff --git a/modules/PhyVersoBSP/board_support_common.cpp b/modules/PhyVersoBSP/board_support_common.cpp new file mode 100644 index 0000000000..305dec5d5b --- /dev/null +++ b/modules/PhyVersoBSP/board_support_common.cpp @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "board_support_common.hpp" + +namespace module { + +types::board_support_common::BspEvent to_bsp_event(CpState s) { + switch (s) { + case CpState_STATE_A: + return {types::board_support_common::Event::A}; + case CpState_STATE_B: + return {types::board_support_common::Event::B}; + case CpState_STATE_C: + return {types::board_support_common::Event::C}; + case CpState_STATE_D: + return {types::board_support_common::Event::D}; + case CpState_STATE_E: + return {types::board_support_common::Event::E}; + case CpState_STATE_F: + return {types::board_support_common::Event::F}; + // This should never happen + default: + return {types::board_support_common::Event::F}; + } +} + +types::board_support_common::BspEvent to_bsp_event(CoilState s) { + // TODO: implement coil type handling + if (s.coil_state) { + return {types::board_support_common::Event::PowerOn}; + } else { + return {types::board_support_common::Event::PowerOff}; + } +} +} // namespace module diff --git a/modules/PhyVersoBSP/board_support_common.hpp b/modules/PhyVersoBSP/board_support_common.hpp new file mode 100644 index 0000000000..d2b95164d1 --- /dev/null +++ b/modules/PhyVersoBSP/board_support_common.hpp @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef EVSE_BOARD_SUPPORT_COMMON_HPP +#define EVSE_BOARD_SUPPORT_COMMON_HPP + +#include "phyverso.pb.h" +#include + +namespace module { +types::board_support_common::BspEvent to_bsp_event(CpState s); +types::board_support_common::BspEvent to_bsp_event(CoilState s); +} // namespace module + +#endif // EVSE_BOARD_SUPPORT_COMMON_HPP diff --git a/modules/PhyVersoBSP/connector_1/evse_board_supportImpl.cpp b/modules/PhyVersoBSP/connector_1/evse_board_supportImpl.cpp new file mode 100644 index 0000000000..7a9fc37a2a --- /dev/null +++ b/modules/PhyVersoBSP/connector_1/evse_board_supportImpl.cpp @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "evse_board_supportImpl.hpp" + +namespace module { +namespace connector_1 { + +void evse_board_supportImpl::init() { + { + std::scoped_lock lock(caps_mutex); + + caps.min_current_A_import = mod->config.conn1_min_current_A_import; + caps.max_current_A_import = mod->config.conn1_max_current_A_import; + caps.min_phase_count_import = mod->config.conn1_min_phase_count_import; + caps.max_phase_count_import = mod->config.conn1_max_phase_count_import; + caps.supports_changing_phases_during_charging = false; + + caps.min_current_A_export = mod->config.conn1_min_current_A_export; + caps.max_current_A_export = mod->config.conn1_max_current_A_export; + caps.min_phase_count_export = mod->config.conn1_min_phase_count_export; + caps.max_phase_count_export = mod->config.conn1_max_phase_count_export; + + if (mod->config.conn1_has_socket) { + caps.connector_type = types::evse_board_support::Connector_type::IEC62196Type2Socket; + } else { + caps.connector_type = types::evse_board_support::Connector_type::IEC62196Type2Cable; + } + } + + mod->serial.signal_cp_state.connect([this](int connector, CpState s) { + if (connector == 1 && s != last_cp_state) { + publish_event(to_bsp_event(s)); + EVLOG_info << "[1] CP State changed: " << to_bsp_event(s); + last_cp_state = s; + } + }); + mod->serial.signal_set_coil_state_response.connect([this](int connector, CoilState s) { + if (connector == 1) { + EVLOG_info << "[1] Relais: " << (s.coil_state ? "ON" : "OFF"); + publish_event(to_bsp_event(s)); + } + }); + + mod->serial.signal_telemetry.connect([this](int connector, Telemetry t) { + if (connector == 1) { + EVLOG_info << "[1] CP Voltage: " << t.cp_voltage_hi << " " << t.cp_voltage_lo; + } + }); +} + +void evse_board_supportImpl::ready() { +} + +void evse_board_supportImpl::handle_setup(bool& three_phases, bool& has_ventilation, std::string& country_code) { + // your code for cmd setup goes here +} + +types::evse_board_support::HardwareCapabilities evse_board_supportImpl::handle_get_hw_capabilities() { + std::scoped_lock lock(caps_mutex); + return caps; +} + +void evse_board_supportImpl::handle_enable(bool& value) { + if (value) { + mod->serial.set_pwm(1, 10000); + } else { + mod->serial.set_pwm(1, 0); + } +} + +void evse_board_supportImpl::handle_pwm_on(double& value) { + if (value >= 0 && value <= 100.) { + mod->serial.set_pwm(1, value * 100); + } else { + EVLOG_warning << "Invalid pwm value " << value; + } +} + +void evse_board_supportImpl::handle_pwm_off() { + mod->serial.set_pwm(1, 10000); +} + +void evse_board_supportImpl::handle_pwm_F() { + mod->serial.set_pwm(1, 0); +} + +void evse_board_supportImpl::handle_allow_power_on(types::evse_board_support::PowerOnOff& value) { + if (mod->config.conn1_dc) { + mod->serial.set_coil_state_request(1, CoilType_COIL_DC1, value.allow_power_on); + // FIXME: implement in MCU with feedback + if (value.allow_power_on) { + publish_event({types::board_support_common::Event::PowerOn}); + } else { + publish_event({types::board_support_common::Event::PowerOff}); + } + } else { + mod->serial.set_coil_state_request(1, CoilType_COIL_AC, value.allow_power_on); + } +} + +void evse_board_supportImpl::handle_ac_switch_three_phases_while_charging(bool& value) { + // your code for cmd ac_switch_three_phases_while_charging goes here +} + +void evse_board_supportImpl::handle_evse_replug(int& value) { + // your code for cmd evse_replug goes here +} + +types::board_support_common::ProximityPilot evse_board_supportImpl::handle_ac_read_pp_ampacity() { + // IMPLEMENT ME + return {types::board_support_common::Ampacity::A_32}; +} + +void evse_board_supportImpl::handle_ac_set_overcurrent_limit_A(double& value) { + // your code for cmd ac_set_overcurrent_limit_A goes here +} + +} // namespace connector_1 +} // namespace module diff --git a/modules/PhyVersoBSP/connector_1/evse_board_supportImpl.hpp b/modules/PhyVersoBSP/connector_1/evse_board_supportImpl.hpp new file mode 100644 index 0000000000..84708659b4 --- /dev/null +++ b/modules/PhyVersoBSP/connector_1/evse_board_supportImpl.hpp @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef CONNECTOR_1_EVSE_BOARD_SUPPORT_IMPL_HPP +#define CONNECTOR_1_EVSE_BOARD_SUPPORT_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include + +#include "../PhyVersoBSP.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here +#include "board_support_common.hpp" +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace connector_1 { + +struct Conf {}; + +class evse_board_supportImpl : public evse_board_supportImplBase { +public: + evse_board_supportImpl() = delete; + evse_board_supportImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + evse_board_supportImplBase(ev, "connector_1"), mod(mod), config(config){}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // command handler functions (virtual) + virtual void handle_setup(bool& three_phases, bool& has_ventilation, std::string& country_code) override; + virtual types::evse_board_support::HardwareCapabilities handle_get_hw_capabilities() override; + virtual void handle_enable(bool& value) override; + virtual void handle_pwm_on(double& value) override; + virtual void handle_pwm_off() override; + virtual void handle_pwm_F() override; + virtual void handle_allow_power_on(types::evse_board_support::PowerOnOff& value) override; + virtual void handle_ac_switch_three_phases_while_charging(bool& value) override; + virtual void handle_evse_replug(int& value) override; + virtual types::board_support_common::ProximityPilot handle_ac_read_pp_ampacity() override; + virtual void handle_ac_set_overcurrent_limit_A(double& value) override; + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + types::evse_board_support::HardwareCapabilities caps; + std::mutex caps_mutex; + CpState last_cp_state; + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace connector_1 +} // namespace module + +#endif // CONNECTOR_1_EVSE_BOARD_SUPPORT_IMPL_HPP diff --git a/modules/PhyVersoBSP/connector_2/evse_board_supportImpl.cpp b/modules/PhyVersoBSP/connector_2/evse_board_supportImpl.cpp new file mode 100644 index 0000000000..8ed77bae90 --- /dev/null +++ b/modules/PhyVersoBSP/connector_2/evse_board_supportImpl.cpp @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "evse_board_supportImpl.hpp" + +namespace module { +namespace connector_2 { + +void evse_board_supportImpl::init() { + { + std::scoped_lock lock(caps_mutex); + + caps.min_current_A_import = mod->config.conn2_min_current_A_import; + caps.max_current_A_import = mod->config.conn2_max_current_A_import; + caps.min_phase_count_import = mod->config.conn2_min_phase_count_import; + caps.max_phase_count_import = mod->config.conn2_max_phase_count_import; + caps.supports_changing_phases_during_charging = false; + + caps.min_current_A_export = mod->config.conn2_min_current_A_export; + caps.max_current_A_export = mod->config.conn2_max_current_A_export; + caps.min_phase_count_export = mod->config.conn2_min_phase_count_export; + caps.max_phase_count_export = mod->config.conn2_max_phase_count_export; + + if (mod->config.conn2_has_socket) { + caps.connector_type = types::evse_board_support::Connector_type::IEC62196Type2Socket; + } else { + caps.connector_type = types::evse_board_support::Connector_type::IEC62196Type2Cable; + } + } + + mod->serial.signal_cp_state.connect([this](int connector, CpState s) { + if (connector == 2 && s != last_cp_state) { + publish_event(to_bsp_event(s)); + EVLOG_info << "[2] CP State changed: " << to_bsp_event(s); + last_cp_state = s; + } + }); + mod->serial.signal_set_coil_state_response.connect([this](int connector, CoilState s) { + if (connector == 2) { + EVLOG_info << "[2] Relais: " << (s.coil_state ? "ON" : "OFF"); + publish_event(to_bsp_event(s)); + } + }); + + mod->serial.signal_telemetry.connect([this](int connector, Telemetry t) { + if (connector == 2) { + EVLOG_info << "[2] CP Voltage: " << t.cp_voltage_hi << " " << t.cp_voltage_lo; + } + }); +} + +void evse_board_supportImpl::ready() { +} + +void evse_board_supportImpl::handle_setup(bool& three_phases, bool& has_ventilation, std::string& country_code) { + // your code for cmd setup goes here +} + +types::evse_board_support::HardwareCapabilities evse_board_supportImpl::handle_get_hw_capabilities() { + std::scoped_lock lock(caps_mutex); + return caps; +} + +void evse_board_supportImpl::handle_enable(bool& value) { + if (value) { + mod->serial.set_pwm(2, 10000); + } else { + mod->serial.set_pwm(2, 0); + } +} + +void evse_board_supportImpl::handle_pwm_on(double& value) { + if (value >= 0 && value <= 100.) { + mod->serial.set_pwm(2, value * 100); + } else { + EVLOG_warning << "Invalid pwm value " << value; + } +} + +void evse_board_supportImpl::handle_pwm_off() { + mod->serial.set_pwm(2, 10000); +} + +void evse_board_supportImpl::handle_pwm_F() { + mod->serial.set_pwm(2, 0); +} + +void evse_board_supportImpl::handle_allow_power_on(types::evse_board_support::PowerOnOff& value) { + if (mod->config.conn2_dc) { + mod->serial.set_coil_state_request(2, CoilType_COIL_DC1, value.allow_power_on); + // FIXME: implement in MCU with feedback + if (value.allow_power_on) { + publish_event({types::board_support_common::Event::PowerOn}); + } else { + publish_event({types::board_support_common::Event::PowerOff}); + } + } else { + mod->serial.set_coil_state_request(2, CoilType_COIL_AC, value.allow_power_on); + } +} + +void evse_board_supportImpl::handle_ac_switch_three_phases_while_charging(bool& value) { + // your code for cmd ac_switch_three_phases_while_charging goes here +} + +void evse_board_supportImpl::handle_evse_replug(int& value) { + // your code for cmd evse_replug goes here +} + +types::board_support_common::ProximityPilot evse_board_supportImpl::handle_ac_read_pp_ampacity() { + // IMPLEMENT ME + return {types::board_support_common::Ampacity::A_32}; +} + +void evse_board_supportImpl::handle_ac_set_overcurrent_limit_A(double& value) { + // your code for cmd ac_set_overcurrent_limit_A goes here +} + +} // namespace connector_2 +} // namespace module diff --git a/modules/PhyVersoBSP/connector_2/evse_board_supportImpl.hpp b/modules/PhyVersoBSP/connector_2/evse_board_supportImpl.hpp new file mode 100644 index 0000000000..c3af6df9ea --- /dev/null +++ b/modules/PhyVersoBSP/connector_2/evse_board_supportImpl.hpp @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef CONNECTOR_2_EVSE_BOARD_SUPPORT_IMPL_HPP +#define CONNECTOR_2_EVSE_BOARD_SUPPORT_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include + +#include "../PhyVersoBSP.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here +#include "board_support_common.hpp" +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace connector_2 { + +struct Conf {}; + +class evse_board_supportImpl : public evse_board_supportImplBase { +public: + evse_board_supportImpl() = delete; + evse_board_supportImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + evse_board_supportImplBase(ev, "connector_2"), mod(mod), config(config){}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // command handler functions (virtual) + virtual void handle_setup(bool& three_phases, bool& has_ventilation, std::string& country_code) override; + virtual types::evse_board_support::HardwareCapabilities handle_get_hw_capabilities() override; + virtual void handle_enable(bool& value) override; + virtual void handle_pwm_on(double& value) override; + virtual void handle_pwm_off() override; + virtual void handle_pwm_F() override; + virtual void handle_allow_power_on(types::evse_board_support::PowerOnOff& value) override; + virtual void handle_ac_switch_three_phases_while_charging(bool& value) override; + virtual void handle_evse_replug(int& value) override; + virtual types::board_support_common::ProximityPilot handle_ac_read_pp_ampacity() override; + virtual void handle_ac_set_overcurrent_limit_A(double& value) override; + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + types::evse_board_support::HardwareCapabilities caps; + std::mutex caps_mutex; + CpState last_cp_state; + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace connector_2 +} // namespace module + +#endif // CONNECTOR_2_EVSE_BOARD_SUPPORT_IMPL_HPP diff --git a/modules/PhyVersoBSP/connector_lock_1/connector_lockImpl.cpp b/modules/PhyVersoBSP/connector_lock_1/connector_lockImpl.cpp new file mode 100644 index 0000000000..d4408e43e3 --- /dev/null +++ b/modules/PhyVersoBSP/connector_lock_1/connector_lockImpl.cpp @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "connector_lockImpl.hpp" + +namespace module { +namespace connector_lock_1 { + +void connector_lockImpl::init() { +} + +void connector_lockImpl::ready() { +} + +void connector_lockImpl::handle_lock() { + // your code for cmd lock goes here +} + +void connector_lockImpl::handle_unlock() { + // your code for cmd unlock goes here +} + +} // namespace connector_lock_1 +} // namespace module diff --git a/modules/PhyVersoBSP/connector_lock_1/connector_lockImpl.hpp b/modules/PhyVersoBSP/connector_lock_1/connector_lockImpl.hpp new file mode 100644 index 0000000000..fb245cbbc5 --- /dev/null +++ b/modules/PhyVersoBSP/connector_lock_1/connector_lockImpl.hpp @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef CONNECTOR_LOCK_1_CONNECTOR_LOCK_IMPL_HPP +#define CONNECTOR_LOCK_1_CONNECTOR_LOCK_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include + +#include "../PhyVersoBSP.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace connector_lock_1 { + +struct Conf {}; + +class connector_lockImpl : public connector_lockImplBase { +public: + connector_lockImpl() = delete; + connector_lockImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + connector_lockImplBase(ev, "connector_lock_1"), mod(mod), config(config){}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // command handler functions (virtual) + virtual void handle_lock() override; + virtual void handle_unlock() override; + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + // insert your private definitions here + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace connector_lock_1 +} // namespace module + +#endif // CONNECTOR_LOCK_1_CONNECTOR_LOCK_IMPL_HPP diff --git a/modules/PhyVersoBSP/connector_lock_2/connector_lockImpl.cpp b/modules/PhyVersoBSP/connector_lock_2/connector_lockImpl.cpp new file mode 100644 index 0000000000..a7ad78ce1e --- /dev/null +++ b/modules/PhyVersoBSP/connector_lock_2/connector_lockImpl.cpp @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "connector_lockImpl.hpp" + +namespace module { +namespace connector_lock_2 { + +void connector_lockImpl::init() { +} + +void connector_lockImpl::ready() { +} + +void connector_lockImpl::handle_lock() { + // your code for cmd lock goes here +} + +void connector_lockImpl::handle_unlock() { + // your code for cmd unlock goes here +} + +} // namespace connector_lock_2 +} // namespace module diff --git a/modules/PhyVersoBSP/connector_lock_2/connector_lockImpl.hpp b/modules/PhyVersoBSP/connector_lock_2/connector_lockImpl.hpp new file mode 100644 index 0000000000..088e73ca10 --- /dev/null +++ b/modules/PhyVersoBSP/connector_lock_2/connector_lockImpl.hpp @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef CONNECTOR_LOCK_2_CONNECTOR_LOCK_IMPL_HPP +#define CONNECTOR_LOCK_2_CONNECTOR_LOCK_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include + +#include "../PhyVersoBSP.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace connector_lock_2 { + +struct Conf {}; + +class connector_lockImpl : public connector_lockImplBase { +public: + connector_lockImpl() = delete; + connector_lockImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + connector_lockImplBase(ev, "connector_lock_2"), mod(mod), config(config){}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // command handler functions (virtual) + virtual void handle_lock() override; + virtual void handle_unlock() override; + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + // insert your private definitions here + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace connector_lock_2 +} // namespace module + +#endif // CONNECTOR_LOCK_2_CONNECTOR_LOCK_IMPL_HPP diff --git a/modules/PhyVersoBSP/doc.rst b/modules/PhyVersoBSP/doc.rst new file mode 100644 index 0000000000..45ea5022fd --- /dev/null +++ b/modules/PhyVersoBSP/doc.rst @@ -0,0 +1,22 @@ +.. _everest_modules_handwritten_PhyVersoBSP: + +.. This file is a placeholder for an optional single file handwritten documentation for + the PhyVersoBSP module. + Please decide weather you want to use this single file, + or a set of files in the doc/ directory. + In the latter case, you can delete this file. + In the former case, you can delete the doc/ directory. + +.. This handwritten documentation is optional. In case + you do not want to write it, you can delete this file + and the doc/ directory. + +.. The documentation can be written in reStructuredText, + and will be converted to HTML and PDF by Sphinx. + +******************************************* +PhyVersoBSP +******************************************* + +:ref:`Link ` to the module's reference. +Driver module for Phytec PhyVerso EV Charging controller with Pionix MCU firmware diff --git a/modules/PhyVersoBSP/docs/index.rst b/modules/PhyVersoBSP/docs/index.rst new file mode 100644 index 0000000000..efd57d6b4a --- /dev/null +++ b/modules/PhyVersoBSP/docs/index.rst @@ -0,0 +1,23 @@ +.. _everest_modules_handwritten_PhyVersoBSP: + +.. This file is a placeholder for an optional multiple files handwritten documentation for + the PhyVersoBSP module. + Please decide weather you want to use tthe doc.rst file + or a set of files in the doc/ directory. + In the latter case, you can delete the doc.rst file. + In the former case, you can delete the doc/ directory. + +.. This handwritten documentation is optional. In case + you do not want to write it, you can delete this file + and the doc/ directory. + +.. The documentation can be written in reStructuredText, + and will be converted to HTML and PDF by Sphinx. + This index.rst file is the entry point for the module documentation. + +******************************************* +PhyVersoBSP +******************************************* + +:ref:`Link ` to the module's reference. +Driver module for Phytec PhyVerso EV Charging controller with Pionix MCU firmware diff --git a/modules/PhyVersoBSP/example_config_pionix.json b/modules/PhyVersoBSP/example_config_pionix.json new file mode 100644 index 0000000000..001e2242cd --- /dev/null +++ b/modules/PhyVersoBSP/example_config_pionix.json @@ -0,0 +1,3 @@ +{ + "motor_lock_type": 2 +} \ No newline at end of file diff --git a/modules/PhyVersoBSP/example_config_qwello.json b/modules/PhyVersoBSP/example_config_qwello.json new file mode 100644 index 0000000000..e95f73bbe4 --- /dev/null +++ b/modules/PhyVersoBSP/example_config_qwello.json @@ -0,0 +1,3 @@ +{ + "motor_lock_type": 1 +} \ No newline at end of file diff --git a/modules/PhyVersoBSP/manifest.yaml b/modules/PhyVersoBSP/manifest.yaml new file mode 100644 index 0000000000..8c09a23a38 --- /dev/null +++ b/modules/PhyVersoBSP/manifest.yaml @@ -0,0 +1,156 @@ +description: Driver module for Phytec PhyVerso EV Charging controller with Pionix MCU firmware +config: + serial_port: + description: Serial port the hardware is connected to + type: string + default: /dev/ttyUSB0 + baud_rate: + description: Serial baud rate to use when communicating with the hardware + type: integer + minimum: 9600 + maximum: 230400 + default: 115200 + reset_gpio: + description: Reset GPIO number to use to HW reset Yeti. If set <0 it is disabled. + type: integer + minimum: -1 + maximum: 1000 + default: -1 + mcu_config_file: + description: config file that should be sent to the MCU + type: string + default: example_config_pionix.json + conn1_max_current_A_import: + description: Maximum import current in amps + type: integer + minimum: 0 + default: 16 + conn1_min_current_A_import: + description: Minimum import current in amps + type: integer + minimum: 0 + default: 6 + conn1_min_phase_count_import: + description: Minimum phase count for import + type: integer + minimum: 1 + maximum: 3 + default: 3 + conn1_max_phase_count_import: + description: Maximum phase count for import + type: integer + minimum: 1 + maximum: 3 + default: 3 + conn1_min_current_A_export: + description: Minimum export current in amps + type: integer + minimum: 0 + maximum: 63 + default: 0 + conn1_max_current_A_export: + description: Maximum export current in amps + type: integer + minimum: 0 + maximum: 63 + default: 0 + conn1_min_phase_count_export: + description: Minimum phase count for export + type: integer + minimum: 1 + maximum: 3 + default: 3 + conn1_max_phase_count_export: + description: Maximum phase count for export + type: integer + minimum: 1 + maximum: 3 + default: 3 + conn1_has_socket: + description: Set to true if it has a socket, false if it has a permanently attached cable + type: boolean + default: false + conn1_dc: + description: Set to true if it is for DC, false if it is AC + type: boolean + default: false + conn2_max_current_A_import: + description: Maximum import current in amps + type: integer + minimum: 0 + default: 16 + conn2_min_current_A_import: + description: Minimum import current in amps + type: integer + minimum: 0 + default: 6 + conn2_min_phase_count_import: + description: Minimum phase count for import + type: integer + minimum: 1 + maximum: 3 + default: 3 + conn2_max_phase_count_import: + description: Maximum phase count for import + type: integer + minimum: 1 + maximum: 3 + default: 3 + conn2_min_current_A_export: + description: Minimum export current in amps + type: integer + minimum: 0 + maximum: 63 + default: 0 + conn2_max_current_A_export: + description: Maximum export current in amps + type: integer + minimum: 0 + maximum: 63 + default: 0 + conn2_min_phase_count_export: + description: Minimum phase count for export + type: integer + minimum: 1 + maximum: 3 + default: 3 + conn2_max_phase_count_export: + description: Maximum phase count for export + type: integer + minimum: 1 + maximum: 3 + default: 3 + conn2_has_socket: + description: Set to true if it has a socket, false if it has a permanently attached cable + type: boolean + default: false + conn2_dc: + description: Set to true if it is for DC, false if it is AC + type: boolean + default: false +provides: + connector_1: + interface: evse_board_support + description: provides the board support interface to low level control the proximity and control pilots, relais and motor lock + connector_2: + interface: evse_board_support + description: provides the board support interface to low level control the proximity and control pilots, relais and motor lock + rcd_1: + interface: ac_rcd + description: RCD interface of the onboard RCD + rcd_2: + interface: ac_rcd + description: RCD interface of the onboard RCD + connector_lock_1: + interface: connector_lock + description: Lock interface + connector_lock_2: + interface: connector_lock + description: Lock interface +enable_external_mqtt: true +enable_telemetry: true +metadata: + license: https://opensource.org/licenses/Apache-2.0 + authors: + - Cornelius Claussen + diff --git a/modules/PhyVersoBSP/phyverso_cli/CMakeLists.txt b/modules/PhyVersoBSP/phyverso_cli/CMakeLists.txt new file mode 100644 index 0000000000..8b46a3e53b --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_cli/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(phyverso_cli VERSION 0.1) +# specify the C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +find_package(Threads REQUIRED) + +# add the executable +add_executable(phyverso_cli main.cpp) +target_include_directories(phyverso_cli + PUBLIC + "${PROJECT_BINARY_DIR}" + "../phyverso_mcu_comms/protobuf" + "../phyverso_mcu_comms" + "../phyverso_config" +) +target_link_libraries(phyverso_cli + PRIVATE + Pal::Sigslot + Threads::Threads + phyverso_mcu_comms + everest::framework + everest::nanopb + phyverso_config +) + +install(TARGETS phyverso_cli + DESTINATION ${EVEREST_MOD_PHYVERSOBSP_DESTINATION}) diff --git a/modules/PhyVersoBSP/phyverso_cli/main.cpp b/modules/PhyVersoBSP/phyverso_cli/main.cpp new file mode 100644 index 0000000000..e5e865b40e --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_cli/main.cpp @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include +#include + +#include "evSerial.h" +#include + +#include "phyverso.pb.h" +#include + +std::atomic_bool sw_version_received = false; + +void help() { + printf("\nUsage: ./phyverso_cli /dev/ttyXXX /path/to/config.json\n\n"); +} + +int main(int argc, char* argv[]) { + int selected_connector = 1; + + printf("-- Phyverso CLI tool --\n"); + + printf("Use the following keys to send packets:\n"); + printf("A or a: set AC coil on or off\n"); + printf("D or d: set DC coil on or off\n"); + printf("L or l: motorlock lock or unlock\n"); + printf("R or r: hard or soft reset\n"); + printf("V: send keep alive/get version\n"); + printf("1: use connector 1\n"); + printf("2: use connector 2\n\n"); + + printf("0: PWM F (0%% DC, -12V)\n"); + printf("5: PWM 5%%\n"); + printf("6: PWM 10%%\n"); + printf("7: PWM 80%%\n"); + printf("8: PWM 97%%\n"); + printf("9: PWM X1 (100%%)\n"); + printf("9: PWM X1 (100%%)\n"); + + printf("U or u: Fan1 50%% or OFF (if your fan supports 0%% duty cycle)\n"); + printf("I or i: Fan2 ON or 20%%\n"); + + if (argc != 3) { + help(); + exit(0); + } + const char* device = argv[1]; + const char* config_path = argv[2]; + + evConfig verso_config; + if (!verso_config.open_file(config_path)) { + printf("Could not open config file \"%s\"\n", config_path); + return -1; + } + + evSerial p; + + if (!p.open_device(device, 115200)) { + printf("Cannot open device \"%s\"\n", device); + } else { + p.run(); + + p.signal_config_request.connect([&]() { + printf("Received config request\n"); + p.send_config(verso_config); + printf("Sent config packet\n"); + }); + + p.signal_keep_alive.connect([](KeepAlive s) { + printf(">> KeepAlive: phyverso MCU SW Version: %s, Hardware %i/rev %i, MCU Timestamp %i\n", + s.sw_version_string, s.hw_type, s.hw_revision, s.time_stamp); + sw_version_received = true; + }); + + p.signal_set_coil_state_response.connect([](int connector, CoilState s) { + if (s.coil_state) + printf(">> Connector %i, Coil %d: Relais CLOSED\n", connector, s.coil_type); + else + printf(">> Connector %i, Coil %d: Relais OPEN\n", connector, s.coil_type); + }); + + p.signal_telemetry.connect([](int connector, Telemetry t) { + printf(">> Connector %i: CP Voltage %i %i\n", connector, t.cp_voltage_hi, t.cp_voltage_lo); + }); + + p.signal_cp_state.connect([](int connector, CpState s) { + switch (s) { + case CpState_STATE_A: + printf(">> Connector %i: CP state A\n", connector); + break; + case CpState_STATE_B: + printf(">> Connector %i: CP state B\n", connector); + break; + case CpState_STATE_C: + printf(">> Connector %i: CP state C\n", connector); + break; + case CpState_STATE_D: + printf(">> Connector %i: CP state D\n", connector); + break; + case CpState_STATE_E: + printf(">> Connector %i: CP state E\n", connector); + break; + case CpState_STATE_F: + printf(">> Connector %i: CP state F\n", connector); + break; + } + }); + + p.signal_pp_state.connect([](int connector, PpState s) { + switch (s) { + case PpState_STATE_NC: + printf(">> Connector %i: PP state NC\n", connector); + break; + case PpState_STATE_13A: + printf(">> Connector %i: PP state 13A\n", connector); + break; + case PpState_STATE_20A: + printf(">> Connector %i: PP state 20A\n", connector); + break; + case PpState_STATE_32A: + printf(">> Connector %i: PP state 32A\n", connector); + break; + case PpState_STATE_70A: + printf(">> Connector %i: PP state 70A\n", connector); + break; + case PpState_STATE_FAULT: + printf(">> Connector %i: PP state FAULT\n", connector); + break; + } + }); + + p.signal_fan_state.connect([](FanState s) { + printf(">> Fan %i: EN=%s, Duty=%d RPM=%d\n", s.fan_id, (s.enabled ? "ON" : "OFF"), s.duty, s.rpm); + }); + + p.signal_lock_state.connect([](int connector, LockState s) { + switch (s) { + case LockState_UNDEFINED: + printf(">> Connector %i: Lock State UNDEFINED\n", connector); + break; + case LockState_LOCKED: + printf(">> Connector %i: Lock State Locked\n", connector); + break; + case LockState_UNLOCKED: + printf(">> Connector %i: Lock State Unlocked\n", connector); + break; + } + }); + + while (true) { + char c = getc(stdin); + switch (c) { + case 'A': + printf("Setting AC coil to ON\n"); + p.set_coil_state_request(selected_connector, CoilType_COIL_AC, true); + break; + case 'a': + printf("Setting AC coil to OFF\n"); + p.set_coil_state_request(selected_connector, CoilType_COIL_AC, false); + break; + case 'D': + printf("Setting DC coil to ON\n"); + p.set_coil_state_request(selected_connector, CoilType_COIL_DC1, true); + break; + case 'd': + printf("Setting DC coil to OFF\n"); + p.set_coil_state_request(selected_connector, CoilType_COIL_DC1, false); + break; + case 'L': + printf("Locking connector\n"); + p.lock(selected_connector, true); + break; + case 'l': + printf("Unlocking connector\n"); + p.lock(selected_connector, false); + break; + case 'r': + printf("Soft reset\n"); + p.reset(-1); + break; + case 'R': + printf("Hard reset\n"); + p.reset(1); + break; + case 'V': + printf("Sending keep alive\n"); + p.keep_alive(); + break; + case '1': + printf("Connector 1 selected.\n"); + selected_connector = 1; + break; + case '2': + printf("Connector 2 selected.\n"); + selected_connector = 2; + break; + case '0': + printf("Set 0%% PWM\n"); + p.set_pwm(selected_connector, 0); + break; + case '5': + printf("Set 5%% PWM\n"); + p.set_pwm(selected_connector, 500); + break; + case '6': + printf("Set 10%% PWM\n"); + p.set_pwm(selected_connector, 1000); + break; + case '7': + printf("Set 80%% PWM\n"); + p.set_pwm(selected_connector, 8000); + break; + case '8': + printf("Set 97%% PWM\n"); + p.set_pwm(selected_connector, 9700); + break; + case '9': + printf("Set 100%% PWM\n"); + p.set_pwm(selected_connector, 10000); + break; + case 'U': + printf("Set fan1 to 50%%\n"); + p.set_fan_state(0, true, 500); + break; + case 'u': + printf("Set fan1 to OFF\n"); + p.set_fan_state(0, false, 500); // example for setting fan off via enable param + // check if your fan supports full OFF on PWM 0% duty (also check PWM on oscilloscope) + // some PWM fans wont turn fully off without switching 12V supply + break; + case 'I': + printf("Set fan2 to ON\n"); + p.set_fan_state(1, true, 1000); + break; + case 'i': + printf("Set fan2 to 20%%\n"); + p.set_fan_state(1, true, 200); + break; + } + } + } + return 0; +} diff --git a/modules/PhyVersoBSP/phyverso_config/CMakeLists.txt b/modules/PhyVersoBSP/phyverso_config/CMakeLists.txt new file mode 100644 index 0000000000..cef1ef1790 --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_config/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(phyverso_config VERSION 0.1) +# specify the C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +#find_package(nlohmann_json 3.2.0 REQUIRED) + +# add the executable +add_library(phyverso_config STATIC) +target_sources(phyverso_config + PRIVATE + evConfig.cpp +) + +target_include_directories(phyverso_config + PUBLIC + "${PROJECT_BINARY_DIR}" + "../phyverso_mcu_comms/protobuf" +) + +target_link_libraries(phyverso_config + PRIVATE + everest::nanopb + nlohmann_json::nlohmann_json +) diff --git a/modules/PhyVersoBSP/phyverso_config/evConfig.cpp b/modules/PhyVersoBSP/phyverso_config/evConfig.cpp new file mode 100644 index 0000000000..17ff1cd2d3 --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_config/evConfig.cpp @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "evConfig.h" +#include "phyverso.pb.h" +#include +#include + +// for convenience +using json = nlohmann::json; + +evConfig::evConfig() { +} + +evConfig::~evConfig() { +} + +bool evConfig::open_file(std::string path) { + try { + std::ifstream f(path); + config_file = json::parse(f); + // check validity first + return check_validity(); + } catch (const std::exception& e) { + std::cerr << "error: " << e.what() << std::endl; + } catch (...) { + std::cerr << "Exception of unknown type!" << std::endl; + } + return false; +} + +// todo: needs to be refactored a lot +bool evConfig::check_validity() { + std::cout << config_file.dump() << "\n\n"; + + if (!config_file.contains("motor_lock_type")) { + std::cout << "Missing 'motor_lock_type' config parameter" << std::endl; + return false; + } + + /* will be used later when separate config for both charging ports is needed + if(!config_file.contains("motor_lock_2")) { + printf("Missing 'motor_lock_2' config parameter\n"); + return false; + } + */ + + return true; +} + +bool evConfig::read_hw_eeprom(ConfigHardwareRevision& hw_rev) { + // TODO: read eeprom on new phyVERSO hw revisions, + // for now return hardcoded value + hw_rev = ConfigHardwareRevision_HW_REV_A; + return true; +} + +// todo: needs to refactored a lot +void evConfig::fill_config_packet() { + config_packet.which_payload = EverestToMcu_config_response_tag; + config_packet.connector = 0; + read_hw_eeprom(config_packet.payload.config_response.hw_rev); + config_packet.payload.config_response.lock_1.type = config_file["motor_lock_type"]; + config_packet.payload.config_response.has_lock_1 = true; + + // TODO: implement handling of two different lock types on MCU, for now only send motor_lock_type + // and use config for both MCU charging ports +} + +EverestToMcu evConfig::get_config_packet() { + fill_config_packet(); + return config_packet; +} diff --git a/modules/PhyVersoBSP/phyverso_config/evConfig.h b/modules/PhyVersoBSP/phyverso_config/evConfig.h new file mode 100644 index 0000000000..60f36c57ae --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_config/evConfig.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef PHYVERSO_CONFIG_EV_CONFIG_HPP +#define PHYVERSO_CONFIG_EV_CONFIG_HPP + +#include "phyverso.pb.h" +#include +#include + +using json = nlohmann::json; + +class evConfig { +public: + evConfig(); + ~evConfig(); + + bool open_file(std::string path); + EverestToMcu get_config_packet(); + +private: + bool check_validity(); + bool read_hw_eeprom(ConfigHardwareRevision& hw_rev); + void fill_config_packet(); + + json config_file; + + EverestToMcu config_packet = EverestToMcu_init_default; +}; + +#endif // PHYVERSO_CONFIG_EV_CONFIG_HPP diff --git a/modules/PhyVersoBSP/phyverso_mcu_comms/CMakeLists.txt b/modules/PhyVersoBSP/phyverso_mcu_comms/CMakeLists.txt new file mode 100644 index 0000000000..9a3f688d3f --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_mcu_comms/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.10) + +# set the project name +project(phyverso_mcu_comms VERSION 0.1) +# specify the C++ standard +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +# add the executable +add_library(phyverso_mcu_comms STATIC) +target_sources(phyverso_mcu_comms + PRIVATE + evSerial.cpp + protobuf/phyverso.pb.c + bsl/bsl_gpio.cpp +) + +target_include_directories(phyverso_mcu_comms + PUBLIC + "${PROJECT_BINARY_DIR}" + protobuf + bsl + "../phyverso_config" +) + +target_link_libraries(phyverso_mcu_comms + PUBLIC + date::date-tz + everest::nanopb + PRIVATE + Pal::Sigslot + everest::framework + phyverso_config +) diff --git a/modules/PhyVersoBSP/phyverso_mcu_comms/bsl/bsl_gpio.cpp b/modules/PhyVersoBSP/phyverso_mcu_comms/bsl/bsl_gpio.cpp new file mode 100644 index 0000000000..a574a2b9ec --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_mcu_comms/bsl/bsl_gpio.cpp @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "bsl_gpio.h" +#include +#include +#include + +BSL_GPIO::BSL_GPIO(_gpio_def _bsl, _gpio_def _reset) : bsl_out(_bsl), reset_out(_reset) { +} + +bool BSL_GPIO::hard_reset(uint16_t ms_reset_time) { + bool status = set_pin(reset_out, true); + if (!status) { + printf("Could set reset active\n"); + return status; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(ms_reset_time)); + + status = set_pin(reset_out, false); + if (!status) { + printf("Could set reset inactive\n"); + return status; + } + + return status; +} + +bool BSL_GPIO::enter_bsl() { + bool status = set_pin(bsl_out, true); + if (!status) { + printf("Could not set BSL pin high\n"); + return status; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(ms_bsl_out_settle)); + status = hard_reset(); + if (!status) { + printf("Could not reset\n"); + return status; + } + std::this_thread::sleep_for(std::chrono::milliseconds(ms_bsl_out_settle)); + status = set_pin(bsl_out, false); + if (!status) { + printf("Could not set BSL pin low\n"); + return status; + } + + return status; +} + +bool BSL_GPIO::set_pin(_gpio_def gpio, bool level) { + char* cmd; + int size = asprintf(&cmd, "gpioset %d %d=%d", gpio.bank, gpio.pin, (level ? 1 : 0)); + if ((size == -1) || (!cmd)) { + return false; + } + // printf("%s\n", cmd); // debug + int status = system(cmd); + free(cmd); + + if (status == 0) { + return true; + } else { + return false; + } +} diff --git a/modules/PhyVersoBSP/phyverso_mcu_comms/bsl/bsl_gpio.h b/modules/PhyVersoBSP/phyverso_mcu_comms/bsl/bsl_gpio.h new file mode 100644 index 0000000000..3e0e14c8e9 --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_mcu_comms/bsl/bsl_gpio.h @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef PHYVERSO_MCU_COMMS_BSL_BSL_GPIO_H +#define PHYVERSO_MCU_COMMS_BSL_BSL_GPIO_H + +#include + +class BSL_GPIO { +public: + struct _gpio_def { + uint8_t bank; + uint8_t pin; + }; + + BSL_GPIO(_gpio_def _bsl = {.bank = 1, .pin = 12}, _gpio_def _reset = {.bank = 1, .pin = 23}); + bool hard_reset(uint16_t ms_reset_time = default_ms_reset_time); + bool enter_bsl(); + +private: + bool set_pin(_gpio_def gpio, bool level); + + static constexpr uint16_t default_ms_reset_time = 10; + static constexpr uint16_t ms_bsl_out_settle = 10; + + // should be changeable later by loading a conf file + _gpio_def bsl_out; + _gpio_def reset_out; +}; + +#endif // PHYVERSO_MCU_COMMS_BSL_BSL_GPIO_H diff --git a/modules/PhyVersoBSP/phyverso_mcu_comms/evSerial.cpp b/modules/PhyVersoBSP/phyverso_mcu_comms/evSerial.cpp new file mode 100644 index 0000000000..bdba546322 --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_mcu_comms/evSerial.cpp @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "evSerial.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +#include "phyverso.pb.h" + +#include "bsl_gpio.h" + +evSerial::evSerial() : fd(0), baud(0), reset_done_flag(false), forced_reset(false) { + cobs_decode_reset(); +} + +evSerial::~evSerial() { + if (fd) { + close(fd); + } +} + +bool evSerial::open_device(const char* device, int _baud) { + + fd = open(device, O_RDWR | O_NOCTTY | O_SYNC); + if (fd < 0) { + printf("Serial: error %d opening %s: %s\n", errno, device, strerror(errno)); + return false; + } // else printf ("Serial: opened %s as %i\n", device, fd); + cobs_decode_reset(); + + switch (_baud) { + case 9600: + baud = B9600; + break; + case 19200: + baud = B19200; + break; + case 38400: + baud = B38400; + break; + case 57600: + baud = B57600; + break; + case 115200: + baud = B115200; + break; + case 230400: + baud = B230400; + break; + default: + baud = 0; + return false; + } + + return set_serial_attributes(); +} + +bool evSerial::set_serial_attributes() { + struct termios tty; + if (tcgetattr(fd, &tty) != 0) { + printf("Serial: error %d from tcgetattr\n", errno); + return false; + } + + cfsetospeed(&tty, baud); + cfsetispeed(&tty, baud); + + tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars + // disable IGNBRK for mismatched speed tests; otherwise receive break + // as \000 chars + tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY); + tty.c_lflag = 0; // no signaling chars, no echo, + // no canonical processing + tty.c_oflag = 0; // no remapping, no delays + tty.c_cc[VMIN] = 0; // read blocks + tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout + + tty.c_cflag |= (CLOCAL | CREAD); // ignore modem controls, + // enable reading + tty.c_cflag &= ~(PARENB | PARODD); // shut off parity + tty.c_cflag &= ~CSTOPB; + tty.c_cflag &= ~CRTSCTS; + + if (tcsetattr(fd, TCSANOW, &tty) != 0) { + printf("Serial: error %d from tcsetattr\n", errno); + return false; + } + return true; +} + +void evSerial::cobs_decode_reset() { + code = 0xff; + block = 0; + decode = msg; +} + +uint32_t evSerial::crc32(uint8_t* buf, int len) { + int i, j; + uint32_t b, crc, msk; + + i = 0; + crc = 0xFFFFFFFF; + while (i < len) { + b = buf[i]; + crc = crc ^ b; + for (j = 7; j >= 0; j--) { + msk = -(crc & 1); + crc = (crc >> 1) ^ (0xEDB88320 & msk); + } + i = i + 1; + } + return crc; +} + +void evSerial::handle_packet(uint8_t* buf, int len) { + + if (crc32(buf, len)) { + printf("CRC mismatch\n"); + return; + } + + len -= 4; + + McuToEverest msg_in; + pb_istream_t istream = pb_istream_from_buffer(buf, len); + + if (pb_decode(&istream, McuToEverest_fields, &msg_in)) + switch (msg_in.which_payload) { + + case McuToEverest_keep_alive_tag: + signal_keep_alive(msg_in.payload.keep_alive); + break; + + case McuToEverest_cp_state_tag: + signal_cp_state(msg_in.connector, msg_in.payload.cp_state); + break; + + case McuToEverest_set_coil_state_response_tag: + signal_set_coil_state_response(msg_in.connector, msg_in.payload.set_coil_state_response); + break; + + case McuToEverest_error_flags_tag: + signal_error_flags(msg_in.connector, msg_in.payload.error_flags); + break; + + case McuToEverest_telemetry_tag: + signal_telemetry(msg_in.connector, msg_in.payload.telemetry); + break; + + case McuToEverest_reset_tag: + reset_done_flag = true; + if (!forced_reset) + signal_spurious_reset(msg_in.payload.reset); + break; + + case McuToEverest_pp_state_tag: + signal_pp_state(msg_in.connector, msg_in.payload.pp_state); + break; + + case McuToEverest_fan_state_tag: + signal_fan_state(msg_in.payload.fan_state); + break; + + case McuToEverest_lock_state_tag: + signal_lock_state(msg_in.connector, msg_in.payload.lock_state); + break; + + case McuToEverest_config_request_tag: + signal_config_request(); + break; + } +} + +void evSerial::cobs_decode(uint8_t* buf, int len) { + for (int i = 0; i < len; i++) + cobs_decode_byte(buf[i]); +} + +void evSerial::cobs_decode_byte(uint8_t byte) { + // check max length + if ((decode - msg == 2048 - 1) && byte != 0x00) { + printf("cobsDecode: Buffer overflow\n"); + cobs_decode_reset(); + } + + if (block) { + // we're currently decoding and should not get a 0 + if (byte == 0x00) { + // probably found some garbage -> reset + printf("cobsDecode: Garbage detected\n"); + cobs_decode_reset(); + return; + } + *decode++ = byte; + } else { + if (code != 0xff) { + *decode++ = 0; + } + block = code = byte; + if (code == 0x00) { + // we're finished, reset everything and commit + if (decode == msg) { + // we received nothing, just a 0x00 + printf("cobsDecode: Received nothing\n"); + } else { + // set back decode with one, as it gets post-incremented + handle_packet(msg, decode - 1 - msg); + } + cobs_decode_reset(); + return; // need to return here, because of block-- + } + } + block--; +} + +void evSerial::run() { + read_thread_handle = std::thread(&evSerial::read_thread, this); + timeout_detection_thread_handle = std::thread(&evSerial::timeout_detection_thread, this); +} + +void evSerial::timeout_detection_thread() { + while (true) { + sleep(1); + if (timeout_detection_thread_handle.shouldExit()) + break; + if (serial_timed_out()) + signal_connection_timeout(); + } +} + +void evSerial::read_thread() { + uint8_t buf[2048]; + int n; + + cobs_decode_reset(); + while (true) { + if (read_thread_handle.shouldExit()) + break; + n = read(fd, buf, sizeof buf); + // printf ("read %u bytes.\n", n); + cobs_decode(buf, n); + } +} + +bool evSerial::link_write(EverestToMcu* m) { + uint8_t tx_packet_buf[1024]; + uint8_t encode_buf[1500]; + pb_ostream_t ostream = pb_ostream_from_buffer(tx_packet_buf, sizeof(tx_packet_buf) - 4); + bool status = pb_encode(&ostream, EverestToMcu_fields, m); + + if (!status) { + // couldn't encode + return false; + } + + size_t tx_payload_len = ostream.bytes_written; + + // add crc32 (CRC-32/JAMCRC) + uint32_t crc = crc32(tx_packet_buf, tx_payload_len); + + for (int byte_pos = 0; byte_pos < 4; ++byte_pos) { + tx_packet_buf[tx_payload_len] = (uint8_t)crc & 0xFF; + crc = crc >> 8; + tx_payload_len++; + } + + size_t tx_encode_len = cobs_encode(tx_packet_buf, tx_payload_len, encode_buf); + write(fd, encode_buf, tx_encode_len); + return true; +} + +size_t evSerial::cobs_encode(const void* data, size_t length, uint8_t* buffer) { + uint8_t* encode = buffer; // Encoded byte pointer + uint8_t* codep = encode++; // Output code pointer + uint8_t code = 1; // Code value + + for (const uint8_t* byte = (const uint8_t*)data; length--; ++byte) { + if (*byte) // Byte not zero, write it + *encode++ = *byte, ++code; + + if (!*byte || code == 0xff) // Input is zero or block completed, restart + { + *codep = code, code = 1, codep = encode; + if (!*byte || length) + ++encode; + } + } + *codep = code; // Write final code value + + // add final 0 + *encode++ = 0x00; + + return encode - buffer; +} + +bool evSerial::serial_timed_out() { + auto now = date::utc_clock::now(); + auto time_since_last_keep_alive = + std::chrono::duration_cast(now - last_keep_alive_lo_timestamp).count(); + if (time_since_last_keep_alive >= 5000) + return true; + return false; +} + +void evSerial::set_pwm(int target_connector, uint32_t duty_cycle_e2) { + EverestToMcu msg_out = EverestToMcu_init_default; + msg_out.which_payload = EverestToMcu_pwm_duty_cycle_tag; + msg_out.payload.pwm_duty_cycle = duty_cycle_e2; + msg_out.connector = target_connector; + link_write(&msg_out); +} + +void evSerial::set_coil_state_request(int target_connector, CoilType type, bool power_on) { + EverestToMcu msg_out = EverestToMcu_init_default; + msg_out.which_payload = EverestToMcu_set_coil_state_request_tag; + msg_out.payload.set_coil_state_request.coil_type = type; + msg_out.payload.set_coil_state_request.coil_state = power_on; + msg_out.connector = target_connector; + link_write(&msg_out); +} + +void evSerial::lock(int target_connector, bool _lock) { + EverestToMcu msg_out = EverestToMcu_init_default; + msg_out.which_payload = EverestToMcu_connector_lock_tag; + msg_out.payload.connector_lock = _lock; + msg_out.connector = target_connector; + link_write(&msg_out); +} + +void evSerial::firmware_update() { + EverestToMcu msg_out = EverestToMcu_init_default; + msg_out.which_payload = EverestToMcu_firmware_update_tag; + msg_out.connector = 0; + link_write(&msg_out); +} + +bool evSerial::reset(const int reset_pin) { + + reset_done_flag = false; + forced_reset = true; + + if (reset_pin > 0) { + printf("Hard reset\n"); + auto bsl_gpio = BSL_GPIO(); + bsl_gpio.hard_reset(); + } else { + // Try to soft reset Yeti controller to be in a known state + EverestToMcu msg_out = EverestToMcu_init_default; + msg_out.which_payload = EverestToMcu_reset_tag; + msg_out.connector = 0; + link_write(&msg_out); + } + + bool success = false; + + // Wait for reset done message from uC + for (int i = 0; i < 20; i++) { + if (reset_done_flag) { + success = true; + break; + } + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + } + + // Reset flag to detect run time spurious resets of uC from now on + reset_done_flag = false; + forced_reset = false; + + // send some dummy packets to resync COBS etc. + keep_alive(); + keep_alive(); + keep_alive(); + + return success; +} + +void evSerial::keep_alive() { + EverestToMcu msg_out = EverestToMcu_init_default; + msg_out.which_payload = EverestToMcu_keep_alive_tag; + msg_out.payload.keep_alive.time_stamp = 0; + msg_out.payload.keep_alive.hw_type = 0; + msg_out.payload.keep_alive.hw_revision = 0; + strcpy(msg_out.payload.keep_alive.sw_version_string, "n/a"); + msg_out.connector = 0; + link_write(&msg_out); +} + +void evSerial::send_config(evConfig& config) { + EverestToMcu config_packet = config.get_config_packet(); + link_write(&config_packet); +} + +void evSerial::set_fan_state(uint8_t fan_id, bool enabled, uint32_t duty) { + EverestToMcu msg_out = EverestToMcu_init_default; + msg_out.which_payload = EverestToMcu_set_fan_state_tag; + msg_out.payload.set_fan_state.fan_id = fan_id; + msg_out.payload.set_fan_state.enabled = enabled; + msg_out.payload.set_fan_state.duty = duty; + msg_out.connector = 0; + link_write(&msg_out); +} diff --git a/modules/PhyVersoBSP/phyverso_mcu_comms/evSerial.h b/modules/PhyVersoBSP/phyverso_mcu_comms/evSerial.h new file mode 100644 index 0000000000..c8e46404de --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_mcu_comms/evSerial.h @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef PHYVERSO_MCU_COMMS_EV_SERIAL_H +#define PHYVERSO_MCU_COMMS_EV_SERIAL_H + +#include "evConfig.h" +#include "phyverso.pb.h" +#include +#include +#include +#include +#include +#include +#include +#include + +class evSerial { + +public: + evSerial(); + ~evSerial(); + + bool open_device(const char* device, int baud); + + void read_thread(); + void run(); + + bool reset(const int reset_pin); + void firmware_update(); + void keep_alive(); + + void set_pwm(int target_connector, uint32_t duty_cycle_e2); + void set_coil_state_request(int target_connector, CoilType type, bool power_on); + void lock(int target_connector, bool _lock); + void unlock(int target_connector); + void set_fan_state(uint8_t fan_id, bool enabled, uint32_t duty); + + void send_config(evConfig& config); + + sigslot::signal signal_keep_alive; + sigslot::signal signal_cp_state; + sigslot::signal signal_set_coil_state_response; + sigslot::signal signal_error_flags; + sigslot::signal signal_telemetry; + sigslot::signal signal_spurious_reset; + sigslot::signal<> signal_connection_timeout; + sigslot::signal signal_pp_state; + sigslot::signal signal_fan_state; + sigslot::signal signal_lock_state; + sigslot::signal<> signal_config_request; + +private: + // Serial interface + bool set_serial_attributes(); + int fd; + int baud; + + // COBS de-/encoder + void cobs_decode_reset(); + void handle_packet(uint8_t* buf, int len); + void cobs_decode(uint8_t* buf, int len); + void cobs_decode_byte(uint8_t byte); + size_t cobs_encode(const void* data, size_t length, uint8_t* buffer); + uint8_t msg[2048]; + uint8_t code; + uint8_t block; + uint8_t* decode; + uint32_t crc32(uint8_t* buf, int len); + + // Read thread for serial port + Everest::Thread read_thread_handle; + Everest::Thread timeout_detection_thread_handle; + + bool link_write(EverestToMcu* m); + std::atomic_bool reset_done_flag; + std::atomic_bool forced_reset; + + bool serial_timed_out(); + void timeout_detection_thread(); + std::chrono::time_point last_keep_alive_lo_timestamp; +}; + +#endif // PHYVERSO_MCU_COMMS_EV_SERIAL_H diff --git a/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/generate_nanopb.sh b/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/generate_nanopb.sh new file mode 100755 index 0000000000..8c8c0a1b8b --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/generate_nanopb.sh @@ -0,0 +1,2 @@ +#!/bin/sh +nanopb_generator -L "#include " -I . -D . phyverso.proto diff --git a/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/phyverso.options b/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/phyverso.options new file mode 100644 index 0000000000..886456c89f --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/phyverso.options @@ -0,0 +1,3 @@ +KeepAlive.sw_version_string max_length:50 +FanState.fan_id int_size:IS_8 +FanState.rpm int_size:IS_16 \ No newline at end of file diff --git a/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/phyverso.pb.c b/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/phyverso.pb.c new file mode 100644 index 0000000000..2478da6648 --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/phyverso.pb.c @@ -0,0 +1,46 @@ +/* Automatically generated nanopb constant definitions */ +/* Generated by nanopb-0.4.8 */ + +#include "phyverso.pb.h" +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +PB_BIND(EverestToMcu, EverestToMcu, AUTO) + + +PB_BIND(McuToEverest, McuToEverest, AUTO) + + +PB_BIND(ErrorFlags, ErrorFlags, AUTO) + + +PB_BIND(KeepAlive, KeepAlive, AUTO) + + +PB_BIND(Telemetry, Telemetry, AUTO) + + +PB_BIND(FanState, FanState, AUTO) + + +PB_BIND(CoilState, CoilState, AUTO) + + +PB_BIND(BootConfigRequest, BootConfigRequest, AUTO) + + +PB_BIND(BootConfigResponse, BootConfigResponse, AUTO) + + +PB_BIND(ConfigMotorLockType, ConfigMotorLockType, AUTO) + + + + + + + + + + diff --git a/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/phyverso.pb.h b/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/phyverso.pb.h new file mode 100644 index 0000000000..aded3ed743 --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/phyverso.pb.h @@ -0,0 +1,398 @@ +/* Automatically generated nanopb header */ +/* Generated by nanopb-0.4.8 */ + +#ifndef PB_PHYVERSO_PB_H_INCLUDED +#define PB_PHYVERSO_PB_H_INCLUDED +#include + +#if PB_PROTO_HEADER_VERSION != 40 +#error Regenerate this file with the current version of nanopb generator. +#endif + +/* Enum definitions */ +typedef enum _CpState { + CpState_STATE_A = 0, + CpState_STATE_B = 1, + CpState_STATE_C = 2, + CpState_STATE_D = 3, + CpState_STATE_E = 4, + CpState_STATE_F = 5 +} CpState; + +typedef enum _ResetReason { + ResetReason_USER = 0, + ResetReason_WATCHDOG = 1 +} ResetReason; + +typedef enum _PpState { + PpState_STATE_NC = 0, + PpState_STATE_13A = 1, + PpState_STATE_20A = 2, + PpState_STATE_32A = 3, + PpState_STATE_70A = 4, + PpState_STATE_FAULT = 5 +} PpState; + +typedef enum _LockState { + LockState_UNDEFINED = 0, + LockState_UNLOCKED = 1, + LockState_LOCKED = 2 +} LockState; + +typedef enum _CoilType { + CoilType_COIL_UNKNOWN = 0, + CoilType_COIL_AC = 1, + CoilType_COIL_DC1 = 2 /* add precharge and discharge coils here later */ +} CoilType; + +typedef enum _ConfigHardwareRevision { + ConfigHardwareRevision_HW_REV_UNKNOWN = 0, + ConfigHardwareRevision_HW_REV_A = 1, + ConfigHardwareRevision_HW_REV_B = 2 +} ConfigHardwareRevision; + +typedef enum _MotorLockType { + MotorLockType_MOTOR_LOCK_UNKNOWN = 0, + MotorLockType_MOTOR_LOCK_QWELLO = 1, + MotorLockType_MOTOR_LOCK_DEBUG_VALEO_HVAC = 2 +} MotorLockType; + +/* Struct definitions */ +typedef struct _ErrorFlags { + bool diode_fault; + bool rcd_selftest_failed; + bool rcd_triggered; + bool ventilation_not_available; + bool connector_lock_failed; + bool cp_signal_fault; +} ErrorFlags; + +typedef struct _KeepAlive { + uint32_t time_stamp; + uint32_t hw_type; + uint32_t hw_revision; + char sw_version_string[51]; +} KeepAlive; + +typedef struct _Telemetry { + uint32_t cp_voltage_hi; + uint32_t cp_voltage_lo; +} Telemetry; + +typedef struct _FanState { + uint8_t fan_id; + bool enabled; + uint32_t duty; /* in 0.1%, 1000 = 100% */ + uint16_t rpm; +} FanState; + +typedef struct _CoilState { + CoilType coil_type; + bool coil_state; /* true -> on; false -> off */ +} CoilState; + +typedef struct _BootConfigRequest { /* TODO */ + char dummy_field; +} BootConfigRequest; + +/* This container message is send from MCU to EVerest and may contain any allowed message in that direction. */ +typedef struct _McuToEverest { + pb_size_t which_payload; + union { + KeepAlive keep_alive; + ResetReason reset; + CpState cp_state; + CoilState set_coil_state_response; + ErrorFlags error_flags; + Telemetry telemetry; + PpState pp_state; + FanState fan_state; + LockState lock_state; + BootConfigRequest config_request; + } payload; + int32_t connector; /* 0: None, 1: Connector 1, 2: Connector 2 */ +} McuToEverest; + +typedef struct _ConfigMotorLockType { + MotorLockType type; +} ConfigMotorLockType; + +typedef struct _BootConfigResponse { + ConfigHardwareRevision hw_rev; + bool has_lock_1; + ConfigMotorLockType lock_1; + bool has_lock_2; + ConfigMotorLockType lock_2; +} BootConfigResponse; + +/* This container message is send from EVerest to MCU and may contain any allowed message in that direction. */ +typedef struct _EverestToMcu { + pb_size_t which_payload; + union { + KeepAlive keep_alive; + bool firmware_update; + bool connector_lock; /* false: unlock, true: lock */ + uint32_t pwm_duty_cycle; /* in 0.01 %, 0 = State F, 10000 = X1 */ + CoilState set_coil_state_request; + bool reset; + BootConfigResponse config_response; + FanState set_fan_state; + } payload; + int32_t connector; /* 0: None, 1: Connector 1, 2: Connector 2 */ +} EverestToMcu; + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Helper constants for enums */ +#define _CpState_MIN CpState_STATE_A +#define _CpState_MAX CpState_STATE_F +#define _CpState_ARRAYSIZE ((CpState)(CpState_STATE_F+1)) + +#define _ResetReason_MIN ResetReason_USER +#define _ResetReason_MAX ResetReason_WATCHDOG +#define _ResetReason_ARRAYSIZE ((ResetReason)(ResetReason_WATCHDOG+1)) + +#define _PpState_MIN PpState_STATE_NC +#define _PpState_MAX PpState_STATE_FAULT +#define _PpState_ARRAYSIZE ((PpState)(PpState_STATE_FAULT+1)) + +#define _LockState_MIN LockState_UNDEFINED +#define _LockState_MAX LockState_LOCKED +#define _LockState_ARRAYSIZE ((LockState)(LockState_LOCKED+1)) + +#define _CoilType_MIN CoilType_COIL_UNKNOWN +#define _CoilType_MAX CoilType_COIL_DC1 +#define _CoilType_ARRAYSIZE ((CoilType)(CoilType_COIL_DC1+1)) + +#define _ConfigHardwareRevision_MIN ConfigHardwareRevision_HW_REV_UNKNOWN +#define _ConfigHardwareRevision_MAX ConfigHardwareRevision_HW_REV_B +#define _ConfigHardwareRevision_ARRAYSIZE ((ConfigHardwareRevision)(ConfigHardwareRevision_HW_REV_B+1)) + +#define _MotorLockType_MIN MotorLockType_MOTOR_LOCK_UNKNOWN +#define _MotorLockType_MAX MotorLockType_MOTOR_LOCK_DEBUG_VALEO_HVAC +#define _MotorLockType_ARRAYSIZE ((MotorLockType)(MotorLockType_MOTOR_LOCK_DEBUG_VALEO_HVAC+1)) + + +#define McuToEverest_payload_reset_ENUMTYPE ResetReason +#define McuToEverest_payload_cp_state_ENUMTYPE CpState +#define McuToEverest_payload_pp_state_ENUMTYPE PpState +#define McuToEverest_payload_lock_state_ENUMTYPE LockState + + + + + +#define CoilState_coil_type_ENUMTYPE CoilType + + +#define BootConfigResponse_hw_rev_ENUMTYPE ConfigHardwareRevision + +#define ConfigMotorLockType_type_ENUMTYPE MotorLockType + + +/* Initializer values for message structs */ +#define EverestToMcu_init_default {0, {KeepAlive_init_default}, 0} +#define McuToEverest_init_default {0, {KeepAlive_init_default}, 0} +#define ErrorFlags_init_default {0, 0, 0, 0, 0, 0} +#define KeepAlive_init_default {0, 0, 0, ""} +#define Telemetry_init_default {0, 0} +#define FanState_init_default {0, 0, 0, 0} +#define CoilState_init_default {_CoilType_MIN, 0} +#define BootConfigRequest_init_default {0} +#define BootConfigResponse_init_default {_ConfigHardwareRevision_MIN, false, ConfigMotorLockType_init_default, false, ConfigMotorLockType_init_default} +#define ConfigMotorLockType_init_default {_MotorLockType_MIN} +#define EverestToMcu_init_zero {0, {KeepAlive_init_zero}, 0} +#define McuToEverest_init_zero {0, {KeepAlive_init_zero}, 0} +#define ErrorFlags_init_zero {0, 0, 0, 0, 0, 0} +#define KeepAlive_init_zero {0, 0, 0, ""} +#define Telemetry_init_zero {0, 0} +#define FanState_init_zero {0, 0, 0, 0} +#define CoilState_init_zero {_CoilType_MIN, 0} +#define BootConfigRequest_init_zero {0} +#define BootConfigResponse_init_zero {_ConfigHardwareRevision_MIN, false, ConfigMotorLockType_init_zero, false, ConfigMotorLockType_init_zero} +#define ConfigMotorLockType_init_zero {_MotorLockType_MIN} + +/* Field tags (for use in manual encoding/decoding) */ +#define ErrorFlags_diode_fault_tag 1 +#define ErrorFlags_rcd_selftest_failed_tag 2 +#define ErrorFlags_rcd_triggered_tag 3 +#define ErrorFlags_ventilation_not_available_tag 4 +#define ErrorFlags_connector_lock_failed_tag 5 +#define ErrorFlags_cp_signal_fault_tag 6 +#define KeepAlive_time_stamp_tag 1 +#define KeepAlive_hw_type_tag 2 +#define KeepAlive_hw_revision_tag 3 +#define KeepAlive_sw_version_string_tag 6 +#define Telemetry_cp_voltage_hi_tag 1 +#define Telemetry_cp_voltage_lo_tag 2 +#define FanState_fan_id_tag 1 +#define FanState_enabled_tag 2 +#define FanState_duty_tag 3 +#define FanState_rpm_tag 4 +#define CoilState_coil_type_tag 1 +#define CoilState_coil_state_tag 2 +#define McuToEverest_keep_alive_tag 1 +#define McuToEverest_reset_tag 2 +#define McuToEverest_cp_state_tag 3 +#define McuToEverest_set_coil_state_response_tag 4 +#define McuToEverest_error_flags_tag 5 +#define McuToEverest_telemetry_tag 7 +#define McuToEverest_pp_state_tag 8 +#define McuToEverest_fan_state_tag 9 +#define McuToEverest_lock_state_tag 10 +#define McuToEverest_config_request_tag 11 +#define McuToEverest_connector_tag 6 +#define ConfigMotorLockType_type_tag 1 +#define BootConfigResponse_hw_rev_tag 1 +#define BootConfigResponse_lock_1_tag 2 +#define BootConfigResponse_lock_2_tag 3 +#define EverestToMcu_keep_alive_tag 1 +#define EverestToMcu_firmware_update_tag 2 +#define EverestToMcu_connector_lock_tag 3 +#define EverestToMcu_pwm_duty_cycle_tag 4 +#define EverestToMcu_set_coil_state_request_tag 5 +#define EverestToMcu_reset_tag 6 +#define EverestToMcu_config_response_tag 8 +#define EverestToMcu_set_fan_state_tag 9 +#define EverestToMcu_connector_tag 7 + +/* Struct field encoding specification for nanopb */ +#define EverestToMcu_FIELDLIST(X, a) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,keep_alive,payload.keep_alive), 1) \ +X(a, STATIC, ONEOF, BOOL, (payload,firmware_update,payload.firmware_update), 2) \ +X(a, STATIC, ONEOF, BOOL, (payload,connector_lock,payload.connector_lock), 3) \ +X(a, STATIC, ONEOF, UINT32, (payload,pwm_duty_cycle,payload.pwm_duty_cycle), 4) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,set_coil_state_request,payload.set_coil_state_request), 5) \ +X(a, STATIC, ONEOF, BOOL, (payload,reset,payload.reset), 6) \ +X(a, STATIC, SINGULAR, INT32, connector, 7) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,config_response,payload.config_response), 8) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,set_fan_state,payload.set_fan_state), 9) +#define EverestToMcu_CALLBACK NULL +#define EverestToMcu_DEFAULT NULL +#define EverestToMcu_payload_keep_alive_MSGTYPE KeepAlive +#define EverestToMcu_payload_set_coil_state_request_MSGTYPE CoilState +#define EverestToMcu_payload_config_response_MSGTYPE BootConfigResponse +#define EverestToMcu_payload_set_fan_state_MSGTYPE FanState + +#define McuToEverest_FIELDLIST(X, a) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,keep_alive,payload.keep_alive), 1) \ +X(a, STATIC, ONEOF, UENUM, (payload,reset,payload.reset), 2) \ +X(a, STATIC, ONEOF, UENUM, (payload,cp_state,payload.cp_state), 3) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,set_coil_state_response,payload.set_coil_state_response), 4) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,error_flags,payload.error_flags), 5) \ +X(a, STATIC, SINGULAR, INT32, connector, 6) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,telemetry,payload.telemetry), 7) \ +X(a, STATIC, ONEOF, UENUM, (payload,pp_state,payload.pp_state), 8) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,fan_state,payload.fan_state), 9) \ +X(a, STATIC, ONEOF, UENUM, (payload,lock_state,payload.lock_state), 10) \ +X(a, STATIC, ONEOF, MESSAGE, (payload,config_request,payload.config_request), 11) +#define McuToEverest_CALLBACK NULL +#define McuToEverest_DEFAULT NULL +#define McuToEverest_payload_keep_alive_MSGTYPE KeepAlive +#define McuToEverest_payload_set_coil_state_response_MSGTYPE CoilState +#define McuToEverest_payload_error_flags_MSGTYPE ErrorFlags +#define McuToEverest_payload_telemetry_MSGTYPE Telemetry +#define McuToEverest_payload_fan_state_MSGTYPE FanState +#define McuToEverest_payload_config_request_MSGTYPE BootConfigRequest + +#define ErrorFlags_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, BOOL, diode_fault, 1) \ +X(a, STATIC, SINGULAR, BOOL, rcd_selftest_failed, 2) \ +X(a, STATIC, SINGULAR, BOOL, rcd_triggered, 3) \ +X(a, STATIC, SINGULAR, BOOL, ventilation_not_available, 4) \ +X(a, STATIC, SINGULAR, BOOL, connector_lock_failed, 5) \ +X(a, STATIC, SINGULAR, BOOL, cp_signal_fault, 6) +#define ErrorFlags_CALLBACK NULL +#define ErrorFlags_DEFAULT NULL + +#define KeepAlive_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, time_stamp, 1) \ +X(a, STATIC, SINGULAR, UINT32, hw_type, 2) \ +X(a, STATIC, SINGULAR, UINT32, hw_revision, 3) \ +X(a, STATIC, SINGULAR, STRING, sw_version_string, 6) +#define KeepAlive_CALLBACK NULL +#define KeepAlive_DEFAULT NULL + +#define Telemetry_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, cp_voltage_hi, 1) \ +X(a, STATIC, SINGULAR, UINT32, cp_voltage_lo, 2) +#define Telemetry_CALLBACK NULL +#define Telemetry_DEFAULT NULL + +#define FanState_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UINT32, fan_id, 1) \ +X(a, STATIC, SINGULAR, BOOL, enabled, 2) \ +X(a, STATIC, SINGULAR, UINT32, duty, 3) \ +X(a, STATIC, SINGULAR, UINT32, rpm, 4) +#define FanState_CALLBACK NULL +#define FanState_DEFAULT NULL + +#define CoilState_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, coil_type, 1) \ +X(a, STATIC, SINGULAR, BOOL, coil_state, 2) +#define CoilState_CALLBACK NULL +#define CoilState_DEFAULT NULL + +#define BootConfigRequest_FIELDLIST(X, a) \ + +#define BootConfigRequest_CALLBACK NULL +#define BootConfigRequest_DEFAULT NULL + +#define BootConfigResponse_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, hw_rev, 1) \ +X(a, STATIC, OPTIONAL, MESSAGE, lock_1, 2) \ +X(a, STATIC, OPTIONAL, MESSAGE, lock_2, 3) +#define BootConfigResponse_CALLBACK NULL +#define BootConfigResponse_DEFAULT NULL +#define BootConfigResponse_lock_1_MSGTYPE ConfigMotorLockType +#define BootConfigResponse_lock_2_MSGTYPE ConfigMotorLockType + +#define ConfigMotorLockType_FIELDLIST(X, a) \ +X(a, STATIC, SINGULAR, UENUM, type, 1) +#define ConfigMotorLockType_CALLBACK NULL +#define ConfigMotorLockType_DEFAULT NULL + +extern const pb_msgdesc_t EverestToMcu_msg; +extern const pb_msgdesc_t McuToEverest_msg; +extern const pb_msgdesc_t ErrorFlags_msg; +extern const pb_msgdesc_t KeepAlive_msg; +extern const pb_msgdesc_t Telemetry_msg; +extern const pb_msgdesc_t FanState_msg; +extern const pb_msgdesc_t CoilState_msg; +extern const pb_msgdesc_t BootConfigRequest_msg; +extern const pb_msgdesc_t BootConfigResponse_msg; +extern const pb_msgdesc_t ConfigMotorLockType_msg; + +/* Defines for backwards compatibility with code written before nanopb-0.4.0 */ +#define EverestToMcu_fields &EverestToMcu_msg +#define McuToEverest_fields &McuToEverest_msg +#define ErrorFlags_fields &ErrorFlags_msg +#define KeepAlive_fields &KeepAlive_msg +#define Telemetry_fields &Telemetry_msg +#define FanState_fields &FanState_msg +#define CoilState_fields &CoilState_msg +#define BootConfigRequest_fields &BootConfigRequest_msg +#define BootConfigResponse_fields &BootConfigResponse_msg +#define ConfigMotorLockType_fields &ConfigMotorLockType_msg + +/* Maximum encoded size of messages (where known) */ +#define BootConfigRequest_size 0 +#define BootConfigResponse_size 10 +#define CoilState_size 4 +#define ConfigMotorLockType_size 2 +#define ErrorFlags_size 12 +#define EverestToMcu_size 83 +#define FanState_size 15 +#define KeepAlive_size 70 +#define McuToEverest_size 83 +#define PHYVERSO_PB_H_MAX_SIZE EverestToMcu_size +#define Telemetry_size 12 + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif diff --git a/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/phyverso.proto b/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/phyverso.proto new file mode 100644 index 0000000000..7b1986ea79 --- /dev/null +++ b/modules/PhyVersoBSP/phyverso_mcu_comms/protobuf/phyverso.proto @@ -0,0 +1,132 @@ +syntax = "proto3"; + +/* + This container message is send from EVerest to MCU and may contain any allowed message in that direction. +*/ +message EverestToMcu { + oneof payload { + KeepAlive keep_alive = 1; + bool firmware_update = 2; + bool connector_lock = 3; // false: unlock, true: lock + uint32 pwm_duty_cycle = 4; // in 0.01 %, 0 = State F, 10000 = X1 + CoilState set_coil_state_request = 5; + bool reset = 6; + BootConfigResponse config_response = 8; + FanState set_fan_state = 9; + } + int32 connector = 7; // 0: None, 1: Connector 1, 2: Connector 2 +} + +/* + This container message is send from MCU to EVerest and may contain any allowed message in that direction. +*/ +message McuToEverest { + oneof payload { + KeepAlive keep_alive = 1; + ResetReason reset = 2; + CpState cp_state = 3; + CoilState set_coil_state_response = 4; + ErrorFlags error_flags = 5; + Telemetry telemetry = 7; + PpState pp_state = 8; + FanState fan_state = 9; + LockState lock_state = 10; + BootConfigRequest config_request = 11; + } + int32 connector = 6; // 0: None, 1: Connector 1, 2: Connector 2 +} + +enum CpState { + STATE_A = 0; + STATE_B = 1; + STATE_C = 2; + STATE_D = 3; + STATE_E = 4; + STATE_F = 5; +} + +message ErrorFlags { + bool diode_fault = 1; + bool rcd_selftest_failed = 2; + bool rcd_triggered = 3; + bool ventilation_not_available = 4; + bool connector_lock_failed = 5; + bool cp_signal_fault = 6; +} + +enum ResetReason { + USER = 0; + WATCHDOG = 1; +} + +message KeepAlive { + uint32 time_stamp = 1; + uint32 hw_type = 2; + uint32 hw_revision = 3; + string sw_version_string = 6; +} + +message Telemetry { + uint32 cp_voltage_hi = 1; + uint32 cp_voltage_lo = 2; +} + +enum PpState { + STATE_NC = 0; + STATE_13A = 1; + STATE_20A = 2; + STATE_32A = 3; + STATE_70A = 4; + STATE_FAULT = 5; +} + +message FanState { + uint32 fan_id = 1; + bool enabled = 2; + uint32 duty = 3; // in 0.1%, 1000 = 100% + uint32 rpm = 4; +} + +enum LockState { + UNDEFINED = 0; + UNLOCKED = 1; + LOCKED = 2; +} + +message CoilState { + CoilType coil_type = 1; + bool coil_state = 2; // true -> on; false -> off +} + +enum CoilType { + COIL_UNKNOWN = 0; + COIL_AC = 1; + COIL_DC1 = 2; + // add precharge and discharge coils here later +} + +message BootConfigRequest { + // TODO +} + +message BootConfigResponse { + ConfigHardwareRevision hw_rev = 1; + ConfigMotorLockType lock_1 = 2; + ConfigMotorLockType lock_2 = 3; +} + +message ConfigMotorLockType { + MotorLockType type = 1; +} + +enum ConfigHardwareRevision { + HW_REV_UNKNOWN = 0; + HW_REV_A = 1; + HW_REV_B = 2; +} + +enum MotorLockType { + MOTOR_LOCK_UNKNOWN = 0; + MOTOR_LOCK_QWELLO = 1; + MOTOR_LOCK_DEBUG_VALEO_HVAC = 2; +} \ No newline at end of file diff --git a/modules/PhyVersoBSP/rcd_1/ac_rcdImpl.cpp b/modules/PhyVersoBSP/rcd_1/ac_rcdImpl.cpp new file mode 100644 index 0000000000..43a5d7ba46 --- /dev/null +++ b/modules/PhyVersoBSP/rcd_1/ac_rcdImpl.cpp @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "ac_rcdImpl.hpp" + +namespace module { +namespace rcd_1 { + +void ac_rcdImpl::init() { +} + +void ac_rcdImpl::ready() { +} + +void ac_rcdImpl::handle_self_test() { + // your code for cmd self_test goes here +} + +bool ac_rcdImpl::handle_reset() { + // your code for cmd reset goes here + return true; +} + +} // namespace rcd_1 +} // namespace module diff --git a/modules/PhyVersoBSP/rcd_1/ac_rcdImpl.hpp b/modules/PhyVersoBSP/rcd_1/ac_rcdImpl.hpp new file mode 100644 index 0000000000..efa7cf5b0b --- /dev/null +++ b/modules/PhyVersoBSP/rcd_1/ac_rcdImpl.hpp @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef RCD_1_AC_RCD_IMPL_HPP +#define RCD_1_AC_RCD_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include + +#include "../PhyVersoBSP.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace rcd_1 { + +struct Conf {}; + +class ac_rcdImpl : public ac_rcdImplBase { +public: + ac_rcdImpl() = delete; + ac_rcdImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + ac_rcdImplBase(ev, "rcd_1"), mod(mod), config(config){}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // command handler functions (virtual) + virtual void handle_self_test() override; + virtual bool handle_reset() override; + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + // insert your private definitions here + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace rcd_1 +} // namespace module + +#endif // RCD_1_AC_RCD_IMPL_HPP diff --git a/modules/PhyVersoBSP/rcd_2/ac_rcdImpl.cpp b/modules/PhyVersoBSP/rcd_2/ac_rcdImpl.cpp new file mode 100644 index 0000000000..45afde22db --- /dev/null +++ b/modules/PhyVersoBSP/rcd_2/ac_rcdImpl.cpp @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#include "ac_rcdImpl.hpp" + +namespace module { +namespace rcd_2 { + +void ac_rcdImpl::init() { +} + +void ac_rcdImpl::ready() { +} + +void ac_rcdImpl::handle_self_test() { + // your code for cmd self_test goes here +} + +bool ac_rcdImpl::handle_reset() { + // your code for cmd reset goes here + return true; +} + +} // namespace rcd_2 +} // namespace module diff --git a/modules/PhyVersoBSP/rcd_2/ac_rcdImpl.hpp b/modules/PhyVersoBSP/rcd_2/ac_rcdImpl.hpp new file mode 100644 index 0000000000..c0e138e7ce --- /dev/null +++ b/modules/PhyVersoBSP/rcd_2/ac_rcdImpl.hpp @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest + +#ifndef RCD_2_AC_RCD_IMPL_HPP +#define RCD_2_AC_RCD_IMPL_HPP + +// +// AUTO GENERATED - MARKED REGIONS WILL BE KEPT +// template version 3 +// + +#include + +#include "../PhyVersoBSP.hpp" + +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 +// insert your custom include headers here +// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1 + +namespace module { +namespace rcd_2 { + +struct Conf {}; + +class ac_rcdImpl : public ac_rcdImplBase { +public: + ac_rcdImpl() = delete; + ac_rcdImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer& mod, Conf& config) : + ac_rcdImplBase(ev, "rcd_2"), mod(mod), config(config){}; + + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + // insert your public definitions here + // ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1 + +protected: + // command handler functions (virtual) + virtual void handle_self_test() override; + virtual bool handle_reset() override; + + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + // insert your protected definitions here + // ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1 + +private: + const Everest::PtrContainer& mod; + const Conf& config; + + virtual void init() override; + virtual void ready() override; + + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 + // insert your private definitions here + // ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1 +}; + +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 +// insert other definitions here +// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1 + +} // namespace rcd_2 +} // namespace module + +#endif // RCD_2_AC_RCD_IMPL_HPP