Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
#
|
||||
# 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
|
||||
add_subdirectory(yeti_comms)
|
||||
add_subdirectory(yeti_fwupdate)
|
||||
|
||||
target_include_directories(${MODULE_NAME}
|
||||
PRIVATE
|
||||
"common"
|
||||
"yeti_comms"
|
||||
"yeti_comms/nanopb"
|
||||
"yeti_comms/protobuf"
|
||||
)
|
||||
|
||||
target_link_libraries(${MODULE_NAME}
|
||||
PRIVATE
|
||||
Pal::Sigslot
|
||||
yeti_comms
|
||||
)
|
||||
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
|
||||
|
||||
target_sources(${MODULE_NAME}
|
||||
PRIVATE
|
||||
"powermeter/powermeterImpl.cpp"
|
||||
"board_support/evse_board_supportImpl.cpp"
|
||||
"rcd/ac_rcdImpl.cpp"
|
||||
"connector_lock/connector_lockImpl.cpp"
|
||||
)
|
||||
|
||||
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
|
||||
# insert other things like install cmds etc here
|
||||
install(FILES yetiR1_2.2_firmware.bin DESTINATION ${CMAKE_INSTALL_DATADIR}/everest/modules/YetiDriver/firmware)
|
||||
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
|
||||
@@ -0,0 +1,172 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
|
||||
#include "YetiDriver.hpp"
|
||||
#include <fmt/core.h>
|
||||
#include <utils/date.hpp>
|
||||
|
||||
namespace module {
|
||||
|
||||
void YetiDriver::init() {
|
||||
|
||||
// initialize serial driver
|
||||
if (!serial.openDevice(config.serial_port.c_str(), config.baud_rate)) {
|
||||
EVLOG_error << "Could not open serial port " << config.serial_port << " with baud rate " << config.baud_rate;
|
||||
return;
|
||||
}
|
||||
|
||||
telemetry_power_path_controller_version = {{"timestamp", ""},
|
||||
{"type", "power_path_controller_version"},
|
||||
{"hardware_version", 1},
|
||||
{"software_version", "1.00"},
|
||||
{"date_manufactured", "N/A"},
|
||||
{"operating_time_h", 5},
|
||||
{"operating_time_h_warning", 5000},
|
||||
{"operating_time_h_error", 6000},
|
||||
{"software_version", "1.00"},
|
||||
{"error", false}};
|
||||
|
||||
telemetry_power_path_controller = {{"timestamp", ""},
|
||||
{"type", "power_path_controller"},
|
||||
{"cp_voltage_high", 0.0},
|
||||
{"cp_voltage_low", 0.0},
|
||||
{"cp_pwm_duty_cycle", 0.0},
|
||||
{"cp_state", "A1"},
|
||||
{"pp_ohm", 0.0},
|
||||
{"supply_voltage_12V", 0.0},
|
||||
{"supply_voltage_minus_12V", 0.0},
|
||||
{"temperature_controller", 0.0},
|
||||
{"temperature_car_connector", 0.0},
|
||||
{"watchdog_reset_count", 0.0},
|
||||
{"error", false}};
|
||||
|
||||
telemetry_power_switch = {{"timestamp", ""},
|
||||
{"type", "power_switch"},
|
||||
{"switching_count", 0},
|
||||
{"switching_count_warning", 30000},
|
||||
{"switching_count_error", 50000},
|
||||
{"is_on", false},
|
||||
{"time_to_switch_on_ms", 100},
|
||||
{"time_to_switch_off_ms", 100},
|
||||
{"temperature_C", 0.0},
|
||||
{"error", false},
|
||||
{"error_over_current", false}};
|
||||
|
||||
telemetry_rcd = {{"timestamp", ""}, //
|
||||
{"type", "rcd"}, //
|
||||
{"enabled", true}, //
|
||||
{"current_mA", 0.0}, //
|
||||
{"triggered", false}, //
|
||||
{"error", false}}; //
|
||||
|
||||
invoke_init(*p_powermeter);
|
||||
invoke_init(*p_board_support);
|
||||
invoke_init(*p_connector_lock);
|
||||
invoke_init(*p_rcd);
|
||||
}
|
||||
|
||||
void YetiDriver::ready() {
|
||||
serial.run();
|
||||
|
||||
if (!serial.reset(config.reset_gpio_chip, config.reset_gpio)) {
|
||||
EVLOG_error << "Yeti reset not successful.";
|
||||
}
|
||||
|
||||
serial.signalSpuriousReset.connect([this]() { EVLOG_error << "Yeti uC spurious reset!"; });
|
||||
serial.signalConnectionTimeout.connect([this]() { EVLOG_error << "Yeti UART timeout!"; });
|
||||
|
||||
invoke_ready(*p_powermeter);
|
||||
invoke_ready(*p_board_support);
|
||||
invoke_ready(*p_connector_lock);
|
||||
invoke_ready(*p_rcd);
|
||||
|
||||
telemetryThreadHandle = std::thread([this]() {
|
||||
while (!telemetryThreadHandle.shouldExit()) {
|
||||
sleep(10);
|
||||
{
|
||||
std::scoped_lock lock(telemetry_mutex);
|
||||
publish_external_telemetry_livedata("power_path_controller", telemetry_power_path_controller);
|
||||
publish_external_telemetry_livedata("rcd", telemetry_rcd);
|
||||
publish_external_telemetry_livedata("power_path_controller_version",
|
||||
telemetry_power_path_controller_version);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
serial.signalErrorFlags.connect([this](ErrorFlags e) { error_handling(e); });
|
||||
|
||||
if (not serial.is_open()) {
|
||||
auto err = p_board_support->error_factory->create_error("evse_board_support/CommunicationFault", "",
|
||||
"Could not open serial port.");
|
||||
p_board_support->raise_error(err);
|
||||
}
|
||||
}
|
||||
|
||||
void YetiDriver::publish_external_telemetry_livedata(const std::string& topic, const Everest::TelemetryMap& data) {
|
||||
if (info.telemetry_enabled) {
|
||||
telemetry.publish("livedata", topic, data);
|
||||
}
|
||||
}
|
||||
|
||||
bool rcd_selftest_failed;
|
||||
|
||||
bool connector_lock_failed;
|
||||
bool cp_signal_fault;
|
||||
|
||||
void YetiDriver::clear_errors_on_unplug() {
|
||||
if (error_MREC2GroundFailure) {
|
||||
p_board_support->clear_error("evse_board_support/MREC2GroundFailure");
|
||||
}
|
||||
error_MREC2GroundFailure = false;
|
||||
|
||||
if (error_MREC1ConnectorLockFailure) {
|
||||
p_connector_lock->clear_error("connector_lock/MREC1ConnectorLockFailure");
|
||||
}
|
||||
error_MREC1ConnectorLockFailure = false;
|
||||
}
|
||||
|
||||
void YetiDriver::error_handling(ErrorFlags e) {
|
||||
|
||||
if (e.diode_fault and not last_error_flags.diode_fault) {
|
||||
Everest::error::Error error_object = p_board_support->error_factory->create_error(
|
||||
"evse_board_support/DiodeFault", "", "Diode Fault", Everest::error::Severity::High);
|
||||
p_board_support->raise_error(error_object);
|
||||
} else if (not e.diode_fault and last_error_flags.diode_fault) {
|
||||
p_board_support->clear_error("evse_board_support/DiodeFault");
|
||||
}
|
||||
|
||||
if (e.rcd_triggered and not last_error_flags.rcd_triggered) {
|
||||
Everest::error::Error error_object = p_board_support->error_factory->create_error(
|
||||
"evse_board_support/MREC2GroundFailure", "", "Onboard RCD triggered", Everest::error::Severity::High);
|
||||
p_board_support->raise_error(error_object);
|
||||
error_MREC2GroundFailure = true;
|
||||
}
|
||||
|
||||
if (e.ventilation_not_available and not last_error_flags.ventilation_not_available) {
|
||||
Everest::error::Error error_object =
|
||||
p_board_support->error_factory->create_error("evse_board_support/VentilationNotAvailable", "",
|
||||
"State D is not supported", Everest::error::Severity::High);
|
||||
p_board_support->raise_error(error_object);
|
||||
} else if (not e.ventilation_not_available and last_error_flags.ventilation_not_available) {
|
||||
p_board_support->clear_error("evse_board_support/VentilationNotAvailable");
|
||||
}
|
||||
|
||||
if (e.connector_lock_failed and not last_error_flags.connector_lock_failed) {
|
||||
Everest::error::Error error_object = p_connector_lock->error_factory->create_error(
|
||||
"connector_lock/MREC1ConnectorLockFailure", "", "Lock motor failure", Everest::error::Severity::High);
|
||||
|
||||
error_MREC1ConnectorLockFailure = true;
|
||||
p_connector_lock->raise_error(error_object);
|
||||
}
|
||||
|
||||
if (e.cp_signal_fault and not last_error_flags.cp_signal_fault) {
|
||||
Everest::error::Error error_object = p_board_support->error_factory->create_error(
|
||||
"evse_board_support/MREC14PilotFault", "", "CP error", Everest::error::Severity::High);
|
||||
p_board_support->raise_error(error_object);
|
||||
} else if (not e.cp_signal_fault and last_error_flags.cp_signal_fault) {
|
||||
p_board_support->clear_error("evse_board_support/MREC14PilotFault");
|
||||
}
|
||||
|
||||
last_error_flags = e;
|
||||
}
|
||||
|
||||
} // namespace module
|
||||
@@ -0,0 +1,96 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef YETI_DRIVER_HPP
|
||||
#define YETI_DRIVER_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 2
|
||||
//
|
||||
|
||||
#include "ld-ev.hpp"
|
||||
|
||||
// headers for provided interface implementations
|
||||
#include <generated/interfaces/ac_rcd/Implementation.hpp>
|
||||
#include <generated/interfaces/connector_lock/Implementation.hpp>
|
||||
#include <generated/interfaces/evse_board_support/Implementation.hpp>
|
||||
#include <generated/interfaces/powermeter/Implementation.hpp>
|
||||
|
||||
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
|
||||
#include "yeti_comms/evSerial.h"
|
||||
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
|
||||
|
||||
namespace module {
|
||||
|
||||
struct Conf {
|
||||
std::string serial_port;
|
||||
int baud_rate;
|
||||
std::string reset_gpio_chip;
|
||||
int reset_gpio;
|
||||
int caps_min_current_A;
|
||||
int caps_max_current_A;
|
||||
};
|
||||
|
||||
class YetiDriver : public Everest::ModuleBase {
|
||||
public:
|
||||
YetiDriver() = delete;
|
||||
YetiDriver(const ModuleInfo& info, Everest::TelemetryProvider& telemetry,
|
||||
std::unique_ptr<powermeterImplBase> p_powermeter,
|
||||
std::unique_ptr<evse_board_supportImplBase> p_board_support, std::unique_ptr<ac_rcdImplBase> p_rcd,
|
||||
std::unique_ptr<connector_lockImplBase> p_connector_lock, Conf& config) :
|
||||
ModuleBase(info),
|
||||
telemetry(telemetry),
|
||||
p_powermeter(std::move(p_powermeter)),
|
||||
p_board_support(std::move(p_board_support)),
|
||||
p_rcd(std::move(p_rcd)),
|
||||
p_connector_lock(std::move(p_connector_lock)),
|
||||
config(config){};
|
||||
|
||||
Everest::TelemetryProvider& telemetry;
|
||||
const std::unique_ptr<powermeterImplBase> p_powermeter;
|
||||
const std::unique_ptr<evse_board_supportImplBase> p_board_support;
|
||||
const std::unique_ptr<ac_rcdImplBase> p_rcd;
|
||||
const std::unique_ptr<connector_lockImplBase> p_connector_lock;
|
||||
const Conf& config;
|
||||
|
||||
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
|
||||
void publish_external_telemetry_livedata(const std::string& topic, const Everest::TelemetryMap& data);
|
||||
evSerial serial;
|
||||
void clear_errors_on_unplug();
|
||||
// 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
|
||||
Everest::TelemetryMap telemetry_power_path_controller_version;
|
||||
Everest::TelemetryMap telemetry_power_path_controller;
|
||||
Everest::TelemetryMap telemetry_power_switch;
|
||||
Everest::TelemetryMap telemetry_rcd;
|
||||
std::mutex telemetry_mutex;
|
||||
Everest::Thread telemetryThreadHandle;
|
||||
void error_handling(ErrorFlags e);
|
||||
ErrorFlags last_error_flags;
|
||||
|
||||
std::atomic_bool error_MREC2GroundFailure{false};
|
||||
std::atomic_bool error_MREC1ConnectorLockFailure{false};
|
||||
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
|
||||
};
|
||||
|
||||
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
|
||||
Everest::json power_meter_data_to_json(const PowerMeter& p);
|
||||
Everest::json keep_alive_lo_to_json(const KeepAliveLo& k);
|
||||
std::string error_type_to_string(ErrorFlags s);
|
||||
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
|
||||
|
||||
} // namespace module
|
||||
|
||||
#endif // YETI_DRIVER_HPP
|
||||
@@ -0,0 +1,200 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "evse_board_supportImpl.hpp"
|
||||
|
||||
namespace module {
|
||||
namespace board_support {
|
||||
|
||||
static types::board_support_common::BspEvent cast_event_type(CpState cp_state) {
|
||||
types::board_support_common::BspEvent event;
|
||||
switch (cp_state) {
|
||||
case CpState_STATE_A:
|
||||
event.event = types::board_support_common::Event::A;
|
||||
break;
|
||||
case CpState_STATE_B:
|
||||
event.event = types::board_support_common::Event::B;
|
||||
break;
|
||||
case CpState_STATE_C:
|
||||
event.event = types::board_support_common::Event::C;
|
||||
break;
|
||||
case CpState_STATE_D:
|
||||
event.event = types::board_support_common::Event::D;
|
||||
break;
|
||||
case CpState_STATE_E:
|
||||
event.event = types::board_support_common::Event::E;
|
||||
break;
|
||||
case CpState_STATE_F:
|
||||
event.event = types::board_support_common::Event::F;
|
||||
break;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
static types::board_support_common::BspEvent cast_event_type(bool relais_state) {
|
||||
types::board_support_common::BspEvent event;
|
||||
if (relais_state) {
|
||||
event.event = types::board_support_common::Event::PowerOn;
|
||||
} else {
|
||||
event.event = types::board_support_common::Event::PowerOff;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
static types::board_support_common::ProximityPilot cast_pp_type(PpState pp_state) {
|
||||
types::board_support_common::ProximityPilot pp;
|
||||
switch (pp_state) {
|
||||
case PpState_STATE_13A:
|
||||
pp.ampacity = types::board_support_common::Ampacity::A_13;
|
||||
break;
|
||||
case PpState_STATE_20A:
|
||||
pp.ampacity = types::board_support_common::Ampacity::A_20;
|
||||
break;
|
||||
case PpState_STATE_32A:
|
||||
pp.ampacity = types::board_support_common::Ampacity::A_32;
|
||||
break;
|
||||
case PpState_STATE_70A:
|
||||
pp.ampacity = types::board_support_common::Ampacity::A_63_3ph_70_1ph;
|
||||
break;
|
||||
case PpState_STATE_FAULT:
|
||||
pp.ampacity = types::board_support_common::Ampacity::None;
|
||||
break;
|
||||
case PpState_STATE_NC:
|
||||
pp.ampacity = types::board_support_common::Ampacity::None;
|
||||
break;
|
||||
}
|
||||
return pp;
|
||||
}
|
||||
|
||||
void evse_board_supportImpl::init() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(capsMutex);
|
||||
|
||||
caps.min_current_A_import = 6;
|
||||
caps.max_current_A_import = 16;
|
||||
caps.min_phase_count_import = 1;
|
||||
caps.max_phase_count_import = 3;
|
||||
caps.supports_changing_phases_during_charging = false;
|
||||
caps.supports_cp_state_E = false;
|
||||
caps.connector_type = types::evse_board_support::Connector_type::IEC62196Type2Cable;
|
||||
|
||||
caps.min_current_A_export = 6;
|
||||
caps.max_current_A_export = 16;
|
||||
caps.min_phase_count_export = 1;
|
||||
caps.max_phase_count_export = 3;
|
||||
}
|
||||
|
||||
mod->serial.signalCPState.connect([this](CpState cp_state) {
|
||||
if (cp_state not_eq last_cp_state) {
|
||||
auto event_cp_state = cast_event_type(cp_state);
|
||||
EVLOG_info << "CP state changed: "
|
||||
<< types::board_support_common::event_to_string(cast_event_type(last_cp_state).event) << " -> "
|
||||
<< types::board_support_common::event_to_string(event_cp_state.event);
|
||||
if (enabled) {
|
||||
publish_event(event_cp_state);
|
||||
}
|
||||
|
||||
if (cp_state == CpState_STATE_A) {
|
||||
mod->clear_errors_on_unplug();
|
||||
}
|
||||
last_cp_state = cp_state;
|
||||
}
|
||||
});
|
||||
mod->serial.signalRelaisState.connect([this](bool relais_state) {
|
||||
if (last_relais_state not_eq relais_state) {
|
||||
publish_event(cast_event_type(relais_state));
|
||||
last_relais_state = relais_state;
|
||||
}
|
||||
});
|
||||
|
||||
mod->serial.signalPPState.connect([this](PpState pp_state) {
|
||||
last_pp = cast_pp_type(pp_state);
|
||||
publish_ac_pp_ampacity(last_pp);
|
||||
});
|
||||
|
||||
mod->serial.signalKeepAliveLo.connect([this](KeepAliveLo l) {
|
||||
std::lock_guard<std::mutex> lock(capsMutex);
|
||||
|
||||
caps.min_current_A_import =
|
||||
(mod->config.caps_min_current_A >= 0 ? mod->config.caps_min_current_A : l.hwcap_min_current);
|
||||
caps.max_current_A_import =
|
||||
(mod->config.caps_max_current_A >= 0 ? mod->config.caps_max_current_A : l.hwcap_max_current);
|
||||
caps.min_phase_count_import = l.hwcap_min_phase_count;
|
||||
caps.max_phase_count_import = l.hwcap_max_phase_count;
|
||||
|
||||
caps.min_current_A_export =
|
||||
(mod->config.caps_min_current_A >= 0 ? mod->config.caps_min_current_A : l.hwcap_min_current);
|
||||
caps.max_current_A_export =
|
||||
(mod->config.caps_max_current_A >= 0 ? mod->config.caps_max_current_A : l.hwcap_max_current);
|
||||
caps.min_phase_count_export = l.hwcap_min_phase_count;
|
||||
caps.max_phase_count_export = l.hwcap_max_phase_count;
|
||||
|
||||
caps.supports_changing_phases_during_charging = l.supports_changing_phases_during_charging;
|
||||
if (not caps_received) {
|
||||
EVLOG_info << "Yeti Controller Configuration:";
|
||||
EVLOG_info << " Hardware revision: " << l.hw_revision;
|
||||
EVLOG_info << " Firmware version: " << l.sw_version_string;
|
||||
EVLOG_info << " Current Limit: " << l.hwcap_max_current;
|
||||
}
|
||||
caps_received = true;
|
||||
});
|
||||
}
|
||||
|
||||
void evse_board_supportImpl::ready() {
|
||||
wait_for_caps();
|
||||
{
|
||||
// Publish caps once in the beginning
|
||||
std::lock_guard<std::mutex> lock(capsMutex);
|
||||
publish_capabilities(caps);
|
||||
}
|
||||
}
|
||||
|
||||
void evse_board_supportImpl::wait_for_caps() {
|
||||
// Wait for caps to be received at least once
|
||||
int i;
|
||||
for (i = 0; i < 50; i++) {
|
||||
if (caps_received)
|
||||
break;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
if (i == 50) {
|
||||
EVLOG_error << "Did not receive hardware capabilities from Yeti hardware, using defaults.";
|
||||
}
|
||||
}
|
||||
|
||||
void evse_board_supportImpl::handle_pwm_on(double& value) {
|
||||
mod->serial.setPWM(value * 100);
|
||||
}
|
||||
|
||||
void evse_board_supportImpl::handle_cp_state_X1() {
|
||||
mod->serial.setPWM(10001);
|
||||
}
|
||||
|
||||
void evse_board_supportImpl::handle_cp_state_F() {
|
||||
mod->serial.setPWM(0);
|
||||
}
|
||||
|
||||
void evse_board_supportImpl::handle_cp_state_E() {
|
||||
EVLOG_warning << "Command cp_state_E is not supported. Ignoring command.";
|
||||
}
|
||||
|
||||
void evse_board_supportImpl::handle_allow_power_on(types::evse_board_support::PowerOnOff& value) {
|
||||
mod->serial.allowPowerOn(value.allow_power_on);
|
||||
}
|
||||
|
||||
void evse_board_supportImpl::handle_ac_set_overcurrent_limit_A(double& value) {
|
||||
// your code for cmd ac_set_overcurrent_limit_A goes here
|
||||
}
|
||||
|
||||
void evse_board_supportImpl::handle_ac_switch_three_phases_while_charging(bool& value) {
|
||||
mod->serial.set_number_of_phases(value);
|
||||
}
|
||||
|
||||
void evse_board_supportImpl::handle_enable(bool& value) {
|
||||
enabled = true;
|
||||
// Publish CP state once on enable
|
||||
publish_event(cast_event_type(last_cp_state));
|
||||
}
|
||||
|
||||
} // namespace board_support
|
||||
} // namespace module
|
||||
@@ -0,0 +1,76 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP
|
||||
#define BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 3
|
||||
//
|
||||
|
||||
#include <generated/interfaces/evse_board_support/Implementation.hpp>
|
||||
|
||||
#include "../YetiDriver.hpp"
|
||||
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
// insert your custom include headers here
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
|
||||
namespace module {
|
||||
namespace board_support {
|
||||
|
||||
struct Conf {};
|
||||
|
||||
class evse_board_supportImpl : public evse_board_supportImplBase {
|
||||
public:
|
||||
evse_board_supportImpl() = delete;
|
||||
evse_board_supportImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<YetiDriver>& mod, Conf& config) :
|
||||
evse_board_supportImplBase(ev, "board_support"), 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_enable(bool& value) override;
|
||||
virtual void handle_pwm_on(double& value) override;
|
||||
virtual void handle_cp_state_X1() override;
|
||||
virtual void handle_cp_state_F() override;
|
||||
virtual void handle_cp_state_E() 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_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<YetiDriver>& mod;
|
||||
const Conf& config;
|
||||
|
||||
virtual void init() override;
|
||||
virtual void ready() override;
|
||||
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
// insert your private definitions here
|
||||
types::evse_board_support::HardwareCapabilities caps;
|
||||
std::atomic_bool caps_received{false};
|
||||
std::mutex capsMutex;
|
||||
CpState last_cp_state{CpState::CpState_STATE_F};
|
||||
bool last_relais_state{false};
|
||||
types::board_support_common::ProximityPilot last_pp{types::board_support_common::Ampacity::None};
|
||||
void wait_for_caps();
|
||||
std::atomic_bool enabled{false};
|
||||
// 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 board_support
|
||||
} // namespace module
|
||||
|
||||
#endif // BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP
|
||||
@@ -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 {
|
||||
|
||||
void connector_lockImpl::init() {
|
||||
mod->serial.signalLockState.connect([this](bool l) { lock_state = l; });
|
||||
}
|
||||
|
||||
void connector_lockImpl::ready() {
|
||||
}
|
||||
|
||||
void connector_lockImpl::handle_lock() {
|
||||
}
|
||||
|
||||
void connector_lockImpl::handle_unlock() {
|
||||
mod->serial.forceUnlock();
|
||||
}
|
||||
|
||||
} // namespace connector_lock
|
||||
} // namespace module
|
||||
@@ -0,0 +1,63 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef CONNECTOR_LOCK_CONNECTOR_LOCK_IMPL_HPP
|
||||
#define CONNECTOR_LOCK_CONNECTOR_LOCK_IMPL_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 3
|
||||
//
|
||||
|
||||
#include <generated/interfaces/connector_lock/Implementation.hpp>
|
||||
|
||||
#include "../YetiDriver.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 {
|
||||
|
||||
struct Conf {};
|
||||
|
||||
class connector_lockImpl : public connector_lockImplBase {
|
||||
public:
|
||||
connector_lockImpl() = delete;
|
||||
connector_lockImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<YetiDriver>& mod, Conf& config) :
|
||||
connector_lockImplBase(ev, "connector_lock"), 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<YetiDriver>& mod;
|
||||
const Conf& config;
|
||||
|
||||
virtual void init() override;
|
||||
virtual void ready() override;
|
||||
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
// insert your private definitions here
|
||||
std::atomic_bool lock_state{false};
|
||||
// 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
|
||||
} // namespace module
|
||||
|
||||
#endif // CONNECTOR_LOCK_CONNECTOR_LOCK_IMPL_HPP
|
||||
@@ -0,0 +1,171 @@
|
||||
.. _everest_modules_handwritten_YetiDriver:
|
||||
|
||||
.. **********
|
||||
.. YetiDriver
|
||||
.. **********
|
||||
|
||||
The module ``YetiDriver`` is a board support driver for Pionix Yeti Power
|
||||
Board.
|
||||
|
||||
Communication between the Yeti microcontroller and this driver module
|
||||
=====================================================================
|
||||
|
||||
The hardware connection between Yeti and Yak (the board running EVerest and
|
||||
this module) is 3.3V TTL UART plus 2 GPIOs (one to reset the microcontroller
|
||||
from Linux and one to wakeup Linux from the microcontroller, which is
|
||||
currrently unused).
|
||||
|
||||
The default configuration is 115200 bps 8N1.
|
||||
|
||||
Protocol
|
||||
========
|
||||
|
||||
EVerest can send commands to Yeti and Yeti publishes data and events back
|
||||
to EVerest. The packets are defined with protobuf to serialize the C structs
|
||||
into a binary representation that is transferred over the serial wire in a
|
||||
stream:
|
||||
|
||||
https://developers.google.com/protocol-buffers
|
||||
|
||||
To be able to split the stream back into packets all data is encoded using COBS
|
||||
before it is transmitted on the UART:
|
||||
|
||||
https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
|
||||
|
||||
COBS
|
||||
----
|
||||
|
||||
COBS is implemented in ``yeti_comms/evSerial.cpp``. Whenever a new packet
|
||||
was extracted from the stream ``handlePacket()`` is called to decode protobuf
|
||||
and generate the corresponding signals.
|
||||
Other parts of the module subscribe to these signals to handle the incoming
|
||||
packets.
|
||||
|
||||
For TX ``linkWrite`` encodes the packet with COBS and outputs it to the UART.
|
||||
|
||||
Protobuf
|
||||
--------
|
||||
|
||||
The actual packet definitions are located under ``yeti_comms/protobuf``.
|
||||
|
||||
``hi2lo.proto`` contains all messages that can be sent from EVerest to Yeti
|
||||
while ``lo2hi.proto`` defines all messages that Yeti sends to EVerest.
|
||||
|
||||
Refer to these files for an up to date definition as they may change
|
||||
frequently.
|
||||
|
||||
To generate the C code nanopb is used:
|
||||
|
||||
``nanopb_generator -I . -D . *.proto``
|
||||
|
||||
The output should also be manually copied to Yeti Firmware to ensure the same
|
||||
definition is used on both sides when making changes.
|
||||
|
||||
EVerest to Yeti
|
||||
---------------
|
||||
|
||||
The most important commands that EVerest sends to Yeti are the following:
|
||||
|
||||
``SetControlMode(mode)``: Yeti firmware can operate in different modes:
|
||||
|
||||
``Mode NONE = 0``: In this mode Yeti does not allow control over UART. It will
|
||||
still send telemetry data. Yeti operates as a standalone non-smart AC charger
|
||||
and EVerest does not need to be running.
|
||||
|
||||
``HIGH = 1``: In this mode high level control is possible.
|
||||
Yeti operates as a standalone AC charger and EVerest does not need to be
|
||||
running, but it does allow certain control such as setMaxCurrent from EVerest.
|
||||
This mode is not documented here as it is not used by EVerest anymore.
|
||||
|
||||
``LOW = 2``: In this mode Yeti allows low level control. Yeti does not act
|
||||
as a standalone charger, it needs to be controlled by EVerest. It does however
|
||||
still run the very basic state machine to follow the car's states A-F and
|
||||
switches relais on and off accordingly. This ensures that basic electrical
|
||||
safety remains within the microcontroller and not within EVerest.
|
||||
It generates more human readable events from state A-F transitions.
|
||||
|
||||
PWM is directly controlled from EVerest in this mode.
|
||||
|
||||
Low control mode:
|
||||
_________________
|
||||
|
||||
The following commands describe the Low level control mode only:
|
||||
|
||||
``AllowPowerOn(bool)``: Inform yeti that it is allowed to switch on the power
|
||||
relais/contactors to the car on (true) or must switch off now (false). The
|
||||
final decision remains with Yeti in case of power on, it should only power on
|
||||
after all other requirements are met (such as RCD current is below limit,
|
||||
car is in CP state B etc). On power off Yeti must switch off immediately.
|
||||
|
||||
``SetPWM(mode, duty_cycle)``: mode 0: OFF (+12V), 1: ON (PWM with duty_cycle),
|
||||
2: F (-12V). Yeti sets the PWM immediately.
|
||||
|
||||
|
||||
Other commands for all modes:
|
||||
_____________________________
|
||||
|
||||
``FirmwareUpdate(bool)``: Send true to reboot Yeti into ROM boot loader.
|
||||
After that stm32flash tool can be used to flash any firmware binary to it.
|
||||
Note that this is a dev kit and for a real product this needs to be implemented
|
||||
differently.
|
||||
|
||||
``KeepAliveHi``: Send this packet to Yeti at 1Hz. If no heartbeat is received
|
||||
for a longer amount of time Yeti may fall back to control mode NONE to act
|
||||
as a stand alone emergency backup charger or go into failure mode (can be
|
||||
modified in the firmware).
|
||||
|
||||
``SetThreePhases``: true: switches to 3ph on next switch on, else single phase.
|
||||
Only works on hardware configurations with dual relais. Does not switch while
|
||||
charging session is running, Yeti firmware will delay the change to the next
|
||||
charging session.
|
||||
|
||||
``EnableRCD``: enable or disable the onboard RCD. Some cars generate quite high
|
||||
residual current spikes and may not charge properly if RCD is enabled.
|
||||
|
||||
``Enable``: Enable CP output
|
||||
|
||||
``Disable``: Disable CP output (goes to floating/high impedance)
|
||||
|
||||
``Reset``: Reset yeti firmware
|
||||
|
||||
``Replug``: Initiate special virtual replug sequence without starting a new
|
||||
charging session.
|
||||
|
||||
``SwitchThreePhasesWhileCharging``: Change between 1 and 3 phases while
|
||||
charging. This is currently not implemented in yeti firmware and will need
|
||||
special precautions because some cars may be destroyed by switching from one
|
||||
phase to three phase while charging is running (E.g. Zoe 1)
|
||||
|
||||
``ForceUnlock``: Force unlock motor lock now regardless of state.
|
||||
|
||||
Yeti to EVerest
|
||||
---------------
|
||||
|
||||
The following messages are relevant for LOW control mode:
|
||||
|
||||
``Event``: This is the most important message from Yeti. It will send an event
|
||||
on CP transitions:
|
||||
|
||||
* ``CAR_PLUGGED_IN``: CP State A -> B
|
||||
* ``CAR_REQUESTED_POWER``: CP State B->C or B->D
|
||||
* ``POWER_ON``: Relais switched on succesfully (i.e. after mirror contact check)
|
||||
* ``POWER_OFF``: Relais switched off succesfully
|
||||
* ``CAR_REQUESTED_STOP_POWER``: CP State C/D -> any other state
|
||||
* ``CAR_UNPLUGGED``: any other state -> A
|
||||
* ``ERROR_E``: any other state -> E
|
||||
* ``ERROR_DF``: Car diode failure detected
|
||||
* ``ERROR_RELAIS``: Relais error (mirror contact check failed)
|
||||
* ``ERROR_RCD``:: RCD over current event
|
||||
* ``ERROR_VENTILATION_NOT_AVAILABLE``: Car requested D but no ventilation available
|
||||
* ``ERROR_OVER_CURRENT``: Yeti detected quick over current on AC lines
|
||||
* ``ENTER_BCD``: any other state -> B/C/D. Used to start SLAC
|
||||
* ``LEAVE_BCD``: B/C/D -> any other state. Stops SLAC.
|
||||
* ``PERMANENT_FAULT``: Permanent fault that cannot be cleared by unplugging car
|
||||
|
||||
``PowerMeter``: Contains all data from the power measurement, sent at roughly
|
||||
1Hz
|
||||
|
||||
``KeepAliveLo``: Yeti sends this at 1Hz to keep up connection.
|
||||
|
||||
``ResetDone``: Sent once on boot of yeti firmware.
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
description: Driver module for the YETI hardware v1.0
|
||||
config:
|
||||
serial_port:
|
||||
description: Serial port the Yeti hardware is connected to
|
||||
type: string
|
||||
default: /dev/ttyUSB0
|
||||
baud_rate:
|
||||
description: Serial baud rate to use when communicating with Yeti hardware
|
||||
type: integer
|
||||
minimum: 9600
|
||||
maximum: 230400
|
||||
default: 115200
|
||||
reset_gpio_chip:
|
||||
description: >-
|
||||
Reset GPIO chip to use to HW reset Yeti. If set to empty string, it is disabled.
|
||||
type: string
|
||||
default: 'gpiochip0'
|
||||
reset_gpio:
|
||||
description: GPIO line to use to reset Yeti
|
||||
type: integer
|
||||
default: 27
|
||||
caps_min_current_A:
|
||||
description: Minimal current on AC side. For AC this is typically 6, but for HLC this can be less. -1 means use limit reported by HW.
|
||||
type: integer
|
||||
default: -1
|
||||
caps_max_current_A:
|
||||
description: Maximum current on AC side. For AC this is typically 16 or 32, but for HLC this can be less. -1 means use limit reported by HW.
|
||||
type: integer
|
||||
default: -1
|
||||
provides:
|
||||
powermeter:
|
||||
interface: powermeter
|
||||
description: provides the Yeti Internal Power Meter
|
||||
board_support:
|
||||
interface: evse_board_support
|
||||
description: provides the board support Interface to low level control control pilot, relais, motor lock
|
||||
rcd:
|
||||
interface: ac_rcd
|
||||
description: RCD interface of the onboard RCD
|
||||
connector_lock:
|
||||
interface: connector_lock
|
||||
description: Interface for the motor lock
|
||||
enable_telemetry: true
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Cornelius Claussen
|
||||
- Kai-Uwe Hermann
|
||||
- Thilo Molitor
|
||||
- Anton Wöllert
|
||||
@@ -0,0 +1,71 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
|
||||
#include "powermeterImpl.hpp"
|
||||
#include <utils/date.hpp>
|
||||
|
||||
namespace module {
|
||||
namespace powermeter {
|
||||
|
||||
static types::powermeter::Powermeter yeti_to_everest(const PowerMeter& p) {
|
||||
types::powermeter::Powermeter j;
|
||||
|
||||
j.timestamp = Everest::Date::to_rfc3339(date::utc_clock::now());
|
||||
j.meter_id = "YETI_POWERMETER";
|
||||
j.phase_seq_error = p.phaseSeqError;
|
||||
|
||||
j.energy_Wh_import.total = p.totalWattHr;
|
||||
j.energy_Wh_import.L1 = p.wattHrL1;
|
||||
j.energy_Wh_import.L2 = p.wattHrL2;
|
||||
j.energy_Wh_import.L3 = p.wattHrL3;
|
||||
|
||||
types::units::Power pwr;
|
||||
pwr.total = p.wattL1 + p.wattL2 + p.wattL3;
|
||||
pwr.L1 = p.wattL1;
|
||||
pwr.L2 = p.wattL2;
|
||||
pwr.L3 = p.wattL3;
|
||||
j.power_W = pwr;
|
||||
|
||||
types::units::Voltage volt;
|
||||
volt.L1 = p.vrmsL1;
|
||||
volt.L2 = p.vrmsL2;
|
||||
volt.L3 = p.vrmsL3;
|
||||
j.voltage_V = volt;
|
||||
|
||||
types::units::Current amp;
|
||||
amp.L1 = p.irmsL1;
|
||||
amp.L2 = p.irmsL2;
|
||||
amp.L3 = p.irmsL3;
|
||||
amp.N = p.irmsN;
|
||||
j.current_A = amp;
|
||||
|
||||
types::units::Frequency freq;
|
||||
freq.L1 = p.freqL1;
|
||||
freq.L2 = p.freqL2;
|
||||
freq.L3 = p.freqL3;
|
||||
j.frequency_Hz = freq;
|
||||
|
||||
return j;
|
||||
}
|
||||
|
||||
void powermeterImpl::init() {
|
||||
mod->serial.signalPowerMeter.connect([this](const PowerMeter& p) { publish_powermeter(yeti_to_everest(p)); });
|
||||
}
|
||||
|
||||
void powermeterImpl::ready() {
|
||||
}
|
||||
|
||||
types::powermeter::TransactionStopResponse powermeterImpl::handle_stop_transaction(std::string& transaction_id) {
|
||||
return {types::powermeter::TransactionRequestStatus::NOT_SUPPORTED,
|
||||
{},
|
||||
{},
|
||||
"YetiDriver powermeter does not support the stop_transaction command"};
|
||||
};
|
||||
|
||||
types::powermeter::TransactionStartResponse
|
||||
powermeterImpl::handle_start_transaction(types::powermeter::TransactionReq& value) {
|
||||
return {types::powermeter::TransactionRequestStatus::NOT_SUPPORTED,
|
||||
"YetiDriver powermeter does not support the start_transaction command"};
|
||||
}
|
||||
|
||||
} // namespace powermeter
|
||||
} // namespace module
|
||||
@@ -0,0 +1,63 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef POWERMETER_POWERMETER_IMPL_HPP
|
||||
#define POWERMETER_POWERMETER_IMPL_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 3
|
||||
//
|
||||
|
||||
#include <generated/interfaces/powermeter/Implementation.hpp>
|
||||
|
||||
#include "../YetiDriver.hpp"
|
||||
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
// insert your custom include headers here
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
|
||||
namespace module {
|
||||
namespace powermeter {
|
||||
|
||||
struct Conf {};
|
||||
|
||||
class powermeterImpl : public powermeterImplBase {
|
||||
public:
|
||||
powermeterImpl() = delete;
|
||||
powermeterImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<YetiDriver>& mod, Conf& config) :
|
||||
powermeterImplBase(ev, "powermeter"), 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 types::powermeter::TransactionStartResponse
|
||||
handle_start_transaction(types::powermeter::TransactionReq& value) override;
|
||||
virtual types::powermeter::TransactionStopResponse handle_stop_transaction(std::string& transaction_id) override;
|
||||
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
// insert your protected definitions here
|
||||
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
|
||||
|
||||
private:
|
||||
const Everest::PtrContainer<YetiDriver>& 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 powermeter
|
||||
} // namespace module
|
||||
|
||||
#endif // POWERMETER_POWERMETER_IMPL_HPP
|
||||
@@ -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 {
|
||||
|
||||
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
|
||||
} // namespace module
|
||||
@@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef RCD_AC_RCD_IMPL_HPP
|
||||
#define RCD_AC_RCD_IMPL_HPP
|
||||
|
||||
//
|
||||
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
// template version 3
|
||||
//
|
||||
|
||||
#include <generated/interfaces/ac_rcd/Implementation.hpp>
|
||||
|
||||
#include "../YetiDriver.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 {
|
||||
|
||||
struct Conf {};
|
||||
|
||||
class ac_rcdImpl : public ac_rcdImplBase {
|
||||
public:
|
||||
ac_rcdImpl() = delete;
|
||||
ac_rcdImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<YetiDriver>& mod, Conf& config) :
|
||||
ac_rcdImplBase(ev, "rcd"), 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<YetiDriver>& 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
|
||||
} // namespace module
|
||||
|
||||
#endif // RCD_AC_RCD_IMPL_HPP
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
# set the project name
|
||||
project(yeti_comms VERSION 0.1)
|
||||
# specify the C++ standard
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
# add the executable
|
||||
add_library(yeti_comms STATIC)
|
||||
ev_register_library_target(yeti_comms)
|
||||
|
||||
target_sources(yeti_comms
|
||||
PRIVATE
|
||||
evSerial.cpp
|
||||
protobuf/yeti.pb.c
|
||||
)
|
||||
|
||||
target_include_directories(yeti_comms
|
||||
PUBLIC
|
||||
"${PROJECT_BINARY_DIR}"
|
||||
protobuf
|
||||
)
|
||||
|
||||
target_link_libraries(yeti_comms
|
||||
PUBLIC
|
||||
date::date-tz
|
||||
everest::nanopb
|
||||
PRIVATE
|
||||
Pal::Sigslot
|
||||
everest::framework
|
||||
everest::gpio
|
||||
)
|
||||
@@ -0,0 +1,410 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
#include "evSerial.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <date/date.h>
|
||||
#include <date/tz.h>
|
||||
|
||||
#include <everest/3rd_party/nanopb/pb_decode.h>
|
||||
#include <everest/3rd_party/nanopb/pb_encode.h>
|
||||
|
||||
#include <everest/gpio/gpio.hpp>
|
||||
|
||||
#include "yeti.pb.h"
|
||||
|
||||
evSerial::evSerial() {
|
||||
fd = 0;
|
||||
baud = 0;
|
||||
reset_done_flag = false;
|
||||
forced_reset = false;
|
||||
cobsDecodeReset();
|
||||
}
|
||||
|
||||
evSerial::~evSerial() {
|
||||
if (fd)
|
||||
close(fd);
|
||||
}
|
||||
|
||||
bool evSerial::openDevice(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);
|
||||
cobsDecodeReset();
|
||||
|
||||
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 setSerialAttributes();
|
||||
}
|
||||
|
||||
bool evSerial::setSerialAttributes() {
|
||||
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;
|
||||
}
|
||||
// printf ("Success setting tcsetattr\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
void evSerial::cobsDecodeReset() {
|
||||
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;
|
||||
}
|
||||
// printf("%X",crc);
|
||||
return crc;
|
||||
}
|
||||
|
||||
void evSerial::handlePacket(uint8_t* buf, int len) {
|
||||
// printf ("packet received len %u\n", len);
|
||||
|
||||
// Check CRC32 (last 4 bytes)
|
||||
// uint32_t crc = calculateCrc(rx_packet_buf, rx_packet_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:
|
||||
// printf("Received keep_alive_lo\n");
|
||||
signalKeepAliveLo(msg_in.payload.keep_alive);
|
||||
// detect connection timeout if keep_alive packets stop coming...
|
||||
last_keep_alive_lo_timestamp = date::utc_clock::now();
|
||||
break;
|
||||
case McuToEverest_power_meter_tag: {
|
||||
auto unix_timestamp = std::chrono::seconds(std::time(NULL));
|
||||
msg_in.payload.power_meter.time_stamp = unix_timestamp.count();
|
||||
signalPowerMeter(msg_in.payload.power_meter);
|
||||
} break;
|
||||
case McuToEverest_cp_state_tag:
|
||||
signalCPState(msg_in.payload.cp_state);
|
||||
break;
|
||||
case McuToEverest_pp_state_tag:
|
||||
signalPPState(msg_in.payload.pp_state);
|
||||
break;
|
||||
case McuToEverest_relais_state_tag:
|
||||
signalRelaisState(msg_in.payload.relais_state);
|
||||
break;
|
||||
case McuToEverest_lock_state_tag:
|
||||
signalLockState(msg_in.payload.lock_state);
|
||||
break;
|
||||
case McuToEverest_error_flags_tag:
|
||||
signalErrorFlags(msg_in.payload.error_flags);
|
||||
break;
|
||||
case McuToEverest_reset_tag:
|
||||
// printf("Received reset_done\n");
|
||||
reset_done_flag = true;
|
||||
if (!forced_reset)
|
||||
signalSpuriousReset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void evSerial::cobsDecode(uint8_t* buf, int len) {
|
||||
for (int i = 0; i < len; i++)
|
||||
cobsDecodeByte(buf[i]);
|
||||
}
|
||||
|
||||
void evSerial::cobsDecodeByte(uint8_t byte) {
|
||||
// check max length
|
||||
if ((decode - msg == 2048 - 1) && byte != 0x00) {
|
||||
printf("cobsDecode: Buffer overflow\n");
|
||||
cobsDecodeReset();
|
||||
}
|
||||
|
||||
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");
|
||||
cobsDecodeReset();
|
||||
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
|
||||
handlePacket(msg, decode - 1 - msg);
|
||||
}
|
||||
cobsDecodeReset();
|
||||
return; // need to return here, because of block--
|
||||
}
|
||||
}
|
||||
block--;
|
||||
}
|
||||
|
||||
void evSerial::run() {
|
||||
readThreadHandle = std::thread(&evSerial::readThread, this);
|
||||
timeoutDetectionThreadHandle = std::thread(&evSerial::timeoutDetectionThread, this);
|
||||
}
|
||||
|
||||
void evSerial::timeoutDetectionThread() {
|
||||
while (true) {
|
||||
sleep(1);
|
||||
if (timeoutDetectionThreadHandle.shouldExit())
|
||||
break;
|
||||
if (serial_timed_out())
|
||||
signalConnectionTimeout();
|
||||
}
|
||||
}
|
||||
|
||||
void evSerial::readThread() {
|
||||
uint8_t buf[2048];
|
||||
int n;
|
||||
|
||||
cobsDecodeReset();
|
||||
while (true) {
|
||||
if (readThreadHandle.shouldExit())
|
||||
break;
|
||||
if (fd > 0) {
|
||||
n = read(fd, buf, sizeof buf);
|
||||
cobsDecode(buf, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool evSerial::linkWrite(EverestToMcu* m) {
|
||||
if (fd <= 0) {
|
||||
return false;
|
||||
}
|
||||
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 = cobsEncode(tx_packet_buf, tx_payload_len, encode_buf);
|
||||
// std::cout << "Write "<<tx_encode_len<<" bytes to serial port." << std::endl;
|
||||
write(fd, encode_buf, tx_encode_len);
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t evSerial::cobsEncode(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 timeSinceLastKeepAlive =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(now - last_keep_alive_lo_timestamp).count();
|
||||
if (timeSinceLastKeepAlive >= 5000)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void evSerial::setPWM(uint32_t dc) {
|
||||
EverestToMcu msg_out = EverestToMcu_init_default;
|
||||
msg_out.which_payload = EverestToMcu_pwm_duty_cycle_tag;
|
||||
msg_out.payload.pwm_duty_cycle = dc;
|
||||
linkWrite(&msg_out);
|
||||
}
|
||||
|
||||
void evSerial::allowPowerOn(bool p) {
|
||||
EverestToMcu msg_out = EverestToMcu_init_default;
|
||||
msg_out.which_payload = EverestToMcu_allow_power_on_tag;
|
||||
msg_out.payload.allow_power_on = p;
|
||||
linkWrite(&msg_out);
|
||||
}
|
||||
|
||||
void evSerial::forceUnlock() {
|
||||
EverestToMcu msg_out = EverestToMcu_init_default;
|
||||
msg_out.which_payload = EverestToMcu_connector_lock_tag;
|
||||
msg_out.payload.connector_lock = false;
|
||||
linkWrite(&msg_out);
|
||||
}
|
||||
|
||||
void evSerial::set_number_of_phases(bool p) {
|
||||
EverestToMcu msg_out = EverestToMcu_init_default;
|
||||
msg_out.which_payload = EverestToMcu_set_number_of_phases_tag;
|
||||
msg_out.payload.set_number_of_phases = p;
|
||||
linkWrite(&msg_out);
|
||||
}
|
||||
|
||||
bool evSerial::reset(const std::string& reset_chip, const int reset_line) {
|
||||
|
||||
reset_done_flag = false;
|
||||
forced_reset = true;
|
||||
|
||||
if (not reset_chip.empty()) {
|
||||
// Try to hardware reset Yeti controller to be in a known state
|
||||
Everest::Gpio reset_gpio;
|
||||
reset_gpio.open(reset_chip, reset_line);
|
||||
reset_gpio.set_output(true);
|
||||
reset_gpio.set(true);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
reset_gpio.set(false);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
reset_gpio.set(true);
|
||||
} 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;
|
||||
linkWrite(&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.
|
||||
keepAlive();
|
||||
keepAlive();
|
||||
keepAlive();
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void evSerial::firmwareUpdate(bool rom) {
|
||||
EverestToMcu msg_out = EverestToMcu_init_default;
|
||||
msg_out.which_payload = EverestToMcu_firmware_update_tag;
|
||||
msg_out.payload.firmware_update.invoke_rom_bootloader = rom;
|
||||
linkWrite(&msg_out);
|
||||
}
|
||||
|
||||
void evSerial::keepAlive() {
|
||||
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");
|
||||
linkWrite(&msg_out);
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef YETI_SERIAL
|
||||
#define YETI_SERIAL
|
||||
|
||||
#include "yeti.pb.h"
|
||||
#include <date/date.h>
|
||||
#include <date/tz.h>
|
||||
#include <sigslot/signal.hpp>
|
||||
#include <stdint.h>
|
||||
#include <termios.h>
|
||||
#include <utils/thread.hpp>
|
||||
|
||||
class evSerial {
|
||||
|
||||
public:
|
||||
evSerial();
|
||||
~evSerial();
|
||||
|
||||
bool openDevice(const char* device, int baud);
|
||||
bool is_open() {
|
||||
return fd > 0;
|
||||
};
|
||||
|
||||
void readThread();
|
||||
void run();
|
||||
|
||||
bool reset(const std::string& reset_chip, const int reset_line);
|
||||
void firmwareUpdate(bool rom);
|
||||
void keepAlive();
|
||||
|
||||
void setPWM(uint32_t dc);
|
||||
void allowPowerOn(bool p);
|
||||
void forceUnlock();
|
||||
void set_number_of_phases(bool p);
|
||||
|
||||
sigslot::signal<KeepAliveLo> signalKeepAliveLo;
|
||||
sigslot::signal<PowerMeter> signalPowerMeter;
|
||||
sigslot::signal<CpState> signalCPState;
|
||||
sigslot::signal<PpState> signalPPState;
|
||||
sigslot::signal<ErrorFlags> signalErrorFlags;
|
||||
sigslot::signal<bool> signalRelaisState;
|
||||
sigslot::signal<bool> signalLockState;
|
||||
|
||||
sigslot::signal<> signalSpuriousReset;
|
||||
sigslot::signal<> signalConnectionTimeout;
|
||||
|
||||
private:
|
||||
// Serial interface
|
||||
bool setSerialAttributes();
|
||||
int fd;
|
||||
int baud;
|
||||
|
||||
// COBS de-/encoder
|
||||
void cobsDecodeReset();
|
||||
void handlePacket(uint8_t* buf, int len);
|
||||
void cobsDecode(uint8_t* buf, int len);
|
||||
void cobsDecodeByte(uint8_t byte);
|
||||
size_t cobsEncode(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 readThreadHandle;
|
||||
Everest::Thread timeoutDetectionThreadHandle;
|
||||
|
||||
bool linkWrite(EverestToMcu* m);
|
||||
volatile bool reset_done_flag;
|
||||
volatile bool forced_reset;
|
||||
|
||||
bool serial_timed_out();
|
||||
void timeoutDetectionThread();
|
||||
std::chrono::time_point<date::utc_clock> last_keep_alive_lo_timestamp;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,126 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#ifndef YETI_FIRMWARE_VERSION
|
||||
#define YETI_FIRMWARE_VERSION
|
||||
|
||||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
/*
|
||||
Helper class to handle yeti Firmware Versions of the following style:
|
||||
|
||||
Reported by MCU: 2.0-1-g2d51638
|
||||
Included in file name: yetiR1_2.0-1_firmware.bin
|
||||
|
||||
*/
|
||||
|
||||
static std::vector<std::string> split_by_delimeters(const std::string& s, const std::string& delimeters) {
|
||||
std::regex re("[" + delimeters + "]");
|
||||
std::sregex_token_iterator first{s.begin(), s.end(), re, -1}, last;
|
||||
return {first, last};
|
||||
}
|
||||
|
||||
// parse version string from filename
|
||||
static std::string from_filename(const std::string& v) {
|
||||
auto tokens = split_by_delimeters(v, "_");
|
||||
if (tokens.size() >= 2) {
|
||||
return tokens[1];
|
||||
}
|
||||
return "0.0-0";
|
||||
}
|
||||
|
||||
class YetiFirmwareVersion {
|
||||
|
||||
public:
|
||||
YetiFirmwareVersion() {
|
||||
}
|
||||
|
||||
YetiFirmwareVersion(const std::string& _version_string) {
|
||||
from_string(_version_string);
|
||||
}
|
||||
|
||||
std::string to_string() {
|
||||
return std::to_string(major) + "." + std::to_string(minor) + "-" + std::to_string(revision);
|
||||
}
|
||||
|
||||
void from_string(const std::string& _version_string) {
|
||||
std::string version_string;
|
||||
// Is it a full filename or just a version string from the MCU?
|
||||
if (_version_string.rfind("yetiR", 0) == 0) {
|
||||
version_string = from_filename(_version_string);
|
||||
} else {
|
||||
version_string = _version_string;
|
||||
}
|
||||
|
||||
auto tokens = split_by_delimeters(version_string, ".-");
|
||||
|
||||
// parse into major, minor and revision
|
||||
if (tokens.size() >= 1) {
|
||||
try {
|
||||
major = std::stoi(tokens[0]);
|
||||
} catch (...) {
|
||||
// Set to 0 if we cannot parse it
|
||||
major = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens.size() >= 2) {
|
||||
try {
|
||||
minor = std::stoi(tokens[1]);
|
||||
} catch (...) {
|
||||
// Set to 0 if we cannot parse it
|
||||
minor = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (tokens.size() >= 3) {
|
||||
try {
|
||||
revision = std::stoi(tokens[2]);
|
||||
} catch (...) {
|
||||
// Set to 0 if we cannot parse it
|
||||
revision = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
YetiFirmwareVersion& operator=(const std::string& s) {
|
||||
from_string(s);
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend bool operator<(const YetiFirmwareVersion& l, const YetiFirmwareVersion& r) {
|
||||
if (l.major < r.major) {
|
||||
return true;
|
||||
} else if (l.major > r.major) {
|
||||
return false;
|
||||
} else if (l.minor < r.minor) {
|
||||
return true;
|
||||
} else if (l.minor > r.minor) {
|
||||
return false;
|
||||
} else if (l.revision < r.revision) {
|
||||
return true;
|
||||
} else if (l.revision > r.revision) {
|
||||
return false;
|
||||
}
|
||||
// they are identical
|
||||
return false;
|
||||
}
|
||||
|
||||
friend bool operator>(const YetiFirmwareVersion& lhs, const YetiFirmwareVersion& rhs) {
|
||||
return rhs < lhs;
|
||||
}
|
||||
|
||||
friend bool operator<=(const YetiFirmwareVersion& lhs, const YetiFirmwareVersion& rhs) {
|
||||
return not(lhs > rhs);
|
||||
}
|
||||
friend bool operator>=(const YetiFirmwareVersion& lhs, const YetiFirmwareVersion& rhs) {
|
||||
return not(lhs < rhs);
|
||||
}
|
||||
|
||||
private:
|
||||
int major{0};
|
||||
int minor{0};
|
||||
int revision{0};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
nanopb_generator.py -L "#include <everest/3rd_party/nanopb/%s>" -I . -D . yeti.proto
|
||||
@@ -0,0 +1,2 @@
|
||||
KeepAlive.sw_version_string max_length:50
|
||||
KeepAliveLo.sw_version_string max_length:50
|
||||
@@ -0,0 +1,37 @@
|
||||
/* Automatically generated nanopb constant definitions */
|
||||
/* Generated by nanopb-0.4.8 */
|
||||
|
||||
#include "yeti.pb.h"
|
||||
#if PB_PROTO_HEADER_VERSION != 40
|
||||
#error Regenerate this file with the current version of nanopb generator.
|
||||
#endif
|
||||
|
||||
PB_BIND(EverestToMcu, EverestToMcu, 2)
|
||||
|
||||
|
||||
PB_BIND(McuToEverest, McuToEverest, 2)
|
||||
|
||||
|
||||
PB_BIND(ErrorFlags, ErrorFlags, AUTO)
|
||||
|
||||
|
||||
PB_BIND(KeepAliveLo, KeepAliveLo, AUTO)
|
||||
|
||||
|
||||
PB_BIND(KeepAlive, KeepAlive, AUTO)
|
||||
|
||||
|
||||
PB_BIND(Telemetry, Telemetry, AUTO)
|
||||
|
||||
|
||||
PB_BIND(PowerMeter, PowerMeter, AUTO)
|
||||
|
||||
|
||||
PB_BIND(FirmwareUpdate, FirmwareUpdate, AUTO)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,399 @@
|
||||
/* Automatically generated nanopb header */
|
||||
/* Generated by nanopb-0.4.8 */
|
||||
|
||||
#ifndef PB_YETI_PB_H_INCLUDED
|
||||
#define PB_YETI_PB_H_INCLUDED
|
||||
#include <everest/3rd_party/nanopb/pb.h>
|
||||
|
||||
#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;
|
||||
|
||||
/* 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;
|
||||
bool over_current;
|
||||
} ErrorFlags;
|
||||
|
||||
typedef struct _KeepAliveLo {
|
||||
uint32_t time_stamp;
|
||||
uint32_t hw_type;
|
||||
uint32_t hw_revision;
|
||||
uint32_t protocol_version_major;
|
||||
uint32_t protocol_version_minor;
|
||||
char sw_version_string[51];
|
||||
float hwcap_max_current;
|
||||
float hwcap_min_current;
|
||||
uint32_t hwcap_max_phase_count;
|
||||
uint32_t hwcap_min_phase_count;
|
||||
bool supports_changing_phases_during_charging;
|
||||
} KeepAliveLo;
|
||||
|
||||
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 _PowerMeter {
|
||||
uint32_t time_stamp;
|
||||
float vrmsL1;
|
||||
float vrmsL2;
|
||||
float vrmsL3;
|
||||
float irmsL1;
|
||||
float irmsL2;
|
||||
float irmsL3;
|
||||
float irmsN;
|
||||
float wattHrL1;
|
||||
float wattHrL2;
|
||||
float wattHrL3;
|
||||
float totalWattHr;
|
||||
float tempL1;
|
||||
float tempL2;
|
||||
float tempL3;
|
||||
float tempN;
|
||||
float wattL1;
|
||||
float wattL2;
|
||||
float wattL3;
|
||||
float freqL1;
|
||||
float freqL2;
|
||||
float freqL3;
|
||||
bool phaseSeqError;
|
||||
} PowerMeter;
|
||||
|
||||
/* 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 {
|
||||
/* Needs to remain the same to allow firmware updates of older versions */
|
||||
KeepAliveLo keep_alive;
|
||||
/* Other IDs are 100+ to avoid compatibility issues with older firmware versions */
|
||||
ResetReason reset;
|
||||
CpState cp_state;
|
||||
bool relais_state; /* false: relais are off, true: relais are on */
|
||||
ErrorFlags error_flags;
|
||||
Telemetry telemetry;
|
||||
PpState pp_state;
|
||||
LockState lock_state;
|
||||
PowerMeter power_meter;
|
||||
} payload;
|
||||
} McuToEverest;
|
||||
|
||||
typedef struct _FirmwareUpdate {
|
||||
bool invoke_rom_bootloader;
|
||||
} FirmwareUpdate;
|
||||
|
||||
/* 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 {
|
||||
/* Needs to remain the same to allow firmware updates of older versions */
|
||||
FirmwareUpdate firmware_update;
|
||||
/* Other IDs are 100+ to avoid compatibility issues with older firmware versions */
|
||||
KeepAlive keep_alive;
|
||||
bool connector_lock; /* false: unlock, true: lock */
|
||||
uint32_t pwm_duty_cycle; /* in 0.01 %, 0 = State F, 10000 = X1 */
|
||||
bool allow_power_on;
|
||||
bool reset;
|
||||
bool set_number_of_phases;
|
||||
} payload;
|
||||
} 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 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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* Initializer values for message structs */
|
||||
#define EverestToMcu_init_default {0, {FirmwareUpdate_init_default}}
|
||||
#define McuToEverest_init_default {0, {KeepAliveLo_init_default}}
|
||||
#define ErrorFlags_init_default {0, 0, 0, 0, 0, 0, 0}
|
||||
#define KeepAliveLo_init_default {0, 0, 0, 0, 0, "", 0, 0, 0, 0, 0}
|
||||
#define KeepAlive_init_default {0, 0, 0, ""}
|
||||
#define Telemetry_init_default {0, 0}
|
||||
#define PowerMeter_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define FirmwareUpdate_init_default {0}
|
||||
#define EverestToMcu_init_zero {0, {FirmwareUpdate_init_zero}}
|
||||
#define McuToEverest_init_zero {0, {KeepAliveLo_init_zero}}
|
||||
#define ErrorFlags_init_zero {0, 0, 0, 0, 0, 0, 0}
|
||||
#define KeepAliveLo_init_zero {0, 0, 0, 0, 0, "", 0, 0, 0, 0, 0}
|
||||
#define KeepAlive_init_zero {0, 0, 0, ""}
|
||||
#define Telemetry_init_zero {0, 0}
|
||||
#define PowerMeter_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
#define FirmwareUpdate_init_zero {0}
|
||||
|
||||
/* 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 ErrorFlags_over_current_tag 7
|
||||
#define KeepAliveLo_time_stamp_tag 1
|
||||
#define KeepAliveLo_hw_type_tag 2
|
||||
#define KeepAliveLo_hw_revision_tag 3
|
||||
#define KeepAliveLo_protocol_version_major_tag 4
|
||||
#define KeepAliveLo_protocol_version_minor_tag 5
|
||||
#define KeepAliveLo_sw_version_string_tag 6
|
||||
#define KeepAliveLo_hwcap_max_current_tag 7
|
||||
#define KeepAliveLo_hwcap_min_current_tag 8
|
||||
#define KeepAliveLo_hwcap_max_phase_count_tag 9
|
||||
#define KeepAliveLo_hwcap_min_phase_count_tag 10
|
||||
#define KeepAliveLo_supports_changing_phases_during_charging_tag 11
|
||||
#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 PowerMeter_time_stamp_tag 1
|
||||
#define PowerMeter_vrmsL1_tag 2
|
||||
#define PowerMeter_vrmsL2_tag 3
|
||||
#define PowerMeter_vrmsL3_tag 4
|
||||
#define PowerMeter_irmsL1_tag 5
|
||||
#define PowerMeter_irmsL2_tag 6
|
||||
#define PowerMeter_irmsL3_tag 7
|
||||
#define PowerMeter_irmsN_tag 8
|
||||
#define PowerMeter_wattHrL1_tag 9
|
||||
#define PowerMeter_wattHrL2_tag 10
|
||||
#define PowerMeter_wattHrL3_tag 11
|
||||
#define PowerMeter_totalWattHr_tag 12
|
||||
#define PowerMeter_tempL1_tag 13
|
||||
#define PowerMeter_tempL2_tag 14
|
||||
#define PowerMeter_tempL3_tag 15
|
||||
#define PowerMeter_tempN_tag 16
|
||||
#define PowerMeter_wattL1_tag 17
|
||||
#define PowerMeter_wattL2_tag 18
|
||||
#define PowerMeter_wattL3_tag 19
|
||||
#define PowerMeter_freqL1_tag 20
|
||||
#define PowerMeter_freqL2_tag 21
|
||||
#define PowerMeter_freqL3_tag 22
|
||||
#define PowerMeter_phaseSeqError_tag 23
|
||||
#define McuToEverest_keep_alive_tag 3
|
||||
#define McuToEverest_reset_tag 101
|
||||
#define McuToEverest_cp_state_tag 102
|
||||
#define McuToEverest_relais_state_tag 103
|
||||
#define McuToEverest_error_flags_tag 104
|
||||
#define McuToEverest_telemetry_tag 105
|
||||
#define McuToEverest_pp_state_tag 106
|
||||
#define McuToEverest_lock_state_tag 107
|
||||
#define McuToEverest_power_meter_tag 108
|
||||
#define FirmwareUpdate_invoke_rom_bootloader_tag 1
|
||||
#define EverestToMcu_firmware_update_tag 16
|
||||
#define EverestToMcu_keep_alive_tag 100
|
||||
#define EverestToMcu_connector_lock_tag 102
|
||||
#define EverestToMcu_pwm_duty_cycle_tag 103
|
||||
#define EverestToMcu_allow_power_on_tag 104
|
||||
#define EverestToMcu_reset_tag 105
|
||||
#define EverestToMcu_set_number_of_phases_tag 106
|
||||
|
||||
/* Struct field encoding specification for nanopb */
|
||||
#define EverestToMcu_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,firmware_update,payload.firmware_update), 16) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,keep_alive,payload.keep_alive), 100) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payload,connector_lock,payload.connector_lock), 102) \
|
||||
X(a, STATIC, ONEOF, UINT32, (payload,pwm_duty_cycle,payload.pwm_duty_cycle), 103) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payload,allow_power_on,payload.allow_power_on), 104) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payload,reset,payload.reset), 105) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payload,set_number_of_phases,payload.set_number_of_phases), 106)
|
||||
#define EverestToMcu_CALLBACK NULL
|
||||
#define EverestToMcu_DEFAULT NULL
|
||||
#define EverestToMcu_payload_firmware_update_MSGTYPE FirmwareUpdate
|
||||
#define EverestToMcu_payload_keep_alive_MSGTYPE KeepAlive
|
||||
|
||||
#define McuToEverest_FIELDLIST(X, a) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,keep_alive,payload.keep_alive), 3) \
|
||||
X(a, STATIC, ONEOF, UENUM, (payload,reset,payload.reset), 101) \
|
||||
X(a, STATIC, ONEOF, UENUM, (payload,cp_state,payload.cp_state), 102) \
|
||||
X(a, STATIC, ONEOF, BOOL, (payload,relais_state,payload.relais_state), 103) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,error_flags,payload.error_flags), 104) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,telemetry,payload.telemetry), 105) \
|
||||
X(a, STATIC, ONEOF, UENUM, (payload,pp_state,payload.pp_state), 106) \
|
||||
X(a, STATIC, ONEOF, UENUM, (payload,lock_state,payload.lock_state), 107) \
|
||||
X(a, STATIC, ONEOF, MESSAGE, (payload,power_meter,payload.power_meter), 108)
|
||||
#define McuToEverest_CALLBACK NULL
|
||||
#define McuToEverest_DEFAULT NULL
|
||||
#define McuToEverest_payload_keep_alive_MSGTYPE KeepAliveLo
|
||||
#define McuToEverest_payload_error_flags_MSGTYPE ErrorFlags
|
||||
#define McuToEverest_payload_telemetry_MSGTYPE Telemetry
|
||||
#define McuToEverest_payload_power_meter_MSGTYPE PowerMeter
|
||||
|
||||
#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) \
|
||||
X(a, STATIC, SINGULAR, BOOL, over_current, 7)
|
||||
#define ErrorFlags_CALLBACK NULL
|
||||
#define ErrorFlags_DEFAULT NULL
|
||||
|
||||
#define KeepAliveLo_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, UINT32, protocol_version_major, 4) \
|
||||
X(a, STATIC, SINGULAR, UINT32, protocol_version_minor, 5) \
|
||||
X(a, STATIC, SINGULAR, STRING, sw_version_string, 6) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, hwcap_max_current, 7) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, hwcap_min_current, 8) \
|
||||
X(a, STATIC, SINGULAR, UINT32, hwcap_max_phase_count, 9) \
|
||||
X(a, STATIC, SINGULAR, UINT32, hwcap_min_phase_count, 10) \
|
||||
X(a, STATIC, SINGULAR, BOOL, supports_changing_phases_during_charging, 11)
|
||||
#define KeepAliveLo_CALLBACK NULL
|
||||
#define KeepAliveLo_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 PowerMeter_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, UINT32, time_stamp, 1) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, vrmsL1, 2) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, vrmsL2, 3) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, vrmsL3, 4) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, irmsL1, 5) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, irmsL2, 6) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, irmsL3, 7) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, irmsN, 8) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, wattHrL1, 9) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, wattHrL2, 10) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, wattHrL3, 11) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, totalWattHr, 12) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, tempL1, 13) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, tempL2, 14) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, tempL3, 15) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, tempN, 16) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, wattL1, 17) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, wattL2, 18) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, wattL3, 19) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, freqL1, 20) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, freqL2, 21) \
|
||||
X(a, STATIC, SINGULAR, FLOAT, freqL3, 22) \
|
||||
X(a, STATIC, SINGULAR, BOOL, phaseSeqError, 23)
|
||||
#define PowerMeter_CALLBACK NULL
|
||||
#define PowerMeter_DEFAULT NULL
|
||||
|
||||
#define FirmwareUpdate_FIELDLIST(X, a) \
|
||||
X(a, STATIC, SINGULAR, BOOL, invoke_rom_bootloader, 1)
|
||||
#define FirmwareUpdate_CALLBACK NULL
|
||||
#define FirmwareUpdate_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 KeepAliveLo_msg;
|
||||
extern const pb_msgdesc_t KeepAlive_msg;
|
||||
extern const pb_msgdesc_t Telemetry_msg;
|
||||
extern const pb_msgdesc_t PowerMeter_msg;
|
||||
extern const pb_msgdesc_t FirmwareUpdate_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 KeepAliveLo_fields &KeepAliveLo_msg
|
||||
#define KeepAlive_fields &KeepAlive_msg
|
||||
#define Telemetry_fields &Telemetry_msg
|
||||
#define PowerMeter_fields &PowerMeter_msg
|
||||
#define FirmwareUpdate_fields &FirmwareUpdate_msg
|
||||
|
||||
/* Maximum encoded size of messages (where known) */
|
||||
#define ErrorFlags_size 14
|
||||
#define EverestToMcu_size 73
|
||||
#define FirmwareUpdate_size 2
|
||||
#define KeepAliveLo_size 106
|
||||
#define KeepAlive_size 70
|
||||
#define McuToEverest_size 124
|
||||
#define PowerMeter_size 121
|
||||
#define Telemetry_size 12
|
||||
#define YETI_PB_H_MAX_SIZE McuToEverest_size
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,134 @@
|
||||
syntax = "proto3";
|
||||
|
||||
/*
|
||||
This container message is send from EVerest to MCU and may contain any allowed message in that direction.
|
||||
*/
|
||||
message EverestToMcu {
|
||||
oneof payload {
|
||||
// Needs to remain the same to allow firmware updates of older versions
|
||||
FirmwareUpdate firmware_update = 16;
|
||||
|
||||
// Other IDs are 100+ to avoid compatibility issues with older firmware versions
|
||||
KeepAlive keep_alive = 100;
|
||||
bool connector_lock = 102; // false: unlock, true: lock
|
||||
uint32 pwm_duty_cycle = 103; // in 0.01 %, 0 = State F, 10000 = X1
|
||||
bool allow_power_on = 104;
|
||||
bool reset = 105;
|
||||
bool set_number_of_phases = 106;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
This container message is send from MCU to EVerest and may contain any allowed message in that direction.
|
||||
*/
|
||||
message McuToEverest {
|
||||
oneof payload {
|
||||
// Needs to remain the same to allow firmware updates of older versions
|
||||
KeepAliveLo keep_alive = 3;
|
||||
|
||||
// Other IDs are 100+ to avoid compatibility issues with older firmware versions
|
||||
ResetReason reset = 101;
|
||||
CpState cp_state = 102;
|
||||
bool relais_state = 103; // false: relais are off, true: relais are on
|
||||
ErrorFlags error_flags = 104;
|
||||
Telemetry telemetry = 105;
|
||||
PpState pp_state = 106;
|
||||
LockState lock_state = 107;
|
||||
PowerMeter power_meter = 108;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
bool over_current = 7;
|
||||
}
|
||||
|
||||
enum ResetReason {
|
||||
USER = 0;
|
||||
WATCHDOG = 1;
|
||||
}
|
||||
|
||||
message KeepAliveLo {
|
||||
uint32 time_stamp = 1;
|
||||
uint32 hw_type = 2;
|
||||
uint32 hw_revision = 3;
|
||||
uint32 protocol_version_major = 4;
|
||||
uint32 protocol_version_minor = 5;
|
||||
string sw_version_string = 6;
|
||||
float hwcap_max_current = 7;
|
||||
float hwcap_min_current = 8;
|
||||
uint32 hwcap_max_phase_count = 9;
|
||||
uint32 hwcap_min_phase_count = 10;
|
||||
bool supports_changing_phases_during_charging = 11;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
enum LockState {
|
||||
UNDEFINED = 0;
|
||||
UNLOCKED = 1;
|
||||
LOCKED = 2;
|
||||
}
|
||||
|
||||
message PowerMeter {
|
||||
uint32 time_stamp = 1;
|
||||
float vrmsL1 = 2;
|
||||
float vrmsL2 = 3;
|
||||
float vrmsL3 = 4;
|
||||
float irmsL1 = 5;
|
||||
float irmsL2 = 6;
|
||||
float irmsL3 = 7;
|
||||
float irmsN = 8;
|
||||
float wattHrL1 = 9;
|
||||
float wattHrL2 = 10;
|
||||
float wattHrL3 = 11;
|
||||
float totalWattHr = 12;
|
||||
float tempL1 = 13;
|
||||
float tempL2 = 14;
|
||||
float tempL3 = 15;
|
||||
float tempN = 16;
|
||||
float wattL1 = 17;
|
||||
float wattL2 = 18;
|
||||
float wattL3 = 19;
|
||||
float freqL1 = 20;
|
||||
float freqL2 = 21;
|
||||
float freqL3 = 22;
|
||||
bool phaseSeqError = 23;
|
||||
}
|
||||
|
||||
message FirmwareUpdate {
|
||||
bool invoke_rom_bootloader = 1;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
# set the project name
|
||||
project(yeti_fwupdate VERSION 0.1)
|
||||
# specify the C++ standard
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
# add the executable
|
||||
add_executable(yeti_fwupdate main.cpp)
|
||||
target_include_directories(yeti_fwupdate PUBLIC "${PROJECT_BINARY_DIR}" PUBLIC "../yeti_comms/nanopb" PUBLIC "../yeti_comms/protobuf" PUBLIC "../yeti_comms")
|
||||
target_link_libraries(yeti_fwupdate PRIVATE Pal::Sigslot Threads::Threads yeti_comms everest::framework)
|
||||
|
||||
install(TARGETS yeti_fwupdate)
|
||||
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "evSerial.h"
|
||||
#include <filesystem>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "firmware_version.hpp"
|
||||
#include "yeti.pb.h"
|
||||
#include <sigslot/signal.hpp>
|
||||
|
||||
std::atomic_bool sw_version_received = false;
|
||||
YetiFirmwareVersion installed_fw_version;
|
||||
std::string installed_fw_version_orig;
|
||||
|
||||
void recvKeepAliveLo(KeepAliveLo s) {
|
||||
installed_fw_version = s.sw_version_string;
|
||||
installed_fw_version_orig = s.sw_version_string;
|
||||
sw_version_received = true;
|
||||
}
|
||||
|
||||
void help() {
|
||||
printf("\nUsage: ./yeti_fwupdate /dev/ttyXXX firmware.bin\n\n");
|
||||
printf("This tool uses stm32flash (version 0.6 and above) which needs to be installed.\n");
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
printf("Yeti ROM Bootloader Firmware Updater\n");
|
||||
if (argc != 3) {
|
||||
help();
|
||||
exit(0);
|
||||
}
|
||||
const char* device = argv[1];
|
||||
std::filesystem::path filename = argv[2];
|
||||
|
||||
evSerial* p = new evSerial();
|
||||
|
||||
if (p->openDevice(device, 115200)) {
|
||||
// printf("Running\n");
|
||||
p->run();
|
||||
p->signalKeepAliveLo.connect(recvKeepAliveLo);
|
||||
while (true) {
|
||||
if (sw_version_received)
|
||||
break;
|
||||
usleep(100);
|
||||
}
|
||||
|
||||
YetiFirmwareVersion update_file_fw_version{filename.filename()};
|
||||
printf("Installed Yeti Firmware Version: %s (%s)\n", installed_fw_version.to_string().c_str(),
|
||||
installed_fw_version_orig.c_str());
|
||||
printf("Update File Firmware Version: %s\n", update_file_fw_version.to_string().c_str());
|
||||
|
||||
if (installed_fw_version >= update_file_fw_version) {
|
||||
printf("Latest version already installed. Exiting.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
printf("\nRebooting Yeti in ROM Bootloader mode...\n");
|
||||
// send some dummy commands to make sure protocol is in sync
|
||||
p->keepAlive();
|
||||
p->keepAlive();
|
||||
|
||||
// now reboot uC in boot loader mode
|
||||
p->firmwareUpdate(true);
|
||||
sleep(1);
|
||||
delete p;
|
||||
|
||||
sleep(1);
|
||||
char cmd[1000];
|
||||
sprintf(cmd, "stm32flash -b 115200 %.100s -v -w %.100s -R", device, filename.string().c_str());
|
||||
// sprintf(cmd, "stm32flash -b115200 %.100s", device);
|
||||
printf("Executing %s ...\n", cmd);
|
||||
system(cmd);
|
||||
|
||||
// printf ("Joining\n");
|
||||
} else {
|
||||
printf("Cannot open device \"%s\"\n", device);
|
||||
delete p;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user