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/UUGreenCanDevice.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,49 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "UUGreenPower_UR1000X0.hpp"
|
||||
#include <regex>
|
||||
|
||||
namespace module {
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> parse_module_addresses(const std::string a) {
|
||||
std::vector<uint8_t> addresses;
|
||||
auto adr = split_by_delimeters(a, ",");
|
||||
for (const auto& ad : adr) {
|
||||
addresses.push_back(std::stoi(ad));
|
||||
}
|
||||
return addresses;
|
||||
}
|
||||
|
||||
void UUGreenPower_UR1000X0::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));
|
||||
}
|
||||
|
||||
// configure module address
|
||||
auto module_addresses = parse_module_addresses(config.module_addresses);
|
||||
EVLOG_info << "Amount of modules: " << module_addresses.size();
|
||||
acdc.set_module_address(module_addresses);
|
||||
|
||||
acdc.set_voltage_mode(config.voltage_mode);
|
||||
|
||||
acdc.switch_on(false);
|
||||
|
||||
invoke_init(*p_main);
|
||||
|
||||
acdc.request_module_info();
|
||||
}
|
||||
|
||||
void UUGreenPower_UR1000X0::ready() {
|
||||
invoke_ready(*p_main);
|
||||
}
|
||||
|
||||
} // namespace module
|
||||
@@ -0,0 +1,64 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
#ifndef UUGREEN_POWER_UR1000X0_HPP
|
||||
#define UUGREEN_POWER_UR1000X0_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/UUGreenCanDevice.hpp"
|
||||
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
|
||||
|
||||
namespace module {
|
||||
|
||||
struct Conf {
|
||||
std::string can_device;
|
||||
std::string module_addresses;
|
||||
int voltage_mode;
|
||||
int max_export_current_A;
|
||||
int max_export_power_W;
|
||||
};
|
||||
|
||||
class UUGreenPower_UR1000X0 : public Everest::ModuleBase {
|
||||
public:
|
||||
UUGreenPower_UR1000X0() = delete;
|
||||
UUGreenPower_UR1000X0(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
|
||||
UUGreenCanDevice 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 // UUGREEN_POWER_UR1000X0_HPP
|
||||
@@ -0,0 +1,120 @@
|
||||
// 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 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,114 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "CanPackets.hpp"
|
||||
#include "Conversions.hpp"
|
||||
#include <iostream>
|
||||
|
||||
namespace UU {
|
||||
|
||||
static std::string to_string(CommandType m) {
|
||||
switch (m) {
|
||||
case CommandType::Vout:
|
||||
return "Vout";
|
||||
case CommandType::Iout_slow:
|
||||
return "Iout_slow";
|
||||
case CommandType::VoutReference:
|
||||
return "VoutReference";
|
||||
case CommandType::IoutLimit:
|
||||
return "IoutLimit";
|
||||
case CommandType::ShutdownDCDC:
|
||||
return "ShutdownDCDC";
|
||||
case CommandType::ReadSN:
|
||||
return "ReadSN";
|
||||
case CommandType::ModuleStatus:
|
||||
return "ModuleStatus";
|
||||
case CommandType::Vab:
|
||||
return "Vab";
|
||||
case CommandType::Vbc:
|
||||
return "Vbc";
|
||||
case CommandType::VfanReference:
|
||||
return "VfanReference";
|
||||
case CommandType::Tin:
|
||||
return "Tin";
|
||||
case CommandType::Iout_fastest:
|
||||
return "Iout_fastest";
|
||||
case CommandType::FanSilentLevel:
|
||||
return "FanSilentLevel";
|
||||
case CommandType::GroupAddress:
|
||||
return "GroupAddress";
|
||||
case CommandType::HiMode_LoMode_Selection:
|
||||
return "HiMode_LoMode_Selection";
|
||||
case CommandType::HiMode_LoMode_Status:
|
||||
return "HiMode_LoMode_Status";
|
||||
case CommandType::Vout_fast:
|
||||
return "Vout_fast";
|
||||
case CommandType::TrueHiLo_Status:
|
||||
return "TrueHiLo_Status";
|
||||
case CommandType::CurrentCapability:
|
||||
return "CurrentCapability";
|
||||
case CommandType::CurrentAndCapability:
|
||||
return "CurrentAndCapability";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static std::string to_string(MessageType m) {
|
||||
switch (m) {
|
||||
case MessageType::SetData:
|
||||
return "SetData";
|
||||
case MessageType::SetDataResponse:
|
||||
return "SetDataResponse";
|
||||
case MessageType::ReadData:
|
||||
return "ReadData";
|
||||
case MessageType::ReadDataResponse:
|
||||
return "ReadDataResponse";
|
||||
case MessageType::ReadSerialNumberResponse:
|
||||
return "ReadSerialNumberResponse";
|
||||
case MessageType::AllSetData:
|
||||
return "AllSetData";
|
||||
case MessageType::AllSetDataResponse:
|
||||
return "AllSetDataResponse";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
// helper functions
|
||||
uint32_t encode_can_id(uint8_t module_address) {
|
||||
uint32_t id = (module_address & 0x7F)
|
||||
<< 14; // Module address is bits 20:14 (7 bits). Use 0x00 for broadcast to all modules
|
||||
id |= (1 << 25); // Protocol version 0x01 in bits 28:25 (4 bits)
|
||||
id |= (1 << 21); // Monitor address is 0x01 in bits 24:21 (4 bits)
|
||||
// Production date and SerialNumber is not set on our outgoing monitor frames
|
||||
return id;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const Packet& self) {
|
||||
out << "UUPacket: grp: " << std::to_string(self.group_address) << " MsgType: " << to_string(self.message_type)
|
||||
<< " CmdType: " << to_string(self.command_type) << " Data: " << std::to_string(self.data);
|
||||
return out;
|
||||
}
|
||||
|
||||
Packet::operator std::vector<uint8_t>() {
|
||||
std::vector<uint8_t> out;
|
||||
to_raw(static_cast<uint8_t>(((group_address & 0x0F) << 4) |
|
||||
static_cast<std::underlying_type<MessageType>::type>(message_type) & 0x0F),
|
||||
out); // 4 bits group address, 4 bits message type
|
||||
to_raw(static_cast<uint8_t>(static_cast<std::underlying_type<MessageType>::type>(command_type) & 0x7F),
|
||||
out); // 7 bits of command type
|
||||
to_raw(static_cast<uint16_t>(0x00), out); // 2 reserved bytes
|
||||
to_raw(data, out); // 4 bytes of actual data
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
Packet::Packet(const std::vector<uint8_t> raw) {
|
||||
uint8_t grp_msg_type = from_raw<uint8_t>(raw, 0);
|
||||
group_address = grp_msg_type >> 4;
|
||||
message_type = static_cast<MessageType>(grp_msg_type & 0x0F);
|
||||
command_type = static_cast<CommandType>(from_raw<uint8_t>(raw, 1));
|
||||
bytes23 = from_raw<uint16_t>(raw, 2);
|
||||
data = from_raw<uint32_t>(raw, 4);
|
||||
}
|
||||
|
||||
} // namespace UU
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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 UU {
|
||||
|
||||
uint32_t encode_can_id(uint8_t destination_address);
|
||||
|
||||
inline uint8_t module_address_from_can_id(uint32_t id) {
|
||||
return (id >> 14) & 0x7F;
|
||||
};
|
||||
|
||||
inline uint8_t monitor_adress_from_can_id(uint32_t id) {
|
||||
return (id >> 21) & 0x0F;
|
||||
};
|
||||
|
||||
inline uint8_t production_date_from_can_id(uint32_t id) {
|
||||
return (id >> 9) & 0x1F;
|
||||
};
|
||||
|
||||
inline uint8_t serial_num_low_from_can_id(uint32_t id) {
|
||||
return id & 0x1FF;
|
||||
};
|
||||
|
||||
// Commands
|
||||
const uint8_t CMD_READ = 0x23;
|
||||
const uint8_t CMD_WRITE = 0x24;
|
||||
|
||||
// Addresses
|
||||
const uint8_t ADDR_BROADCAST = 0x00;
|
||||
|
||||
// RX and TX packet definitions
|
||||
|
||||
enum class MessageType : uint8_t {
|
||||
SetData = 0,
|
||||
SetDataResponse = 1,
|
||||
ReadData = 2,
|
||||
ReadDataResponse = 3,
|
||||
ReadSerialNumberResponse = 4,
|
||||
AllSetData = 11,
|
||||
AllSetDataResponse = 12
|
||||
};
|
||||
|
||||
// Note: naming in this enum is questionable, but follows the Can protocol documentation from UUGreenPower
|
||||
enum class CommandType : uint8_t {
|
||||
Vout = 0,
|
||||
Iout_slow = 1,
|
||||
VoutReference = 2,
|
||||
IoutLimit = 3,
|
||||
ShutdownDCDC = 4,
|
||||
ReadSN = 5,
|
||||
ModuleStatus = 8,
|
||||
Vab = 20,
|
||||
Vbc = 21,
|
||||
Vca = 22,
|
||||
VfanReference = 26,
|
||||
Tin = 30,
|
||||
Iout_fastest = 47,
|
||||
FanSilentLevel = 62,
|
||||
GroupAddress = 89,
|
||||
HiMode_LoMode_Selection = 95,
|
||||
HiMode_LoMode_Status = 96,
|
||||
Vout_fast = 98,
|
||||
TrueHiLo_Status = 101,
|
||||
CurrentCapability = 104,
|
||||
CurrentAndCapability = 114
|
||||
};
|
||||
|
||||
struct Packet {
|
||||
Packet(MessageType _message_type, CommandType _command_type, uint32_t _data = 0x00, uint8_t _group_address = 0x00) :
|
||||
message_type(_message_type), command_type(_command_type), data(_data), group_address(_group_address){};
|
||||
|
||||
Packet(const std::vector<uint8_t> raw);
|
||||
friend std::ostream& operator<<(std::ostream& out, const Packet& self);
|
||||
operator std::vector<uint8_t>();
|
||||
|
||||
uint8_t group_address{0};
|
||||
MessageType message_type{MessageType::ReadData};
|
||||
CommandType command_type{CommandType::ReadSN};
|
||||
uint16_t bytes23{0};
|
||||
uint32_t data{0};
|
||||
};
|
||||
|
||||
} // namespace UU
|
||||
|
||||
#endif // CAN_PACKETS_HPP
|
||||
@@ -0,0 +1,73 @@
|
||||
// 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,252 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "UUGreenCanDevice.hpp"
|
||||
#include "CanPackets.hpp"
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <everest/logging.hpp>
|
||||
|
||||
UUGreenCanDevice::~UUGreenCanDevice() {
|
||||
exit_tx_thread = true;
|
||||
exit_cmd_thread = true;
|
||||
}
|
||||
|
||||
bool UUGreenCanDevice::switch_on(bool on) {
|
||||
// actual switching on will be handled in tx thread
|
||||
requested_on = on;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UUGreenCanDevice::switch_on_nolock(bool on) {
|
||||
|
||||
uint32_t data{0x00};
|
||||
if (not on) {
|
||||
data = 0x01;
|
||||
}
|
||||
|
||||
bool success = true;
|
||||
|
||||
for (auto module_address : module_addresses) {
|
||||
if (not tx(module_address, UU::Packet(UU::MessageType::SetData, UU::CommandType::ShutdownDCDC, data))) {
|
||||
success = false;
|
||||
} else {
|
||||
is_on = on;
|
||||
}
|
||||
}
|
||||
|
||||
if (on) {
|
||||
// After switching on, update the voltage/current settings
|
||||
set_voltage_current_nolock(set_point_voltage, set_point_current);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool UUGreenCanDevice::set_voltage_current(float voltage, float current) {
|
||||
requested_set_point_voltage = voltage;
|
||||
requested_set_point_current = current;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UUGreenCanDevice::set_voltage_current_nolock(float voltage, float current) {
|
||||
bool success = true;
|
||||
|
||||
set_point_voltage = voltage;
|
||||
set_point_current = current;
|
||||
|
||||
// check if we need to switch between high and lo voltage mode
|
||||
if (hi_mode_config_setting == VoltageMode::Automatic) {
|
||||
if (voltage > LO_MODE_MAX_VOLTAGE) {
|
||||
hi_mode_commanded = VoltageMode::High;
|
||||
} else {
|
||||
hi_mode_commanded = VoltageMode::Low;
|
||||
}
|
||||
}
|
||||
|
||||
internal_update_voltage_mode();
|
||||
|
||||
for (auto module_address : module_addresses) {
|
||||
if (not tx(module_address, UU::Packet(UU::MessageType::SetData, UU::CommandType::VoutReference,
|
||||
static_cast<uint32_t>(voltage * 1000)))) {
|
||||
success = false;
|
||||
}
|
||||
|
||||
// Split current equally between modules
|
||||
if (not tx(module_address, UU::Packet(UU::MessageType::SetData, UU::CommandType::IoutLimit,
|
||||
static_cast<uint32_t>(current * 1000 / module_addresses.size())))) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
bool UUGreenCanDevice::internal_update_voltage_mode() {
|
||||
|
||||
bool success = true;
|
||||
if (hi_mode_last_commanded not_eq hi_mode_commanded) {
|
||||
hi_mode_last_commanded = hi_mode_commanded;
|
||||
|
||||
bool was_on = is_on;
|
||||
|
||||
if (was_on) {
|
||||
// Switch it off if it was on before
|
||||
switch_on_nolock(false);
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
|
||||
for (auto module_address : module_addresses) {
|
||||
// We need to change hi/lo voltage mode, so power it off, change mode, and power it on again if it was
|
||||
// powered on before
|
||||
if (not tx(module_address,
|
||||
UU::Packet(UU::MessageType::SetData, UU::CommandType::HiMode_LoMode_Selection,
|
||||
static_cast<std::underlying_type<VoltageMode>::type>(hi_mode_commanded)))) {
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
if (was_on) {
|
||||
// Switch it on if it was on before
|
||||
switch_on_nolock(true);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static std::string to_serial_number(uint16_t a, uint32_t b) {
|
||||
|
||||
std::string ser = "Module type: " + std::to_string((int)(a & 0x01FF));
|
||||
ser += " | OV Type code: " + std::to_string((int)(a >> 10));
|
||||
// Note: the S/N reported here does not match the sticker on the case
|
||||
ser += " | S/N: " + std::to_string(b);
|
||||
return ser;
|
||||
}
|
||||
|
||||
void UUGreenCanDevice::rx_handler(uint32_t can_id, const std::vector<uint8_t>& payload) {
|
||||
// We only use extended frames here
|
||||
if (!(can_id & CAN_EFF_FLAG)) {
|
||||
return;
|
||||
}
|
||||
|
||||
EVLOG_debug << "UUGreen: CAN frame received. ID: 0x" << std::hex << can_id;
|
||||
|
||||
// is it for us?
|
||||
|
||||
auto monitor_address = UU::monitor_adress_from_can_id(can_id);
|
||||
|
||||
if (not(monitor_address == UU::ADDR_BROADCAST or monitor_address == 0x01)) {
|
||||
EVLOG_debug << "UU: Not for us: ";
|
||||
return;
|
||||
}
|
||||
|
||||
// parse packet
|
||||
auto packet = UU::Packet(payload);
|
||||
|
||||
uint8_t source_address = UU::module_address_from_can_id(can_id);
|
||||
EVLOG_debug << "RX packet: " << packet;
|
||||
|
||||
if (packet.message_type == UU::MessageType::ReadDataResponse) {
|
||||
switch (packet.command_type) {
|
||||
|
||||
case UU::CommandType::Vout: {
|
||||
telemetries[source_address].voltage = packet.data / 1000.;
|
||||
// report average voltage
|
||||
float voltage = 0.;
|
||||
for (const auto& t : telemetries) {
|
||||
voltage += t.second.voltage;
|
||||
}
|
||||
voltage /= telemetries.size();
|
||||
signal_voltage_current(voltage, total_current);
|
||||
} break;
|
||||
|
||||
case UU::CommandType::Iout_slow: {
|
||||
// report sum of all currents
|
||||
telemetries[source_address].current = packet.data / 1000.;
|
||||
|
||||
// calculate total current
|
||||
total_current = 0.;
|
||||
for (const auto& t : telemetries) {
|
||||
total_current += t.second.current;
|
||||
}
|
||||
|
||||
} break;
|
||||
|
||||
case UU::CommandType::ReadSN: {
|
||||
// print serial number information
|
||||
signal_serial_number(source_address, to_serial_number(packet.bytes23, packet.data));
|
||||
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
if (packet.message_type == UU::MessageType::ReadSerialNumberResponse) {
|
||||
// print serial number information
|
||||
signal_serial_number(source_address, to_serial_number(packet.bytes23, packet.data));
|
||||
}
|
||||
}
|
||||
|
||||
void UUGreenCanDevice::request_module_info() {
|
||||
|
||||
// Request information about modules once
|
||||
for (auto module_address : module_addresses) {
|
||||
// request serial number
|
||||
tx(module_address, UU::Packet(UU::MessageType::ReadData, UU::CommandType::ReadSN));
|
||||
}
|
||||
}
|
||||
|
||||
void UUGreenCanDevice::tx_thread() {
|
||||
|
||||
while (!exit_tx_thread) {
|
||||
{
|
||||
for (auto module_address : module_addresses) {
|
||||
// request current system DC voltage. Answer will be processed by RX thread.
|
||||
tx(module_address, UU::Packet(UU::MessageType::ReadData, UU::CommandType::Vout));
|
||||
|
||||
// request current system DC current. Answer will be processed by RX thread.
|
||||
tx(module_address, UU::Packet(UU::MessageType::ReadData, UU::CommandType::Iout_slow));
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
}
|
||||
}
|
||||
|
||||
void UUGreenCanDevice::cmd_thread() {
|
||||
while (!exit_cmd_thread) {
|
||||
|
||||
{
|
||||
|
||||
// Do we need to switch on or off?
|
||||
if (last_requested_on not_eq requested_on) {
|
||||
last_requested_on = requested_on;
|
||||
switch_on_nolock(requested_on);
|
||||
}
|
||||
|
||||
// Do we need to set voltage/current limits?
|
||||
if (requested_set_point_voltage not_eq set_point_voltage or
|
||||
requested_set_point_current not_eq set_point_current) {
|
||||
set_voltage_current_nolock(requested_set_point_voltage, requested_set_point_current);
|
||||
}
|
||||
}
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
}
|
||||
|
||||
bool UUGreenCanDevice::tx(const uint8_t module_address, const std::vector<uint8_t>& payload) {
|
||||
uint32_t can_id = UU::encode_can_id(module_address);
|
||||
can_id |= 0x80000000U; // Extended frame format
|
||||
return _tx(can_id, payload);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, const UUGreenCanDevice::Telemetry& self) {
|
||||
out << "\n------------------------------------------------\nTelemetry\n---------\n";
|
||||
|
||||
out << "DC output: " << std::to_string(self.voltage) << "V / " << std::to_string(self.current) << "A" << std::endl;
|
||||
out << "------------------------------------------------\n";
|
||||
|
||||
return out;
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// 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 <map>
|
||||
#include <mutex>
|
||||
#include <sigslot/signal.hpp>
|
||||
#include <thread>
|
||||
|
||||
class UUGreenCanDevice : public CanDevice {
|
||||
public:
|
||||
UUGreenCanDevice() {
|
||||
// spawn thread that requests some data periodically to keep the connection alive
|
||||
exit_tx_thread = false;
|
||||
tx_thread_handle = std::thread(&UUGreenCanDevice::tx_thread, this);
|
||||
exit_cmd_thread = false;
|
||||
cmd_thread_handle = std::thread(&UUGreenCanDevice::cmd_thread, this);
|
||||
};
|
||||
|
||||
~UUGreenCanDevice();
|
||||
|
||||
void set_module_address(const std::vector<uint8_t>& _module_addresses) {
|
||||
module_addresses = _module_addresses;
|
||||
};
|
||||
|
||||
// Commands
|
||||
|
||||
bool switch_on(bool on);
|
||||
bool set_voltage_current(float voltage, float current);
|
||||
|
||||
enum class VoltageMode : uint32_t {
|
||||
Automatic = 0,
|
||||
High = 1,
|
||||
Low = 2,
|
||||
};
|
||||
|
||||
bool set_voltage_mode(VoltageMode high_mode) {
|
||||
hi_mode_config_setting = high_mode;
|
||||
return internal_update_voltage_mode();
|
||||
}
|
||||
|
||||
bool set_voltage_mode(int high_mode) {
|
||||
return set_voltage_mode(from_int(high_mode));
|
||||
}
|
||||
|
||||
int get_number_of_modules() {
|
||||
return module_addresses.size();
|
||||
}
|
||||
|
||||
void request_module_info();
|
||||
|
||||
// Data out
|
||||
sigslot::signal<float, float> signal_voltage_current;
|
||||
sigslot::signal<int, const std::string&> signal_serial_number;
|
||||
|
||||
struct Telemetry {
|
||||
float voltage{0.};
|
||||
float current{0.};
|
||||
};
|
||||
|
||||
float total_current{0.};
|
||||
|
||||
std::map<uint8_t, Telemetry> telemetries;
|
||||
|
||||
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:
|
||||
VoltageMode from_int(int mode) {
|
||||
VoltageMode m{VoltageMode::Automatic};
|
||||
if (mode == 1) {
|
||||
m = VoltageMode::High;
|
||||
} else if (mode == 2) {
|
||||
m = VoltageMode::Low;
|
||||
}
|
||||
return m;
|
||||
};
|
||||
|
||||
std::atomic_bool exit_tx_thread;
|
||||
std::thread tx_thread_handle;
|
||||
void tx_thread();
|
||||
|
||||
std::atomic_bool exit_cmd_thread;
|
||||
std::thread cmd_thread_handle;
|
||||
void cmd_thread();
|
||||
|
||||
bool tx(const uint8_t module_address, const std::vector<uint8_t>& payload);
|
||||
|
||||
std::vector<uint8_t> module_addresses{UU::ADDR_BROADCAST};
|
||||
|
||||
bool switch_on_nolock(bool on);
|
||||
bool internal_update_voltage_mode();
|
||||
|
||||
bool set_voltage_current_nolock(float voltage, float current);
|
||||
|
||||
float set_point_voltage{0.};
|
||||
float set_point_current{0.};
|
||||
|
||||
std::atomic<float> requested_set_point_voltage{0.};
|
||||
std::atomic<float> requested_set_point_current{0.};
|
||||
|
||||
VoltageMode hi_mode_config_setting{VoltageMode::Automatic};
|
||||
VoltageMode hi_mode_commanded{VoltageMode::Low};
|
||||
VoltageMode hi_mode_last_commanded{VoltageMode::High};
|
||||
|
||||
bool is_on{false};
|
||||
std::atomic_bool requested_on{false};
|
||||
bool last_requested_on{false};
|
||||
|
||||
static constexpr float LO_MODE_MAX_VOLTAGE{500};
|
||||
};
|
||||
|
||||
#endif // INFY_CAN_DEVICE_HPP
|
||||
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "power_supply_DCImpl.hpp"
|
||||
|
||||
namespace module {
|
||||
namespace main {
|
||||
|
||||
void power_supply_DCImpl::init() {
|
||||
|
||||
int number_of_modules = mod->acdc.get_number_of_modules();
|
||||
caps.bidirectional = false;
|
||||
|
||||
caps.current_regulation_tolerance_A = 1;
|
||||
caps.peak_current_ripple_A = 0.5;
|
||||
|
||||
caps.min_export_current_A = 0;
|
||||
caps.max_export_current_A = number_of_modules * mod->config.max_export_current_A;
|
||||
caps.min_export_voltage_V = 150;
|
||||
caps.max_export_voltage_V = 1000;
|
||||
caps.max_export_power_W = number_of_modules * mod->config.max_export_power_W;
|
||||
|
||||
caps.max_import_current_A = 25;
|
||||
caps.min_import_current_A = 0;
|
||||
caps.max_import_power_W = 10000;
|
||||
caps.min_import_voltage_V = 200;
|
||||
caps.max_import_voltage_V = 1000;
|
||||
|
||||
mod->acdc.signal_voltage_current.connect([this](float voltage, float current) {
|
||||
static uint8_t throttle_cnt = 0;
|
||||
if (throttle_cnt++ % 10 == 0) {
|
||||
types::power_supply_DC::VoltageCurrent vc;
|
||||
vc.current_A = current;
|
||||
vc.voltage_V = voltage;
|
||||
publish_voltage_current(vc);
|
||||
}
|
||||
});
|
||||
|
||||
mod->acdc.signal_serial_number.connect([this](int module_id, const std::string& serial_number) {
|
||||
EVLOG_info << "Module ID " << module_id << ": " << serial_number;
|
||||
});
|
||||
}
|
||||
|
||||
void power_supply_DCImpl::ready() {
|
||||
publish_capabilities(caps);
|
||||
mod->acdc.switch_on(false);
|
||||
}
|
||||
|
||||
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 == types::power_supply_DC::Mode::Off) {
|
||||
mod->acdc.switch_on(false);
|
||||
} else if (mode == types::power_supply_DC::Mode::Export) {
|
||||
mod->acdc.switch_on(true);
|
||||
} else if (mode == types::power_supply_DC::Mode::Import) {
|
||||
mod->acdc.switch_on(false);
|
||||
} else if (mode == types::power_supply_DC::Mode::Fault) {
|
||||
mod->acdc.switch_on(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);
|
||||
|
||||
export_voltage = voltage;
|
||||
export_current_limit = current;
|
||||
|
||||
EVLOG_info << "Updating voltage/current via CAN: " << export_voltage << "V / " << export_current_limit << "A";
|
||||
mod->acdc.set_voltage_current(export_voltage, export_current_limit);
|
||||
};
|
||||
|
||||
void power_supply_DCImpl::handle_setImportVoltageCurrent(double& voltage, double& current) {
|
||||
EVLOG_error << "Not implemented";
|
||||
}
|
||||
|
||||
} // namespace main
|
||||
} // namespace module
|
||||
@@ -0,0 +1,72 @@
|
||||
// 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 "../UUGreenPower_UR1000X0.hpp"
|
||||
|
||||
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
|
||||
// insert your custom include headers here
|
||||
// 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<UUGreenPower_UR1000X0>& 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<UUGreenPower_UR1000X0>& mod;
|
||||
const Conf& config;
|
||||
|
||||
virtual void init() override;
|
||||
virtual void ready() override;
|
||||
|
||||
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
|
||||
std::mutex settings_mutex;
|
||||
double export_voltage{0.};
|
||||
double export_current_limit{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};
|
||||
// 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,38 @@
|
||||
description: Driver for UUGreenPower_UR1000X0 ACDC power supply. Currently supports only one module.
|
||||
config:
|
||||
can_device:
|
||||
description: CAN interface name
|
||||
type: string
|
||||
default: can0
|
||||
module_addresses:
|
||||
description: >-
|
||||
Module Addresses to use. Set to "0" for broadcast. Make sure to connect only one PSU on the CAN bus in broadcast mode, otherwise
|
||||
it will charge at a too high current (as every module will produce the full current the EV requests!)
|
||||
If you have multiple PSUs, use individual addresses and list them comma separated, e.g. "1,2". Then it will share the current between
|
||||
these PSUs.
|
||||
type: string
|
||||
default: "0"
|
||||
voltage_mode:
|
||||
description: Set to 0 for automatic switching, 1 for high voltage mode, 2 for low voltage mode
|
||||
type: integer
|
||||
default: 0
|
||||
max_export_current_A:
|
||||
description: Maximum current that the PSU module can deliver. When using multiple PSUs in parallel, this refers to each individual PSU current capability.
|
||||
type: integer
|
||||
default: 100
|
||||
max_export_power_W:
|
||||
description: Maximum power that the PSU module can deliver. When using multiple PSUs in parallel, this refers to each individual PSU power capability.
|
||||
type: integer
|
||||
default: 40000
|
||||
provides:
|
||||
main:
|
||||
description: Main interface
|
||||
interface: power_supply_DC
|
||||
config: {}
|
||||
metadata:
|
||||
license: https://opensource.org/licenses/Apache-2.0
|
||||
authors:
|
||||
- Cornelius Claussen
|
||||
- Jan Christoph Habig
|
||||
- Andreas Heinrich
|
||||
- Florin Mihut
|
||||
@@ -0,0 +1,13 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
# set the project name
|
||||
project(uugreen-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(uugreen-acdc-test-tool main.cpp ../can_driver_acdc/CanDevice.cpp ../can_driver_acdc/UUGreenCanDevice.cpp ../can_driver_acdc/CanPackets.cpp)
|
||||
target_include_directories(uugreen-acdc-test-tool PRIVATE ".." "../can_driver_acdc" ${everest-framework_SOURCE_DIR})
|
||||
target_link_libraries(uugreen-acdc-test-tool PRIVATE Pal::Sigslot date::date date::date-tz everest::log)
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2025 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "UUGreenCanDevice.hpp"
|
||||
#include <iostream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <everest/logging.hpp>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
UUGreenCanDevice can;
|
||||
|
||||
if (!can.open_device("can0")) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
EVLOG_debug << "CAN device started.";
|
||||
|
||||
can.switch_on(true);
|
||||
can.set_voltage_current(200, 10);
|
||||
|
||||
while (true) {
|
||||
usleep(50000);
|
||||
}
|
||||
|
||||
can.close_device();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user