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:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,31 @@
#
# 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
target_sources(${MODULE_NAME}
PRIVATE
main/can_broker.cpp
)
target_link_libraries(${MODULE_NAME}
PRIVATE
can_protocols::dpm1000
everest::gpio
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
target_sources(${MODULE_NAME}
PRIVATE
"main/power_supply_DCImpl.cpp"
)
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "DPM1000.hpp"
namespace module {
void DPM1000::init() {
invoke_init(*p_main);
}
void DPM1000::ready() {
invoke_ready(*p_main);
}
} // namespace module

View File

@@ -0,0 +1,69 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef DPM1000_HPP
#define DPM1000_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for provided interface implementations
#include <generated/interfaces/power_supply_DC/Implementation.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
// insert your custom include headers here
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {
std::string device;
int device_address;
double power_limit_W;
double current_limit_A;
double voltage_limit_V;
std::string series_parallel_mode;
std::string discharge_gpio_chip;
int discharge_gpio_line;
bool discharge_gpio_polarity;
bool debug_print_all_telemetry;
};
class DPM1000 : public Everest::ModuleBase {
public:
DPM1000() = delete;
DPM1000(const ModuleInfo& info, std::unique_ptr<power_supply_DCImplBase> p_main, Conf& config) :
ModuleBase(info), p_main(std::move(p_main)), config(config){};
const std::unique_ptr<power_supply_DCImplBase> p_main;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
// insert your public definitions here
// 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
// 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 // DPM1000_HPP

View File

@@ -0,0 +1,221 @@
#include "can_broker.hpp"
#include <cstring>
#include <stdexcept>
#include <net/if.h>
#include <poll.h>
#include <sys/eventfd.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <unistd.h>
namespace dpm1000 = can::protocol::dpm1000;
// FIXME (aw): this helper doesn't really belong here
static void throw_with_error(const std::string& msg) {
throw std::runtime_error(msg + ": (" + std::string(strerror(errno)) + ")");
}
CanBroker::CanBroker(const std::string& interface_name, uint8_t _device_src) : device_src(_device_src) {
can_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
if (can_fd == -1) {
throw_with_error("Failed to open socket");
}
// retrieve interface index from interface name
struct ifreq ifr;
if (interface_name.size() >= sizeof(ifr.ifr_name)) {
throw_with_error("Interface name too long: " + interface_name);
} else {
strcpy(ifr.ifr_name, interface_name.c_str());
}
if (ioctl(can_fd, SIOCGIFINDEX, &ifr) == -1) {
throw_with_error("Failed with ioctl/SIOCGIFINDEX on interface " + interface_name);
}
// bind to the interface
struct sockaddr_can addr;
memset(&addr, 0, sizeof(addr));
addr.can_family = AF_CAN;
addr.can_ifindex = ifr.ifr_ifindex;
if (bind(can_fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)) == -1) {
throw_with_error("Failed with bind");
}
event_fd = eventfd(0, 0);
loop_thread = std::thread(&CanBroker::loop, this);
}
CanBroker::~CanBroker() {
uint64_t quit_value = 1;
write(event_fd, &quit_value, sizeof(quit_value));
loop_thread.join();
close(can_fd);
close(event_fd);
}
void CanBroker::loop() {
std::array<struct pollfd, 2> pollfds = {{
{can_fd, POLLIN, 0},
{event_fd, POLLIN, 0},
}};
while (true) {
const auto poll_result = poll(pollfds.data(), pollfds.size(), -1);
if (poll_result == 0) {
// timeout
continue;
}
if (pollfds[0].revents & POLLIN) {
struct can_frame frame;
read(can_fd, &frame, sizeof(frame));
handle_can_input(frame);
}
if (pollfds[1].revents & POLLIN) {
uint64_t tmp;
read(event_fd, &tmp, sizeof(tmp));
// new event, for now, we do not care, later on we could check, if it is an exit event code
return;
}
}
}
void CanBroker::set_state(bool enabled) {
struct can_frame frame;
dpm1000::power_on(frame, enabled, enabled);
dpm1000::set_header(frame, monitor_id, device_src);
write_to_can(frame);
// Do an extra module ON command as sometimes the bits in the header are not enough to actually switch on
set_data_int(dpm1000::def::SetValueType::SWITCH_ON_OFF_SETTING, (enabled ? 0 : 1));
}
CanBroker::AccessReturnType CanBroker::dispatch_frame(const struct can_frame& frame, uint16_t id,
uint32_t* return_payload) {
// wait until we get access
std::lock_guard<std::mutex> access_lock(access_mtx);
std::unique_lock<std::mutex> request_lock(request.mutex);
write_to_can(frame);
request.msg_type = id;
request.state = CanRequest::State::ISSUED;
const auto finished = request.cv.wait_for(request_lock, ACCESS_TIMEOUT,
[this]() { return request.state != CanRequest::State::ISSUED; });
if (not finished) {
return AccessReturnType::TIMEOUT;
}
if (request.state == CanRequest::State::FAILED) {
return AccessReturnType::FAILED;
}
// success
if (return_payload) {
memcpy(return_payload, request.response.data(), sizeof(std::remove_pointer_t<decltype(return_payload)>));
}
return AccessReturnType::SUCCESS;
}
CanBroker::AccessReturnType CanBroker::read_data(dpm1000::def::ReadValueType value_type, float& result) {
const auto id = static_cast<std::underlying_type_t<decltype(value_type)>>(value_type);
struct can_frame frame;
dpm1000::request_data(frame, value_type);
dpm1000::set_header(frame, monitor_id, device_src);
uint32_t tmp;
const auto status = dispatch_frame(frame, id, &tmp);
if (status == AccessReturnType::SUCCESS) {
memcpy(&result, &tmp, sizeof(result));
}
return status;
}
CanBroker::AccessReturnType CanBroker::read_data_int(dpm1000::def::ReadValueType value_type, uint32_t& result) {
const auto id = static_cast<std::underlying_type_t<decltype(value_type)>>(value_type);
struct can_frame frame;
dpm1000::request_data(frame, value_type);
dpm1000::set_header(frame, monitor_id, device_src);
uint32_t tmp;
const auto status = dispatch_frame(frame, id, &tmp);
if (status == AccessReturnType::SUCCESS) {
result = tmp;
}
return status;
}
CanBroker::AccessReturnType CanBroker::set_data(dpm1000::def::SetValueType value_type, float payload) {
const auto id = static_cast<std::underlying_type_t<decltype(value_type)>>(value_type);
uint8_t raw_payload[sizeof(payload)];
memcpy(raw_payload, &payload, sizeof(payload));
struct can_frame frame;
dpm1000::set_data(frame, value_type, {raw_payload[3], raw_payload[2], raw_payload[1], raw_payload[0]});
dpm1000::set_header(frame, monitor_id, device_src);
return dispatch_frame(frame, id);
}
CanBroker::AccessReturnType CanBroker::set_data_int(dpm1000::def::SetValueType value_type, uint32_t payload) {
const auto id = static_cast<std::underlying_type_t<decltype(value_type)>>(value_type);
uint8_t raw_payload[sizeof(payload)];
memcpy(raw_payload, &payload, sizeof(payload));
struct can_frame frame;
dpm1000::set_data(frame, value_type, {raw_payload[3], raw_payload[2], raw_payload[1], raw_payload[0]});
dpm1000::set_header(frame, monitor_id, device_src);
return dispatch_frame(frame, id);
}
void CanBroker::write_to_can(const struct can_frame& frame) {
write(can_fd, &frame, sizeof(frame));
}
void CanBroker::handle_can_input(const struct can_frame& frame) {
if (((frame.can_id >> dpm1000::def::MESSAGE_HEADER_BIT_SHIFT) & dpm1000::def::MESSAGE_HEADER_MASK) !=
dpm1000::def::MESSAGE_HEADER) {
return;
}
std::unique_lock<std::mutex> request_lock(request.mutex);
if ((request.state != CanRequest::State::ISSUED) or (request.msg_type != dpm1000::parse_msg_type(frame))) {
return;
}
if (dpm1000::is_error_flag_set(frame)) {
request.state = CanRequest::State::FAILED;
} else {
// this is ugly
for (auto i = 0; i < request.response.size(); ++i) {
request.response[i] = frame.data[7 - i];
}
request.state = CanRequest::State::COMPLETED;
}
request_lock.unlock();
request.cv.notify_one();
}

View File

@@ -0,0 +1,70 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef DPM1000_MAIN_DC_CAN_BROKER_HPP
#define DPM1000_MAIN_DC_CAN_BROKER_HPP
#include <array>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <stdexcept>
#include <string>
#include <thread>
#include <everest/can/protocol/dpm1000.hpp>
struct CanRequest {
enum class State {
IDLE,
ISSUED,
COMPLETED,
FAILED,
} state{State::IDLE};
uint16_t msg_type;
std::array<uint8_t, 4> response;
std::condition_variable cv;
std::mutex mutex;
};
class CanBroker {
public:
enum class AccessReturnType {
SUCCESS,
FAILED,
TIMEOUT,
NOT_READY,
};
CanBroker(const std::string& interface_name, uint8_t _device_src);
AccessReturnType read_data(can::protocol::dpm1000::def::ReadValueType, float& result);
AccessReturnType read_data_int(can::protocol::dpm1000::def::ReadValueType, uint32_t& result);
AccessReturnType set_data(can::protocol::dpm1000::def::SetValueType, float value);
AccessReturnType set_data_int(can::protocol::dpm1000::def::SetValueType, uint32_t value);
void set_state(bool enabled);
~CanBroker();
private:
constexpr static auto ACCESS_TIMEOUT = std::chrono::milliseconds(250);
void loop();
void write_to_can(const struct can_frame& frame);
AccessReturnType dispatch_frame(const struct can_frame& frame, uint16_t id, uint32_t* result = nullptr);
void handle_can_input(const struct can_frame& frame);
uint8_t device_src;
std::mutex access_mtx;
CanRequest request;
const uint8_t monitor_id{0xf0};
std::thread loop_thread;
int event_fd{-1};
int can_fd{-1};
};
#endif // DPM1000_MAIN_DC_CAN_BROKER_HPP

View File

@@ -0,0 +1,294 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include "power_supply_DCImpl.hpp"
#include <memory>
#include "can_broker.hpp"
#include <fmt/core.h>
#include <utils/formatter.hpp>
std::unique_ptr<CanBroker> can_broker;
namespace dpm1000 = can::protocol::dpm1000;
namespace module {
namespace main {
static void log_status_on_fail(const std::string& msg, CanBroker::AccessReturnType status) {
using ReturnStatus = CanBroker::AccessReturnType;
std::string reason;
switch (status) {
case ReturnStatus::FAILED:
reason = "failed";
break;
case ReturnStatus::NOT_READY:
reason = "not ready";
break;
case ReturnStatus::TIMEOUT:
reason = "timeout";
default:
return;
}
EVLOG_info << msg << " reason: (" << reason << ")";
}
static std::string alarm_to_string(uint32_t alarm) {
std::string alarmflags;
if (alarm & (1 << (int)dpm1000::def::Alarm::FUSE_BURN_OUT))
alarmflags += "[FUSE_BURN_OUT]";
if (alarm & (1 << (int)dpm1000::def::Alarm::PFC_DCDC_COMMUNICATION_ERROR))
alarmflags += "[PFC_DCDC_COMMUNICATION_ERROR]";
if (alarm & (1 << (int)dpm1000::def::Alarm::UNBALANCED_BUS_VOLTAGE))
alarmflags += "[UNBALANCED_BUS_VOLTAGE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::BUS_OVER_VOLTAGE))
alarmflags += "[BUS_OVER_VOLTAGE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::BUS_ABNORMAL_VOLTAGE))
alarmflags += "[BUS_ABNORMAL_VOLTAGE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::PHASE_OVER_VOLTAGE))
alarmflags += "[PHASE_OVER_VOLTAGE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::ID_NUMBER_REPETITION))
alarmflags += "[ID_NUMBER_REPETITION]";
if (alarm & (1 << (int)dpm1000::def::Alarm::BUS_UNDER_VOLTAGE))
alarmflags += "[BUS_UNDER_VOLTAGE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::PHASE_LOSS))
alarmflags += "[PHASE_LOSS]";
if (alarm & (1 << (int)dpm1000::def::Alarm::PHASE_UNDER_VOLTAGE))
alarmflags += "[PHASE_UNDER_VOLTAGE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::CAN_COMMUNICATION_FAULT))
alarmflags += "[CAN_COMMUNICATION_FAULT]";
if (alarm & (1 << (int)dpm1000::def::Alarm::DCDC_UNEVEN_CURRENT_SHARING))
alarmflags += "[DCDC_UNEVEN_CURRENT_SHARING]";
if (alarm & (1 << (int)dpm1000::def::Alarm::PFC_POWER_OFF))
alarmflags += "[PFC_POWER_OFF]";
if (alarm & (1 << (int)dpm1000::def::Alarm::FAN_FULL_SPEED))
alarmflags += "[FAN_FULL_SPEED]";
if (alarm & (1 << (int)dpm1000::def::Alarm::POWER_LIMITING))
alarmflags += "[POWER_LIMITING]";
if (alarm & (1 << (int)dpm1000::def::Alarm::DCDC_POWER_OFF))
alarmflags += "[DCDC_POWER_OFF]";
if (alarm & (1 << (int)dpm1000::def::Alarm::TEMPERATURE_POWER_LIMITING))
alarmflags += "[TEMPERATURE_POWER_LIMITING]";
if (alarm & (1 << (int)dpm1000::def::Alarm::AC_POWER_LIMITING))
alarmflags += "[AC_POWER_LIMITING]";
if (alarm & (1 << (int)dpm1000::def::Alarm::DCDC_EEPROM_FAULTS))
alarmflags += "[DCDC_EEPROM_FAULTS]";
if (alarm & (1 << (int)dpm1000::def::Alarm::FAN_FAULTS))
alarmflags += "[FAN_FAULTS]";
if (alarm & (1 << (int)dpm1000::def::Alarm::DCDC_SHORT_CIRCUIT))
alarmflags += "[DCDC_SHORT_CIRCUIT]";
if (alarm & (1 << (int)dpm1000::def::Alarm::PFC_EEPROM_FAULTS))
alarmflags += "[PFC_EEPROM_FAULTS]";
if (alarm & (1 << (int)dpm1000::def::Alarm::DCDC_OVER_TEMPERATURE))
alarmflags += "[DCDC_OVER_TEMPERATURE]";
if (alarm & (1 << (int)dpm1000::def::Alarm::DCDC_OUTPUT_OVER_VOLTAGE))
alarmflags += "[DCDC_OUTPUT_OVER_VOLTAGE]";
return alarmflags;
}
void power_supply_DCImpl::init() {
current = 0;
voltage = 300;
config_current_limit = mod->config.current_limit_A;
config_voltage_limit = mod->config.voltage_limit_V;
config_power_limit = mod->config.power_limit_W;
if (!mod->config.discharge_gpio_chip.empty()) {
discharge_gpio.open(mod->config.discharge_gpio_chip, mod->config.discharge_gpio_line,
!mod->config.discharge_gpio_polarity);
discharge_gpio.set_output(false);
}
can_broker = std::make_unique<CanBroker>(mod->config.device, mod->config.device_address);
// ensure the module is switched off
can_broker->set_state(false);
// Configure module for series or parallel mode
// 0 is automatic switching mode
float series_parallel_mode = 0.;
if (mod->config.series_parallel_mode == "Series") {
series_parallel_mode = 1050.;
config_min_voltage_limit = 300.;
parallel_mode = false;
} else if (mod->config.series_parallel_mode == "Parallel") {
series_parallel_mode = 520.;
if (config_voltage_limit > 520) {
config_voltage_limit = 520;
}
parallel_mode = true;
}
// WTF: This really uses a float to set one of the three modes automatic, series or parallel.
auto status = can_broker->set_data(dpm1000::def::SetValueType::SERIES_PARALLEL_MODE, series_parallel_mode);
log_status_on_fail("Set current limit failed", status);
}
void power_supply_DCImpl::ready() {
types::power_supply_DC::Capabilities caps;
caps.bidirectional = false;
caps.max_export_current_A = config_current_limit;
caps.max_export_voltage_V = config_voltage_limit;
caps.min_export_current_A = 0;
caps.min_export_voltage_V = config_min_voltage_limit;
caps.max_export_power_W = config_power_limit;
caps.current_regulation_tolerance_A = 0.5;
caps.peak_current_ripple_A = 1;
caps.conversion_efficiency_export = 0.95;
publish_capabilities(caps);
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(250));
// Send voltage, current and power limits
auto status = can_broker->set_data(dpm1000::def::SetValueType::CURRENT_LIMIT, current);
log_status_on_fail("Set current limit failed", status);
status = can_broker->set_data(dpm1000::def::SetValueType::DEFAULT_CURRENT_LIMIT, 1.0);
log_status_on_fail("Set default current limit failed", status);
status = can_broker->set_data(dpm1000::def::SetValueType::VOLTAGE, voltage);
log_status_on_fail("Set voltage failed", status);
status = can_broker->set_data(dpm1000::def::SetValueType::POWER_LIMIT, 1.0);
log_status_on_fail("Set current limit failed", status);
// Read voltage and current
float tmp;
types::power_supply_DC::VoltageCurrent vc;
status = can_broker->read_data(dpm1000::def::ReadValueType::VOLTAGE, tmp);
log_status_on_fail("Read voltage failed", status);
if (status != CanBroker::AccessReturnType::SUCCESS) {
continue;
}
vc.voltage_V = tmp;
status = can_broker->read_data(dpm1000::def::ReadValueType::CURRENT, tmp);
log_status_on_fail("Read current failed", status);
if (status != CanBroker::AccessReturnType::SUCCESS) {
continue;
}
// Publish voltage and current var
// Current scaling depends on series/parallel mode operation.
vc.current_A = tmp;
if (parallel_mode) {
vc.current_A *= 2.;
}
publish_voltage_current(vc);
// read alarm flags
uint32_t alarm = 0;
status = can_broker->read_data_int(dpm1000::def::ReadValueType::ALARM, alarm);
log_status_on_fail("Read alarm failed", status);
if (status == CanBroker::AccessReturnType::SUCCESS) {
if (last_alarm_flags != alarm) {
auto alarmflags = alarm_to_string(alarm);
if (alarmflags != "") {
EVLOG_warning << "Alarm flags changed: " << alarmflags;
} else {
EVLOG_info << "All Alarm flags cleared.";
}
last_alarm_flags = alarm;
}
}
// Discharge output if it is higher then setpoint voltage.
// Note that this has no timeout, so HW must be designed to sustain the worst case load (e.g. 1000V) continously
if (vc.voltage_V > (voltage + 10)) {
discharge_gpio.set(true);
} else {
discharge_gpio.set(false);
}
if (mod->config.debug_print_all_telemetry) {
// read additional meta data
float current_real_part = 0, current_limit = 0, dcdc_temperature = 0, ac_voltage = 0, voltage_limit = 0,
pfc0_voltage = 0, pfc1_voltage = 0, env_temperature = 0, ac_voltage_phase_a = 0,
ac_voltage_phase_b = 0, ac_voltage_phase_c = 0, pfc_temperature = 0, power_limit = 0;
status = can_broker->read_data(dpm1000::def::ReadValueType::CURRENT_REAL_PART, current_real_part);
log_status_on_fail("Read CURRENT_REAL_PART failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::CURRENT_LIMIT, current_limit);
log_status_on_fail("Read CURRENT_LIMIT failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::DCDC_TEMPERATURE, dcdc_temperature);
log_status_on_fail("Read DCDC_TEMPERATURE failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::AC_VOLTAGE, ac_voltage);
log_status_on_fail("Read AC_VOLTAGE failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::VOLTAGE_LIMIT, voltage_limit);
log_status_on_fail("Read VOLTAGE_LIMIT failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::PFC0_VOLTAGE, pfc0_voltage);
log_status_on_fail("Read PFC0_VOLTAGE failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::PFC1_VOLTAGE, pfc1_voltage);
log_status_on_fail("Read PFC1_VOLTAGE failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::ENV_TEMPERATURE, env_temperature);
log_status_on_fail("Read ENV_TEMPERATURE failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::AC_VOLTAGE_PHASE_A, ac_voltage_phase_a);
log_status_on_fail("Read AC_VOLTAGE_PHASE_A failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::AC_VOLTAGE_PHASE_B, ac_voltage_phase_b);
log_status_on_fail("Read AC_VOLTAGE_PHASE_B failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::AC_VOLTAGE_PHASE_C, ac_voltage_phase_c);
log_status_on_fail("Read AC_VOLTAGE_PHASE_C failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::PFC_TEMPERATURE, pfc_temperature);
log_status_on_fail("Read PFC_TEMPERATURE failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::POWER_LIMIT, power_limit);
log_status_on_fail("Read POWER_LIMIT failed", status);
status = can_broker->read_data(dpm1000::def::ReadValueType::ENV_TEMPERATURE, env_temperature);
log_status_on_fail("Read ENV_TEMPERATURE failed", status);
EVLOG_info << fmt::format(
"set_voltage {} set_current {} vc.current_A {} vc.voltage_V {} current_real_part {} current_limit {} "
"dcdc_temperature {} ac_voltage {} "
"voltage_limit "
"{} pfc0_voltage {} pfc1_voltage {} env_temperature {} ac_voltage_phase_a {} ac_voltage_phase_b {} "
"ac_voltage_phase_c {} pfc_temperature {} power_limit {}",
voltage, current, vc.current_A, vc.voltage_V, current_real_part, current_limit, dcdc_temperature,
ac_voltage, voltage_limit, pfc0_voltage, pfc1_voltage, env_temperature, ac_voltage_phase_a,
ac_voltage_phase_b, ac_voltage_phase_c, pfc_temperature, power_limit);
}
}
}
void power_supply_DCImpl::handle_setMode(types::power_supply_DC::Mode& mode,
types::power_supply_DC::ChargingPhase& phase) {
if (mode == types::power_supply_DC::Mode::Export) {
can_broker->set_state(true);
} else {
can_broker->set_state(false);
}
}
void power_supply_DCImpl::handle_setExportVoltageCurrent(double& voltage, double& current) {
if (voltage <= config_voltage_limit && voltage >= config_min_voltage_limit && current <= config_current_limit) {
this->voltage = voltage;
this->current = current / 100.;
} else {
EVLOG_error << fmt::format("Out of range voltage/current settings ignored: {}V / {}A", voltage, current);
}
}
void power_supply_DCImpl::handle_setImportVoltageCurrent(double& voltage, double& current) {
// power supply is uni directional only
}
} // namespace main
} // namespace module

View File

@@ -0,0 +1,76 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef MAIN_POWER_SUPPLY_DC_IMPL_HPP
#define MAIN_POWER_SUPPLY_DC_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/power_supply_DC/Implementation.hpp>
#include "../DPM1000.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
#include <atomic>
#include <everest/gpio/gpio.hpp>
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace main {
struct Conf {};
class power_supply_DCImpl : public power_supply_DCImplBase {
public:
power_supply_DCImpl() = delete;
power_supply_DCImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<DPM1000>& mod, Conf& config) :
power_supply_DCImplBase(ev, "main"), 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_setMode(types::power_supply_DC::Mode& mode,
types::power_supply_DC::ChargingPhase& phase) override;
virtual void handle_setExportVoltageCurrent(double& voltage, double& current) override;
virtual void handle_setImportVoltageCurrent(double& voltage, double& current) override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<DPM1000>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
std::atomic<float> voltage;
std::atomic<float> current;
std::atomic<uint32_t> last_alarm_flags{0};
float config_current_limit{0};
float config_voltage_limit{0};
float config_power_limit{0};
float config_min_voltage_limit{50.};
Everest::Gpio discharge_gpio;
bool parallel_mode{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 main
} // namespace module
#endif // MAIN_POWER_SUPPLY_DC_IMPL_HPP

View File

@@ -0,0 +1,61 @@
description: DC Power Supply Driver
provides:
main:
description: Power supply driver for DPM 1000-30 from SCU Power. Currently supports only one module.
interface: power_supply_DC
config:
device:
description: Interface name for can device
type: string
default: can0
device_address:
description: Device address (as selected on front LED panel)
type: integer
default: 0
power_limit_W:
description: Maximum Power Limit in Watt
type: number
maximum: 30000
default: 30000
current_limit_A:
description: Maximum Current Limit in Ampere
type: number
maximum: 100
default: 100
voltage_limit_V:
description: Maximum Voltage Limit in Volt. Will be limited by series parallel setting as well.
type: number
maximum: 1000
default: 1000
series_parallel_mode:
description:
Select series (300-1000V), parallel (50-500) or automatic switching mode (50-1000).
This switches the internal configuration of one module and should not be confused with parallel operation of multiple modules.
type: string
enum:
- Series
- Parallel
- Automatic
default: Series
discharge_gpio_chip:
description: >-
GPIO chip to use to switch external discharge load on and off. An empty string disables discharging.
Note that the hardware load must be designed to allow permanent discharge from the highest voltage (e.g. 1000V)
type: string
default: ''
discharge_gpio_line:
description: GPIO line to use to switch discharge load
type: integer
default: 0
discharge_gpio_polarity:
description: GPIO polarity, false means active low, true means active high
type: boolean
default: true
debug_print_all_telemetry:
description: Read and print all telemetry from the power module. Helpful while debugging.
type: boolean
default: false
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- aw@pionix.de