Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,154 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
// Portions (c) 2025 Analog Devices Inc.
#include "AdAcEvse22KwzKitBSP.hpp"
#include <fmt/core.h>
#include <utils/date.hpp>
namespace module {
void AdAcEvse22KwzKitBSP::init() {
// initialize serial driver
if (!serial.openDevice(config.serial_port.c_str(), config.baud_rate)) {
EVLOG_error << "Could not open serial port " << config.serial_port << " with baud rate " << config.baud_rate;
return;
}
telemetry_power_path_controller_version = {{"timestamp", ""},
{"type", "power_path_controller_version"},
{"hardware_version", 1},
{"software_version", "1.00"},
{"date_manufactured", "N/A"},
{"operating_time_h", 5},
{"operating_time_h_warning", 5000},
{"operating_time_h_error", 6000},
{"software_version", "1.00"},
{"error", false}};
telemetry_power_path_controller = {{"timestamp", ""},
{"type", "power_path_controller"},
{"cp_voltage_high", 0.0},
{"cp_voltage_low", 0.0},
{"cp_pwm_duty_cycle", 0.0},
{"cp_state", "A1"},
{"pp_ohm", 0.0},
{"supply_voltage_12V", 0.0},
{"supply_voltage_minus_12V", 0.0},
{"temperature_controller", 0.0},
{"temperature_car_connector", 0.0},
{"watchdog_reset_count", 0.0},
{"error", false}};
telemetry_power_switch = {{"timestamp", ""},
{"type", "power_switch"},
{"switching_count", 0},
{"switching_count_warning", 30000},
{"switching_count_error", 50000},
{"is_on", false},
{"time_to_switch_on_ms", 100},
{"time_to_switch_off_ms", 100},
{"temperature_C", 0.0},
{"error", false},
{"error_over_current", false}};
telemetry_rcd = {{"timestamp", ""}, //
{"type", "rcd"}, //
{"enabled", true}, //
{"current_mA", 0.0}, //
{"triggered", false}, //
{"error", false}}; //
invoke_init(*p_powermeter);
invoke_init(*p_board_support);
}
void AdAcEvse22KwzKitBSP::ready() {
serial.run();
if (!serial.reset(config.reset_gpio_chip, config.reset_gpio)) {
EVLOG_error << "AD-ACEVSE22KWZ-KIT reset not successful.";
}
serial.signalSpuriousReset.connect([this]() { EVLOG_error << "AD-ACEVSE22KWZ-KIT spurious reset!"; });
serial.signalConnectionTimeout.connect([this]() { EVLOG_error << "AD-ACEVSE22KWZ-KIT UART timeout!"; });
invoke_ready(*p_powermeter);
invoke_ready(*p_board_support);
telemetryThreadHandle = std::thread([this]() {
while (!telemetryThreadHandle.shouldExit()) {
sleep(10);
{
std::scoped_lock lock(telemetry_mutex);
publish_external_telemetry_livedata("power_path_controller", telemetry_power_path_controller);
publish_external_telemetry_livedata("rcd", telemetry_rcd);
publish_external_telemetry_livedata("power_path_controller_version",
telemetry_power_path_controller_version);
}
}
});
serial.signalErrorFlags.connect([this](ErrorFlags e) { error_handling(e); });
if (not serial.is_open()) {
auto err = p_board_support->error_factory->create_error("evse_board_support/CommunicationFault", "",
"Could not open serial port.");
p_board_support->raise_error(err);
}
}
void AdAcEvse22KwzKitBSP::publish_external_telemetry_livedata(const std::string& topic,
const Everest::TelemetryMap& data) {
if (info.telemetry_enabled) {
telemetry.publish("livedata", topic, data);
}
}
bool cp_signal_fault;
void AdAcEvse22KwzKitBSP::clear_errors_on_unplug() {
if (error_MREC2GroundFailure) {
p_board_support->clear_error("evse_board_support/MREC2GroundFailure");
}
error_MREC2GroundFailure = false;
}
void AdAcEvse22KwzKitBSP::error_handling(ErrorFlags e) {
if (e.diode_fault and not last_error_flags.diode_fault) {
Everest::error::Error error_object = p_board_support->error_factory->create_error(
"evse_board_support/DiodeFault", "", "Diode Fault", Everest::error::Severity::High);
p_board_support->raise_error(error_object);
} else if (not e.diode_fault and last_error_flags.diode_fault) {
p_board_support->clear_error("evse_board_support/DiodeFault");
}
if (e.rcd_triggered and not last_error_flags.rcd_triggered) {
Everest::error::Error error_object = p_board_support->error_factory->create_error(
"evse_board_support/MREC2GroundFailure", "", "Onboard RCD triggered", Everest::error::Severity::High);
p_board_support->raise_error(error_object);
error_MREC2GroundFailure = true;
}
if (e.ventilation_not_available and not last_error_flags.ventilation_not_available) {
Everest::error::Error error_object =
p_board_support->error_factory->create_error("evse_board_support/VentilationNotAvailable", "",
"State D is not supported", Everest::error::Severity::High);
p_board_support->raise_error(error_object);
} else if (not e.ventilation_not_available and last_error_flags.ventilation_not_available) {
p_board_support->clear_error("evse_board_support/VentilationNotAvailable");
}
if (e.cp_signal_fault and not last_error_flags.cp_signal_fault) {
Everest::error::Error error_object = p_board_support->error_factory->create_error(
"evse_board_support/MREC14PilotFault", "", "CP error", Everest::error::Severity::High);
p_board_support->raise_error(error_object);
} else if (not e.cp_signal_fault and last_error_flags.cp_signal_fault) {
p_board_support->clear_error("evse_board_support/MREC14PilotFault");
}
last_error_flags = e;
}
} // namespace module

View File

@@ -0,0 +1,88 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef AD_AC_EVSE22KWZ_KIT_BSP_HPP
#define AD_AC_EVSE22KWZ_KIT_BSP_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 2
//
#include "ld-ev.hpp"
// headers for provided interface implementations
#include <generated/interfaces/evse_board_support/Implementation.hpp>
#include <generated/interfaces/powermeter/Implementation.hpp>
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
#include "adkit_comms/evSerial.hpp"
// ev@4bf81b14-a215-475c-a1d3-0a484ae48918:v1
namespace module {
struct Conf {
std::string serial_port;
int baud_rate;
std::string reset_gpio_chip;
int reset_gpio;
int caps_min_current_A;
int caps_max_current_A;
};
class AdAcEvse22KwzKitBSP : public Everest::ModuleBase {
public:
AdAcEvse22KwzKitBSP() = delete;
AdAcEvse22KwzKitBSP(const ModuleInfo& info, Everest::TelemetryProvider& telemetry,
std::unique_ptr<powermeterImplBase> p_powermeter,
std::unique_ptr<evse_board_supportImplBase> p_board_support, Conf& config) :
ModuleBase(info),
telemetry(telemetry),
p_powermeter(std::move(p_powermeter)),
p_board_support(std::move(p_board_support)),
config(config){};
Everest::TelemetryProvider& telemetry;
const std::unique_ptr<powermeterImplBase> p_powermeter;
const std::unique_ptr<evse_board_supportImplBase> p_board_support;
const Conf& config;
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
void publish_external_telemetry_livedata(const std::string& topic, const Everest::TelemetryMap& data);
evSerial serial;
void clear_errors_on_unplug();
// ev@1fce4c5e-0ab8-41bb-90f7-14277703d2ac:v1
protected:
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
// insert your protected definitions here
// ev@4714b2ab-a24f-4b95-ab81-36439e1478de:v1
private:
friend class LdEverest;
void init();
void ready();
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
// insert your private definitions here
Everest::TelemetryMap telemetry_power_path_controller_version;
Everest::TelemetryMap telemetry_power_path_controller;
Everest::TelemetryMap telemetry_power_switch;
Everest::TelemetryMap telemetry_rcd;
std::mutex telemetry_mutex;
Everest::Thread telemetryThreadHandle;
void error_handling(ErrorFlags e);
ErrorFlags last_error_flags;
std::atomic_bool error_MREC2GroundFailure{false};
// ev@211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8:v1
};
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
Everest::json power_meter_data_to_json(const PowerMeter& p);
Everest::json keep_alive_lo_to_json(const KeepAliveLo& k);
std::string error_type_to_string(ErrorFlags s);
// ev@087e516b-124c-48df-94fb-109508c7cda9:v1
} // namespace module
#endif // AD_AC_EVSE22KWZ_KIT_BSP_HPP

View File

@@ -0,0 +1,36 @@
#
# AUTO GENERATED - MARKED REGIONS WILL BE KEPT
# template version 3
#
# module setup:
# - ${MODULE_NAME}: module name
ev_setup_cpp_module()
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
add_subdirectory(adkit_comms)
target_include_directories(${MODULE_NAME}
PRIVATE
"common"
"adkit_comms"
"adkit_comms/nanopb"
"adkit_comms/protobuf"
)
target_link_libraries(${MODULE_NAME}
PRIVATE
Pal::Sigslot
adkit_comms
)
# ev@bcc62523-e22b-41d7-ba2f-825b493a3c97:v1
target_sources(${MODULE_NAME}
PRIVATE
"powermeter/powermeterImpl.cpp"
"board_support/evse_board_supportImpl.cpp"
)
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1
# insert other things like install cmds etc here
# ev@c55432ab-152c-45a9-9d2e-7281d50c69c3:v1

View File

@@ -0,0 +1,33 @@
cmake_minimum_required(VERSION 3.10)
# set the project name
project(adkit_comms VERSION 0.1)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# add the executable
add_library(adkit_comms STATIC)
ev_register_library_target(adkit_comms)
target_sources(adkit_comms
PRIVATE
evSerial.cpp
protobuf/adkit.pb.c
)
target_include_directories(adkit_comms
PUBLIC
"${PROJECT_BINARY_DIR}"
protobuf
)
target_link_libraries(adkit_comms
PUBLIC
date::date-tz
everest::nanopb
PRIVATE
Pal::Sigslot
everest::framework
everest::gpio
)

View File

@@ -0,0 +1,387 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
// Portions (c) 2025 Analog Devices Inc.
#include "evSerial.hpp"
#include <cerrno>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include <fcntl.h>
#include <unistd.h>
#include <date/date.h>
#include <date/tz.h>
#include <everest/3rd_party/nanopb/pb_decode.h>
#include <everest/3rd_party/nanopb/pb_encode.h>
#include <everest/gpio/gpio.hpp>
#include "adkit.pb.h"
evSerial::evSerial() {
fd = 0;
baud = 0;
reset_done_flag = false;
forced_reset = false;
cobsDecodeReset();
}
evSerial::~evSerial() {
if (fd)
close(fd);
}
bool evSerial::openDevice(const char* device, int _baud) {
fd = open(device, O_RDWR | O_NOCTTY | O_SYNC);
if (fd < 0) {
printf("Serial: error %d opening %s: %s\n", errno, device, strerror(errno));
return false;
} // else printf ("Serial: opened %s as %i\n", device, fd);
cobsDecodeReset();
switch (_baud) {
case 9600:
baud = B9600;
break;
case 19200:
baud = B19200;
break;
case 38400:
baud = B38400;
break;
case 57600:
baud = B57600;
break;
case 115200:
baud = B115200;
break;
case 230400:
baud = B230400;
break;
default:
baud = 0;
return false;
}
return setSerialAttributes();
}
bool evSerial::setSerialAttributes() {
struct termios tty;
if (tcgetattr(fd, &tty) != 0) {
printf("Serial: error %d from tcgetattr\n", errno);
return false;
}
cfsetospeed(&tty, baud);
cfsetispeed(&tty, baud);
tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; // 8-bit chars
// disable IGNBRK for mismatched speed tests; otherwise receive break
// as \000 chars
tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF | IXANY);
tty.c_lflag = 0; // no signaling chars, no echo,
// no canonical processing
tty.c_oflag = 0; // no remapping, no delays
tty.c_cc[VMIN] = 0; // read blocks
tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
tty.c_cflag |= (CLOCAL | CREAD); // ignore modem controls,
// enable reading
tty.c_cflag &= ~(PARENB | PARODD); // shut off parity
tty.c_cflag &= ~CSTOPB;
tty.c_cflag &= ~CRTSCTS;
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Serial: error %d from tcsetattr\n", errno);
return false;
}
// printf ("Success setting tcsetattr\n");
return true;
}
void evSerial::cobsDecodeReset() {
code = 0xff;
block = 0;
decode = msg;
}
uint32_t evSerial::crc32(uint8_t* buf, int len) {
int i, j;
uint32_t b, crc, msk;
i = 0;
crc = 0xFFFFFFFF;
while (i < len) {
b = buf[i];
crc = crc ^ b;
for (j = 7; j >= 0; j--) {
msk = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & msk);
}
i = i + 1;
}
// printf("%X",crc);
return crc;
}
void evSerial::handlePacket(uint8_t* buf, int len) {
// printf ("packet received len %u\n", len);
// Check CRC32 (last 4 bytes)
// uint32_t crc = calculateCrc(rx_packet_buf, rx_packet_len);
if (crc32(buf, len)) {
printf("CRC mismatch\n");
return;
}
len -= 4;
McuToEverest msg_in;
pb_istream_t istream = pb_istream_from_buffer(buf, len);
if (pb_decode(&istream, McuToEverest_fields, &msg_in))
switch (msg_in.which_payload) {
case McuToEverest_keep_alive_tag:
// detect connection timeout if keep_alive packets stop coming...
last_timeout_detection_timestamp = date::utc_clock::now();
signalKeepAliveLo(msg_in.payload.keep_alive);
break;
case McuToEverest_power_meter_tag: {
auto unix_timestamp = std::chrono::seconds(std::time(NULL));
msg_in.payload.power_meter.time_stamp = unix_timestamp.count();
signalPowerMeter(msg_in.payload.power_meter);
} break;
case McuToEverest_cp_state_tag:
signalCPState(msg_in.payload.cp_state);
break;
case McuToEverest_pp_state_tag:
signalPPState(msg_in.payload.pp_state);
break;
case McuToEverest_relais_state_tag:
signalRelaisState(msg_in.payload.relais_state);
break;
case McuToEverest_error_flags_tag:
signalErrorFlags(msg_in.payload.error_flags);
break;
case McuToEverest_reset_tag:
// reset keep alive so that timeouts don't occur upon reset during initial start up
last_timeout_detection_timestamp = date::utc_clock::now();
reset_done_flag = true;
if (!forced_reset)
signalSpuriousReset();
break;
}
}
void evSerial::cobsDecode(uint8_t* buf, int len) {
for (int i = 0; i < len; i++)
cobsDecodeByte(buf[i]);
}
void evSerial::cobsDecodeByte(uint8_t byte) {
// check max length
if ((decode - msg == 2048 - 1) && byte != 0x00) {
printf("cobsDecode: Buffer overflow\n");
cobsDecodeReset();
}
if (block) {
// we're currently decoding and should not get a 0
if (byte == 0x00) {
// probably found some garbage -> reset
printf("cobsDecode: Garbage detected\n");
cobsDecodeReset();
return;
}
*decode++ = byte;
} else {
if (code != 0xff) {
*decode++ = 0;
}
block = code = byte;
if (code == 0x00) {
// we're finished, reset everything and commit
if (decode == msg) {
// we received nothing, just a 0x00
printf("cobsDecode: Received nothing\n");
} else {
// set back decode with one, as it gets post-incremented
handlePacket(msg, decode - 1 - msg);
}
cobsDecodeReset();
return; // need to return here, because of block--
}
}
block--;
}
void evSerial::run() {
readThreadHandle = std::thread(&evSerial::readThread, this);
timeoutDetectionThreadHandle = std::thread(&evSerial::timeoutDetectionThread, this);
}
void evSerial::timeoutDetectionThread() {
while (true) {
sleep(1);
if (timeoutDetectionThreadHandle.shouldExit())
break;
if (serial_timed_out())
signalConnectionTimeout();
}
}
void evSerial::readThread() {
uint8_t buf[2048];
int n;
cobsDecodeReset();
while (true) {
if (readThreadHandle.shouldExit())
break;
if (fd > 0) {
n = read(fd, buf, sizeof buf);
cobsDecode(buf, n);
}
}
}
bool evSerial::linkWrite(EverestToMcu* m) {
if (fd <= 0) {
return false;
}
uint8_t tx_packet_buf[1024];
uint8_t encode_buf[1500];
pb_ostream_t ostream = pb_ostream_from_buffer(tx_packet_buf, sizeof(tx_packet_buf) - 4);
bool status = pb_encode(&ostream, EverestToMcu_fields, m);
if (!status) {
// couldn't encode
return false;
}
size_t tx_payload_len = ostream.bytes_written;
// add crc32 (CRC-32/JAMCRC)
uint32_t crc = crc32(tx_packet_buf, tx_payload_len);
for (int byte_pos = 0; byte_pos < 4; ++byte_pos) {
tx_packet_buf[tx_payload_len] = (uint8_t)crc & 0xFF;
crc = crc >> 8;
tx_payload_len++;
}
size_t tx_encode_len = cobsEncode(tx_packet_buf, tx_payload_len, encode_buf);
// std::cout << "Write "<<tx_encode_len<<" bytes to serial port." << std::endl;
write(fd, encode_buf, tx_encode_len);
return true;
}
size_t evSerial::cobsEncode(const void* data, size_t length, uint8_t* buffer) {
uint8_t* encode = buffer; // Encoded byte pointer
uint8_t* codep = encode++; // Output code pointer
uint8_t code = 1; // Code value
for (const uint8_t* byte = (const uint8_t*)data; length--; ++byte) {
if (*byte) // Byte not zero, write it
*encode++ = *byte, ++code;
if (!*byte || code == 0xff) // Input is zero or block completed, restart
{
*codep = code, code = 1, codep = encode;
if (!*byte || length)
++encode;
}
}
*codep = code; // Write final code value
// add final 0
*encode++ = 0x00;
return encode - buffer;
}
bool evSerial::serial_timed_out() {
auto now = date::utc_clock::now();
auto timeSinceLastKeepAlive =
std::chrono::duration_cast<std::chrono::milliseconds>(now - last_timeout_detection_timestamp).count();
if (timeSinceLastKeepAlive >= 5000)
return true;
return false;
}
void evSerial::setPWM(uint32_t dc) {
EverestToMcu msg_out = EverestToMcu_init_default;
msg_out.which_payload = EverestToMcu_pwm_duty_cycle_tag;
msg_out.payload.pwm_duty_cycle = dc;
linkWrite(&msg_out);
}
void evSerial::allowPowerOn(bool p) {
EverestToMcu msg_out = EverestToMcu_init_default;
msg_out.which_payload = EverestToMcu_allow_power_on_tag;
msg_out.payload.allow_power_on = p;
linkWrite(&msg_out);
}
bool evSerial::reset(const std::string& reset_chip, const int reset_line) {
reset_done_flag = false;
forced_reset = true;
if (not reset_chip.empty()) {
// Try to hardware reset AD-ACEVSE22KWZ-KIT controller to be in a known state
Everest::Gpio reset_gpio;
reset_gpio.open(reset_chip, reset_line);
reset_gpio.set_output(true);
reset_gpio.set(true);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
reset_gpio.set(false);
std::this_thread::sleep_for(std::chrono::milliseconds(10));
reset_gpio.set(true);
} else {
// Try to soft reset AD-ACEVSE22KWZ-KIT controller to be in a known state
EverestToMcu msg_out = EverestToMcu_init_default;
msg_out.which_payload = EverestToMcu_reset_tag;
linkWrite(&msg_out);
}
bool success = false;
// Wait for reset done message from uC
for (int i = 0; i < 100; i++) {
if (reset_done_flag) {
success = true;
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(50));
}
// Reset flag to detect run time spurious resets of uC from now on
reset_done_flag = false;
forced_reset = false;
// send some dummy packets to resync COBS etc.
keepAlive();
keepAlive();
keepAlive();
return success;
}
void evSerial::keepAlive() {
EverestToMcu msg_out = EverestToMcu_init_default;
msg_out.which_payload = EverestToMcu_keep_alive_tag;
msg_out.payload.keep_alive.time_stamp = 0;
msg_out.payload.keep_alive.hw_type = 0;
msg_out.payload.keep_alive.hw_revision = 0;
strcpy(msg_out.payload.keep_alive.sw_version_string, "n/a");
linkWrite(&msg_out);
}

View File

@@ -0,0 +1,76 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
// Portions (c) 2025 Analog Devices Inc.
#ifndef AD_ACEVSE22KWZ_KIT_SERIAL_HPP
#define AD_ACEVSE22KWZ_KIT_SERIAL_HPP
#include "adkit.pb.h"
#include <date/date.h>
#include <date/tz.h>
#include <sigslot/signal.hpp>
#include <stdint.h>
#include <termios.h>
#include <utils/thread.hpp>
class evSerial {
public:
evSerial();
~evSerial();
bool openDevice(const char* device, int baud);
bool is_open() {
return fd > 0;
};
void readThread();
void run();
bool reset(const std::string& reset_chip, const int reset_line);
void keepAlive();
void setPWM(uint32_t dc);
void allowPowerOn(bool p);
sigslot::signal<KeepAliveLo> signalKeepAliveLo;
sigslot::signal<PowerMeter> signalPowerMeter;
sigslot::signal<CpState> signalCPState;
sigslot::signal<PpState> signalPPState;
sigslot::signal<ErrorFlags> signalErrorFlags;
sigslot::signal<bool> signalRelaisState;
sigslot::signal<> signalSpuriousReset;
sigslot::signal<> signalConnectionTimeout;
private:
// Serial interface
bool setSerialAttributes();
int fd;
int baud;
// COBS de-/encoder
void cobsDecodeReset();
void handlePacket(uint8_t* buf, int len);
void cobsDecode(uint8_t* buf, int len);
void cobsDecodeByte(uint8_t byte);
size_t cobsEncode(const void* data, size_t length, uint8_t* buffer);
uint8_t msg[2048];
uint8_t code;
uint8_t block;
uint8_t* decode;
uint32_t crc32(uint8_t* buf, int len);
// Read thread for serial port
Everest::Thread readThreadHandle;
Everest::Thread timeoutDetectionThreadHandle;
bool linkWrite(EverestToMcu* m);
volatile bool reset_done_flag;
volatile bool forced_reset;
bool serial_timed_out();
void timeoutDetectionThread();
std::chrono::time_point<date::utc_clock> last_timeout_detection_timestamp;
};
#endif

View File

@@ -0,0 +1,2 @@
KeepAlive.sw_version_string max_length:50
KeepAliveLo.sw_version_string max_length:50

View File

@@ -0,0 +1,33 @@
/* Automatically generated nanopb constant definitions */
/* Generated by nanopb-0.4.8 */
#include "adkit.pb.h"
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
PB_BIND(EverestToMcu, EverestToMcu, 2)
PB_BIND(McuToEverest, McuToEverest, 2)
PB_BIND(ErrorFlags, ErrorFlags, AUTO)
PB_BIND(KeepAliveLo, KeepAliveLo, AUTO)
PB_BIND(KeepAlive, KeepAlive, AUTO)
PB_BIND(Telemetry, Telemetry, AUTO)
PB_BIND(PowerMeter, PowerMeter, AUTO)

View File

@@ -0,0 +1,352 @@
/* Automatically generated nanopb header */
/* Generated by nanopb-0.4.8 */
#ifndef PB_ADKIT_PB_H_INCLUDED
#define PB_ADKIT_PB_H_INCLUDED
#include <everest/3rd_party/nanopb/pb.h>
#if PB_PROTO_HEADER_VERSION != 40
#error Regenerate this file with the current version of nanopb generator.
#endif
/* Enum definitions */
typedef enum _CpState {
CpState_STATE_A = 0,
CpState_STATE_B = 1,
CpState_STATE_C = 2,
CpState_STATE_D = 3,
CpState_STATE_E = 4,
CpState_STATE_F = 5
} CpState;
typedef enum _ResetReason {
ResetReason_USER = 0,
ResetReason_WATCHDOG = 1
} ResetReason;
typedef enum _PpState {
PpState_STATE_NC = 0,
PpState_STATE_13A = 1,
PpState_STATE_20A = 2,
PpState_STATE_32A = 3,
PpState_STATE_70A = 4,
PpState_STATE_FAULT = 5
} PpState;
/* Struct definitions */
typedef struct _ErrorFlags {
bool diode_fault;
bool rcd_selftest_failed;
bool rcd_triggered;
bool ventilation_not_available;
bool cp_signal_fault;
bool over_current;
} ErrorFlags;
typedef struct _KeepAliveLo {
uint32_t time_stamp;
uint32_t hw_type;
uint32_t hw_revision;
uint32_t protocol_version_major;
uint32_t protocol_version_minor;
char sw_version_string[51];
float hwcap_max_current;
float hwcap_min_current;
uint32_t hwcap_max_phase_count;
uint32_t hwcap_min_phase_count;
bool supports_changing_phases_during_charging;
} KeepAliveLo;
typedef struct _KeepAlive {
uint32_t time_stamp;
uint32_t hw_type;
uint32_t hw_revision;
char sw_version_string[51];
} KeepAlive;
/* This container message is send from EVerest to MCU and may contain any allowed message in that direction. */
typedef struct _EverestToMcu {
pb_size_t which_payload;
union {
KeepAlive keep_alive;
uint32_t pwm_duty_cycle; /* in 0.01 %, 0 = State F, 10000 = X1 */
bool allow_power_on;
bool reset;
} payload;
} EverestToMcu;
typedef struct _Telemetry {
uint32_t cp_voltage_hi;
uint32_t cp_voltage_lo;
} Telemetry;
typedef struct _PowerMeter {
uint32_t time_stamp;
float vrmsL1;
float vrmsL2;
float vrmsL3;
float irmsL1;
float irmsL2;
float irmsL3;
float irmsN;
float wattHrL1;
float wattHrL2;
float wattHrL3;
float totalWattHr;
float tempL1;
float tempL2;
float tempL3;
float tempN;
float wattL1;
float wattL2;
float wattL3;
float freqL1;
float freqL2;
float freqL3;
bool phaseSeqError;
} PowerMeter;
/* This container message is send from MCU to EVerest and may contain any allowed message in that direction. */
typedef struct _McuToEverest {
pb_size_t which_payload;
union {
KeepAliveLo keep_alive;
ResetReason reset;
CpState cp_state;
bool relais_state; /* false: relais are off, true: relais are on */
ErrorFlags error_flags;
Telemetry telemetry;
PpState pp_state;
PowerMeter power_meter;
} payload;
} McuToEverest;
#ifdef __cplusplus
extern "C" {
#endif
/* Helper constants for enums */
#define _CpState_MIN CpState_STATE_A
#define _CpState_MAX CpState_STATE_F
#define _CpState_ARRAYSIZE ((CpState)(CpState_STATE_F+1))
#define _ResetReason_MIN ResetReason_USER
#define _ResetReason_MAX ResetReason_WATCHDOG
#define _ResetReason_ARRAYSIZE ((ResetReason)(ResetReason_WATCHDOG+1))
#define _PpState_MIN PpState_STATE_NC
#define _PpState_MAX PpState_STATE_FAULT
#define _PpState_ARRAYSIZE ((PpState)(PpState_STATE_FAULT+1))
#define McuToEverest_payload_reset_ENUMTYPE ResetReason
#define McuToEverest_payload_cp_state_ENUMTYPE CpState
#define McuToEverest_payload_pp_state_ENUMTYPE PpState
/* Initializer values for message structs */
#define EverestToMcu_init_default {0, {KeepAlive_init_default}}
#define McuToEverest_init_default {0, {KeepAliveLo_init_default}}
#define ErrorFlags_init_default {0, 0, 0, 0, 0, 0}
#define KeepAliveLo_init_default {0, 0, 0, 0, 0, "", 0, 0, 0, 0, 0}
#define KeepAlive_init_default {0, 0, 0, ""}
#define Telemetry_init_default {0, 0}
#define PowerMeter_init_default {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
#define EverestToMcu_init_zero {0, {KeepAlive_init_zero}}
#define McuToEverest_init_zero {0, {KeepAliveLo_init_zero}}
#define ErrorFlags_init_zero {0, 0, 0, 0, 0, 0}
#define KeepAliveLo_init_zero {0, 0, 0, 0, 0, "", 0, 0, 0, 0, 0}
#define KeepAlive_init_zero {0, 0, 0, ""}
#define Telemetry_init_zero {0, 0}
#define PowerMeter_init_zero {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
/* Field tags (for use in manual encoding/decoding) */
#define ErrorFlags_diode_fault_tag 1
#define ErrorFlags_rcd_selftest_failed_tag 2
#define ErrorFlags_rcd_triggered_tag 3
#define ErrorFlags_ventilation_not_available_tag 4
#define ErrorFlags_cp_signal_fault_tag 6
#define ErrorFlags_over_current_tag 7
#define KeepAliveLo_time_stamp_tag 1
#define KeepAliveLo_hw_type_tag 2
#define KeepAliveLo_hw_revision_tag 3
#define KeepAliveLo_protocol_version_major_tag 4
#define KeepAliveLo_protocol_version_minor_tag 5
#define KeepAliveLo_sw_version_string_tag 6
#define KeepAliveLo_hwcap_max_current_tag 7
#define KeepAliveLo_hwcap_min_current_tag 8
#define KeepAliveLo_hwcap_max_phase_count_tag 9
#define KeepAliveLo_hwcap_min_phase_count_tag 10
#define KeepAliveLo_supports_changing_phases_during_charging_tag 11
#define KeepAlive_time_stamp_tag 1
#define KeepAlive_hw_type_tag 2
#define KeepAlive_hw_revision_tag 3
#define KeepAlive_sw_version_string_tag 6
#define EverestToMcu_keep_alive_tag 100
#define EverestToMcu_pwm_duty_cycle_tag 103
#define EverestToMcu_allow_power_on_tag 104
#define EverestToMcu_reset_tag 105
#define Telemetry_cp_voltage_hi_tag 1
#define Telemetry_cp_voltage_lo_tag 2
#define PowerMeter_time_stamp_tag 1
#define PowerMeter_vrmsL1_tag 2
#define PowerMeter_vrmsL2_tag 3
#define PowerMeter_vrmsL3_tag 4
#define PowerMeter_irmsL1_tag 5
#define PowerMeter_irmsL2_tag 6
#define PowerMeter_irmsL3_tag 7
#define PowerMeter_irmsN_tag 8
#define PowerMeter_wattHrL1_tag 9
#define PowerMeter_wattHrL2_tag 10
#define PowerMeter_wattHrL3_tag 11
#define PowerMeter_totalWattHr_tag 12
#define PowerMeter_tempL1_tag 13
#define PowerMeter_tempL2_tag 14
#define PowerMeter_tempL3_tag 15
#define PowerMeter_tempN_tag 16
#define PowerMeter_wattL1_tag 17
#define PowerMeter_wattL2_tag 18
#define PowerMeter_wattL3_tag 19
#define PowerMeter_freqL1_tag 20
#define PowerMeter_freqL2_tag 21
#define PowerMeter_freqL3_tag 22
#define PowerMeter_phaseSeqError_tag 23
#define McuToEverest_keep_alive_tag 3
#define McuToEverest_reset_tag 101
#define McuToEverest_cp_state_tag 102
#define McuToEverest_relais_state_tag 103
#define McuToEverest_error_flags_tag 104
#define McuToEverest_telemetry_tag 105
#define McuToEverest_pp_state_tag 106
#define McuToEverest_power_meter_tag 108
/* Struct field encoding specification for nanopb */
#define EverestToMcu_FIELDLIST(X, a) \
X(a, STATIC, ONEOF, MESSAGE, (payload,keep_alive,payload.keep_alive), 100) \
X(a, STATIC, ONEOF, UINT32, (payload,pwm_duty_cycle,payload.pwm_duty_cycle), 103) \
X(a, STATIC, ONEOF, BOOL, (payload,allow_power_on,payload.allow_power_on), 104) \
X(a, STATIC, ONEOF, BOOL, (payload,reset,payload.reset), 105)
#define EverestToMcu_CALLBACK NULL
#define EverestToMcu_DEFAULT NULL
#define EverestToMcu_payload_keep_alive_MSGTYPE KeepAlive
#define McuToEverest_FIELDLIST(X, a) \
X(a, STATIC, ONEOF, MESSAGE, (payload,keep_alive,payload.keep_alive), 3) \
X(a, STATIC, ONEOF, UENUM, (payload,reset,payload.reset), 101) \
X(a, STATIC, ONEOF, UENUM, (payload,cp_state,payload.cp_state), 102) \
X(a, STATIC, ONEOF, BOOL, (payload,relais_state,payload.relais_state), 103) \
X(a, STATIC, ONEOF, MESSAGE, (payload,error_flags,payload.error_flags), 104) \
X(a, STATIC, ONEOF, MESSAGE, (payload,telemetry,payload.telemetry), 105) \
X(a, STATIC, ONEOF, UENUM, (payload,pp_state,payload.pp_state), 106) \
X(a, STATIC, ONEOF, MESSAGE, (payload,power_meter,payload.power_meter), 108)
#define McuToEverest_CALLBACK NULL
#define McuToEverest_DEFAULT NULL
#define McuToEverest_payload_keep_alive_MSGTYPE KeepAliveLo
#define McuToEverest_payload_error_flags_MSGTYPE ErrorFlags
#define McuToEverest_payload_telemetry_MSGTYPE Telemetry
#define McuToEverest_payload_power_meter_MSGTYPE PowerMeter
#define ErrorFlags_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, BOOL, diode_fault, 1) \
X(a, STATIC, SINGULAR, BOOL, rcd_selftest_failed, 2) \
X(a, STATIC, SINGULAR, BOOL, rcd_triggered, 3) \
X(a, STATIC, SINGULAR, BOOL, ventilation_not_available, 4) \
X(a, STATIC, SINGULAR, BOOL, cp_signal_fault, 6) \
X(a, STATIC, SINGULAR, BOOL, over_current, 7)
#define ErrorFlags_CALLBACK NULL
#define ErrorFlags_DEFAULT NULL
#define KeepAliveLo_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, time_stamp, 1) \
X(a, STATIC, SINGULAR, UINT32, hw_type, 2) \
X(a, STATIC, SINGULAR, UINT32, hw_revision, 3) \
X(a, STATIC, SINGULAR, UINT32, protocol_version_major, 4) \
X(a, STATIC, SINGULAR, UINT32, protocol_version_minor, 5) \
X(a, STATIC, SINGULAR, STRING, sw_version_string, 6) \
X(a, STATIC, SINGULAR, FLOAT, hwcap_max_current, 7) \
X(a, STATIC, SINGULAR, FLOAT, hwcap_min_current, 8) \
X(a, STATIC, SINGULAR, UINT32, hwcap_max_phase_count, 9) \
X(a, STATIC, SINGULAR, UINT32, hwcap_min_phase_count, 10) \
X(a, STATIC, SINGULAR, BOOL, supports_changing_phases_during_charging, 11)
#define KeepAliveLo_CALLBACK NULL
#define KeepAliveLo_DEFAULT NULL
#define KeepAlive_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, time_stamp, 1) \
X(a, STATIC, SINGULAR, UINT32, hw_type, 2) \
X(a, STATIC, SINGULAR, UINT32, hw_revision, 3) \
X(a, STATIC, SINGULAR, STRING, sw_version_string, 6)
#define KeepAlive_CALLBACK NULL
#define KeepAlive_DEFAULT NULL
#define Telemetry_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, cp_voltage_hi, 1) \
X(a, STATIC, SINGULAR, UINT32, cp_voltage_lo, 2)
#define Telemetry_CALLBACK NULL
#define Telemetry_DEFAULT NULL
#define PowerMeter_FIELDLIST(X, a) \
X(a, STATIC, SINGULAR, UINT32, time_stamp, 1) \
X(a, STATIC, SINGULAR, FLOAT, vrmsL1, 2) \
X(a, STATIC, SINGULAR, FLOAT, vrmsL2, 3) \
X(a, STATIC, SINGULAR, FLOAT, vrmsL3, 4) \
X(a, STATIC, SINGULAR, FLOAT, irmsL1, 5) \
X(a, STATIC, SINGULAR, FLOAT, irmsL2, 6) \
X(a, STATIC, SINGULAR, FLOAT, irmsL3, 7) \
X(a, STATIC, SINGULAR, FLOAT, irmsN, 8) \
X(a, STATIC, SINGULAR, FLOAT, wattHrL1, 9) \
X(a, STATIC, SINGULAR, FLOAT, wattHrL2, 10) \
X(a, STATIC, SINGULAR, FLOAT, wattHrL3, 11) \
X(a, STATIC, SINGULAR, FLOAT, totalWattHr, 12) \
X(a, STATIC, SINGULAR, FLOAT, tempL1, 13) \
X(a, STATIC, SINGULAR, FLOAT, tempL2, 14) \
X(a, STATIC, SINGULAR, FLOAT, tempL3, 15) \
X(a, STATIC, SINGULAR, FLOAT, tempN, 16) \
X(a, STATIC, SINGULAR, FLOAT, wattL1, 17) \
X(a, STATIC, SINGULAR, FLOAT, wattL2, 18) \
X(a, STATIC, SINGULAR, FLOAT, wattL3, 19) \
X(a, STATIC, SINGULAR, FLOAT, freqL1, 20) \
X(a, STATIC, SINGULAR, FLOAT, freqL2, 21) \
X(a, STATIC, SINGULAR, FLOAT, freqL3, 22) \
X(a, STATIC, SINGULAR, BOOL, phaseSeqError, 23)
#define PowerMeter_CALLBACK NULL
#define PowerMeter_DEFAULT NULL
extern const pb_msgdesc_t EverestToMcu_msg;
extern const pb_msgdesc_t McuToEverest_msg;
extern const pb_msgdesc_t ErrorFlags_msg;
extern const pb_msgdesc_t KeepAliveLo_msg;
extern const pb_msgdesc_t KeepAlive_msg;
extern const pb_msgdesc_t Telemetry_msg;
extern const pb_msgdesc_t PowerMeter_msg;
/* Defines for backwards compatibility with code written before nanopb-0.4.0 */
#define EverestToMcu_fields &EverestToMcu_msg
#define McuToEverest_fields &McuToEverest_msg
#define ErrorFlags_fields &ErrorFlags_msg
#define KeepAliveLo_fields &KeepAliveLo_msg
#define KeepAlive_fields &KeepAlive_msg
#define Telemetry_fields &Telemetry_msg
#define PowerMeter_fields &PowerMeter_msg
/* Maximum encoded size of messages (where known) */
#define ADKIT_PB_H_MAX_SIZE McuToEverest_size
#define ErrorFlags_size 12
#define EverestToMcu_size 73
#define KeepAliveLo_size 106
#define KeepAlive_size 70
#define McuToEverest_size 124
#define PowerMeter_size 121
#define Telemetry_size 12
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif

View File

@@ -0,0 +1,123 @@
syntax = "proto3";
/*
This container message is send from EVerest to MCU and may contain any allowed message in that direction.
*/
message EverestToMcu {
// Please keep IDs and don't use reserved IDs to maintain support with older firmware versions
reserved 16, 102, 106;
oneof payload {
KeepAlive keep_alive = 100;
uint32 pwm_duty_cycle = 103; // in 0.01 %, 0 = State F, 10000 = X1
bool allow_power_on = 104;
bool reset = 105;
}
}
/*
This container message is send from MCU to EVerest and may contain any allowed message in that direction.
*/
message McuToEverest {
// Please keep IDs and don't use reserved IDs to maintain support with older firmware versions
reserved 107;
oneof payload {
KeepAliveLo keep_alive = 3;
ResetReason reset = 101;
CpState cp_state = 102;
bool relais_state = 103; // false: relais are off, true: relais are on
ErrorFlags error_flags = 104;
Telemetry telemetry = 105;
PpState pp_state = 106;
PowerMeter power_meter = 108;
}
}
enum CpState {
STATE_A = 0;
STATE_B = 1;
STATE_C = 2;
STATE_D = 3;
STATE_E = 4;
STATE_F = 5;
}
message ErrorFlags {
// Please keep IDs and don't use reserved IDs to maintain support with older firmware versions
reserved 5;
bool diode_fault = 1;
bool rcd_selftest_failed = 2;
bool rcd_triggered = 3;
bool ventilation_not_available = 4;
bool cp_signal_fault = 6;
bool over_current = 7;
}
enum ResetReason {
USER = 0;
WATCHDOG = 1;
}
message KeepAliveLo {
uint32 time_stamp = 1;
uint32 hw_type = 2;
uint32 hw_revision = 3;
uint32 protocol_version_major = 4;
uint32 protocol_version_minor = 5;
string sw_version_string = 6;
float hwcap_max_current = 7;
float hwcap_min_current = 8;
uint32 hwcap_max_phase_count = 9;
uint32 hwcap_min_phase_count = 10;
bool supports_changing_phases_during_charging = 11;
}
message KeepAlive {
uint32 time_stamp = 1;
uint32 hw_type = 2;
uint32 hw_revision = 3;
string sw_version_string = 6;
}
message Telemetry {
uint32 cp_voltage_hi = 1;
uint32 cp_voltage_lo = 2;
}
enum PpState {
STATE_NC = 0;
STATE_13A = 1;
STATE_20A = 2;
STATE_32A = 3;
STATE_70A = 4;
STATE_FAULT = 5;
}
message PowerMeter {
uint32 time_stamp = 1;
float vrmsL1 = 2;
float vrmsL2 = 3;
float vrmsL3 = 4;
float irmsL1 = 5;
float irmsL2 = 6;
float irmsL3 = 7;
float irmsN = 8;
float wattHrL1 = 9;
float wattHrL2 = 10;
float wattHrL3 = 11;
float totalWattHr = 12;
float tempL1 = 13;
float tempL2 = 14;
float tempL3 = 15;
float tempN = 16;
float wattL1 = 17;
float wattL2 = 18;
float wattL3 = 19;
float freqL1 = 20;
float freqL2 = 21;
float freqL3 = 22;
bool phaseSeqError = 23;
}

View File

@@ -0,0 +1,2 @@
#!/bin/sh
nanopb_generator.py -L "#include <everest/3rd_party/nanopb/%s>" -I . -D . adkit.proto

View File

@@ -0,0 +1,200 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
// Portions (c) 2025 Analog Devices Inc.
#include "evse_board_supportImpl.hpp"
namespace module {
namespace board_support {
static types::board_support_common::BspEvent cast_event_type(CpState cp_state) {
types::board_support_common::BspEvent event;
switch (cp_state) {
case CpState_STATE_A:
event.event = types::board_support_common::Event::A;
break;
case CpState_STATE_B:
event.event = types::board_support_common::Event::B;
break;
case CpState_STATE_C:
event.event = types::board_support_common::Event::C;
break;
case CpState_STATE_D:
event.event = types::board_support_common::Event::D;
break;
case CpState_STATE_E:
event.event = types::board_support_common::Event::E;
break;
case CpState_STATE_F:
event.event = types::board_support_common::Event::F;
break;
}
return event;
}
static types::board_support_common::BspEvent cast_event_type(bool relais_state) {
types::board_support_common::BspEvent event;
if (relais_state) {
event.event = types::board_support_common::Event::PowerOn;
} else {
event.event = types::board_support_common::Event::PowerOff;
}
return event;
}
static types::board_support_common::ProximityPilot cast_pp_type(PpState pp_state) {
types::board_support_common::ProximityPilot pp;
switch (pp_state) {
case PpState_STATE_13A:
pp.ampacity = types::board_support_common::Ampacity::A_13;
break;
case PpState_STATE_20A:
pp.ampacity = types::board_support_common::Ampacity::A_20;
break;
case PpState_STATE_32A:
pp.ampacity = types::board_support_common::Ampacity::A_32;
break;
case PpState_STATE_70A:
pp.ampacity = types::board_support_common::Ampacity::A_63_3ph_70_1ph;
break;
case PpState_STATE_FAULT:
pp.ampacity = types::board_support_common::Ampacity::None;
break;
case PpState_STATE_NC:
pp.ampacity = types::board_support_common::Ampacity::None;
break;
}
return pp;
}
void evse_board_supportImpl::init() {
{
std::lock_guard<std::mutex> lock(capsMutex);
caps.min_current_A_import = 6;
caps.max_current_A_import = 16;
caps.min_phase_count_import = 1;
caps.max_phase_count_import = 3;
caps.supports_changing_phases_during_charging = false;
caps.supports_cp_state_E = false;
caps.connector_type = types::evse_board_support::Connector_type::IEC62196Type2Cable;
caps.min_current_A_export = 6;
caps.max_current_A_export = 16;
caps.min_phase_count_export = 1;
caps.max_phase_count_export = 3;
}
mod->serial.signalCPState.connect([this](CpState cp_state) {
if (cp_state not_eq last_cp_state) {
auto event_cp_state = cast_event_type(cp_state);
EVLOG_info << "CP state changed: "
<< types::board_support_common::event_to_string(cast_event_type(last_cp_state).event) << " -> "
<< types::board_support_common::event_to_string(event_cp_state.event);
if (enabled) {
publish_event(event_cp_state);
}
if (cp_state == CpState_STATE_A) {
mod->clear_errors_on_unplug();
}
last_cp_state = cp_state;
}
});
mod->serial.signalRelaisState.connect([this](bool relais_state) {
if (last_relais_state not_eq relais_state) {
publish_event(cast_event_type(relais_state));
last_relais_state = relais_state;
}
});
mod->serial.signalPPState.connect([this](PpState pp_state) {
last_pp = cast_pp_type(pp_state);
publish_ac_pp_ampacity(last_pp);
});
mod->serial.signalKeepAliveLo.connect([this](KeepAliveLo l) {
std::lock_guard<std::mutex> lock(capsMutex);
caps.min_current_A_import =
(mod->config.caps_min_current_A >= 0 ? mod->config.caps_min_current_A : l.hwcap_min_current);
caps.max_current_A_import =
(mod->config.caps_max_current_A >= 0 ? mod->config.caps_max_current_A : l.hwcap_max_current);
caps.min_phase_count_import = l.hwcap_min_phase_count;
caps.max_phase_count_import = l.hwcap_max_phase_count;
caps.min_current_A_export =
(mod->config.caps_min_current_A >= 0 ? mod->config.caps_min_current_A : l.hwcap_min_current);
caps.max_current_A_export =
(mod->config.caps_max_current_A >= 0 ? mod->config.caps_max_current_A : l.hwcap_max_current);
caps.min_phase_count_export = l.hwcap_min_phase_count;
caps.max_phase_count_export = l.hwcap_max_phase_count;
caps.supports_changing_phases_during_charging = l.supports_changing_phases_during_charging;
if (not caps_received) {
EVLOG_info << "AD-ACEVSE22KWZ-KIT Configuration:";
EVLOG_info << " Hardware revision: " << l.hw_revision;
EVLOG_info << " Firmware version: " << l.sw_version_string;
EVLOG_info << " Current Limit: " << l.hwcap_max_current;
}
caps_received = true;
});
}
void evse_board_supportImpl::ready() {
wait_for_caps();
{
// Publish caps once in the beginning
std::lock_guard<std::mutex> lock(capsMutex);
publish_capabilities(caps);
}
}
void evse_board_supportImpl::wait_for_caps() {
// Wait for caps to be received at least once
int i;
for (i = 0; i < 50; i++) {
if (caps_received)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
if (i == 50) {
EVLOG_error << "Did not receive hardware capabilities from AD-ACEVSE22KWZ-KIT hardware, using defaults.";
}
}
void evse_board_supportImpl::handle_pwm_on(double& value) {
mod->serial.setPWM(value * 100);
}
void evse_board_supportImpl::handle_cp_state_X1() {
mod->serial.setPWM(10001);
}
void evse_board_supportImpl::handle_cp_state_F() {
mod->serial.setPWM(0);
}
void evse_board_supportImpl::handle_cp_state_E() {
EVLOG_warning << "Command cp_state_E is not supported. Ignoring command.";
}
void evse_board_supportImpl::handle_allow_power_on(types::evse_board_support::PowerOnOff& value) {
mod->serial.allowPowerOn(value.allow_power_on);
}
void evse_board_supportImpl::handle_ac_set_overcurrent_limit_A(double& value) {
// your code for cmd ac_set_overcurrent_limit_A goes here
}
void evse_board_supportImpl::handle_ac_switch_three_phases_while_charging(bool& value) {
EVLOG_warning << "AdAdEvse22KwzKitBSP doesn't support ac_switch_three_phases_while_charging, ignoring command.";
}
void evse_board_supportImpl::handle_enable(bool& value) {
enabled = true;
// Publish CP state once on enable
publish_event(cast_event_type(last_cp_state));
}
} // namespace board_support
} // namespace module

View File

@@ -0,0 +1,77 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP
#define BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/evse_board_support/Implementation.hpp>
#include "../AdAcEvse22KwzKitBSP.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace board_support {
struct Conf {};
class evse_board_supportImpl : public evse_board_supportImplBase {
public:
evse_board_supportImpl() = delete;
evse_board_supportImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<AdAcEvse22KwzKitBSP>& mod,
Conf& config) :
evse_board_supportImplBase(ev, "board_support"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// command handler functions (virtual)
virtual void handle_enable(bool& value) override;
virtual void handle_pwm_on(double& value) override;
virtual void handle_cp_state_X1() override;
virtual void handle_cp_state_F() override;
virtual void handle_cp_state_E() override;
virtual void handle_allow_power_on(types::evse_board_support::PowerOnOff& value) override;
virtual void handle_ac_switch_three_phases_while_charging(bool& value) override;
virtual void handle_ac_set_overcurrent_limit_A(double& value) override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<AdAcEvse22KwzKitBSP>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
types::evse_board_support::HardwareCapabilities caps;
std::atomic_bool caps_received{false};
std::mutex capsMutex;
CpState last_cp_state{CpState::CpState_STATE_F};
bool last_relais_state{false};
types::board_support_common::ProximityPilot last_pp{types::board_support_common::Ampacity::None};
void wait_for_caps();
std::atomic_bool enabled{false};
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace board_support
} // namespace module
#endif // BOARD_SUPPORT_EVSE_BOARD_SUPPORT_IMPL_HPP

View File

@@ -0,0 +1,222 @@
.. _everest_modules_handwritten_AdAcEvse22KwzKitBSP:
.. ************************
.. AdAcEvse22KwzKitBSP
.. ************************
The module ``AdAcEvse22KwzKitBSP`` is a board support driver for the Analog Devices
`AD-ACEVSE22KWZ-KIT <https://www.analog.com/en/resources/evaluation-hardware-and-software/evaluation-boards-kits/ad-acevse22kwz-kit.html#eb-overview>`_
EVSE reference design.
AdAcEvse22KwzKitBSP Quickstart
==============================
A typical hardware setup would consist of the AD-ACEVSE22KWZ-KIT and a Raspberry Pi 4 running
EVerest with this module. Communication between AD-ACEVSE22KWZ-KIT and the AdAcEvse22KwzKitBSP
on the Raspberry Pi 4 is through a 3.3V TTL UART link with a default configuration of 115200 bps 8N1.
There is also a GPIO used to reset AD-ACEVSE22KWZ-KIT from EVerest so the firmware is in a known state.
Hardware Connectivity
---------------------
By default, AD-ACEVSE22KWZ-KIT supports the Pionix Yak board and Raspberry Pi 4 as host boards running
EVerest.
The simplest way to get AD-ACEVSE22KWZ-KIT up and running with EVerest is with a Pionix Yak board. First,
you must connect AD-ACEVSE22KWZ-KIT's P3 to Yak's J3 using a 10-wire cable. Note that using the 10-wire
cable has Yak powered off AD-ACEVSE22KWZ-KIT's 12V supply.
Next, we need to add the AdAcEvse22KwzKitBSP as an active module inside the configuration YAML file
passed to EVerest:
.. code-block:: yaml
active_modules:
# ** Other EVerest modules **
adacevse22kwz_driver:
telemetry:
id: 1
config_module:
baud_rate: 115200 # default baud rate
reset_gpio: 27 # Yak reset GPIO pin
serial_port: /dev/serial0 # Yak UART0 port
connections: {}
module: AdAcEvse22KwzKitBSP
# ** Other EVerest modules **
Next, we need to link our active AdAcEvse22KwzKitBSP to EvseManager to facilitate high-level charging
logic. This can be done by adding the following to EvseManager inside the EVerest configuration YAML:
.. code-block:: yaml
active_modules:
# ** Other EVerest modules **
evse_manager:
config_module:
# ** Various EvseManager configurations **
connections:
bsp:
- implementation_id: board_support
module_id: adacevse22kwz_driver
powermeter_grid_side:
- implementation_id: powermeter
module_id: adacevse22kwz_driver
module: EvseManager
telemetry:
id: 1
# ** Other EVerest modules **
Finally, we can start EVerest. AD-ACEVSE22KWZ-KIT should now be able to communicate with EVerest
on the Yak board using AdAcEvse22KwzKitBSP.
AD-ACEVSE22KWZ-KIT can also be run off a Raspberry Pi 4 by using various test points on the
AD-ACEVSE22KWZ-KIT instead of the 10-wire cable. These test points can then be connected to
Raspberry Pi pins which can operate as a UART link and a standard GPIO:
.. list-table::
:header-rows: 1
* - Purpose
- Test Point
- RPI Pin
* - Reset Pin
- TP_18
- GPIO27
* - Host TX
- TP_28
- TX_D0
* - Host RX
- TP_41
- RX_D0
* - Shared ground
- Any GND pin
- Any GND pin
You can now follow the steps used for configuring EVerest on the Yak board to start EVerest
on the Raspberry Pi.
Protocol
========
EVerest can send commands to AD-ACEVSE22KWZ-KIT and AD-ACEVSE22KWZ-KIT publishes
data and events back to EVerest. The packets are defined with protobuf to serialize the C structs
into a binary representation that is transferred over the serial wire in a
stream:
https://developers.google.com/protocol-buffers
To be able to split the stream back into packets all data is encoded using COBS
before it is transmitted on the UART:
https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
COBS
----
COBS is implemented in ``adkit_comms/evSerial.cpp``. Whenever a new packet
was extracted from the stream ``handlePacket()`` is called to decode protobuf
and generate the corresponding signals.
Other parts of the module subscribe to these signals to handle the incoming
packets.
For TX ``linkWrite`` encodes the packet with COBS and outputs it to the UART.
Protobuf
--------
The actual packet definitions are located under ``adkit_comms/protobuf``.
``adkit.proto`` contains all messages that can be sent by EVerest and AD-ACEVSE22KWZ-KIT.
Refer to these files for an up-to-date definition as they may change
frequently.
To generate the C code nanopb is used:
``nanopb_generator -I . -D . *.proto``
The output should also be manually copied to AD-ACEVSE22KWZ-KIT Firmware to ensure the same
definition is used on both sides when making changes.
Modes of Operation
-----------------------------
AD-ACEVSE22KWZ-KIT board operates in the following two modes:
``Hostless mode``: AD-ACEVSE22KWZ-KIT acts as a standalone EVSE
and will control PWM and relay state without external influence. In this
mode, PWM will be enabled immediately upon entering state B1 with the
relay closing in state C2 assuming no errors occur. If an error occurs
(i.e. RCD trigger, diode short, C1 timeout, etc.), AD-ACEVSE22KWZ-KIT
will open the relay and disable PWM until state A1 is reentered where
the errors will be cleared.
``Host-driven mode``: AD-ACEVSE22KWZ-KIT will allow EVerest to influence PWM
and relay states. In this mode, PWM will not be enabled until an EVerest
``PwmDutyCycle`` command is received. Similarly, the relay will not open in state
C2 until an ``AllowPowerOn`` message is received. AD-ACEVSE22KWZ-KIT can
override relay and PWM state in the event of an error.
By default, the AD-ACEVSE22KWZ-KIT operates in hostless mode until a message
is received from the host. Additionally, all outbound messages from
AD-ACEVSE22KWZ-KIT are sent irrespective of mode of operation. This enables
AD-ACEVSE22KWZ-KIT evaluation without using EVerest.
Message types
-------------
AD-ACEVSE22KWZ-KIT supports the following set of messages:
EVerest to AD-ACEVSE22KWZ-KIT:
______________________________
``AllowPowerOn(bool)``: Inform AD-ACEVSE22KWZ-KIT that it is allowed to
switch on the power relays/contactors to the car on (true) or must switch
off now (false). The final decision remains with AD-ACEVSE22KWZ-KIT in
case of power on, it should only power on after all other requirements
are met (such as RCD current is below limit, car is in CP state C etc).
On power off, AD-ACEVSE22KWZ-KIT will switch off immediately.
``PwmDutyCycle(uint32)``: Set AD-ACEVSE22KWZ-KIT PWM state and duty
cycle. PWM can be enabled at specific duty cycle by passing a value of
1-10000, where each value corresponds to 0.0001% duty cycle (i.e. 50%
duty cycle = 5000 passed value). AD-ACEVSE22KWZ will ignore any duty cycles
greater than 5333 as this corresponds to the maximum duty cycle supported.
PWM can be disabled by passing a value greater than 10000. PWM state F can
be enabled by passing a PWM value of 0.
``KeepAlive(Message)``: EVerest sends this packet to AD-ACEVSE22KWZ-KIT at 1Hz.
Currently unused by AD-ACEVSE22KWZ-KIT.
``Reset(bool)``: Reset AD-ACEVSE22KWZ-KIT firmware.
AD-ACEVSE22KWZ-KIT to EVerest
-----------------------------
``CpState(enum)``: Notify EVerest of current CP state (A/B/C/D/E/F). Sent upon
state change. AD-ACEVSE22KWZ-KIT currently doesn't support State D.
``RelaisState(bool)``: Notify EVerest of current relay state. Sent upon relay
closing/opening. True corresponds to relay closed and false is sent when relay
is open.
``PpState(enum)``: Notify EVerest of current PP state (NC/13A/20A/32A/70A/F).
AD-ACEVSE22KWZ-KIT currently doesn't support PP for maximum output current
so 32A is sent by default.
``PowerMeter(Message)``: Sent roughly every second when relay is closed.
Contains all data from the ADE9178 power measurement.
``ErrorState(Message)``: Notify EVerest of active errors. Sent when an errors
are set/cleared. Each error has an associated boolean value where true
corresponds to active error and false corresponds to error not active. Currently,
only diode faults, RCD triggered, and overcurrent are supported by
AD-ACEVSE22KWZ-KIT.
``Telemetry(Message)``: Telemetry message with cp pwm high and low voltage
values. Not currently supported by AD-ACEVSE22KWZ-KIT firmware.
``KeepAliveLo(Message)``: AD-ACEVSE22KWZ-KIT sends this every 3 seconds to keep
connection online.
``ResetReason(enum)``: Sent once on boot of the AD-ACEVSE22KWZ-KIT firmware.

View File

@@ -0,0 +1,45 @@
description: Board support package module for the AD-ACEVSE22KWZ-KIT reference design
config:
serial_port:
description: Serial port the AD-ACEVSE22KWZ-KIT is connected to
type: string
default: /dev/ttyUSB0
baud_rate:
description: Serial baud rate to use when communicating with the AD-ACEVSE22KWZ-KIT
type: integer
minimum: 9600
maximum: 230400
default: 115200
reset_gpio_chip:
description: >-
Reset GPIO chip to use to HW reset the AD-ACEVSE22KWZ-KIT. If set to empty string, it is disabled.
type: string
default: 'gpiochip0'
reset_gpio:
description: GPIO line to use to reset AD-ACEVSE22KWZ-KIT
type: integer
default: 27
caps_min_current_A:
description: Minimal current on AC side. For AC this is typically 6, but for HLC this can be less. -1 means use limit reported by HW.
type: integer
default: -1
caps_max_current_A:
description: Maximum current on AC side. For AC this is typically 16 or 32, but for HLC this can be less. -1 means use limit reported by HW.
type: integer
default: -1
provides:
powermeter:
interface: powermeter
description: provides the AD-ACEVSE22KWZ-KIT Internal Power Meter
board_support:
interface: evse_board_support
description: provides the board support Interface to low level control control pilot, relais, motor lock
enable_telemetry: true
metadata:
license: https://opensource.org/licenses/Apache-2.0
authors:
- Cornelius Claussen
- Kai-Uwe Hermann
- Thilo Molitor
- Anton Wöllert
- Ryan Wiebe

View File

@@ -0,0 +1,73 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020 - 2021 Pionix GmbH and Contributors to EVerest
// Portions (c) 2025 Analog Devices Inc.
#include "powermeterImpl.hpp"
#include <utils/date.hpp>
namespace module {
namespace powermeter {
static types::powermeter::Powermeter adacevse22kwzkit_to_everest(const PowerMeter& p) {
types::powermeter::Powermeter j;
j.timestamp = Everest::Date::to_rfc3339(date::utc_clock::now());
j.meter_id = "AD_ACEVSE22KWZ_KIT_POWERMETER";
j.phase_seq_error = p.phaseSeqError;
j.energy_Wh_import.total = p.totalWattHr;
j.energy_Wh_import.L1 = p.wattHrL1;
j.energy_Wh_import.L2 = p.wattHrL2;
j.energy_Wh_import.L3 = p.wattHrL3;
types::units::Power pwr;
pwr.total = p.wattL1 + p.wattL2 + p.wattL3;
pwr.L1 = p.wattL1;
pwr.L2 = p.wattL2;
pwr.L3 = p.wattL3;
j.power_W = pwr;
types::units::Voltage volt;
volt.L1 = p.vrmsL1;
volt.L2 = p.vrmsL2;
volt.L3 = p.vrmsL3;
j.voltage_V = volt;
types::units::Current amp;
amp.L1 = p.irmsL1;
amp.L2 = p.irmsL2;
amp.L3 = p.irmsL3;
amp.N = p.irmsN;
j.current_A = amp;
types::units::Frequency freq;
freq.L1 = p.freqL1;
freq.L2 = p.freqL2;
freq.L3 = p.freqL3;
j.frequency_Hz = freq;
return j;
}
void powermeterImpl::init() {
mod->serial.signalPowerMeter.connect(
[this](const PowerMeter& p) { publish_powermeter(adacevse22kwzkit_to_everest(p)); });
}
void powermeterImpl::ready() {
}
types::powermeter::TransactionStopResponse powermeterImpl::handle_stop_transaction(std::string& transaction_id) {
return {types::powermeter::TransactionRequestStatus::NOT_SUPPORTED,
{},
{},
"AdAcEvse22KwzKitBSP powermeter does not support the stop_transaction command"};
};
types::powermeter::TransactionStartResponse
powermeterImpl::handle_start_transaction(types::powermeter::TransactionReq& value) {
return {types::powermeter::TransactionRequestStatus::NOT_SUPPORTED,
"AdAcEvse22KwzKitBSP powermeter does not support the start_transaction command"};
}
} // namespace powermeter
} // namespace module

View File

@@ -0,0 +1,63 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#ifndef POWERMETER_POWERMETER_IMPL_HPP
#define POWERMETER_POWERMETER_IMPL_HPP
//
// AUTO GENERATED - MARKED REGIONS WILL BE KEPT
// template version 3
//
#include <generated/interfaces/powermeter/Implementation.hpp>
#include "../AdAcEvse22KwzKitBSP.hpp"
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
// insert your custom include headers here
// ev@75ac1216-19eb-4182-a85c-820f1fc2c091:v1
namespace module {
namespace powermeter {
struct Conf {};
class powermeterImpl : public powermeterImplBase {
public:
powermeterImpl() = delete;
powermeterImpl(Everest::ModuleAdapter* ev, const Everest::PtrContainer<AdAcEvse22KwzKitBSP>& mod, Conf& config) :
powermeterImplBase(ev, "powermeter"), mod(mod), config(config){};
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
// insert your public definitions here
// ev@8ea32d28-373f-4c90-ae5e-b4fcc74e2a61:v1
protected:
// command handler functions (virtual)
virtual types::powermeter::TransactionStartResponse
handle_start_transaction(types::powermeter::TransactionReq& value) override;
virtual types::powermeter::TransactionStopResponse handle_stop_transaction(std::string& transaction_id) override;
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
// insert your protected definitions here
// ev@d2d1847a-7b88-41dd-ad07-92785f06f5c4:v1
private:
const Everest::PtrContainer<AdAcEvse22KwzKitBSP>& mod;
const Conf& config;
virtual void init() override;
virtual void ready() override;
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
// insert your private definitions here
// ev@3370e4dd-95f4-47a9-aaec-ea76f34a66c9:v1
};
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
// insert other definitions here
// ev@3d7da0ad-02c2-493d-9920-0bbbd56b9876:v1
} // namespace powermeter
} // namespace module
#endif // POWERMETER_POWERMETER_IMPL_HPP