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,34 @@
|
||||
#
|
||||
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
|
||||
# template version 3
|
||||
#
|
||||
|
||||
# module setup:
|
||||
# - ${MODULE_NAME}: module name
|
||||
ev_setup_cpp_module()
|
||||
|
||||
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
|
||||
# insert your custom targets and additional config variables here
|
||||
add_subdirectory(test-tool)
|
||||
target_sources(${MODULE_NAME}
|
||||
PRIVATE
|
||||
"can_driver_acdc/CanDevice.cpp"
|
||||
"can_driver_acdc/InfyCanDevice.cpp"
|
||||
"can_driver_acdc/CanPackets.cpp"
|
||||
)
|
||||
|
||||
target_link_libraries(${MODULE_NAME}
|
||||
PRIVATE
|
||||
Pal::Sigslot
|
||||
everest::log
|
||||
)
|
||||
# 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
|
||||
@@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "InfyPower_BEG1K075G.hpp"
|
||||
|
||||
namespace module {
|
||||
|
||||
void InfyPower_BEG1K075G::init() {
|
||||
// open DCDC CAN device
|
||||
if (!acdc.open_device(config.can_device.c_str())) {
|
||||
EVLOG_AND_THROW(EVEXCEPTION(Everest::EverestConfigError, "Could not open CAN interface ", config.can_device));
|
||||
}
|
||||
|
||||
invoke_init(*p_main);
|
||||
}
|
||||
|
||||
void InfyPower_BEG1K075G::ready() {
|
||||
invoke_ready(*p_main);
|
||||
}
|
||||
|
||||
} // namespace module
|
||||
@@ -0,0 +1,60 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef INFY_POWER_BEG1K075G_HPP
|
||||
#define INFY_POWER_BEG1K075G_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
|
||||
#include "can_driver_acdc/InfyCanDevice.hpp"
|
||||
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
|
||||
|
||||
namespace module {
|
||||
|
||||
struct Conf {
|
||||
std::string can_device;
|
||||
};
|
||||
|
||||
class InfyPower_BEG1K075G : public Everest::ModuleBase {
|
||||
public:
|
||||
InfyPower_BEG1K075G() = delete;
|
||||
InfyPower_BEG1K075G(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
|
||||
InfyCanDevice acdc;
|
||||
// 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 // INFY_POWER_BEG1K075G_HPP
|
||||
@@ -0,0 +1,122 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <net/if.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <linux/can.h>
|
||||
#include <linux/can/raw.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "CanDevice.hpp"
|
||||
|
||||
#include <everest/logging.hpp>
|
||||
|
||||
CanDevice::CanDevice() : exit_rx_thread{false} {
|
||||
can_fd = 0;
|
||||
}
|
||||
|
||||
CanDevice::~CanDevice() {
|
||||
close_device();
|
||||
}
|
||||
|
||||
bool CanDevice::open_device(const char* dev) {
|
||||
if (!dev || std::strlen(dev) >= IFNAMSIZ) {
|
||||
fprintf(stderr, "Interface name is invalid or too long: %s\n", dev ? dev : "NULL");
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((can_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) {
|
||||
perror("Socket");
|
||||
return false;
|
||||
}
|
||||
|
||||
// retrieve interface index from interface name
|
||||
struct ifreq ifr;
|
||||
std::memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, dev, IFNAMSIZ - 1);
|
||||
if (ioctl(can_fd, SIOCGIFINDEX, &ifr) < 0) {
|
||||
perror(dev);
|
||||
close(can_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
|
||||
perror("Bind");
|
||||
close(can_fd);
|
||||
return false;
|
||||
}
|
||||
|
||||
// spawn read thread
|
||||
exit_rx_thread = false;
|
||||
rx_thread_handle = std::thread(&CanDevice::rx_thread, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CanDevice::close_device() {
|
||||
if (can_fd != 0 && close(can_fd) == 0) {
|
||||
can_fd = 0;
|
||||
exit_rx_thread = true;
|
||||
rx_thread_handle.join();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void CanDevice::rx_thread() {
|
||||
can_frame frame;
|
||||
while (!exit_rx_thread) {
|
||||
|
||||
size_t nbytes = read(can_fd, &frame, sizeof(struct can_frame));
|
||||
|
||||
if (nbytes < 0) {
|
||||
perror("Read");
|
||||
} else if (nbytes > 0) {
|
||||
// Received a new CAN packet...
|
||||
std::vector<uint8_t> payload;
|
||||
payload.assign(frame.data, frame.data + frame.can_dlc);
|
||||
rx_handler(frame.can_id, payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CanDevice::rx_handler(uint32_t can_id, const std::vector<uint8_t>& payload) {
|
||||
EVLOG_debug << "CAN frame received";
|
||||
}
|
||||
|
||||
bool CanDevice::_tx(uint32_t can_id, const std::vector<uint8_t>& payload) {
|
||||
if (can_fd == 0)
|
||||
return false;
|
||||
|
||||
struct can_frame frame;
|
||||
if (payload.size() > sizeof(frame.data)) {
|
||||
throw std::runtime_error("Size of can payload data to large (" + std::to_string(payload.size()) + " bytes)");
|
||||
}
|
||||
|
||||
frame.can_id = can_id;
|
||||
frame.can_dlc = payload.size();
|
||||
memcpy(frame.data, payload.data(), payload.size());
|
||||
|
||||
if (write(can_fd, &frame, sizeof(can_frame)) != sizeof(can_frame)) {
|
||||
throw std::runtime_error(std::string("Failed to send can packet :") + strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef CAN_DEVICE_HPP
|
||||
#define CAN_DEVICE_HPP
|
||||
|
||||
#include "CanPackets.hpp"
|
||||
#include <atomic>
|
||||
#include <linux/can.h>
|
||||
#include <thread>
|
||||
|
||||
class CanDevice {
|
||||
public:
|
||||
CanDevice();
|
||||
virtual ~CanDevice();
|
||||
bool open_device(const char* dev);
|
||||
bool close_device();
|
||||
|
||||
protected:
|
||||
virtual void rx_handler(uint32_t can_id, const std::vector<uint8_t>& payload);
|
||||
bool _tx(uint32_t can_id, const std::vector<uint8_t>& payload);
|
||||
|
||||
private:
|
||||
int can_fd;
|
||||
std::atomic_bool exit_rx_thread;
|
||||
std::thread rx_thread_handle;
|
||||
void rx_thread();
|
||||
};
|
||||
|
||||
#endif // CAN_DEVICE_HPP
|
||||
@@ -0,0 +1,306 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "CanPackets.hpp"
|
||||
#include "Conversions.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace can_packet_acdc {
|
||||
|
||||
// helper functions
|
||||
uint32_t encode_can_id(uint8_t source_address, uint8_t destination_address, uint8_t command_number,
|
||||
uint8_t device_number, uint8_t error_code) {
|
||||
uint32_t id = source_address;
|
||||
id |= destination_address << 8;
|
||||
command_number &= 0x3F;
|
||||
id |= command_number << 16;
|
||||
device_number &= 0x0F;
|
||||
id |= device_number << 22;
|
||||
error_code &= 0x07;
|
||||
id |= error_code << 26;
|
||||
return id;
|
||||
}
|
||||
|
||||
/*
|
||||
destination_address is either 0x3F (broadcast) or group address
|
||||
*/
|
||||
uint32_t encode_can_id(uint8_t destination_address, uint8_t command_number) {
|
||||
uint8_t device_number;
|
||||
if (destination_address == 0x3F) {
|
||||
device_number = 0x0A;
|
||||
} else {
|
||||
device_number = 0x0B;
|
||||
}
|
||||
|
||||
return encode_can_id(0xF0, destination_address, command_number, device_number, 0);
|
||||
}
|
||||
|
||||
uint8_t destination_address_from_can_id(uint32_t id) {
|
||||
return (id >> 8) & 0xFF;
|
||||
}
|
||||
uint8_t source_address_from_can_id(uint32_t id) {
|
||||
return id & 0xFF;
|
||||
}
|
||||
|
||||
uint8_t command_number_from_can_id(uint32_t id) {
|
||||
return (id >> 16) & 0x3F;
|
||||
}
|
||||
|
||||
uint8_t error_code_from_can_id(uint32_t id) {
|
||||
return (id >> 26) & 0x07;
|
||||
}
|
||||
|
||||
// packet definitions
|
||||
|
||||
// GenericSetting
|
||||
|
||||
GenericSetting::GenericSetting(const std::vector<uint8_t> raw) {
|
||||
byte0 = from_raw<uint8_t>(raw, 0);
|
||||
byte1 = from_raw<uint8_t>(raw, 1);
|
||||
value = from_raw<uint32_t>(raw, 4);
|
||||
}
|
||||
|
||||
GenericSetting::GenericSetting(uint8_t _byte0, uint8_t _byte1, uint32_t _value) :
|
||||
byte0(_byte0), byte1(_byte1), value(_value) {
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const GenericSetting& self) {
|
||||
out << "GenericSetting: byte0: " << std::to_string(self.byte0) << " byte1: " << std::to_string(self.byte0)
|
||||
<< " Value: " << std::to_string(self.value);
|
||||
return out;
|
||||
}
|
||||
|
||||
GenericSetting::operator std::vector<uint8_t>() {
|
||||
std::vector<uint8_t> data;
|
||||
to_raw(byte0, data);
|
||||
to_raw(byte1, data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(value, data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// SystemDCVoltage
|
||||
|
||||
SystemDCVoltage::SystemDCVoltage(const std::vector<uint8_t> raw) {
|
||||
volt = (float)from_raw<uint32_t>(raw, 4) / 1000.;
|
||||
}
|
||||
|
||||
SystemDCVoltage::SystemDCVoltage(float _volt) : volt(_volt) {
|
||||
}
|
||||
|
||||
SystemDCVoltage::SystemDCVoltage() : volt(0.) {
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const SystemDCVoltage& self) {
|
||||
out << "SystemDCVoltage: " << std::to_string(self.volt);
|
||||
return out;
|
||||
}
|
||||
|
||||
SystemDCVoltage::operator std::vector<uint8_t>() {
|
||||
std::vector<uint8_t> data;
|
||||
to_raw(static_cast<uint8_t>(0x10), data);
|
||||
to_raw(static_cast<uint8_t>(0x01), data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(static_cast<uint32_t>(volt * 1000), data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// SystemDCCurrent
|
||||
|
||||
SystemDCCurrent::SystemDCCurrent(const std::vector<uint8_t> raw) {
|
||||
ampere = (float)from_raw<uint32_t>(raw, 4) / 1000.;
|
||||
}
|
||||
|
||||
SystemDCCurrent::SystemDCCurrent(float _ampere) : ampere(_ampere) {
|
||||
}
|
||||
|
||||
SystemDCCurrent::SystemDCCurrent() : ampere(0.) {
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const SystemDCCurrent& self) {
|
||||
out << "SystemDCCurrent: " << std::to_string(self.ampere);
|
||||
return out;
|
||||
}
|
||||
|
||||
SystemDCCurrent::operator std::vector<uint8_t>() {
|
||||
std::vector<uint8_t> data;
|
||||
to_raw(static_cast<uint8_t>(0x10), data);
|
||||
to_raw(static_cast<uint8_t>(0x02), data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(static_cast<uint32_t>(ampere * 1000), data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// PowerModuleNumber
|
||||
|
||||
PowerModuleNumber::PowerModuleNumber(const std::vector<uint8_t> raw) {
|
||||
number = from_raw<uint16_t>(raw, 6);
|
||||
}
|
||||
|
||||
PowerModuleNumber::PowerModuleNumber() : number(0) {
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const PowerModuleNumber& self) {
|
||||
out << "PowerModuleNumber: " << std::to_string(self.number);
|
||||
return out;
|
||||
}
|
||||
|
||||
PowerModuleNumber::operator std::vector<uint8_t>() {
|
||||
std::vector<uint8_t> data;
|
||||
to_raw(static_cast<uint8_t>(0x10), data);
|
||||
to_raw(static_cast<uint8_t>(0x10), data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(static_cast<uint32_t>(0x00), data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// PowerModuleStatus
|
||||
|
||||
PowerModuleStatus::PowerModuleStatus() {
|
||||
}
|
||||
|
||||
PowerModuleStatus::PowerModuleStatus(const std::vector<uint8_t> raw) {
|
||||
uint8_t status0 = from_raw<uint8_t>(raw, 7);
|
||||
uint8_t status1 = from_raw<uint8_t>(raw, 6);
|
||||
uint8_t status2 = from_raw<uint8_t>(raw, 5);
|
||||
|
||||
output_short_current = status0 & (1 << 0);
|
||||
sleeping = status0 & (1 << 4);
|
||||
discharge_abnormal = status0 & (1 << 5);
|
||||
dc_side_off = status1 & (1 << 0);
|
||||
fault_alarm = status1 & (1 << 1);
|
||||
protection_alarm = status1 & (1 << 2);
|
||||
fan_fault_alarm = status1 & (1 << 3);
|
||||
over_temperature_alarm = status1 & (1 << 4);
|
||||
output_over_voltage_alarm = status1 & (1 << 5);
|
||||
walk_in_enable = status1 & (1 << 6);
|
||||
communication_interrupt_alarm = status1 & (1 << 7);
|
||||
power_limit_status = status2 & (1 << 0);
|
||||
id_repeat_alarm = status2 & (1 << 1);
|
||||
load_sharing_alarm = status2 & (1 << 2);
|
||||
input_phase_lost_alarm = status2 & (1 << 3);
|
||||
input_unbalanced_alarm = status2 & (1 << 4);
|
||||
input_low_voltage_alarm = status2 & (1 << 5);
|
||||
input_over_voltage_protection = status2 & (1 << 6);
|
||||
pfc_side_off = status2 & (1 << 7);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const PowerModuleStatus& self) {
|
||||
out << "PowerModuleStatus: " << (self.output_short_current ? "output_short_current " : "")
|
||||
<< (self.sleeping ? "sleeping " : "") << (self.discharge_abnormal ? "discharge_abnormal " : "")
|
||||
<< (self.dc_side_off ? "dc_side_off " : "") << (self.fault_alarm ? "fault_alarm " : "")
|
||||
<< (self.protection_alarm ? "protection_alarm " : "") << (self.fan_fault_alarm ? "fan_fault_alarm " : "")
|
||||
<< (self.over_temperature_alarm ? "over_temperature_alarm " : "")
|
||||
<< (self.output_over_voltage_alarm ? "output_over_voltage_alarm " : "")
|
||||
<< (self.walk_in_enable ? "walk_in_enable " : "")
|
||||
<< (self.communication_interrupt_alarm ? "communication_interrupt_alarm " : "")
|
||||
<< (self.power_limit_status ? "power_limit_status " : "") << (self.id_repeat_alarm ? "id_repeat_alarm " : "")
|
||||
<< (self.load_sharing_alarm ? "load_sharing_alarm " : "")
|
||||
<< (self.input_phase_lost_alarm ? "input_phase_lost_alarm " : "")
|
||||
<< (self.input_unbalanced_alarm ? "input_unbalanced_alarm " : "")
|
||||
<< (self.input_low_voltage_alarm ? "input_low_voltage_alarm " : "")
|
||||
<< (self.input_over_voltage_protection ? "input_over_voltage_protection " : "")
|
||||
<< (self.pfc_side_off ? "pfc_side_off " : "");
|
||||
return out;
|
||||
}
|
||||
|
||||
PowerModuleStatus::operator std::vector<uint8_t>() {
|
||||
std::vector<uint8_t> data;
|
||||
to_raw(static_cast<uint8_t>(0x11), data);
|
||||
to_raw(static_cast<uint8_t>(0x10), data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(static_cast<uint32_t>(0x00), data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// InverterStatus
|
||||
|
||||
InverterStatus::InverterStatus(const std::vector<uint8_t> raw) {
|
||||
uint8_t status0 = from_raw<uint8_t>(raw, 7);
|
||||
|
||||
invert_mode = status0 & (1 << 0);
|
||||
}
|
||||
|
||||
InverterStatus::InverterStatus() : invert_mode{false} {
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const InverterStatus& self) {
|
||||
out << "InverterStatus: " << (self.invert_mode ? "inverter" : "rectifier");
|
||||
return out;
|
||||
}
|
||||
|
||||
InverterStatus::operator std::vector<uint8_t>() {
|
||||
std::vector<uint8_t> data;
|
||||
to_raw(static_cast<uint8_t>(0x11), data);
|
||||
to_raw(static_cast<uint8_t>(0x11), data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(static_cast<uint32_t>(0), data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// OnOff
|
||||
|
||||
OnOff::OnOff(bool o) : on{o} {};
|
||||
|
||||
OnOff::operator std::vector<uint8_t>() {
|
||||
std::vector<uint8_t> data;
|
||||
to_raw(static_cast<uint8_t>(0x11), data);
|
||||
to_raw(static_cast<uint8_t>(0x10), data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(static_cast<uint16_t>(on ? 0xA0 : 0xA1), data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// Walk in enable
|
||||
|
||||
WalkInEnable::WalkInEnable(bool e) : enabled{e} {};
|
||||
|
||||
WalkInEnable::operator std::vector<uint8_t>() {
|
||||
std::vector<uint8_t> data;
|
||||
to_raw(static_cast<uint8_t>(0x11), data);
|
||||
to_raw(static_cast<uint8_t>(0x22), data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(static_cast<uint16_t>(enabled ? 0xA1 : 0xA0), data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// WorkingMode
|
||||
|
||||
WorkingMode::WorkingMode(bool o) : inverter{o} {};
|
||||
|
||||
WorkingMode::operator std::vector<uint8_t>() {
|
||||
std::vector<uint8_t> data;
|
||||
to_raw(static_cast<uint8_t>(0x21), data);
|
||||
to_raw(static_cast<uint8_t>(0x10), data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(static_cast<uint16_t>(inverter ? 0xA1 : 0xA0), data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// PowerFactorAdjust
|
||||
|
||||
PowerFactorAdjust::PowerFactorAdjust(float p) : pf{p} {};
|
||||
|
||||
PowerFactorAdjust::operator std::vector<uint8_t>() {
|
||||
std::vector<uint8_t> data;
|
||||
to_raw(static_cast<uint8_t>(0x21), data);
|
||||
to_raw(static_cast<uint8_t>(0x05), data);
|
||||
to_raw(static_cast<uint16_t>(0x00), data);
|
||||
to_raw(static_cast<int32_t>(pf * 1000.), data);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace can_packet_acdc
|
||||
@@ -0,0 +1,172 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef CAN_PACKETS_HPP
|
||||
#define CAN_PACKETS_HPP
|
||||
|
||||
#include <linux/can.h>
|
||||
#include <ostream>
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
namespace can_packet_acdc {
|
||||
|
||||
uint32_t encode_can_id(uint8_t source_address, uint8_t destination_address, uint8_t command_number,
|
||||
uint8_t device_number, uint8_t error_code);
|
||||
uint32_t encode_can_id(uint8_t destination_address, uint8_t command_number);
|
||||
|
||||
uint8_t destination_address_from_can_id(uint32_t id);
|
||||
uint8_t source_address_from_can_id(uint32_t id);
|
||||
uint8_t command_number_from_can_id(uint32_t id);
|
||||
uint8_t error_code_from_can_id(uint32_t id);
|
||||
|
||||
/*
|
||||
CAN IDs are not just simple IDs with this inverter. More information is encoded into the 29 bit CAN IDs:
|
||||
|
||||
bits 0..7: Source Address
|
||||
bits 18..15: Destination Address
|
||||
0x3F: broadcast message
|
||||
0x00..0x3E: if device number 0x0A: actual module address range, automatic allocation after power on.
|
||||
if device number 0x0B: group address, set with dial on the module.
|
||||
0xF0..0xF8: controller address range, default is 0xF0.
|
||||
|
||||
bits 16..21: Command number
|
||||
0x23: read power module data, actual content encoded in first to bytes
|
||||
0x24: write power module data, actual content encoded in first to bytes
|
||||
Byte 0:
|
||||
0x10: System basic information
|
||||
0x11: Single power module basic information
|
||||
0x21: Single power module AC side information
|
||||
0x41: Single power module bidirectional DC/DC side information
|
||||
|
||||
|
||||
|
||||
|
||||
bits 22..25: Device number
|
||||
0x0A: Controller to single module
|
||||
0x0B: Controller to module group
|
||||
|
||||
bits 26..28: Error code:
|
||||
0x00: Normal
|
||||
0x02: Command invalid
|
||||
0x03: Data invalid
|
||||
0x07: In start processing
|
||||
|
||||
*/
|
||||
|
||||
// Commands
|
||||
const uint8_t CMD_READ = 0x23;
|
||||
const uint8_t CMD_WRITE = 0x24;
|
||||
|
||||
// Addresses
|
||||
const uint8_t ADDR_BROADCAST = 0x3F;
|
||||
const uint8_t ADDR_MODULE = 0x00;
|
||||
|
||||
// RX and TX packet definitions
|
||||
|
||||
struct GenericSetting {
|
||||
GenericSetting(uint8_t _byte0, uint8_t _byte1, uint32_t _value);
|
||||
GenericSetting(const std::vector<uint8_t> raw);
|
||||
friend std::ostream& operator<<(std::ostream& out, const GenericSetting& self);
|
||||
operator std::vector<uint8_t>();
|
||||
uint32_t value{0};
|
||||
uint8_t byte0{0};
|
||||
uint8_t byte1{0};
|
||||
};
|
||||
|
||||
struct SystemDCVoltage {
|
||||
SystemDCVoltage();
|
||||
SystemDCVoltage(float _volt);
|
||||
SystemDCVoltage(const std::vector<uint8_t> raw);
|
||||
friend std::ostream& operator<<(std::ostream& out, const SystemDCVoltage& self);
|
||||
operator std::vector<uint8_t>();
|
||||
|
||||
float volt{0};
|
||||
};
|
||||
|
||||
struct SystemDCCurrent {
|
||||
SystemDCCurrent();
|
||||
SystemDCCurrent(float _ampere);
|
||||
SystemDCCurrent(const std::vector<uint8_t> raw);
|
||||
friend std::ostream& operator<<(std::ostream& out, const SystemDCCurrent& self);
|
||||
operator std::vector<uint8_t>();
|
||||
|
||||
float ampere{0};
|
||||
};
|
||||
|
||||
// RX packet definitions
|
||||
|
||||
struct PowerModuleNumber {
|
||||
PowerModuleNumber();
|
||||
PowerModuleNumber(const std::vector<uint8_t> raw);
|
||||
friend std::ostream& operator<<(std::ostream& out, const PowerModuleNumber& self);
|
||||
operator std::vector<uint8_t>();
|
||||
|
||||
uint16_t number{0};
|
||||
};
|
||||
|
||||
struct PowerModuleStatus {
|
||||
PowerModuleStatus();
|
||||
PowerModuleStatus(const std::vector<uint8_t> raw);
|
||||
friend std::ostream& operator<<(std::ostream& out, const PowerModuleStatus& self);
|
||||
operator std::vector<uint8_t>();
|
||||
|
||||
bool output_short_current{false};
|
||||
bool sleeping{false};
|
||||
bool discharge_abnormal{false};
|
||||
bool dc_side_off{false};
|
||||
bool fault_alarm{false};
|
||||
bool protection_alarm{false};
|
||||
bool fan_fault_alarm{false};
|
||||
bool over_temperature_alarm{false};
|
||||
bool output_over_voltage_alarm{false};
|
||||
bool walk_in_enable{false};
|
||||
bool communication_interrupt_alarm{false};
|
||||
bool power_limit_status{false};
|
||||
bool id_repeat_alarm{false};
|
||||
bool load_sharing_alarm{false};
|
||||
bool input_phase_lost_alarm{false};
|
||||
bool input_unbalanced_alarm{false};
|
||||
bool input_low_voltage_alarm{false};
|
||||
bool input_over_voltage_protection{false};
|
||||
bool pfc_side_off{false};
|
||||
};
|
||||
|
||||
struct InverterStatus {
|
||||
InverterStatus();
|
||||
InverterStatus(const std::vector<uint8_t> raw);
|
||||
friend std::ostream& operator<<(std::ostream& out, const InverterStatus& self);
|
||||
operator std::vector<uint8_t>();
|
||||
|
||||
bool invert_mode{false};
|
||||
};
|
||||
|
||||
// TX packet definitions
|
||||
|
||||
struct OnOff {
|
||||
OnOff(bool o);
|
||||
operator std::vector<uint8_t>();
|
||||
bool on{false};
|
||||
};
|
||||
|
||||
struct WalkInEnable {
|
||||
WalkInEnable(bool o);
|
||||
operator std::vector<uint8_t>();
|
||||
bool enabled{false};
|
||||
};
|
||||
|
||||
struct WorkingMode {
|
||||
WorkingMode(bool o);
|
||||
operator std::vector<uint8_t>();
|
||||
bool inverter{false};
|
||||
};
|
||||
|
||||
struct PowerFactorAdjust {
|
||||
PowerFactorAdjust(float pf);
|
||||
operator std::vector<uint8_t>();
|
||||
float pf{1.0};
|
||||
};
|
||||
|
||||
} // namespace can_packet_acdc
|
||||
|
||||
#endif // CAN_PACKETS_HPP
|
||||
@@ -0,0 +1,72 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef CONVERSIONS_HPP
|
||||
#define CONVERSIONS_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
#include <endian.h>
|
||||
|
||||
template <class T> typename std::enable_if_t<sizeof(T) == 1, T> from_raw(const std::vector<uint8_t> raw, int idx) {
|
||||
T ret = raw[idx];
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class T> typename std::enable_if_t<sizeof(T) == 2, T> from_raw(const std::vector<uint8_t> raw, int idx) {
|
||||
uint16_t tmp = be16toh(*((uint16_t*)((uint8_t*)raw.data() + idx)));
|
||||
T ret;
|
||||
memcpy(&ret, &tmp, 2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class T> typename std::enable_if_t<sizeof(T) == 4, T> from_raw(const std::vector<uint8_t> raw, int idx) {
|
||||
uint32_t tmp = be32toh(*((uint32_t*)((uint8_t*)raw.data() + idx)));
|
||||
T ret;
|
||||
memcpy(&ret, &tmp, 4);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class T> typename std::enable_if_t<sizeof(T) == 8, T> from_raw(const std::vector<uint8_t> raw, int idx) {
|
||||
uint64_t tmp = be64toh(*((uint64_t*)((uint8_t*)raw.data() + idx)));
|
||||
T ret;
|
||||
memcpy(&ret, &tmp, 8);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <class T> typename std::enable_if_t<sizeof(T) == 1> to_raw(T src, std::vector<uint8_t>& dest) {
|
||||
uint8_t tmp;
|
||||
memcpy(&tmp, &src, 1);
|
||||
dest.push_back(tmp);
|
||||
}
|
||||
|
||||
// FIXME (aw): these conversions should be optimized!
|
||||
template <class T> typename std::enable_if_t<sizeof(T) == 2> to_raw(T src, std::vector<uint8_t>& dest) {
|
||||
uint16_t tmp;
|
||||
memcpy(&tmp, &src, 2);
|
||||
tmp = htobe16(tmp);
|
||||
uint8_t ret[2];
|
||||
memcpy(ret, &tmp, 2);
|
||||
dest.insert(dest.end(), {ret[0], ret[1]});
|
||||
}
|
||||
|
||||
template <class T> typename std::enable_if_t<sizeof(T) == 4> to_raw(T src, std::vector<uint8_t>& dest) {
|
||||
uint32_t tmp;
|
||||
memcpy(&tmp, &src, 4);
|
||||
tmp = htobe32(tmp);
|
||||
uint8_t ret[4];
|
||||
memcpy(ret, &tmp, 4);
|
||||
dest.insert(dest.end(), {ret[0], ret[1], ret[2], ret[3]});
|
||||
}
|
||||
|
||||
template <class T> typename std::enable_if_t<sizeof(T) == 8> to_raw(T src, std::vector<uint8_t>& dest) {
|
||||
uint64_t tmp;
|
||||
memcpy(&tmp, &src, 8);
|
||||
tmp = htobe64(tmp);
|
||||
uint8_t ret[8];
|
||||
memcpy(ret, &tmp, 8);
|
||||
dest.insert(dest.end(), {ret[0], ret[1], ret[2], ret[3], ret[4], ret[5], ret[6], ret[7]});
|
||||
}
|
||||
|
||||
#endif // CONVERSIONS_HPP
|
||||
@@ -0,0 +1,367 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "InfyCanDevice.hpp"
|
||||
#include "CanPackets.hpp"
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <everest/logging.hpp>
|
||||
|
||||
InfyCanDevice::InfyCanDevice() {
|
||||
// spawn thread that requests some data periodically to keep the connection alive
|
||||
exitTxThread = false;
|
||||
txThreadHandle = std::thread(&InfyCanDevice::txThread, this);
|
||||
}
|
||||
|
||||
InfyCanDevice::~InfyCanDevice() {
|
||||
exitTxThread = true;
|
||||
}
|
||||
|
||||
void InfyCanDevice::rx_handler(uint32_t can_id, const std::vector<uint8_t>& payload) {
|
||||
EVLOG_debug << "Infy: CAN frame received. ID: 0x" << std::hex << can_id;
|
||||
|
||||
// We only use extended frames here
|
||||
if (!(can_id & CAN_EFF_FLAG)) {
|
||||
EVLOG_debug << "Infy: Ignoring, not extended protocol.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (can_packet_acdc::command_number_from_can_id(can_id) == can_packet_acdc::CMD_WRITE) {
|
||||
switch (can_packet_acdc::error_code_from_can_id(can_id)) {
|
||||
case 0x02:
|
||||
EVLOG_error << "Infy: ERROR: Command invalid.";
|
||||
break;
|
||||
case 0x03:
|
||||
EVLOG_error << "Infy: ERROR: Data invalid.";
|
||||
break;
|
||||
case 0x07:
|
||||
EVLOG_error << "Infy: ERROR: In start processing.";
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// is it a reply to a read command?
|
||||
if (can_packet_acdc::command_number_from_can_id(can_id) != can_packet_acdc::CMD_READ) {
|
||||
return;
|
||||
}
|
||||
|
||||
// is it for us?
|
||||
if (can_packet_acdc::destination_address_from_can_id(can_id) != 0xF0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint16_t packet_type = payload[0] << 8 | payload[1];
|
||||
|
||||
if (can_packet_acdc::error_code_from_can_id(can_id) > 0) {
|
||||
EVLOG_debug << "Infy: Parsing CAN packet type: " << std::hex << packet_type << " Error code:" << std::hex
|
||||
<< (int)can_packet_acdc::error_code_from_can_id(can_id);
|
||||
}
|
||||
|
||||
switch (packet_type) {
|
||||
case 0x1001: {
|
||||
telemetry.voltage = payload;
|
||||
} break;
|
||||
|
||||
case 0x1002: {
|
||||
telemetry.current = payload;
|
||||
signalVoltageCurrent(telemetry.voltage.volt, telemetry.current.ampere);
|
||||
} break;
|
||||
|
||||
case 0x1010: {
|
||||
can_packet_acdc::PowerModuleNumber n(payload);
|
||||
} break;
|
||||
|
||||
case 0x1110: {
|
||||
can_packet_acdc::PowerModuleStatus s(payload);
|
||||
telemetry.status = s;
|
||||
} break;
|
||||
|
||||
case 0x1111: {
|
||||
can_packet_acdc::InverterStatus s(payload);
|
||||
signalModuleStatus(telemetry.status, s);
|
||||
} break;
|
||||
|
||||
case 0x1103: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_ab_line_voltage = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x1104: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_bc_line_voltage = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x1105: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_ca_line_voltage = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x1106: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ambient_temperature = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x1130: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.dc_max_output_voltage = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x1131: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.dc_min_output_voltage = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x1132: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.dc_max_output_current = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x1133: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.dc_rated_output_power = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2101: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_a_voltage = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2102: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_b_voltage = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2103: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_c_voltage = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2104: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_a_current = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2105: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_b_current = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2106: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_c_current = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2107: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_frequency = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2109: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_a_active_power = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x210A: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_b_active_power = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x210B: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_c_active_power = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2108: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_total_active_ower = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x210D: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_a_reactive_power = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x210E: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_b_reactive_power = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x210F: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_c_reactive_power = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x210C: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_total_reactive_ower = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2110: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_a_apparent_power = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2111: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_b_apparent_power = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2112: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_phase_c_apparent_power = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x2113: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.ac_total_apparent_ower = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x4101: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.dc_high_side_voltage = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
case 0x4102: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
telemetry.dc_high_side_current = s.value / 1000.;
|
||||
} break;
|
||||
|
||||
default: {
|
||||
can_packet_acdc::GenericSetting s(payload);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InfyCanDevice::txThread() {
|
||||
|
||||
while (!exitTxThread) {
|
||||
const int delay_us = 50000;
|
||||
|
||||
// request current system DC voltage. Answer will be processed by RX thread.
|
||||
request_rx(can_packet_acdc::ADDR_BROADCAST, can_packet_acdc::SystemDCVoltage());
|
||||
usleep(delay_us);
|
||||
|
||||
// request current system DC current. Answer will be processed by RX thread.
|
||||
request_rx(can_packet_acdc::ADDR_BROADCAST, can_packet_acdc::SystemDCCurrent());
|
||||
usleep(delay_us);
|
||||
|
||||
// request state. Answer will be processed by RX thread.
|
||||
request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::PowerModuleStatus());
|
||||
usleep(delay_us);
|
||||
|
||||
// request inverter state. Answer will be processed by RX thread.
|
||||
request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::InverterStatus());
|
||||
usleep(delay_us);
|
||||
|
||||
tx(can_packet_acdc::ADDR_BROADCAST, can_packet_acdc::OnOff(on));
|
||||
usleep(delay_us);
|
||||
|
||||
if (setpoint_voltage > 150.) {
|
||||
tx(can_packet_acdc::ADDR_BROADCAST, can_packet_acdc::SystemDCVoltage(setpoint_voltage));
|
||||
usleep(delay_us);
|
||||
tx(can_packet_acdc::ADDR_BROADCAST, can_packet_acdc::SystemDCCurrent(setpoint_current));
|
||||
usleep(delay_us);
|
||||
}
|
||||
|
||||
tx(can_packet_acdc::ADDR_BROADCAST, can_packet_acdc::WalkInEnable(walkin_enable));
|
||||
usleep(delay_us);
|
||||
|
||||
tx(can_packet_acdc::ADDR_BROADCAST, can_packet_acdc::WorkingMode(inverter_mode));
|
||||
usleep(delay_us);
|
||||
}
|
||||
}
|
||||
|
||||
bool InfyCanDevice::switch_on_off(bool _on) {
|
||||
on = _on;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InfyCanDevice::set_walkin_enabled(bool on) {
|
||||
walkin_enable = on;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InfyCanDevice::set_inverter_mode(bool i) {
|
||||
inverter_mode = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InfyCanDevice::adjust_power_factor(float pf) {
|
||||
return tx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::PowerFactorAdjust(pf));
|
||||
}
|
||||
|
||||
bool InfyCanDevice::set_voltage_current(float voltage, float current) {
|
||||
setpoint_current = current;
|
||||
setpoint_voltage = voltage;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InfyCanDevice::set_generic_setting(uint8_t byte0, uint8_t byte1, uint32_t value) {
|
||||
return tx(can_packet_acdc::ADDR_BROADCAST, can_packet_acdc::GenericSetting(byte0, byte1, value));
|
||||
}
|
||||
|
||||
bool InfyCanDevice::set_output_mode(OutputMode mode) {
|
||||
return set_generic_setting(0x11, 0x26, static_cast<std::underlying_type<OutputMode>::type>(mode));
|
||||
}
|
||||
|
||||
bool InfyCanDevice::tx(const uint8_t destination_address, const std::vector<uint8_t>& payload) {
|
||||
uint32_t can_id = can_packet_acdc::encode_can_id(destination_address, can_packet_acdc::CMD_WRITE);
|
||||
can_id |= 0x80000000U; // Extended frame format
|
||||
return _tx(can_id, payload);
|
||||
}
|
||||
|
||||
bool InfyCanDevice::request_rx(const uint8_t destination_address, const std::vector<uint8_t>& payload) {
|
||||
uint32_t can_id = can_packet_acdc::encode_can_id(destination_address, can_packet_acdc::CMD_READ);
|
||||
can_id |= 0x80000000U; // Extended frame format
|
||||
return _tx(can_id, payload);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const InfyCanDevice::Telemetry& self) {
|
||||
out << "\n------------------------------------------------\nTelemetry\n---------\n";
|
||||
|
||||
out << "AC line: AB:" << std::to_string(self.ac_ab_line_voltage)
|
||||
<< " BC: " << std::to_string(self.ac_bc_line_voltage) << " CA: " << std::to_string(self.ac_ca_line_voltage)
|
||||
<< std::endl;
|
||||
|
||||
out << "AC Phase Voltages: V_A:" << std::to_string(self.ac_phase_a_voltage)
|
||||
<< " V_B: " << std::to_string(self.ac_phase_b_voltage) << " V_C: " << std::to_string(self.ac_phase_c_voltage)
|
||||
<< std::endl;
|
||||
|
||||
out << "AC Phase Currents: I_A:" << std::to_string(self.ac_phase_a_current)
|
||||
<< " I_B: " << std::to_string(self.ac_phase_b_current) << " I_C: " << std::to_string(self.ac_phase_c_current)
|
||||
<< std::endl;
|
||||
|
||||
out << "AC Active power: Total: " << std::to_string(self.ac_total_active_ower)
|
||||
<< "P_A:" << std::to_string(self.ac_phase_a_active_power)
|
||||
<< " P_B: " << std::to_string(self.ac_phase_b_active_power)
|
||||
<< " P_C: " << std::to_string(self.ac_phase_c_active_power) << std::endl;
|
||||
|
||||
out << "AC Re-Active power: Total: " << std::to_string(self.ac_total_reactive_ower)
|
||||
<< "P_A:" << std::to_string(self.ac_phase_a_reactive_power)
|
||||
<< " P_B: " << std::to_string(self.ac_phase_b_reactive_power)
|
||||
<< " P_C: " << std::to_string(self.ac_phase_c_reactive_power) << std::endl;
|
||||
|
||||
out << "AC Apparent power: Total: " << std::to_string(self.ac_total_apparent_ower)
|
||||
<< "P_A:" << std::to_string(self.ac_phase_a_apparent_power)
|
||||
<< " P_B: " << std::to_string(self.ac_phase_b_apparent_power)
|
||||
<< " P_C: " << std::to_string(self.ac_phase_c_apparent_power) << std::endl;
|
||||
|
||||
out << "AC frequency: " << self.ac_frequency << std::endl;
|
||||
out << "Ambient temperature: " << self.ambient_temperature << std::endl;
|
||||
|
||||
out << "DC High Voltage side: Voltage: " << std::to_string(self.dc_high_side_voltage)
|
||||
<< " Current: " << std::to_string(self.dc_high_side_current) << std::endl;
|
||||
|
||||
out << "Capabilities: dc_min: " << self.dc_min_output_voltage << "V dc_max: " << self.dc_max_output_voltage
|
||||
<< "V dc_max_current: " << self.dc_max_output_current << "A max_watt: " << self.dc_rated_output_power << "W "
|
||||
<< std::endl;
|
||||
|
||||
out << self.status << std::endl;
|
||||
out << self.voltage << std::endl << self.current << std::endl;
|
||||
|
||||
out << "------------------------------------------------\n";
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef INFY_CAN_DEVICE_HPP
|
||||
#define INFY_CAN_DEVICE_HPP
|
||||
|
||||
#include "CanDevice.hpp"
|
||||
#include <atomic>
|
||||
#include <linux/can.h>
|
||||
#include <mutex>
|
||||
#include <sigslot/signal.hpp>
|
||||
#include <thread>
|
||||
|
||||
class InfyCanDevice : public CanDevice {
|
||||
public:
|
||||
InfyCanDevice();
|
||||
~InfyCanDevice();
|
||||
|
||||
enum class OutputMode {
|
||||
Parallel = 0xA0,
|
||||
Series = 0xA1,
|
||||
Automatic = 0xA2
|
||||
};
|
||||
|
||||
// Commands
|
||||
|
||||
bool switch_on_off(bool on);
|
||||
bool set_walkin_enabled(bool on);
|
||||
bool set_inverter_mode(bool inverter);
|
||||
bool set_voltage_current(float voltage, float current);
|
||||
bool set_output_mode(OutputMode mode);
|
||||
bool adjust_power_factor(float pf);
|
||||
bool set_generic_setting(uint8_t byte0, uint8_t byte1, uint32_t value);
|
||||
|
||||
// Data out
|
||||
sigslot::signal<float, float, float> signalacdcTemperatures;
|
||||
sigslot::signal<float, float> signalVoltageCurrent;
|
||||
sigslot::signal<can_packet_acdc::PowerModuleStatus, can_packet_acdc::InverterStatus> signalModuleStatus;
|
||||
bool request_rx(const uint8_t destination_address, const std::vector<uint8_t>& payload);
|
||||
|
||||
struct Telemetry {
|
||||
float ac_ab_line_voltage{0.};
|
||||
float ac_bc_line_voltage{0.};
|
||||
float ac_ca_line_voltage{0.};
|
||||
float ambient_temperature{0.};
|
||||
float dc_max_output_voltage{0.};
|
||||
float dc_min_output_voltage{0.};
|
||||
float dc_max_output_current{0.};
|
||||
float dc_rated_output_power{0.};
|
||||
float ac_phase_a_current{0.};
|
||||
float ac_phase_b_current{0.};
|
||||
float ac_phase_c_current{0.};
|
||||
float ac_phase_a_voltage{0.};
|
||||
float ac_phase_b_voltage{0.};
|
||||
float ac_phase_c_voltage{0.};
|
||||
float ac_frequency{0.};
|
||||
float ac_phase_a_active_power{0.};
|
||||
float ac_phase_b_active_power{0.};
|
||||
float ac_phase_c_active_power{0.};
|
||||
float ac_total_active_ower{0.};
|
||||
|
||||
float ac_phase_a_reactive_power{0.};
|
||||
float ac_phase_b_reactive_power{0.};
|
||||
float ac_phase_c_reactive_power{0.};
|
||||
float ac_total_reactive_ower{0.};
|
||||
|
||||
float ac_phase_a_apparent_power{0.};
|
||||
float ac_phase_b_apparent_power{0.};
|
||||
float ac_phase_c_apparent_power{0.};
|
||||
float ac_total_apparent_ower{0.};
|
||||
|
||||
float dc_high_side_voltage{0.};
|
||||
float dc_high_side_current{0.};
|
||||
|
||||
can_packet_acdc::SystemDCVoltage voltage;
|
||||
can_packet_acdc::SystemDCCurrent current;
|
||||
can_packet_acdc::PowerModuleStatus status;
|
||||
} telemetry;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& out, const Telemetry& self);
|
||||
|
||||
protected:
|
||||
virtual void rx_handler(uint32_t can_id, const std::vector<uint8_t>& payload);
|
||||
|
||||
private:
|
||||
std::atomic_bool exitTxThread;
|
||||
std::thread txThreadHandle;
|
||||
void txThread();
|
||||
|
||||
bool tx(const uint8_t destination_address, const std::vector<uint8_t>& payload);
|
||||
|
||||
std::atomic<float> setpoint_voltage{0}, setpoint_current{0};
|
||||
std::atomic_bool on;
|
||||
std::atomic_bool walkin_enable;
|
||||
std::atomic_bool inverter_mode;
|
||||
|
||||
std::mutex settingsMutex;
|
||||
};
|
||||
|
||||
#endif // INFY_CAN_DEVICE_HPP
|
||||
@@ -0,0 +1,173 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "power_supply_DCImpl.hpp"
|
||||
#include <cmath>
|
||||
#include <iomanip>
|
||||
|
||||
namespace module {
|
||||
namespace main {
|
||||
|
||||
namespace {
|
||||
constexpr double voltage_log_threshold_v = 0.5;
|
||||
constexpr double current_log_threshold_a = 0.2;
|
||||
|
||||
bool should_log_setpoint(std::optional<double>& last_voltage, std::optional<double>& last_current, double voltage,
|
||||
double current) {
|
||||
if (!last_voltage.has_value() || !last_current.has_value() ||
|
||||
std::abs(voltage - *last_voltage) >= voltage_log_threshold_v ||
|
||||
std::abs(current - *last_current) >= current_log_threshold_a) {
|
||||
last_voltage = voltage;
|
||||
last_current = current;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void power_supply_DCImpl::init() {
|
||||
caps.bidirectional = true;
|
||||
|
||||
caps.current_regulation_tolerance_A = 1;
|
||||
caps.peak_current_ripple_A = 0.2;
|
||||
|
||||
caps.min_export_current_A = 1;
|
||||
caps.max_export_current_A = 73.3;
|
||||
caps.min_export_voltage_V = 200;
|
||||
caps.max_export_voltage_V = 1000;
|
||||
caps.max_export_power_W = 22000;
|
||||
caps.conversion_efficiency_export = 0.95;
|
||||
|
||||
caps.max_import_current_A = 73.3;
|
||||
caps.min_import_current_A = 0;
|
||||
caps.max_import_power_W = 22000;
|
||||
caps.min_import_voltage_V = 200;
|
||||
caps.max_import_voltage_V = 1000;
|
||||
caps.conversion_efficiency_import = 0.95;
|
||||
|
||||
mod->acdc.signalVoltageCurrent.connect([this](float voltage, float current) {
|
||||
types::power_supply_DC::VoltageCurrent vc;
|
||||
if (last_publish_mode == types::power_supply_DC::Mode::Import) {
|
||||
// According to ISO 15118-20 V2G20-1034 / V2G20-1035,
|
||||
// negative current indicates EV -> EVSE power transfer (discharging).
|
||||
current = -current;
|
||||
}
|
||||
vc.current_A = current;
|
||||
vc.voltage_V = voltage;
|
||||
publish_voltage_current(vc);
|
||||
});
|
||||
|
||||
mod->acdc.signalModuleStatus.connect(
|
||||
[this](can_packet_acdc::PowerModuleStatus status, can_packet_acdc::InverterStatus inverter_status) {
|
||||
static bool firsttime = true;
|
||||
|
||||
// Publish mode changes
|
||||
types::power_supply_DC::Mode mode;
|
||||
|
||||
if (status.fault_alarm) {
|
||||
mode = types::power_supply_DC::Mode::Fault;
|
||||
} else if (status.dc_side_off) {
|
||||
mode = types::power_supply_DC::Mode::Off;
|
||||
} else if (inverter_status.invert_mode) {
|
||||
mode = types::power_supply_DC::Mode::Import;
|
||||
} else {
|
||||
mode = types::power_supply_DC::Mode::Export;
|
||||
}
|
||||
|
||||
if (last_publish_mode != mode || firsttime) {
|
||||
publish_mode(mode);
|
||||
last_publish_mode = mode;
|
||||
firsttime = false;
|
||||
}
|
||||
});
|
||||
|
||||
mod->acdc.switch_on_off(false);
|
||||
mod->acdc.adjust_power_factor(1.0);
|
||||
mod->acdc.set_output_mode(InfyCanDevice::OutputMode::Automatic);
|
||||
}
|
||||
|
||||
void power_supply_DCImpl::ready() {
|
||||
publish_capabilities(caps);
|
||||
}
|
||||
|
||||
void power_supply_DCImpl::handle_setMode(types::power_supply_DC::Mode& mode,
|
||||
types::power_supply_DC::ChargingPhase& phase) {
|
||||
std::scoped_lock lock(settings_mutex);
|
||||
|
||||
if (mode != last_publish_mode) {
|
||||
last_logged_export_voltage.reset();
|
||||
last_logged_export_current.reset();
|
||||
last_logged_import_voltage.reset();
|
||||
last_logged_import_current.reset();
|
||||
}
|
||||
|
||||
if (mode == types::power_supply_DC::Mode::Off) {
|
||||
mod->acdc.switch_on_off(false);
|
||||
mod->acdc.set_inverter_mode(false);
|
||||
} else if (mode == types::power_supply_DC::Mode::Export) {
|
||||
mod->acdc.set_inverter_mode(false);
|
||||
mod->acdc.switch_on_off(true);
|
||||
} else if (mode == types::power_supply_DC::Mode::Import) {
|
||||
mod->acdc.set_inverter_mode(true);
|
||||
mod->acdc.switch_on_off(true);
|
||||
} else if (mode == types::power_supply_DC::Mode::Fault) {
|
||||
mod->acdc.switch_on_off(false);
|
||||
mod->acdc.set_inverter_mode(false);
|
||||
}
|
||||
};
|
||||
|
||||
void power_supply_DCImpl::handle_setExportVoltageCurrent(double& voltage, double& current) {
|
||||
|
||||
if (voltage > caps.max_export_voltage_V)
|
||||
voltage = caps.max_export_voltage_V;
|
||||
else if (voltage < caps.min_export_voltage_V)
|
||||
voltage = caps.min_export_voltage_V;
|
||||
|
||||
if (current > caps.max_export_current_A)
|
||||
current = caps.max_export_current_A;
|
||||
else if (current < caps.min_export_current_A)
|
||||
current = caps.min_export_current_A;
|
||||
|
||||
std::scoped_lock lock(settings_mutex);
|
||||
|
||||
exportVoltage = voltage;
|
||||
exportCurrentLimit = current;
|
||||
|
||||
if (should_log_setpoint(this->last_logged_export_voltage, this->last_logged_export_current, exportVoltage,
|
||||
exportCurrentLimit)) {
|
||||
EVLOG_info << std::fixed << std::setprecision(2) << "Updating voltage/current via CAN: " << exportVoltage
|
||||
<< "V / " << exportCurrentLimit << "A";
|
||||
}
|
||||
mod->acdc.set_voltage_current(exportVoltage, exportCurrentLimit);
|
||||
};
|
||||
|
||||
void power_supply_DCImpl::handle_setImportVoltageCurrent(double& voltage, double& current) {
|
||||
|
||||
if (caps.min_import_voltage_V.has_value() && caps.max_import_current_A.has_value()) {
|
||||
|
||||
if (voltage > caps.max_import_voltage_V.value())
|
||||
voltage = caps.max_import_voltage_V.value();
|
||||
else if (voltage < caps.min_import_voltage_V.value())
|
||||
voltage = caps.min_import_voltage_V.value();
|
||||
|
||||
if (current > caps.max_import_current_A.value())
|
||||
current = caps.max_import_current_A.value();
|
||||
else if (current < caps.min_import_current_A.value())
|
||||
current = caps.min_import_current_A.value();
|
||||
|
||||
std::scoped_lock lock(settings_mutex);
|
||||
minImportVoltage = voltage;
|
||||
importCurrentLimit = current;
|
||||
|
||||
if (should_log_setpoint(this->last_logged_import_voltage, this->last_logged_import_current, minImportVoltage,
|
||||
importCurrentLimit)) {
|
||||
EVLOG_info << std::fixed << std::setprecision(2) << "Updating voltage/current via CAN: " << minImportVoltage
|
||||
<< "V / " << importCurrentLimit << "A";
|
||||
}
|
||||
mod->acdc.set_voltage_current(minImportVoltage, importCurrentLimit);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace main
|
||||
} // namespace module
|
||||
@@ -0,0 +1,77 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 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 "../InfyPower_BEG1K075G.hpp"
|
||||
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
// insert your custom include headers here
|
||||
#include <optional>
|
||||
// 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<InfyPower_BEG1K075G>& 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<InfyPower_BEG1K075G>& mod;
|
||||
const Conf& config;
|
||||
|
||||
virtual void init() override;
|
||||
virtual void ready() override;
|
||||
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
std::mutex settings_mutex;
|
||||
double exportVoltage{0.};
|
||||
double exportCurrentLimit{0.};
|
||||
double minImportVoltage{0.};
|
||||
double importCurrentLimit{0.};
|
||||
types::power_supply_DC::Capabilities caps;
|
||||
|
||||
types::power_supply_DC::Mode last_publish_mode{types::power_supply_DC::Mode::Off};
|
||||
std::optional<double> last_logged_export_voltage;
|
||||
std::optional<double> last_logged_export_current;
|
||||
std::optional<double> last_logged_import_voltage;
|
||||
std::optional<double> last_logged_import_current;
|
||||
// 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
|
||||
@@ -0,0 +1,16 @@
|
||||
description: Driver for InfyPower ACDC power supply. Currently supports BEG1K075G
|
||||
with only one module and group dial set to 0.
|
||||
config:
|
||||
can_device:
|
||||
description: CAN interface name
|
||||
type: string
|
||||
default: can0
|
||||
provides:
|
||||
main:
|
||||
description: Main interface
|
||||
interface: power_supply_DC
|
||||
config: {}
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Cornelius Claussen
|
||||
@@ -0,0 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
# set the project name
|
||||
project(infy-acdc-test-tool VERSION 0.1)
|
||||
|
||||
# specify the C++ standard
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
# add the executable
|
||||
add_executable(infy-acdc-test-tool main.cpp ../can_driver_acdc/CanDevice.cpp ../can_driver_acdc/InfyCanDevice.cpp ../can_driver_acdc/CanPackets.cpp)
|
||||
target_include_directories(infy-acdc-test-tool PRIVATE ".." "../can_driver_acdc" ${everest-framework_SOURCE_DIR})
|
||||
target_link_libraries(infy-acdc-test-tool PRIVATE Pal::Sigslot date::date date::date-tz everest::log)
|
||||
@@ -0,0 +1,78 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "InfyCanDevice.hpp"
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <everest/logging.hpp>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
InfyCanDevice can;
|
||||
|
||||
if (!can.open_device("can0")) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
EVLOG_debug << "CAN device started.";
|
||||
can.switch_on_off(false);
|
||||
can.adjust_power_factor(1.0);
|
||||
can.set_output_mode(InfyCanDevice::OutputMode::Automatic);
|
||||
|
||||
sleep(2);
|
||||
can.set_generic_setting(0x21, 0x20, 420 * 1000);
|
||||
can.set_generic_setting(0x21, 0x21, 5 * 1000);
|
||||
can.set_generic_setting(0x21, 0x22, 440 * 1000);
|
||||
can.set_generic_setting(0x21, 0x23, 5 * 1000);
|
||||
|
||||
can.set_generic_setting(0x21, 0x24, 200 * 1000);
|
||||
can.set_generic_setting(0x21, 0x25, 5 * 1000);
|
||||
can.set_generic_setting(0x21, 0x26, 190 * 1000);
|
||||
can.set_generic_setting(0x21, 0x27, 5 * 1000);
|
||||
|
||||
can.set_generic_setting(0x21, 0x28, 53 * 1000);
|
||||
can.set_generic_setting(0x21, 0x29, 5 * 1000);
|
||||
can.set_generic_setting(0x21, 0x2A, 54 * 1000);
|
||||
can.set_generic_setting(0x21, 0x2B, 5 * 1000);
|
||||
|
||||
can.set_generic_setting(0x21, 0x2C, 47 * 1000);
|
||||
can.set_generic_setting(0x21, 0x2D, 5 * 1000);
|
||||
can.set_generic_setting(0x21, 0x2E, 46 * 1000);
|
||||
can.set_generic_setting(0x21, 0x2F, 5 * 1000);
|
||||
EVLOG_debug << "Protection settings applied";
|
||||
|
||||
can.set_inverter_mode(false);
|
||||
can.set_voltage_current(200, 10);
|
||||
can.set_walkin_enabled(false);
|
||||
|
||||
can.switch_on_off(true);
|
||||
can.set_voltage_current(200, 10);
|
||||
|
||||
can.adjust_power_factor(1.0);
|
||||
|
||||
while (true) {
|
||||
can.request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::GenericSetting(0x11, 0x03, 0x00));
|
||||
can.request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::GenericSetting(0x11, 0x04, 0x00));
|
||||
can.request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::GenericSetting(0x11, 0x05, 0x00));
|
||||
can.request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::GenericSetting(0x11, 0x06, 0x00));
|
||||
|
||||
can.request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::GenericSetting(0x11, 0x30, 0x00));
|
||||
can.request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::GenericSetting(0x11, 0x31, 0x00));
|
||||
|
||||
can.request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::GenericSetting(0x11, 0x32, 0x00));
|
||||
can.request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::GenericSetting(0x11, 0x33, 0x00));
|
||||
for (uint8_t i = 0x01; i < 0x14; i++) {
|
||||
can.request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::GenericSetting(0x21, i, 0x00));
|
||||
}
|
||||
|
||||
can.request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::GenericSetting(0x41, 0x01, 0x00));
|
||||
can.request_rx(can_packet_acdc::ADDR_MODULE, can_packet_acdc::GenericSetting(0x41, 0x02, 0x00));
|
||||
|
||||
usleep(50000);
|
||||
|
||||
EVLOG_debug << can.telemetry;
|
||||
}
|
||||
|
||||
can.close_device();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user